summaryrefslogtreecommitdiff
path: root/src/parser/parsers.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/parser/parsers.h')
-rw-r--r--src/parser/parsers.h192
1 files changed, 174 insertions, 18 deletions
diff --git a/src/parser/parsers.h b/src/parser/parsers.h
index 9201314dc..f16cca2b5 100644
--- a/src/parser/parsers.h
+++ b/src/parser/parsers.h
@@ -56,6 +56,7 @@ template<typename Ctx> Result<typename Ctx::BlockTypeT> blocktype(Ctx&);
template<typename Ctx> MaybeResult<> block(Ctx&, bool);
template<typename Ctx> MaybeResult<> ifelse(Ctx&, bool);
template<typename Ctx> MaybeResult<> loop(Ctx&, bool);
+template<typename Ctx> MaybeResult<> trycatch(Ctx&, bool);
template<typename Ctx> Result<> makeUnreachable(Ctx&, Index);
template<typename Ctx> Result<> makeNop(Ctx&, Index);
template<typename Ctx> Result<> makeBinary(Ctx&, Index, BinaryOp op);
@@ -113,9 +114,6 @@ template<typename Ctx> Result<> makeTableSize(Ctx&, Index);
template<typename Ctx> Result<> makeTableGrow(Ctx&, Index);
template<typename Ctx> Result<> makeTableFill(Ctx&, Index);
template<typename Ctx> Result<> makeTableCopy(Ctx&, Index);
-template<typename Ctx> Result<> makeTry(Ctx&, Index);
-template<typename Ctx>
-Result<> makeTryOrCatchBody(Ctx&, Index, Type type, bool isTry);
template<typename Ctx> Result<> makeThrow(Ctx&, Index);
template<typename Ctx> Result<> makeRethrow(Ctx&, Index);
template<typename Ctx> Result<> makeTupleMake(Ctx&, Index);
@@ -173,7 +171,8 @@ template<typename Ctx> Result<typename Ctx::MemoryIdxT> memidx(Ctx&);
template<typename Ctx> MaybeResult<typename Ctx::MemoryIdxT> maybeMemuse(Ctx&);
template<typename Ctx> Result<typename Ctx::GlobalIdxT> globalidx(Ctx&);
template<typename Ctx> Result<typename Ctx::LocalIdxT> localidx(Ctx&);
-template<typename Ctx> Result<typename Ctx::LabelIdxT> labelidx(Ctx&);
+template<typename Ctx>
+Result<typename Ctx::LabelIdxT> labelidx(Ctx&, bool inDelegate = false);
template<typename Ctx> Result<typename Ctx::TagIdxT> tagidx(Ctx&);
template<typename Ctx> Result<typename Ctx::TypeUseT> typeuse(Ctx&);
MaybeResult<ImportNames> inlineImport(ParseInput&);
@@ -573,6 +572,9 @@ template<typename Ctx> MaybeResult<> foldedBlockinstr(Ctx& ctx) {
if (auto i = loop(ctx, true)) {
return i;
}
+ if (auto i = trycatch(ctx, true)) {
+ return i;
+ }
// TODO: Other block instructions
return {};
}
@@ -587,6 +589,9 @@ template<typename Ctx> MaybeResult<> unfoldedBlockinstr(Ctx& ctx) {
if (auto i = loop(ctx, false)) {
return i;
}
+ if (auto i = trycatch(ctx, false)) {
+ return i;
+ }
// TODO: Other block instructions
return {};
}
@@ -619,7 +624,9 @@ template<typename Ctx> MaybeResult<> instr(Ctx& ctx) {
// Check for valid strings that are not instructions.
if (auto tok = ctx.in.peek()) {
if (auto keyword = tok->getKeyword()) {
- if (keyword == "end"sv || keyword == "then"sv || keyword == "else"sv) {
+ if (keyword == "end"sv || keyword == "then"sv || keyword == "else"sv ||
+ keyword == "catch"sv || keyword == "catch_all"sv ||
+ keyword == "delegate"sv) {
return {};
}
}
@@ -860,7 +867,7 @@ template<typename Ctx> MaybeResult<> ifelse(Ctx& ctx, bool folded) {
return ctx.visitEnd();
}
-// loop ::= 'loop' label blocktype instr* end id?
+// loop ::= 'loop' label blocktype instr* 'end' id?
// | '(' 'loop' label blocktype instr* ')'
template<typename Ctx> MaybeResult<> loop(Ctx& ctx, bool folded) {
auto pos = ctx.in.getPos();
@@ -895,6 +902,163 @@ template<typename Ctx> MaybeResult<> loop(Ctx& ctx, bool folded) {
return ctx.visitEnd();
}
+// trycatch ::= 'try' label blocktype instr* ('catch' id? tagidx instr*)*
+// ('catch_all' id? instr*)? 'end' id?
+// | '(' 'try' label blocktype '(' 'do' instr* ')'
+// ('(' 'catch' tagidx instr* ')')*
+// ('(' 'catch_all' instr* ')')? ')'
+// | 'try' label blocktype instr* 'deledate' label
+// | '(' 'try' label blocktype '(' 'do' instr* ')'
+// '(' 'delegate' label ')' ')'
+template<typename Ctx> MaybeResult<> trycatch(Ctx& ctx, bool folded) {
+ auto pos = ctx.in.getPos();
+
+ if ((folded && !ctx.in.takeSExprStart("try"sv)) ||
+ (!folded && !ctx.in.takeKeyword("try"sv))) {
+ return {};
+ }
+
+ auto label = ctx.in.takeID();
+
+ auto type = blocktype(ctx);
+ CHECK_ERR(type);
+
+ CHECK_ERR(ctx.makeTry(pos, label, *type));
+
+ if (folded) {
+ if (!ctx.in.takeSExprStart("do"sv)) {
+ return ctx.in.err("expected 'do' in try");
+ }
+ }
+
+ CHECK_ERR(instrs(ctx));
+
+ if (folded) {
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected ')' at end of do");
+ }
+ }
+
+ if ((folded && ctx.in.takeSExprStart("delegate")) ||
+ (!folded && ctx.in.takeKeyword("delegate"))) {
+ auto delegatePos = ctx.in.getPos();
+
+ auto label = labelidx(ctx, true);
+ CHECK_ERR(label);
+
+ if (folded) {
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected ')' at end of delegate");
+ }
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected ')' at end of try");
+ }
+ }
+
+ CHECK_ERR(ctx.visitDelegate(delegatePos, *label));
+ return Ok{};
+ }
+
+ while (true) {
+ auto catchPos = ctx.in.getPos();
+
+ if ((folded && !ctx.in.takeSExprStart("catch"sv)) ||
+ (!folded && !ctx.in.takeKeyword("catch"sv))) {
+ break;
+ }
+
+ // It can be ambiguous whether the name after `catch` is intended to be the
+ // optional ID or the tag identifier. For example:
+ //
+ // (tag $t)
+ // (func $ambiguous
+ // try $t
+ // catch $t
+ // end
+ // )
+ //
+ // When parsing the `catch`, the parser first tries to parse an optional ID
+ // that must match the label of the `try`, and it succeeds because it sees
+ // `$t` after the catch. However, when it then tries to parse the mandatory
+ // tag index, it fails because the next token is `end`. The problem is that
+ // the `$t` after the `catch` was the tag name and there was no optional ID
+ // after all. The parser sets `parseID = false` and resets to just after the
+ // `catch`, and now it skips parsing the optional ID so it correctly parses
+ // the `$t` as a tag name.
+ bool parseID = !folded;
+ auto afterCatchPos = ctx.in.getPos();
+ while (true) {
+ if (!folded && parseID) {
+ auto id = ctx.in.takeID();
+ if (id && id != label) {
+ // Instead of returning an error, retry without the ID.
+ parseID = false;
+ ctx.in.lexer.setIndex(afterCatchPos);
+ continue;
+ }
+ }
+
+ auto tag = tagidx(ctx);
+ if (parseID && tag.getErr()) {
+ // Instead of returning an error, retry without the ID.
+ parseID = false;
+ ctx.in.lexer.setIndex(afterCatchPos);
+ continue;
+ }
+ CHECK_ERR(tag);
+
+ CHECK_ERR(ctx.visitCatch(catchPos, *tag));
+
+ CHECK_ERR(instrs(ctx));
+
+ if (folded) {
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected ')' at end of catch");
+ }
+ }
+ break;
+ }
+ }
+
+ if ((folded && ctx.in.takeSExprStart("catch_all"sv)) ||
+ (!folded && ctx.in.takeKeyword("catch_all"sv))) {
+ auto catchPos = ctx.in.getPos();
+
+ if (!folded) {
+ auto id = ctx.in.takeID();
+ if (id && id != label) {
+ return ctx.in.err("catch_all label does not match try label");
+ }
+ }
+
+ CHECK_ERR(ctx.visitCatchAll(catchPos));
+
+ CHECK_ERR(instrs(ctx));
+
+ if (folded) {
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected ')' at end of catch_all");
+ }
+ }
+ }
+
+ if (folded) {
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected ')' at end of try");
+ }
+ } else {
+ if (!ctx.in.takeKeyword("end"sv)) {
+ return ctx.in.err("expected 'end' at end of try");
+ }
+
+ auto id = ctx.in.takeID();
+ if (id && id != label) {
+ return ctx.in.err("end label does not match try label");
+ }
+ }
+ return ctx.visitEnd();
+}
+
template<typename Ctx> Result<> makeUnreachable(Ctx& ctx, Index pos) {
return ctx.makeUnreachable(pos);
}
@@ -1266,15 +1430,6 @@ template<typename Ctx> Result<> makeTableCopy(Ctx& ctx, Index pos) {
return ctx.in.err("unimplemented instruction");
}
-template<typename Ctx> Result<> makeTry(Ctx& ctx, Index pos) {
- return ctx.in.err("unimplemented instruction");
-}
-
-template<typename Ctx>
-Result<> makeTryOrCatchBody(Ctx& ctx, Index pos, Type type, bool isTry) {
- return ctx.in.err("unimplemented instruction");
-}
-
template<typename Ctx> Result<> makeThrow(Ctx& ctx, Index pos) {
auto tag = tagidx(ctx);
CHECK_ERR(tag);
@@ -1621,12 +1776,13 @@ template<typename Ctx> Result<typename Ctx::LocalIdxT> localidx(Ctx& ctx) {
// labelidx ::= x:u32 => x
// | v:id => x (if labels[x] = v)
-template<typename Ctx> Result<typename Ctx::LabelIdxT> labelidx(Ctx& ctx) {
+template<typename Ctx>
+Result<typename Ctx::LabelIdxT> labelidx(Ctx& ctx, bool inDelegate) {
if (auto x = ctx.in.takeU32()) {
- return ctx.getLabelFromIdx(*x);
+ return ctx.getLabelFromIdx(*x, inDelegate);
}
if (auto id = ctx.in.takeID()) {
- return ctx.getLabelFromName(*id);
+ return ctx.getLabelFromName(*id, inDelegate);
}
return ctx.in.err("expected label index or identifier");
}