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.h2036
1 files changed, 2036 insertions, 0 deletions
diff --git a/src/parser/parsers.h b/src/parser/parsers.h
new file mode 100644
index 000000000..5f9f23a2a
--- /dev/null
+++ b/src/parser/parsers.h
@@ -0,0 +1,2036 @@
+/*
+ * Copyright 2023 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef parser_parsers_h
+#define parser_parsers_h
+
+#include "common.h"
+#include "input.h"
+
+namespace wasm::WATParser {
+
+using namespace std::string_view_literals;
+
+// Types
+template<typename Ctx> Result<typename Ctx::HeapTypeT> heaptype(Ctx&);
+template<typename Ctx> MaybeResult<typename Ctx::RefTypeT> reftype(Ctx&);
+template<typename Ctx> Result<typename Ctx::TypeT> valtype(Ctx&);
+template<typename Ctx> MaybeResult<typename Ctx::ParamsT> params(Ctx&);
+template<typename Ctx> MaybeResult<typename Ctx::ResultsT> results(Ctx&);
+template<typename Ctx> MaybeResult<typename Ctx::SignatureT> functype(Ctx&);
+template<typename Ctx> Result<typename Ctx::FieldT> storagetype(Ctx&);
+template<typename Ctx> Result<typename Ctx::FieldT> fieldtype(Ctx&);
+template<typename Ctx> Result<typename Ctx::FieldsT> fields(Ctx&);
+template<typename Ctx> MaybeResult<typename Ctx::StructT> structtype(Ctx&);
+template<typename Ctx> MaybeResult<typename Ctx::ArrayT> arraytype(Ctx&);
+template<typename Ctx> Result<typename Ctx::LimitsT> limits32(Ctx&);
+template<typename Ctx> Result<typename Ctx::LimitsT> limits64(Ctx&);
+template<typename Ctx> Result<typename Ctx::MemTypeT> memtype(Ctx&);
+template<typename Ctx> Result<typename Ctx::GlobalTypeT> globaltype(Ctx&);
+
+// Instructions
+template<typename Ctx> MaybeResult<typename Ctx::InstrT> foldedBlockinstr(Ctx&);
+template<typename Ctx>
+MaybeResult<typename Ctx::InstrT> unfoldedBlockinstr(Ctx&);
+template<typename Ctx> MaybeResult<typename Ctx::InstrT> blockinstr(Ctx&);
+template<typename Ctx> MaybeResult<typename Ctx::InstrT> plaininstr(Ctx&);
+template<typename Ctx> MaybeResult<typename Ctx::InstrT> instr(Ctx&);
+template<typename Ctx> Result<typename Ctx::InstrsT> instrs(Ctx&);
+template<typename Ctx> Result<typename Ctx::ExprT> expr(Ctx&);
+template<typename Ctx> Result<typename Ctx::MemargT> memarg(Ctx&, uint32_t);
+template<typename Ctx> Result<typename Ctx::BlockTypeT> blocktype(Ctx&);
+template<typename Ctx> MaybeResult<typename Ctx::InstrT> block(Ctx&, bool);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeUnreachable(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeNop(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeBinary(Ctx&, Index, BinaryOp op);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeUnary(Ctx&, Index, UnaryOp op);
+template<typename Ctx> Result<typename Ctx::InstrT> makeSelect(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeDrop(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeMemorySize(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeMemoryGrow(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeLocalGet(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeLocalTee(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeLocalSet(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeGlobalGet(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeGlobalSet(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeBlock(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeThenOrElse(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeConst(Ctx&, Index, Type type);
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeLoad(Ctx&, Index, Type type, bool signed_, int bytes, bool isAtomic);
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeStore(Ctx&, Index, Type type, int bytes, bool isAtomic);
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeAtomicRMW(Ctx&, Index, AtomicRMWOp op, Type type, uint8_t bytes);
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeAtomicCmpxchg(Ctx&, Index, Type type, uint8_t bytes);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeAtomicWait(Ctx&, Index, Type type);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeAtomicNotify(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeAtomicFence(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeSIMDExtract(Ctx&, Index, SIMDExtractOp op, size_t lanes);
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeSIMDReplace(Ctx&, Index, SIMDReplaceOp op, size_t lanes);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeSIMDShuffle(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeSIMDTernary(Ctx&, Index, SIMDTernaryOp op);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeSIMDShift(Ctx&, Index, SIMDShiftOp op);
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeSIMDLoad(Ctx&, Index, SIMDLoadOp op, int bytes);
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeSIMDLoadStoreLane(Ctx&, Index, SIMDLoadStoreLaneOp op, int bytes);
+template<typename Ctx> Result<typename Ctx::InstrT> makeMemoryInit(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeDataDrop(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeMemoryCopy(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeMemoryFill(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makePop(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeIf(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeMaybeBlock(Ctx&, Index, size_t i, Type type);
+template<typename Ctx> Result<typename Ctx::InstrT> makeLoop(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeCall(Ctx&, Index, bool isReturn);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeCallIndirect(Ctx&, Index, bool isReturn);
+template<typename Ctx> Result<typename Ctx::InstrT> makeBreak(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeBreakTable(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeReturn(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeRefNull(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeRefIsNull(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeRefFunc(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeRefEq(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeTableGet(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeTableSet(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeTableSize(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeTableGrow(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeTableFill(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeTry(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeTryOrCatchBody(Ctx&, Index, Type type, bool isTry);
+template<typename Ctx> Result<typename Ctx::InstrT> makeThrow(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeRethrow(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeTupleMake(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeTupleExtract(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeCallRef(Ctx&, Index, bool isReturn);
+template<typename Ctx> Result<typename Ctx::InstrT> makeRefI31(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeI31Get(Ctx&, Index, bool signed_);
+template<typename Ctx> Result<typename Ctx::InstrT> makeRefTest(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeRefCast(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeBrOnNull(Ctx&, Index, bool onFail = false);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeBrOnCast(Ctx&, Index, bool onFail = false);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeStructNew(Ctx&, Index, bool default_);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeStructGet(Ctx&, Index, bool signed_ = false);
+template<typename Ctx> Result<typename Ctx::InstrT> makeStructSet(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeArrayNew(Ctx&, Index, bool default_);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeArrayNewData(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeArrayNewElem(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeArrayNewFixed(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeArrayGet(Ctx&, Index, bool signed_ = false);
+template<typename Ctx> Result<typename Ctx::InstrT> makeArraySet(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeArrayLen(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeArrayCopy(Ctx&, Index);
+template<typename Ctx> Result<typename Ctx::InstrT> makeArrayFill(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeArrayInitData(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeArrayInitElem(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeRefAs(Ctx&, Index, RefAsOp op);
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeStringNew(Ctx&, Index, StringNewOp op, bool try_);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeStringConst(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeStringMeasure(Ctx&, Index, StringMeasureOp op);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeStringEncode(Ctx&, Index, StringEncodeOp op);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeStringConcat(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeStringEq(Ctx&, Index, StringEqOp);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeStringAs(Ctx&, Index, StringAsOp op);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeStringWTF8Advance(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeStringWTF16Get(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeStringIterNext(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeStringIterMove(Ctx&, Index, StringIterMoveOp op);
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeStringSliceWTF(Ctx&, Index, StringSliceWTFOp op);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeStringSliceIter(Ctx&, Index);
+
+// Modules
+template<typename Ctx> MaybeResult<Index> maybeTypeidx(Ctx& ctx);
+template<typename Ctx> Result<typename Ctx::HeapTypeT> typeidx(Ctx&);
+template<typename Ctx>
+Result<typename Ctx::FieldIdxT> fieldidx(Ctx&, typename Ctx::HeapTypeT);
+template<typename Ctx> MaybeResult<typename Ctx::MemoryIdxT> maybeMemidx(Ctx&);
+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::TypeUseT> typeuse(Ctx&);
+MaybeResult<ImportNames> inlineImport(ParseInput&);
+Result<std::vector<Name>> inlineExports(ParseInput&);
+template<typename Ctx> Result<> strtype(Ctx&);
+template<typename Ctx> MaybeResult<typename Ctx::ModuleNameT> subtype(Ctx&);
+template<typename Ctx> MaybeResult<> deftype(Ctx&);
+template<typename Ctx> MaybeResult<typename Ctx::LocalsT> locals(Ctx&);
+template<typename Ctx> MaybeResult<> func(Ctx&);
+template<typename Ctx> MaybeResult<> memory(Ctx&);
+template<typename Ctx> MaybeResult<> global(Ctx&);
+template<typename Ctx> Result<typename Ctx::DataStringT> datastring(Ctx&);
+template<typename Ctx> MaybeResult<> data(Ctx&);
+template<typename Ctx> MaybeResult<> modulefield(Ctx&);
+template<typename Ctx> Result<> module(Ctx&);
+
+// =========
+// Utilities
+// =========
+
+// RAII utility for temporarily changing the parsing position of a parsing
+// context.
+template<typename Ctx> struct WithPosition {
+ Ctx& ctx;
+ Index original;
+
+ WithPosition(Ctx& ctx, Index pos) : ctx(ctx), original(ctx.in.getPos()) {
+ ctx.in.lexer.setIndex(pos);
+ }
+
+ ~WithPosition() { ctx.in.lexer.setIndex(original); }
+};
+
+// Deduction guide to satisfy -Wctad-maybe-unsupported.
+template<typename Ctx> WithPosition(Ctx& ctx, Index) -> WithPosition<Ctx>;
+
+// =====
+// Types
+// =====
+
+// heaptype ::= x:typeidx => types[x]
+// | 'func' => func
+// | 'extern' => extern
+template<typename Ctx> Result<typename Ctx::HeapTypeT> heaptype(Ctx& ctx) {
+ if (ctx.in.takeKeyword("func"sv)) {
+ return ctx.makeFunc();
+ }
+ if (ctx.in.takeKeyword("any"sv)) {
+ return ctx.makeAny();
+ }
+ if (ctx.in.takeKeyword("extern"sv)) {
+ return ctx.makeExtern();
+ }
+ if (ctx.in.takeKeyword("eq"sv)) {
+ return ctx.makeEq();
+ }
+ if (ctx.in.takeKeyword("i31"sv)) {
+ return ctx.makeI31();
+ }
+ if (ctx.in.takeKeyword("struct"sv)) {
+ return ctx.makeStructType();
+ }
+ if (ctx.in.takeKeyword("array"sv)) {
+ return ctx.makeArrayType();
+ }
+ auto type = typeidx(ctx);
+ CHECK_ERR(type);
+ return *type;
+}
+
+// reftype ::= 'funcref' => funcref
+// | 'externref' => externref
+// | 'anyref' => anyref
+// | 'eqref' => eqref
+// | 'i31ref' => i31ref
+// | 'structref' => structref
+// | 'arrayref' => arrayref
+// | '(' ref null? t:heaptype ')' => ref null? t
+template<typename Ctx> MaybeResult<typename Ctx::TypeT> reftype(Ctx& ctx) {
+ if (ctx.in.takeKeyword("funcref"sv)) {
+ return ctx.makeRefType(ctx.makeFunc(), Nullable);
+ }
+ if (ctx.in.takeKeyword("externref"sv)) {
+ return ctx.makeRefType(ctx.makeExtern(), Nullable);
+ }
+ if (ctx.in.takeKeyword("anyref"sv)) {
+ return ctx.makeRefType(ctx.makeAny(), Nullable);
+ }
+ if (ctx.in.takeKeyword("eqref"sv)) {
+ return ctx.makeRefType(ctx.makeEq(), Nullable);
+ }
+ if (ctx.in.takeKeyword("i31ref"sv)) {
+ return ctx.makeRefType(ctx.makeI31(), Nullable);
+ }
+ if (ctx.in.takeKeyword("structref"sv)) {
+ return ctx.makeRefType(ctx.makeStructType(), Nullable);
+ }
+ if (ctx.in.takeKeyword("arrayref"sv)) {
+ return ctx.in.err("arrayref not yet supported");
+ }
+
+ if (!ctx.in.takeSExprStart("ref"sv)) {
+ return {};
+ }
+
+ auto nullability = ctx.in.takeKeyword("null"sv) ? Nullable : NonNullable;
+
+ auto type = heaptype(ctx);
+ CHECK_ERR(type);
+
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of reftype");
+ }
+
+ return ctx.makeRefType(*type, nullability);
+}
+
+// numtype ::= 'i32' => i32
+// | 'i64' => i64
+// | 'f32' => f32
+// | 'f64' => f64
+// vectype ::= 'v128' => v128
+// valtype ::= t:numtype => t
+// | t:vectype => t
+// | t:reftype => t
+template<typename Ctx> Result<typename Ctx::TypeT> valtype(Ctx& ctx) {
+ if (ctx.in.takeKeyword("i32"sv)) {
+ return ctx.makeI32();
+ } else if (ctx.in.takeKeyword("i64"sv)) {
+ return ctx.makeI64();
+ } else if (ctx.in.takeKeyword("f32"sv)) {
+ return ctx.makeF32();
+ } else if (ctx.in.takeKeyword("f64"sv)) {
+ return ctx.makeF64();
+ } else if (ctx.in.takeKeyword("v128"sv)) {
+ return ctx.makeV128();
+ } else if (auto type = reftype(ctx)) {
+ CHECK_ERR(type);
+ return *type;
+ } else {
+ return ctx.in.err("expected valtype");
+ }
+}
+
+// param ::= '(' 'param id? t:valtype ')' => [t]
+// | '(' 'param t*:valtype* ')' => [t*]
+// params ::= param*
+template<typename Ctx> MaybeResult<typename Ctx::ParamsT> params(Ctx& ctx) {
+ bool hasAny = false;
+ auto res = ctx.makeParams();
+ while (ctx.in.takeSExprStart("param"sv)) {
+ hasAny = true;
+ if (auto id = ctx.in.takeID()) {
+ // Single named param
+ auto type = valtype(ctx);
+ CHECK_ERR(type);
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of param");
+ }
+ ctx.appendParam(res, *id, *type);
+ } else {
+ // Repeated unnamed params
+ while (!ctx.in.takeRParen()) {
+ auto type = valtype(ctx);
+ CHECK_ERR(type);
+ ctx.appendParam(res, {}, *type);
+ }
+ }
+ }
+ if (hasAny) {
+ return res;
+ }
+ return {};
+}
+
+// result ::= '(' 'result' t*:valtype ')' => [t*]
+// results ::= result*
+template<typename Ctx> MaybeResult<typename Ctx::ResultsT> results(Ctx& ctx) {
+ bool hasAny = false;
+ auto res = ctx.makeResults();
+ while (ctx.in.takeSExprStart("result"sv)) {
+ hasAny = true;
+ while (!ctx.in.takeRParen()) {
+ auto type = valtype(ctx);
+ CHECK_ERR(type);
+ ctx.appendResult(res, *type);
+ }
+ }
+ if (hasAny) {
+ return res;
+ }
+ return {};
+}
+
+// functype ::= '(' 'func' t1*:vec(param) t2*:vec(result) ')' => [t1*] -> [t2*]
+template<typename Ctx>
+MaybeResult<typename Ctx::SignatureT> functype(Ctx& ctx) {
+ if (!ctx.in.takeSExprStart("func"sv)) {
+ return {};
+ }
+
+ auto parsedParams = params(ctx);
+ CHECK_ERR(parsedParams);
+
+ auto parsedResults = results(ctx);
+ CHECK_ERR(parsedResults);
+
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of functype");
+ }
+
+ return ctx.makeFuncType(parsedParams.getPtr(), parsedResults.getPtr());
+}
+
+// storagetype ::= valtype | packedtype
+// packedtype ::= i8 | i16
+template<typename Ctx> Result<typename Ctx::FieldT> storagetype(Ctx& ctx) {
+ if (ctx.in.takeKeyword("i8"sv)) {
+ return ctx.makeI8();
+ }
+ if (ctx.in.takeKeyword("i16"sv)) {
+ return ctx.makeI16();
+ }
+ auto type = valtype(ctx);
+ CHECK_ERR(type);
+ return ctx.makeStorageType(*type);
+}
+
+// fieldtype ::= t:storagetype => const t
+// | '(' 'mut' t:storagetype ')' => var t
+template<typename Ctx> Result<typename Ctx::FieldT> fieldtype(Ctx& ctx) {
+ auto mutability = Immutable;
+ if (ctx.in.takeSExprStart("mut"sv)) {
+ mutability = Mutable;
+ }
+
+ auto field = storagetype(ctx);
+ CHECK_ERR(field);
+
+ if (mutability == Mutable) {
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of field type");
+ }
+ }
+
+ return ctx.makeFieldType(*field, mutability);
+}
+
+// field ::= '(' 'field' id t:fieldtype ')' => [(id, t)]
+// | '(' 'field' t*:fieldtype* ')' => [(_, t*)*]
+// | fieldtype
+template<typename Ctx> Result<typename Ctx::FieldsT> fields(Ctx& ctx) {
+ auto res = ctx.makeFields();
+ while (true) {
+ if (auto t = ctx.in.peek(); !t || t->isRParen()) {
+ return res;
+ }
+ if (ctx.in.takeSExprStart("field")) {
+ if (auto id = ctx.in.takeID()) {
+ auto field = fieldtype(ctx);
+ CHECK_ERR(field);
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of field");
+ }
+ ctx.appendField(res, *id, *field);
+ } else {
+ while (!ctx.in.takeRParen()) {
+ auto field = fieldtype(ctx);
+ CHECK_ERR(field);
+ ctx.appendField(res, {}, *field);
+ }
+ }
+ } else {
+ auto field = fieldtype(ctx);
+ CHECK_ERR(field);
+ ctx.appendField(res, {}, *field);
+ }
+ }
+}
+
+// structtype ::= '(' 'struct' field* ')'
+template<typename Ctx> MaybeResult<typename Ctx::StructT> structtype(Ctx& ctx) {
+ if (!ctx.in.takeSExprStart("struct"sv)) {
+ return {};
+ }
+ auto namedFields = fields(ctx);
+ CHECK_ERR(namedFields);
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of struct definition");
+ }
+
+ return ctx.makeStruct(*namedFields);
+}
+
+// arraytype ::= '(' 'array' field ')'
+template<typename Ctx> MaybeResult<typename Ctx::ArrayT> arraytype(Ctx& ctx) {
+ if (!ctx.in.takeSExprStart("array"sv)) {
+ return {};
+ }
+ auto namedFields = fields(ctx);
+ CHECK_ERR(namedFields);
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of array definition");
+ }
+
+ if (auto array = ctx.makeArray(*namedFields)) {
+ return *array;
+ }
+ return ctx.in.err("expected exactly one field in array definition");
+}
+
+// limits32 ::= n:u32 m:u32?
+template<typename Ctx> Result<typename Ctx::LimitsT> limits32(Ctx& ctx) {
+ auto n = ctx.in.takeU32();
+ if (!n) {
+ return ctx.in.err("expected initial size");
+ }
+ std::optional<uint64_t> m = ctx.in.takeU32();
+ return ctx.makeLimits(uint64_t(*n), m);
+}
+
+// limits64 ::= n:u64 m:u64?
+template<typename Ctx> Result<typename Ctx::LimitsT> limits64(Ctx& ctx) {
+ auto n = ctx.in.takeU64();
+ if (!n) {
+ return ctx.in.err("expected initial size");
+ }
+ std::optional<uint64_t> m = ctx.in.takeU64();
+ return ctx.makeLimits(uint64_t(*n), m);
+}
+
+// memtype ::= (limits32 | 'i32' limits32 | 'i64' limit64) shared?
+template<typename Ctx> Result<typename Ctx::MemTypeT> memtype(Ctx& ctx) {
+ auto type = Type::i32;
+ if (ctx.in.takeKeyword("i64"sv)) {
+ type = Type::i64;
+ } else {
+ ctx.in.takeKeyword("i32"sv);
+ }
+ auto limits = type == Type::i32 ? limits32(ctx) : limits64(ctx);
+ CHECK_ERR(limits);
+ bool shared = false;
+ if (ctx.in.takeKeyword("shared"sv)) {
+ shared = true;
+ }
+ return ctx.makeMemType(type, *limits, shared);
+}
+
+// globaltype ::= t:valtype => const t
+// | '(' 'mut' t:valtype ')' => var t
+template<typename Ctx> Result<typename Ctx::GlobalTypeT> globaltype(Ctx& ctx) {
+ auto mutability = Immutable;
+ if (ctx.in.takeSExprStart("mut"sv)) {
+ mutability = Mutable;
+ }
+
+ auto type = valtype(ctx);
+ CHECK_ERR(type);
+
+ if (mutability == Mutable && !ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of globaltype");
+ }
+
+ return ctx.makeGlobalType(mutability, *type);
+}
+
+// ============
+// Instructions
+// ============
+
+// blockinstr ::= block | loop | if-else | try-catch
+template<typename Ctx>
+MaybeResult<typename Ctx::InstrT> foldedBlockinstr(Ctx& ctx) {
+ if (auto i = block(ctx, true)) {
+ return i;
+ }
+ // TODO: Other block instructions
+ return {};
+}
+
+template<typename Ctx>
+MaybeResult<typename Ctx::InstrT> unfoldedBlockinstr(Ctx& ctx) {
+ if (auto i = block(ctx, false)) {
+ return i;
+ }
+ // TODO: Other block instructions
+ return {};
+}
+
+template<typename Ctx> MaybeResult<typename Ctx::InstrT> blockinstr(Ctx& ctx) {
+ if (auto i = foldedBlockinstr(ctx)) {
+ return i;
+ }
+ if (auto i = unfoldedBlockinstr(ctx)) {
+ return i;
+ }
+ return {};
+}
+
+// plaininstr ::= ... all plain instructions ...
+template<typename Ctx> MaybeResult<typename Ctx::InstrT> plaininstr(Ctx& ctx) {
+ auto pos = ctx.in.getPos();
+ auto keyword = ctx.in.takeKeyword();
+ if (!keyword) {
+ return {};
+ }
+
+#define NEW_INSTRUCTION_PARSER
+#define NEW_WAT_PARSER
+#include <gen-s-parser.inc>
+}
+
+// instr ::= plaininstr | blockinstr
+template<typename Ctx> MaybeResult<typename Ctx::InstrT> 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) {
+ return {};
+ }
+ }
+ }
+ if (auto i = blockinstr(ctx)) {
+ return i;
+ }
+ if (auto i = plaininstr(ctx)) {
+ return i;
+ }
+ // TODO: Handle folded plain instructions as well.
+ return {};
+}
+
+template<typename Ctx> Result<typename Ctx::InstrsT> instrs(Ctx& ctx) {
+ auto insts = ctx.makeInstrs();
+
+ while (true) {
+ if (auto blockinst = foldedBlockinstr(ctx)) {
+ CHECK_ERR(blockinst);
+ ctx.appendInstr(insts, *blockinst);
+ continue;
+ }
+ // 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<std::pair<Index, std::optional<Index>>> 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();
+ }
+
+ // 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);
+ ctx.appendInstr(insts, *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);
+ ctx.appendInstr(insts, *inst);
+ } else {
+ 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");
+ }
+ }
+ continue;
+ }
+
+ // A non-folded instruction.
+ if (auto inst = instr(ctx)) {
+ CHECK_ERR(inst);
+ ctx.appendInstr(insts, *inst);
+ } else {
+ break;
+ }
+ }
+
+ return ctx.finishInstrs(insts);
+}
+
+template<typename Ctx> Result<typename Ctx::ExprT> expr(Ctx& ctx) {
+ auto insts = instrs(ctx);
+ CHECK_ERR(insts);
+ return ctx.makeExpr(*insts);
+}
+
+// memarg_n ::= o:offset a:align_n
+// offset ::= 'offset='o:u64 => o | _ => 0
+// align_n ::= 'align='a:u32 => a | _ => n
+template<typename Ctx>
+Result<typename Ctx::MemargT> memarg(Ctx& ctx, uint32_t n) {
+ uint64_t offset = 0;
+ uint32_t align = n;
+ if (auto o = ctx.in.takeOffset()) {
+ offset = *o;
+ }
+ if (auto a = ctx.in.takeAlign()) {
+ align = *a;
+ }
+ return ctx.getMemarg(offset, align);
+}
+
+// blocktype ::= (t:result)? => t? | x,I:typeuse => x if I = {}
+template<typename Ctx> Result<typename Ctx::BlockTypeT> blocktype(Ctx& ctx) {
+ auto pos = ctx.in.getPos();
+
+ if (auto res = results(ctx)) {
+ CHECK_ERR(res);
+ if (ctx.getResultsSize(*res) == 1) {
+ return ctx.getBlockTypeFromResult(*res);
+ }
+ }
+
+ // We either had no results or multiple results. Reset and parse again as a
+ // type use.
+ ctx.in.lexer.setIndex(pos);
+ auto use = typeuse(ctx);
+ CHECK_ERR(use);
+
+ auto type = ctx.getBlockTypeFromTypeUse(pos, *use);
+ CHECK_ERR(type);
+ return *type;
+}
+
+// block ::= 'block' label blocktype instr* 'end' id? if id = {} or id = label
+// | '(' 'block' label blocktype instr* ')'
+template<typename Ctx>
+MaybeResult<typename Ctx::InstrT> block(Ctx& ctx, bool folded) {
+ auto pos = ctx.in.getPos();
+
+ if (folded) {
+ if (!ctx.in.takeSExprStart("block"sv)) {
+ return {};
+ }
+ } else {
+ if (!ctx.in.takeKeyword("block"sv)) {
+ return {};
+ }
+ }
+
+ auto label = ctx.in.takeID();
+
+ auto type = blocktype(ctx);
+ CHECK_ERR(type);
+
+ ctx.makeBlock(pos, label, *type);
+
+ auto insts = instrs(ctx);
+ CHECK_ERR(insts);
+
+ if (folded) {
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected ')' at end of block");
+ }
+ } else {
+ if (!ctx.in.takeKeyword("end"sv)) {
+ return ctx.in.err("expected 'end' at end of block");
+ }
+ auto id = ctx.in.takeID();
+ if (id && id != label) {
+ return ctx.in.err("end label does not match block label");
+ }
+ }
+
+ return ctx.finishBlock(pos, std::move(*insts));
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeUnreachable(Ctx& ctx, Index pos) {
+ return ctx.makeUnreachable(pos);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeNop(Ctx& ctx, Index pos) {
+ return ctx.makeNop(pos);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeBinary(Ctx& ctx, Index pos, BinaryOp op) {
+ return ctx.makeBinary(pos, op);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeUnary(Ctx& ctx, Index pos, UnaryOp op) {
+ return ctx.makeUnary(pos, op);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeSelect(Ctx& ctx, Index pos) {
+ auto res = results(ctx);
+ CHECK_ERR(res);
+ return ctx.makeSelect(pos, res.getPtr());
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeDrop(Ctx& ctx, Index pos) {
+ return ctx.makeDrop(pos);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeMemorySize(Ctx& ctx, Index pos) {
+ auto mem = maybeMemidx(ctx);
+ CHECK_ERR(mem);
+ return ctx.makeMemorySize(pos, mem.getPtr());
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeMemoryGrow(Ctx& ctx, Index pos) {
+ auto mem = maybeMemidx(ctx);
+ CHECK_ERR(mem);
+ return ctx.makeMemoryGrow(pos, mem.getPtr());
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeLocalGet(Ctx& ctx, Index pos) {
+ auto local = localidx(ctx);
+ CHECK_ERR(local);
+ return ctx.makeLocalGet(pos, *local);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeLocalTee(Ctx& ctx, Index pos) {
+ auto local = localidx(ctx);
+ CHECK_ERR(local);
+ return ctx.makeLocalTee(pos, *local);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeLocalSet(Ctx& ctx, Index pos) {
+ auto local = localidx(ctx);
+ CHECK_ERR(local);
+ return ctx.makeLocalSet(pos, *local);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeGlobalGet(Ctx& ctx, Index pos) {
+ auto global = globalidx(ctx);
+ CHECK_ERR(global);
+ return ctx.makeGlobalGet(pos, *global);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeGlobalSet(Ctx& ctx, Index pos) {
+ auto global = globalidx(ctx);
+ CHECK_ERR(global);
+ return ctx.makeGlobalSet(pos, *global);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeBlock(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeThenOrElse(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeConst(Ctx& ctx, Index pos, Type type) {
+ assert(type.isBasic());
+ switch (type.getBasic()) {
+ case Type::i32:
+ if (auto c = ctx.in.takeI32()) {
+ return ctx.makeI32Const(pos, *c);
+ }
+ return ctx.in.err("expected i32");
+ case Type::i64:
+ if (auto c = ctx.in.takeI64()) {
+ return ctx.makeI64Const(pos, *c);
+ }
+ return ctx.in.err("expected i64");
+ case Type::f32:
+ if (auto c = ctx.in.takeF32()) {
+ return ctx.makeF32Const(pos, *c);
+ }
+ return ctx.in.err("expected f32");
+ case Type::f64:
+ if (auto c = ctx.in.takeF64()) {
+ return ctx.makeF64Const(pos, *c);
+ }
+ return ctx.in.err("expected f64");
+ case Type::v128:
+ return ctx.in.err("unimplemented instruction");
+ case Type::none:
+ case Type::unreachable:
+ break;
+ }
+ WASM_UNREACHABLE("unexpected type");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeLoad(
+ Ctx& ctx, Index pos, Type type, bool signed_, int bytes, bool isAtomic) {
+ auto mem = maybeMemidx(ctx);
+ CHECK_ERR(mem);
+ auto arg = memarg(ctx, bytes);
+ CHECK_ERR(arg);
+ return ctx.makeLoad(pos, type, signed_, bytes, isAtomic, mem.getPtr(), *arg);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeStore(Ctx& ctx, Index pos, Type type, int bytes, bool isAtomic) {
+ auto mem = maybeMemidx(ctx);
+ CHECK_ERR(mem);
+ auto arg = memarg(ctx, bytes);
+ CHECK_ERR(arg);
+ return ctx.makeStore(pos, type, bytes, isAtomic, mem.getPtr(), *arg);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeAtomicRMW(Ctx& ctx, Index pos, AtomicRMWOp op, Type type, uint8_t bytes) {
+ auto mem = maybeMemidx(ctx);
+ CHECK_ERR(mem);
+ auto arg = memarg(ctx, bytes);
+ CHECK_ERR(arg);
+ return ctx.makeAtomicRMW(pos, op, type, bytes, mem.getPtr(), *arg);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeAtomicCmpxchg(Ctx& ctx, Index pos, Type type, uint8_t bytes) {
+ auto mem = maybeMemidx(ctx);
+ CHECK_ERR(mem);
+ auto arg = memarg(ctx, bytes);
+ CHECK_ERR(arg);
+ return ctx.makeAtomicCmpxchg(pos, type, bytes, mem.getPtr(), *arg);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeAtomicWait(Ctx& ctx, Index pos, Type type) {
+ auto mem = maybeMemidx(ctx);
+ CHECK_ERR(mem);
+ auto arg = memarg(ctx, type == Type::i32 ? 4 : 8);
+ CHECK_ERR(arg);
+ return ctx.makeAtomicWait(pos, type, mem.getPtr(), *arg);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeAtomicNotify(Ctx& ctx, Index pos) {
+ auto mem = maybeMemidx(ctx);
+ CHECK_ERR(mem);
+ auto arg = memarg(ctx, 4);
+ CHECK_ERR(arg);
+ return ctx.makeAtomicNotify(pos, mem.getPtr(), *arg);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeAtomicFence(Ctx& ctx, Index pos) {
+ return ctx.makeAtomicFence(pos);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeSIMDExtract(Ctx& ctx, Index pos, SIMDExtractOp op, size_t) {
+ auto lane = ctx.in.takeU8();
+ if (!lane) {
+ return ctx.in.err("expected lane index");
+ }
+ return ctx.makeSIMDExtract(pos, op, *lane);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeSIMDReplace(Ctx& ctx, Index pos, SIMDReplaceOp op, size_t lanes) {
+ auto lane = ctx.in.takeU8();
+ if (!lane) {
+ return ctx.in.err("expected lane index");
+ }
+ return ctx.makeSIMDReplace(pos, op, *lane);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeSIMDShuffle(Ctx& ctx, Index pos) {
+ std::array<uint8_t, 16> lanes;
+ for (int i = 0; i < 16; ++i) {
+ auto lane = ctx.in.takeU8();
+ if (!lane) {
+ return ctx.in.err("expected lane index");
+ }
+ lanes[i] = *lane;
+ }
+ return ctx.makeSIMDShuffle(pos, lanes);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeSIMDTernary(Ctx& ctx, Index pos, SIMDTernaryOp op) {
+ return ctx.makeSIMDTernary(pos, op);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeSIMDShift(Ctx& ctx, Index pos, SIMDShiftOp op) {
+ return ctx.makeSIMDShift(pos, op);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeSIMDLoad(Ctx& ctx, Index pos, SIMDLoadOp op, int bytes) {
+ auto mem = maybeMemidx(ctx);
+ CHECK_ERR(mem);
+ auto arg = memarg(ctx, bytes);
+ CHECK_ERR(arg);
+ return ctx.makeSIMDLoad(pos, op, mem.getPtr(), *arg);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeSIMDLoadStoreLane(Ctx& ctx, Index pos, SIMDLoadStoreLaneOp op, int bytes) {
+ auto reset = ctx.in.getPos();
+
+ auto retry = [&]() -> Result<typename Ctx::InstrT> {
+ // We failed to parse. Maybe the lane index was accidentally parsed as the
+ // optional memory index. Try again without parsing a memory index.
+ WithPosition with(ctx, reset);
+ auto arg = memarg(ctx, bytes);
+ CHECK_ERR(arg);
+ auto lane = ctx.in.takeU8();
+ if (!lane) {
+ return ctx.in.err("expected lane index");
+ }
+ return ctx.makeSIMDLoadStoreLane(pos, op, nullptr, *arg, *lane);
+ };
+
+ auto mem = maybeMemidx(ctx);
+ if (mem.getErr()) {
+ return retry();
+ }
+ auto arg = memarg(ctx, bytes);
+ CHECK_ERR(arg);
+ auto lane = ctx.in.takeU8();
+ if (!lane) {
+ return retry();
+ }
+ return ctx.makeSIMDLoadStoreLane(pos, op, mem.getPtr(), *arg, *lane);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeMemoryInit(Ctx& ctx, Index pos) {
+ auto reset = ctx.in.getPos();
+
+ auto retry = [&]() -> Result<typename Ctx::InstrT> {
+ // We failed to parse. Maybe the data index was accidentally parsed as the
+ // optional memory index. Try again without parsing a memory index.
+ WithPosition with(ctx, reset);
+ auto data = dataidx(ctx);
+ CHECK_ERR(data);
+ return ctx.makeMemoryInit(pos, nullptr, *data);
+ };
+
+ auto mem = maybeMemidx(ctx);
+ if (mem.getErr()) {
+ return retry();
+ }
+ auto data = dataidx(ctx);
+ if (data.getErr()) {
+ return retry();
+ }
+ return ctx.makeMemoryInit(pos, mem.getPtr(), *data);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeDataDrop(Ctx& ctx, Index pos) {
+ auto data = dataidx(ctx);
+ CHECK_ERR(data);
+ return ctx.makeDataDrop(pos, *data);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeMemoryCopy(Ctx& ctx, Index pos) {
+ auto destMem = maybeMemidx(ctx);
+ CHECK_ERR(destMem);
+ std::optional<typename Ctx::MemoryIdxT> srcMem = std::nullopt;
+ if (destMem) {
+ auto mem = memidx(ctx);
+ CHECK_ERR(mem);
+ srcMem = *mem;
+ }
+ return ctx.makeMemoryCopy(pos, destMem.getPtr(), srcMem ? &*srcMem : nullptr);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeMemoryFill(Ctx& ctx, Index pos) {
+ auto mem = maybeMemidx(ctx);
+ CHECK_ERR(mem);
+ return ctx.makeMemoryFill(pos, mem.getPtr());
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makePop(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeIf(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeMaybeBlock(Ctx& ctx, Index pos, size_t i, Type type) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeLoop(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeCall(Ctx& ctx, Index pos, bool isReturn) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeCallIndirect(Ctx& ctx, Index pos, bool isReturn) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeBreak(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeBreakTable(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeReturn(Ctx& ctx, Index pos) {
+ return ctx.makeReturn(pos);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeRefNull(Ctx& ctx, Index pos) {
+ auto t = heaptype(ctx);
+ CHECK_ERR(t);
+ return ctx.makeRefNull(pos, *t);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeRefIsNull(Ctx& ctx, Index pos) {
+ return ctx.makeRefIsNull(pos);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeRefFunc(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeRefEq(Ctx& ctx, Index pos) {
+ return ctx.makeRefEq(pos);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeTableGet(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeTableSet(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeTableSize(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeTableGrow(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeTableFill(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeTry(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeTryOrCatchBody(Ctx& ctx, Index pos, Type type, bool isTry) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeThrow(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeRethrow(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeTupleMake(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeTupleExtract(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeCallRef(Ctx& ctx, Index pos, bool isReturn) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeRefI31(Ctx& ctx, Index pos) {
+ return ctx.makeRefI31(pos);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeI31Get(Ctx& ctx, Index pos, bool signed_) {
+ return ctx.makeI31Get(pos, signed_);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeRefTest(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeRefCast(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeBrOnNull(Ctx& ctx, Index pos, bool onFail) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeBrOnCast(Ctx& ctx, Index pos, bool onFail) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeStructNew(Ctx& ctx, Index pos, bool default_) {
+ auto type = typeidx(ctx);
+ CHECK_ERR(type);
+ if (default_) {
+ return ctx.makeStructNewDefault(pos, *type);
+ }
+ return ctx.makeStructNew(pos, *type);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeStructGet(Ctx& ctx, Index pos, bool signed_) {
+ auto type = typeidx(ctx);
+ CHECK_ERR(type);
+ auto field = fieldidx(ctx, *type);
+ CHECK_ERR(field);
+ return ctx.makeStructGet(pos, *type, *field, signed_);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeStructSet(Ctx& ctx, Index pos) {
+ auto type = typeidx(ctx);
+ CHECK_ERR(type);
+ auto field = fieldidx(ctx, *type);
+ CHECK_ERR(field);
+ return ctx.makeStructSet(pos, *type, *field);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeArrayNew(Ctx& ctx, Index pos, bool default_) {
+ auto type = typeidx(ctx);
+ CHECK_ERR(type);
+ if (default_) {
+ return ctx.makeArrayNewDefault(pos, *type);
+ }
+ return ctx.makeArrayNew(pos, *type);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeArrayNewData(Ctx& ctx, Index pos) {
+ auto type = typeidx(ctx);
+ CHECK_ERR(type);
+ auto data = dataidx(ctx);
+ CHECK_ERR(data);
+ return ctx.makeArrayNewData(pos, *type, *data);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeArrayNewElem(Ctx& ctx, Index pos) {
+ auto type = typeidx(ctx);
+ CHECK_ERR(type);
+ auto data = dataidx(ctx);
+ CHECK_ERR(data);
+ return ctx.makeArrayNewElem(pos, *type, *data);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeArrayNewFixed(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeArrayGet(Ctx& ctx, Index pos, bool signed_) {
+ auto type = typeidx(ctx);
+ CHECK_ERR(type);
+ return ctx.makeArrayGet(pos, *type, signed_);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeArraySet(Ctx& ctx, Index pos) {
+ auto type = typeidx(ctx);
+ CHECK_ERR(type);
+ return ctx.makeArraySet(pos, *type);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeArrayLen(Ctx& ctx, Index pos) {
+ return ctx.makeArrayLen(pos);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeArrayCopy(Ctx& ctx, Index pos) {
+ auto destType = typeidx(ctx);
+ CHECK_ERR(destType);
+ auto srcType = typeidx(ctx);
+ CHECK_ERR(srcType);
+ return ctx.makeArrayCopy(pos, *destType, *srcType);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeArrayFill(Ctx& ctx, Index pos) {
+ auto type = typeidx(ctx);
+ CHECK_ERR(type);
+ return ctx.makeArrayFill(pos, *type);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeArrayInitData(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeArrayInitElem(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeRefAs(Ctx& ctx, Index pos, RefAsOp op) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeStringNew(Ctx& ctx, Index pos, StringNewOp op, bool try_) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeStringConst(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeStringMeasure(Ctx& ctx, Index pos, StringMeasureOp op) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeStringEncode(Ctx& ctx, Index pos, StringEncodeOp op) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeStringConcat(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeStringEq(Ctx& ctx, Index pos, StringEqOp op) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeStringAs(Ctx& ctx, Index pos, StringAsOp op) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeStringWTF8Advance(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeStringWTF16Get(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeStringIterNext(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeStringIterMove(Ctx& ctx, Index pos, StringIterMoveOp op) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeStringSliceWTF(Ctx& ctx, Index pos, StringSliceWTFOp op) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeStringSliceIter(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+// =======
+// Modules
+// =======
+
+// typeidx ::= x:u32 => x
+// | v:id => x (if types[x] = v)
+template<typename Ctx> MaybeResult<Index> maybeTypeidx(Ctx& ctx) {
+ if (auto x = ctx.in.takeU32()) {
+ return *x;
+ }
+ if (auto id = ctx.in.takeID()) {
+ // TODO: Fix position to point to start of id, not next element.
+ auto idx = ctx.getTypeIndex(*id);
+ CHECK_ERR(idx);
+ return *idx;
+ }
+ return {};
+}
+
+template<typename Ctx> Result<typename Ctx::HeapTypeT> typeidx(Ctx& ctx) {
+ if (auto idx = maybeTypeidx(ctx)) {
+ CHECK_ERR(idx);
+ return ctx.getHeapTypeFromIdx(*idx);
+ }
+ return ctx.in.err("expected type index or identifier");
+}
+
+// fieldidx_t ::= x:u32 => x
+// | v:id => x (if t.fields[x] = v)
+template<typename Ctx>
+Result<typename Ctx::FieldIdxT> fieldidx(Ctx& ctx,
+ typename Ctx::HeapTypeT type) {
+ if (auto x = ctx.in.takeU32()) {
+ return ctx.getFieldFromIdx(type, *x);
+ }
+ if (auto id = ctx.in.takeID()) {
+ return ctx.getFieldFromName(type, *id);
+ }
+ return ctx.in.err("expected field index or identifier");
+}
+
+// memidx ::= x:u32 => x
+// | v:id => x (if memories[x] = v)
+template<typename Ctx>
+MaybeResult<typename Ctx::MemoryIdxT> maybeMemidx(Ctx& ctx) {
+ if (auto x = ctx.in.takeU32()) {
+ return ctx.getMemoryFromIdx(*x);
+ }
+ if (auto id = ctx.in.takeID()) {
+ return ctx.getMemoryFromName(*id);
+ }
+ return {};
+}
+
+template<typename Ctx> Result<typename Ctx::MemoryIdxT> memidx(Ctx& ctx) {
+ if (auto idx = maybeMemidx(ctx)) {
+ CHECK_ERR(idx);
+ return *idx;
+ }
+ return ctx.in.err("expected memory index or identifier");
+}
+
+// memuse ::= '(' 'memory' x:memidx ')' => x
+template<typename Ctx>
+MaybeResult<typename Ctx::MemoryIdxT> maybeMemuse(Ctx& ctx) {
+ if (!ctx.in.takeSExprStart("memory"sv)) {
+ return {};
+ }
+ auto idx = memidx(ctx);
+ CHECK_ERR(idx);
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of memory use");
+ }
+ return *idx;
+}
+
+// globalidx ::= x:u32 => x
+// | v:id => x (if globals[x] = v)
+template<typename Ctx> Result<typename Ctx::GlobalIdxT> globalidx(Ctx& ctx) {
+ if (auto x = ctx.in.takeU32()) {
+ return ctx.getGlobalFromIdx(*x);
+ }
+ if (auto id = ctx.in.takeID()) {
+ return ctx.getGlobalFromName(*id);
+ }
+ return ctx.in.err("expected global index or identifier");
+}
+
+// dataidx ::= x:u32 => x
+// | v:id => x (if data[x] = v)
+template<typename Ctx> Result<typename Ctx::DataIdxT> dataidx(Ctx& ctx) {
+ if (auto x = ctx.in.takeU32()) {
+ return ctx.getDataFromIdx(*x);
+ }
+ if (auto id = ctx.in.takeID()) {
+ return ctx.getDataFromName(*id);
+ }
+ return ctx.in.err("expected data index or identifier");
+}
+
+// localidx ::= x:u32 => x
+// | v:id => x (if locals[x] = v)
+template<typename Ctx> Result<typename Ctx::LocalIdxT> localidx(Ctx& ctx) {
+ if (auto x = ctx.in.takeU32()) {
+ return ctx.getLocalFromIdx(*x);
+ }
+ if (auto id = ctx.in.takeID()) {
+ return ctx.getLocalFromName(*id);
+ }
+ return ctx.in.err("expected local index or identifier");
+}
+
+// typeuse ::= '(' 'type' x:typeidx ')' => x, []
+// (if typedefs[x] = [t1*] -> [t2*]
+// | '(' 'type' x:typeidx ')' ((t1,IDs):param)* (t2:result)* => x, IDs
+// (if typedefs[x] = [t1*] -> [t2*])
+// | ((t1,IDs):param)* (t2:result)* => x, IDs
+// (if x is minimum s.t. typedefs[x] = [t1*] -> [t2*])
+template<typename Ctx> Result<typename Ctx::TypeUseT> typeuse(Ctx& ctx) {
+ auto pos = ctx.in.getPos();
+ std::optional<typename Ctx::HeapTypeT> type;
+ if (ctx.in.takeSExprStart("type"sv)) {
+ auto x = typeidx(ctx);
+ CHECK_ERR(x);
+
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of type use");
+ }
+
+ type = *x;
+ }
+
+ auto namedParams = params(ctx);
+ CHECK_ERR(namedParams);
+
+ auto resultTypes = results(ctx);
+ CHECK_ERR(resultTypes);
+
+ return ctx.makeTypeUse(pos, type, namedParams.getPtr(), resultTypes.getPtr());
+}
+
+// ('(' 'import' mod:name nm:name ')')?
+MaybeResult<ImportNames> inlineImport(ParseInput& in) {
+ if (!in.takeSExprStart("import"sv)) {
+ return {};
+ }
+ auto mod = in.takeName();
+ if (!mod) {
+ return in.err("expected import module");
+ }
+ auto nm = in.takeName();
+ if (!nm) {
+ return in.err("expected import name");
+ }
+ if (!in.takeRParen()) {
+ return in.err("expected end of import");
+ }
+ // TODO: Return Ok when parsing Decls.
+ return {{*mod, *nm}};
+}
+
+// ('(' 'export' name ')')*
+Result<std::vector<Name>> inlineExports(ParseInput& in) {
+ std::vector<Name> exports;
+ while (in.takeSExprStart("export"sv)) {
+ auto name = in.takeName();
+ if (!name) {
+ return in.err("expected export name");
+ }
+ if (!in.takeRParen()) {
+ return in.err("expected end of import");
+ }
+ exports.push_back(*name);
+ }
+ return exports;
+}
+
+// strtype ::= ft:functype => ft
+// | st:structtype => st
+// | at:arraytype => at
+template<typename Ctx> Result<> strtype(Ctx& ctx) {
+ if (auto type = functype(ctx)) {
+ CHECK_ERR(type);
+ ctx.addFuncType(*type);
+ return Ok{};
+ }
+ if (auto type = structtype(ctx)) {
+ CHECK_ERR(type);
+ ctx.addStructType(*type);
+ return Ok{};
+ }
+ if (auto type = arraytype(ctx)) {
+ CHECK_ERR(type);
+ ctx.addArrayType(*type);
+ return Ok{};
+ }
+ return ctx.in.err("expected type description");
+}
+
+// subtype ::= '(' 'type' id? '(' 'sub' typeidx? strtype ')' ')'
+// | '(' 'type' id? strtype ')'
+template<typename Ctx> MaybeResult<> subtype(Ctx& ctx) {
+ auto pos = ctx.in.getPos();
+
+ if (!ctx.in.takeSExprStart("type"sv)) {
+ return {};
+ }
+
+ Name name;
+ if (auto id = ctx.in.takeID()) {
+ name = *id;
+ }
+
+ if (ctx.in.takeSExprStart("sub"sv)) {
+ if (ctx.in.takeKeyword("open"sv)) {
+ ctx.setOpen();
+ }
+ if (auto super = maybeTypeidx(ctx)) {
+ CHECK_ERR(super);
+ CHECK_ERR(ctx.addSubtype(*super));
+ }
+
+ CHECK_ERR(strtype(ctx));
+
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of subtype definition");
+ }
+ } else {
+ CHECK_ERR(strtype(ctx));
+ }
+
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of type definition");
+ }
+
+ ctx.finishSubtype(name, pos);
+ return Ok{};
+}
+
+// deftype ::= '(' 'rec' subtype* ')'
+// | subtype
+template<typename Ctx> MaybeResult<> deftype(Ctx& ctx) {
+ auto pos = ctx.in.getPos();
+
+ if (ctx.in.takeSExprStart("rec"sv)) {
+ size_t startIndex = ctx.getRecGroupStartIndex();
+ size_t groupLen = 0;
+ while (auto type = subtype(ctx)) {
+ CHECK_ERR(type);
+ ++groupLen;
+ }
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected type definition or end of recursion group");
+ }
+ ctx.addRecGroup(startIndex, groupLen);
+ } else if (auto type = subtype(ctx)) {
+ CHECK_ERR(type);
+ } else {
+ return {};
+ }
+
+ ctx.finishDeftype(pos);
+ return Ok{};
+}
+
+// local ::= '(' 'local id? t:valtype ')' => [t]
+// | '(' 'local t*:valtype* ')' => [t*]
+// locals ::= local*
+template<typename Ctx> MaybeResult<typename Ctx::LocalsT> locals(Ctx& ctx) {
+ bool hasAny = false;
+ auto res = ctx.makeLocals();
+ while (ctx.in.takeSExprStart("local"sv)) {
+ hasAny = true;
+ if (auto id = ctx.in.takeID()) {
+ // Single named local
+ auto type = valtype(ctx);
+ CHECK_ERR(type);
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of local");
+ }
+ ctx.appendLocal(res, *id, *type);
+ } else {
+ // Repeated unnamed locals
+ while (!ctx.in.takeRParen()) {
+ auto type = valtype(ctx);
+ CHECK_ERR(type);
+ ctx.appendLocal(res, {}, *type);
+ }
+ }
+ }
+ if (hasAny) {
+ return res;
+ }
+ return {};
+}
+
+// func ::= '(' 'func' id? ('(' 'export' name ')')*
+// x,I:typeuse t*:vec(local) (in:instr)* ')'
+// | '(' 'func' id? ('(' 'export' name ')')*
+// '(' 'import' mod:name nm:name ')' typeuse ')'
+template<typename Ctx> MaybeResult<> func(Ctx& ctx) {
+ auto pos = ctx.in.getPos();
+ if (!ctx.in.takeSExprStart("func"sv)) {
+ return {};
+ }
+
+ Name name;
+ if (auto id = ctx.in.takeID()) {
+ name = *id;
+ }
+
+ auto exports = inlineExports(ctx.in);
+ CHECK_ERR(exports);
+
+ auto import = inlineImport(ctx.in);
+ CHECK_ERR(import);
+
+ auto type = typeuse(ctx);
+ CHECK_ERR(type);
+
+ std::optional<typename Ctx::LocalsT> localVars;
+ if (!import) {
+ if (auto l = locals(ctx)) {
+ CHECK_ERR(l);
+ localVars = *l;
+ }
+ }
+
+ std::optional<typename Ctx::InstrsT> insts;
+ if (!import) {
+ auto i = instrs(ctx);
+ CHECK_ERR(i);
+ insts = *i;
+ }
+
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of function");
+ }
+
+ CHECK_ERR(
+ ctx.addFunc(name, *exports, import.getPtr(), *type, localVars, insts, pos));
+ return Ok{};
+}
+
+// mem ::= '(' 'memory' id? ('(' 'export' name ')')*
+// ('(' 'data' b:datastring ')' | memtype) ')'
+// | '(' 'memory' id? ('(' 'export' name ')')*
+// '(' 'import' mod:name nm:name ')' memtype ')'
+template<typename Ctx> MaybeResult<> memory(Ctx& ctx) {
+ auto pos = ctx.in.getPos();
+ if (!ctx.in.takeSExprStart("memory"sv)) {
+ return {};
+ }
+
+ Name name;
+ if (auto id = ctx.in.takeID()) {
+ name = *id;
+ }
+
+ auto exports = inlineExports(ctx.in);
+ CHECK_ERR(exports);
+
+ auto import = inlineImport(ctx.in);
+ CHECK_ERR(import);
+
+ std::optional<typename Ctx::MemTypeT> mtype;
+ std::optional<typename Ctx::DataStringT> data;
+ if (ctx.in.takeSExprStart("data"sv)) {
+ if (import) {
+ return ctx.in.err("imported memories cannot have inline data");
+ }
+ auto datastr = datastring(ctx);
+ CHECK_ERR(datastr);
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of inline data");
+ }
+ mtype = ctx.makeMemType(Type::i32, ctx.getLimitsFromData(*datastr), false);
+ data = *datastr;
+ } else {
+ auto type = memtype(ctx);
+ CHECK_ERR(type);
+ mtype = *type;
+ }
+
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of memory declaration");
+ }
+
+ CHECK_ERR(ctx.addMemory(name, *exports, import.getPtr(), *mtype, pos));
+
+ if (data) {
+ CHECK_ERR(ctx.addImplicitData(std::move(*data)));
+ }
+
+ return Ok{};
+}
+
+// global ::= '(' 'global' id? ('(' 'export' name ')')* gt:globaltype e:expr ')'
+// | '(' 'global' id? ('(' 'export' name ')')*
+// '(' 'import' mod:name nm:name ')' gt:globaltype ')'
+template<typename Ctx> MaybeResult<> global(Ctx& ctx) {
+ auto pos = ctx.in.getPos();
+ if (!ctx.in.takeSExprStart("global"sv)) {
+ return {};
+ }
+
+ Name name;
+ if (auto id = ctx.in.takeID()) {
+ name = *id;
+ }
+
+ auto exports = inlineExports(ctx.in);
+ CHECK_ERR(exports);
+
+ auto import = inlineImport(ctx.in);
+ CHECK_ERR(import);
+
+ auto type = globaltype(ctx);
+ CHECK_ERR(type);
+
+ std::optional<typename Ctx::ExprT> exp;
+ if (!import) {
+ auto e = expr(ctx);
+ CHECK_ERR(e);
+ exp = *e;
+ }
+
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of global");
+ }
+
+ CHECK_ERR(ctx.addGlobal(name, *exports, import.getPtr(), *type, exp, pos));
+ return Ok{};
+}
+
+// datastring ::= (b:string)* => concat(b*)
+template<typename Ctx> Result<typename Ctx::DataStringT> datastring(Ctx& ctx) {
+ auto data = ctx.makeDataString();
+ while (auto str = ctx.in.takeString()) {
+ ctx.appendDataString(data, *str);
+ }
+ return data;
+}
+
+// data ::= '(' 'data' id? b*:datastring ')' => {init b*, mode passive}
+// | '(' 'data' id? x:memuse? ('(' 'offset' e:expr ')' | e:instr)
+// b*:datastring ')
+// => {init b*, mode active {memory x, offset e}}
+template<typename Ctx> MaybeResult<> data(Ctx& ctx) {
+ auto pos = ctx.in.getPos();
+ if (!ctx.in.takeSExprStart("data"sv)) {
+ return {};
+ }
+
+ Name name;
+ if (auto id = ctx.in.takeID()) {
+ name = *id;
+ }
+
+ auto mem = maybeMemuse(ctx);
+ CHECK_ERR(mem);
+
+ std::optional<typename Ctx::ExprT> offset;
+ if (ctx.in.takeSExprStart("offset"sv)) {
+ auto e = expr(ctx);
+ CHECK_ERR(e);
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of offset expression");
+ }
+ offset = *e;
+ } else if (ctx.in.takeLParen()) {
+ auto inst = instr(ctx);
+ CHECK_ERR(inst);
+ auto offsetExpr = ctx.instrToExpr(*inst);
+ CHECK_ERR(offsetExpr);
+ offset = *offsetExpr;
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of offset instruction");
+ }
+ }
+
+ if (mem && !offset) {
+ return ctx.in.err("expected offset for active segment");
+ }
+
+ auto str = datastring(ctx);
+ CHECK_ERR(str);
+
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of data segment");
+ }
+
+ CHECK_ERR(ctx.addData(name, mem.getPtr(), offset, std::move(*str), pos));
+
+ return Ok{};
+}
+
+// modulefield ::= deftype
+// | import
+// | func
+// | table
+// | memory
+// | global
+// | export
+// | start
+// | elem
+// | data
+template<typename Ctx> MaybeResult<> modulefield(Ctx& ctx) {
+ if (auto t = ctx.in.peek(); !t || t->isRParen()) {
+ return {};
+ }
+ if (auto res = deftype(ctx)) {
+ CHECK_ERR(res);
+ return Ok{};
+ }
+ if (auto res = func(ctx)) {
+ CHECK_ERR(res);
+ return Ok{};
+ }
+ if (auto res = memory(ctx)) {
+ CHECK_ERR(res);
+ return Ok{};
+ }
+ if (auto res = global(ctx)) {
+ CHECK_ERR(res);
+ return Ok{};
+ }
+ if (auto res = data(ctx)) {
+ CHECK_ERR(res);
+ return Ok{};
+ }
+ return ctx.in.err("unrecognized module field");
+}
+
+// module ::= '(' 'module' id? (m:modulefield)* ')'
+// | (m:modulefield)* eof
+template<typename Ctx> Result<> module(Ctx& ctx) {
+ bool outer = ctx.in.takeSExprStart("module"sv);
+
+ if (outer) {
+ if (auto id = ctx.in.takeID()) {
+ ctx.wasm.name = *id;
+ }
+ }
+
+ while (auto field = modulefield(ctx)) {
+ CHECK_ERR(field);
+ }
+
+ if (outer && !ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of module");
+ }
+
+ return Ok{};
+}
+
+} // namespace wasm::WATParser
+
+#endif // parser_parsers_h