diff --git a/src/parser/input-impl.h b/src/parser/input-impl.h index 35a39b2f3e2..7ee358f128c 100644 --- a/src/parser/input-impl.h +++ b/src/parser/input-impl.h @@ -257,6 +257,16 @@ inline bool ParseInput::takeSExprStart(std::string_view expected) { return false; } +inline bool ParseInput::peekSExprStart(std::string_view expected) { + auto original = lexer; + if (!takeLParen()) { + return false; + } + bool ret = takeKeyword(expected); + lexer = original; + return ret; +} + inline Index ParseInput::getPos() { if (auto t = peek()) { return lexer.getIndex() - t->span.size(); diff --git a/src/parser/input.h b/src/parser/input.h index 5c7c57d2005..5cb2bd4717f 100644 --- a/src/parser/input.h +++ b/src/parser/input.h @@ -62,6 +62,7 @@ struct ParseInput { std::optional takeString(); std::optional takeName(); bool takeSExprStart(std::string_view expected); + bool peekSExprStart(std::string_view expected); Index getPos(); [[nodiscard]] Err err(Index pos, std::string reason); diff --git a/src/parser/parsers.h b/src/parser/parsers.h index f36f5ccfb6f..2dd0d9eb453 100644 --- a/src/parser/parsers.h +++ b/src/parser/parsers.h @@ -47,8 +47,9 @@ template MaybeResult<> unfoldedBlockinstr(Ctx&); template MaybeResult<> blockinstr(Ctx&); template MaybeResult<> plaininstr(Ctx&); template MaybeResult<> instr(Ctx&); -template Result<> foldedinstrs(Ctx&); +template MaybeResult<> foldedinstr(Ctx&); template Result<> instrs(Ctx&); +template Result<> foldedinstrs(Ctx&); template Result expr(Ctx&); template Result memarg(Ctx&, uint32_t); template Result blocktype(Ctx&); @@ -600,88 +601,95 @@ template MaybeResult<> instr(Ctx& ctx) { } } } - if (auto i = blockinstr(ctx)) { - return i; + if (auto inst = blockinstr(ctx)) { + return inst; } - if (auto i = plaininstr(ctx)) { - return i; + if (auto inst = plaininstr(ctx)) { + return inst; } // TODO: Handle folded plain instructions as well. return {}; } -template Result<> foldedinstrs(Ctx& ctx) { - if (auto blockinst = foldedBlockinstr(ctx)) { - CHECK_ERR(blockinst); - return Ok{}; +template MaybeResult<> foldedinstr(Ctx& ctx) { + // Check for valid strings that are not instructions. + if (ctx.in.peekSExprStart("then"sv) || ctx.in.peekSExprStart("else")) { + return {}; + } + if (auto inst = foldedBlockinstr(ctx)) { + return inst; + } + if (!ctx.in.takeLParen()) { + return {}; } - // Parse an arbitrary number of folded instructions. - if (ctx.in.takeLParen()) { - // A stack of (start, end) position pairs defining the positions of - // instructions that need to be parsed after their folded children. - std::vector>> foldedInstrs; - - // Begin a folded instruction. Push its start position and a placeholder - // end position. - foldedInstrs.push_back({ctx.in.getPos(), {}}); - while (!foldedInstrs.empty()) { - // Consume everything up to the next paren. This span will be parsed as - // an instruction later after its folded children have been parsed. - if (!ctx.in.takeUntilParen()) { - return ctx.in.err(foldedInstrs.back().first, - "unterminated folded instruction"); - } - if (!foldedInstrs.back().second) { - // The folded instruction we just started should end here. - foldedInstrs.back().second = ctx.in.getPos(); - } + // A stack of (start, end) position pairs defining the positions of + // instructions that need to be parsed after their folded children. + std::vector>> foldedInstrs; - // We have either the start of a new folded child or the end of the last - // one. - if (auto blockinst = foldedBlockinstr(ctx)) { - CHECK_ERR(blockinst); - } else if (ctx.in.takeLParen()) { - foldedInstrs.push_back({ctx.in.getPos(), {}}); - } else if (ctx.in.takeRParen()) { - auto [start, end] = foldedInstrs.back(); - assert(end && "Should have found end of instruction"); - foldedInstrs.pop_back(); - - WithPosition with(ctx, start); - if (auto inst = plaininstr(ctx)) { - CHECK_ERR(inst); - } else { - return ctx.in.err(start, "expected folded instruction"); - } + // Begin a folded instruction. Push its start position and a placeholder + // end position. + foldedInstrs.push_back({ctx.in.getPos(), {}}); + while (!foldedInstrs.empty()) { + // Consume everything up to the next paren. This span will be parsed as + // an instruction later after its folded children have been parsed. + if (!ctx.in.takeUntilParen()) { + return ctx.in.err(foldedInstrs.back().first, + "unterminated folded instruction"); + } - if (ctx.in.getPos() != *end) { - return ctx.in.err("expected end of instruction"); - } + if (!foldedInstrs.back().second) { + // The folded instruction we just started should end here. + foldedInstrs.back().second = ctx.in.getPos(); + } + + // We have either the start of a new folded child or the end of the last + // one. + if (auto blockinst = foldedBlockinstr(ctx)) { + CHECK_ERR(blockinst); + } else if (ctx.in.takeLParen()) { + foldedInstrs.push_back({ctx.in.getPos(), {}}); + } else if (ctx.in.takeRParen()) { + auto [start, end] = foldedInstrs.back(); + assert(end && "Should have found end of instruction"); + foldedInstrs.pop_back(); + + WithPosition with(ctx, start); + if (auto inst = plaininstr(ctx)) { + CHECK_ERR(inst); } else { - WASM_UNREACHABLE("expected paren"); + return ctx.in.err(start, "expected folded instruction"); + } + + if (ctx.in.getPos() != *end) { + return ctx.in.err("expected end of instruction"); } + } else { + WASM_UNREACHABLE("expected paren"); } - return Ok{}; } - return ctx.in.err("expected folded instruction"); + return Ok{}; } template Result<> instrs(Ctx& ctx) { while (true) { - // Try to parse a folded instruction tree. - if (!foldedinstrs(ctx).getErr()) { + if (auto inst = instr(ctx)) { + CHECK_ERR(inst); continue; } - - // Otherwise parse a non-folded instruction. - if (auto inst = instr(ctx)) { + if (auto inst = foldedinstr(ctx)) { CHECK_ERR(inst); - } else { - break; + continue; } + break; } + return Ok{}; +} +template Result<> foldedinstrs(Ctx& ctx) { + while (auto inst = foldedinstr(ctx)) { + CHECK_ERR(inst); + } return Ok{}; } @@ -770,7 +778,7 @@ template MaybeResult<> block(Ctx& ctx, bool folded) { } // if ::= 'if' label blocktype instr1* ('else' id1? instr2*)? 'end' id2? -// | '(' 'if' label blocktype instr* '(' 'then' instr1* ')' +// | '(' 'if' label blocktype foldedinstr* '(' 'then' instr1* ')' // ('(' 'else' instr2* ')')? ')' template MaybeResult<> ifelse(Ctx& ctx, bool folded) { auto pos = ctx.in.getPos(); diff --git a/test/lit/wat-kitchen-sink.wast b/test/lit/wat-kitchen-sink.wast index 375b8c99314..45d2c5d960f 100644 --- a/test/lit/wat-kitchen-sink.wast +++ b/test/lit/wat-kitchen-sink.wast @@ -1119,6 +1119,26 @@ ) ) +;; CHECK: (func $if-else-atypical-condition (type $void) +;; CHECK-NEXT: (if +;; CHECK-NEXT: (i32.const 0) +;; CHECK-NEXT: (nop) +;; CHECK-NEXT: (nop) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (if +;; CHECK-NEXT: (i32.eqz +;; CHECK-NEXT: (i32.const 0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (nop) +;; CHECK-NEXT: (nop) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +(func $if-else-atypical-condition + i32.const 0 + (if (then) (else)) + (if (i32.const 0) (i32.eqz) (then) (else)) +) + ;; CHECK: (func $if-else-mixed (type $void) ;; CHECK-NEXT: (if ;; CHECK-NEXT: (if (result i32)