summaryrefslogtreecommitdiff
path: root/src/parser
diff options
context:
space:
mode:
Diffstat (limited to 'src/parser')
-rw-r--r--src/parser/contexts.h37
-rw-r--r--src/parser/parsers.h192
2 files changed, 206 insertions, 23 deletions
diff --git a/src/parser/contexts.h b/src/parser/contexts.h
index ed4d1283c..adb7694f6 100644
--- a/src/parser/contexts.h
+++ b/src/parser/contexts.h
@@ -308,8 +308,8 @@ struct NullInstrParserCtx {
MemoryIdxT getMemoryFromName(Name) { return Ok{}; }
DataIdxT getDataFromIdx(uint32_t) { return Ok{}; }
DataIdxT getDataFromName(Name) { return Ok{}; }
- LabelIdxT getLabelFromIdx(uint32_t) { return Ok{}; }
- LabelIdxT getLabelFromName(Name) { return Ok{}; }
+ LabelIdxT getLabelFromIdx(uint32_t, bool) { return Ok{}; }
+ LabelIdxT getLabelFromName(Name, bool) { return Ok{}; }
TagIdxT getTagFromIdx(uint32_t) { return Ok{}; }
TagIdxT getTagFromName(Name) { return Ok{}; }
@@ -328,6 +328,13 @@ struct NullInstrParserCtx {
Result<> makeLoop(Index, std::optional<Name>, BlockTypeT) {
return Ok{};
}
+ template<typename BlockTypeT>
+ Result<> makeTry(Index, std::optional<Name>, BlockTypeT) {
+ return Ok{};
+ }
+ Result<> visitCatch(Index, TagIdxT) { return Ok{}; }
+ Result<> visitCatchAll(Index) { return Ok{}; }
+ Result<> visitDelegate(Index, LabelIdxT) { return Ok{}; }
Result<> visitEnd() { return Ok{}; }
Result<> makeUnreachable(Index) { return Ok{}; }
@@ -1020,10 +1027,10 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
return name;
}
- Result<Index> getLabelFromIdx(uint32_t idx) { return idx; }
+ Result<Index> getLabelFromIdx(uint32_t idx, bool) { return idx; }
- Result<Index> getLabelFromName(Name name) {
- return irBuilder.getLabelIndex(name);
+ Result<Index> getLabelFromName(Name name, bool inDelegate) {
+ return irBuilder.getLabelIndex(name, inDelegate);
}
Result<Name> getTagFromIdx(uint32_t idx) {
@@ -1109,6 +1116,26 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
irBuilder.makeLoop(label ? *label : Name{}, type.getSignature().results));
}
+ Result<> makeTry(Index pos, std::optional<Name> label, HeapType type) {
+ // TODO: validate labels?
+ // TODO: Move error on input types to here?
+ return withLoc(
+ pos,
+ irBuilder.makeTry(label ? *label : Name{}, type.getSignature().results));
+ }
+
+ Result<> visitCatch(Index pos, Name tag) {
+ return withLoc(pos, irBuilder.visitCatch(tag));
+ }
+
+ Result<> visitCatchAll(Index pos) {
+ return withLoc(pos, irBuilder.visitCatchAll());
+ }
+
+ Result<> visitDelegate(Index pos, Index label) {
+ return withLoc(pos, irBuilder.visitDelegate(label));
+ }
+
Result<> visitEnd() { return withLoc(irBuilder.visitEnd()); }
Result<> makeUnreachable(Index pos) {
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");
}