diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/wasm/wat-parser.cpp | 956 | ||||
-rw-r--r-- | src/wat-parser.h | 2 |
2 files changed, 516 insertions, 442 deletions
diff --git a/src/wasm/wat-parser.cpp b/src/wasm/wat-parser.cpp index 47fb8ef03..c6c07bb8f 100644 --- a/src/wasm/wat-parser.cpp +++ b/src/wasm/wat-parser.cpp @@ -57,13 +57,6 @@ // may be passed the appropriate context type and return the correct result type // for each phase. -#define RETURN_OR_OK(val) \ - if constexpr (parsingDecls<Ctx>) { \ - return Ok{}; \ - } else { \ - return val; \ - } - #define CHECK_ERR(val) \ if (auto _val = (val); auto err = _val.getErr()) { \ return Err{*err}; \ @@ -274,9 +267,9 @@ struct ParseInput { } }; -// =================== -// POD Utility Structs -// =================== +// ========= +// Utilities +// ========= // The location and possible name of a module-level definition in the input. struct DefPos { @@ -294,34 +287,285 @@ struct ImportNames { Name nm; }; +using IndexMap = std::unordered_map<Name, Index>; + +void applyImportNames(Importable& item, + const std::optional<ImportNames>& names) { + if (names) { + item.module = names->mod; + item.base = names->nm; + } +} + +Result<> addExports(ParseInput& in, + Module& wasm, + const Named* item, + const std::vector<Name>& exports, + ExternalKind kind) { + for (auto name : exports) { + if (wasm.getExportOrNull(name)) { + // TODO: Fix error location + return in.err("repeated export name"); + } + wasm.addExport(Builder(wasm).makeExport(name, item->name, kind)); + } + return Ok{}; +} + +Result<IndexMap> createIndexMap(std::string_view input, + const std::vector<DefPos>& defs) { + IndexMap indices; + for (Index i = 0; i < defs.size(); ++i) { + if (defs[i].name.is()) { + if (!indices.insert({defs[i].name, i}).second) { + return ParseInput(input, defs[i].pos).err("duplicate element name"); + } + } + } + return indices; +} + +std::vector<Type> getUnnamedTypes(const std::vector<NameType>& named) { + std::vector<Type> types; + types.reserve(named.size()); + for (auto& t : named) { + types.push_back(t.type); + } + return types; +} + +template<typename Ctx> +Result<> parseDefs(Ctx& ctx, + std::string_view input, + const std::vector<DefPos>& defs, + MaybeResult<> (*parser)(Ctx&, ParseInput&)) { + for (Index i = 0; i < defs.size(); ++i) { + ctx.index = i; + ParseInput in(input, defs[i].pos); + auto parsed = parser(ctx, in); + CHECK_ERR(parsed); + assert(parsed); + } + return Ok{}; +} + // =============== // Parser Contexts // =============== -using IndexMap = std::unordered_map<Name, Index>; - -// Phase 1: Parse definition spans for top-level module elements and determine -// their indices and names. -struct ParseDeclsCtx { - // At this stage we only look at types to find implicit type definitions, - // which are inserted directly in to the context. We cannot materialize or - // validate any types because we don't know what types exist yet. +struct NullTypeParserCtx { using IndexT = Ok; using HeapTypeT = Ok; using TypeT = Ok; using ParamsT = Ok; using ResultsT = Ok; using SignatureT = Ok; + using StorageT = Ok; using FieldT = Ok; using FieldsT = Ok; using StructT = Ok; using ArrayT = Ok; using GlobalTypeT = Ok; + HeapTypeT makeFunc() { return Ok{}; } + HeapTypeT makeAny() { return Ok{}; } + HeapTypeT makeExtern() { return Ok{}; } + HeapTypeT makeEq() { return Ok{}; } + HeapTypeT makeI31() { return Ok{}; } + HeapTypeT makeData() { return Ok{}; } + + TypeT makeI32() { return Ok{}; } + TypeT makeI64() { return Ok{}; } + TypeT makeF32() { return Ok{}; } + TypeT makeF64() { return Ok{}; } + TypeT makeV128() { return Ok{}; } + + TypeT makeRefType(HeapTypeT, Nullability) { return Ok{}; } + + ParamsT makeParams() { return Ok{}; } + void appendParam(ParamsT&, Name, TypeT) {} + + ResultsT makeResults() { return Ok{}; } + void appendResult(ResultsT&, TypeT) {} + + SignatureT makeFuncType(ParamsT*, ResultsT*) { return Ok{}; } + + StorageT makeI8() { return Ok{}; } + StorageT makeI16() { return Ok{}; } + StorageT makeStorageType(TypeT) { return Ok{}; } + + FieldT makeFieldType(StorageT, Mutability) { return Ok{}; } + + FieldsT makeFields() { return Ok{}; } + void appendField(FieldsT&, Name, FieldT) {} + + StructT makeStruct(FieldsT&) { return Ok{}; } + + std::optional<ArrayT> makeArray(FieldsT&) { return Ok{}; } + + GlobalTypeT makeGlobalType(Mutability, TypeT) { return Ok{}; } + + Result<Index> getTypeIndex(Name, ParseInput&) { return 1; } + Result<HeapTypeT> getHeapTypeFromIdx(Index, ParseInput&) { return Ok{}; } +}; + +template<typename Ctx> struct TypeParserCtx { + using IndexT = Index; + using HeapTypeT = HeapType; + using TypeT = Type; + using ParamsT = std::vector<NameType>; + using ResultsT = std::vector<Type>; + using SignatureT = Signature; + using StorageT = Field; + using FieldT = Field; + using FieldsT = std::pair<std::vector<Name>, std::vector<Field>>; + using StructT = std::pair<std::vector<Name>, Struct>; + using ArrayT = Array; + + // Map heap type names to their indices. + const IndexMap& typeIndices; + + TypeParserCtx(const IndexMap& typeIndices) : typeIndices(typeIndices) {} + + Ctx& self() { return *static_cast<Ctx*>(this); } + + HeapTypeT makeFunc() { return HeapType::func; } + HeapTypeT makeAny() { return HeapType::any; } + HeapTypeT makeExtern() { return HeapType::any; } + HeapTypeT makeEq() { return HeapType::eq; } + HeapTypeT makeI31() { return HeapType::i31; } + HeapTypeT makeData() { return HeapType::data; } + + TypeT makeI32() { return Type::i32; } + TypeT makeI64() { return Type::i64; } + TypeT makeF32() { return Type::f32; } + TypeT makeF64() { return Type::f64; } + TypeT makeV128() { return Type::v128; } + + TypeT makeRefType(HeapTypeT ht, Nullability nullability) { + return Type(ht, nullability); + } + + TypeT makeTupleType(const std::vector<Type> types) { return Tuple(types); } + + ParamsT makeParams() { return {}; } + void appendParam(ParamsT& params, Name id, TypeT type) { + params.push_back({id, type}); + } + + ResultsT makeResults() { return {}; } + void appendResult(ResultsT& results, TypeT type) { results.push_back(type); } + + SignatureT makeFuncType(ParamsT* params, ResultsT* results) { + std::vector<Type> empty; + const auto& paramTypes = params ? getUnnamedTypes(*params) : empty; + const auto& resultTypes = results ? *results : empty; + return Signature(self().makeTupleType(paramTypes), + self().makeTupleType(resultTypes)); + } + + StorageT makeI8() { return Field(Field::i8, Immutable); } + StorageT makeI16() { return Field(Field::i16, Immutable); } + StorageT makeStorageType(TypeT type) { return Field(type, Immutable); } + + FieldT makeFieldType(FieldT field, Mutability mutability) { + if (field.packedType == Field::not_packed) { + return Field(field.type, mutability); + } + return Field(field.packedType, mutability); + } + + FieldsT makeFields() { return {}; } + void appendField(FieldsT& fields, Name name, FieldT field) { + fields.first.push_back(name); + fields.second.push_back(field); + } + + StructT makeStruct(FieldsT& fields) { + return {std::move(fields.first), Struct(std::move(fields.second))}; + } + + std::optional<ArrayT> makeArray(FieldsT& fields) { + if (fields.second.size() == 1) { + return Array(fields.second[0]); + } + return {}; + } + + Result<Index> getTypeIndex(Name id, ParseInput& in) { + auto it = typeIndices.find(id); + if (it == typeIndices.end()) { + return in.err("unknown type identifier"); + } + return it->second; + } +}; + +struct NullInstrParserCtx { using InstrT = Ok; using InstrsT = Ok; using ExprT = Ok; + InstrsT makeInstrs() { return Ok{}; } + void appendInstr(InstrsT&, InstrT) {} + + ExprT makeExpr(InstrsT) { return Ok{}; } + + InstrT makeUnreachable() { return Ok{}; } + InstrT makeNop() { return Ok{}; } + + InstrT makeI32Const(uint32_t) { return Ok{}; } + InstrT makeI64Const(uint64_t) { return Ok{}; } + InstrT makeF32Const(float) { return Ok{}; } + InstrT makeF64Const(double) { return Ok{}; } + + template<typename HeapTypeT> InstrT makeRefNull(HeapTypeT) { return {}; } +}; + +template<typename Ctx> struct InstrParserCtx : TypeParserCtx<Ctx> { + using InstrT = Expression*; + using InstrsT = std::vector<Expression*>; + using ExprT = Expression*; + + Builder builder; + + InstrParserCtx(Module& wasm, const IndexMap& typeIndices) + : TypeParserCtx<Ctx>(typeIndices), builder(wasm) {} + + InstrsT makeInstrs() { return {}; } + void appendInstr(InstrsT& instrs, InstrT instr) { instrs.push_back(instr); } + + ExprT makeExpr(InstrsT& instrs) { + switch (instrs.size()) { + case 0: + return builder.makeNop(); + case 1: + return instrs.front(); + default: + return builder.makeBlock(instrs); + } + } + + InstrT makeUnreachable() { return builder.makeUnreachable(); } + InstrT makeNop() { return builder.makeNop(); } + + InstrT makeI32Const(uint32_t c) { return builder.makeConst(Literal(c)); } + InstrT makeI64Const(uint64_t c) { return builder.makeConst(Literal(c)); } + InstrT makeF32Const(float c) { return builder.makeConst(Literal(c)); } + InstrT makeF64Const(double c) { return builder.makeConst(Literal(c)); } + + InstrT makeRefNull(typename TypeParserCtx<Ctx>::HeapTypeT type) { + return builder.makeRefNull(type); + } +}; + +// Phase 1: Parse definition spans for top-level module elements and determine +// their indices and names. +struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx { + // At this stage we only look at types to find implicit type definitions, + // which are inserted directly in to the context. We cannot materialize or + // validate any types because we don't know what types exist yet. + // Declared module elements are inserted into the module, but their bodies are // not filled out until later parsing phases. Module& wasm; @@ -338,34 +582,63 @@ struct ParseDeclsCtx { bool hasNonImport = false; ParseDeclsCtx(Module& wasm) : wasm(wasm) {} -}; -template<typename Ctx> -inline constexpr bool parsingDecls = std::is_same_v<Ctx, ParseDeclsCtx>; + void addFuncType(SignatureT) {} + void addStructType(StructT) {} + void addArrayType(ArrayT) {} + Result<> addSubtype(Index, ParseInput&) { return Ok{}; } + void finishSubtype(Name name, Index pos) { + subtypeDefs.push_back({name, pos}); + } + size_t getRecGroupStartIndex() { return 0; } + void addRecGroup(Index, size_t) {} + void finishDeftype(Index pos) { typeDefs.push_back({{}, pos}); } + + Result<Global*> addGlobalDecl(ParseInput& in, + Name name, + std::optional<ImportNames> importNames) { + auto g = std::make_unique<Global>(); + if (name.is()) { + if (wasm.getGlobalOrNull(name)) { + // TODO: if the existing global is not explicitly named, fix its name + // and continue. + // TODO: Fix error location to point to name. + return in.err("repeated global name"); + } + g->setExplicitName(name); + } else { + name = (importNames ? "gimport$" : "") + std::to_string(globalCounter++); + name = Names::getValidGlobalName(wasm, name); + g->name = name; + } + applyImportNames(*g, importNames); + return wasm.addGlobal(std::move(g)); + } + + Result<> addGlobal(Name name, + const std::vector<Name>& exports, + ImportNames* import, + GlobalTypeT, + std::optional<ExprT>, + Index pos, + ParseInput& in) { + if (import && hasNonImport) { + return in.err("import after non-import"); + } + auto imp = import ? std::make_optional(*import) : std::nullopt; + auto g = addGlobalDecl(in, name, imp); + CHECK_ERR(g); + CHECK_ERR(addExports(in, wasm, *g, exports, ExternalKind::Global)); + globalDefs.push_back({name, pos}); + return Ok{}; + } +}; // Phase 2: Parse type definitions into a TypeBuilder. -struct ParseTypeDefsCtx { - using IndexT = Index; - using HeapTypeT = HeapType; - using TypeT = Type; - using ParamsT = std::vector<NameType>; - using ResultsT = std::vector<Type>; - using SignatureT = Signature; - using FieldT = Field; - using FieldsT = std::pair<std::vector<Name>, std::vector<Field>>; - using StructT = std::pair<std::vector<Name>, Struct>; - using ArrayT = Array; - - using InstrT = Ok; - using InstrsT = Ok; - using ExprT = Ok; - +struct ParseTypeDefsCtx : TypeParserCtx<ParseTypeDefsCtx> { // We update slots in this builder as we parse type definitions. TypeBuilder& builder; - // Map heap type names to their indices. - const IndexMap& typeIndices; - // Parse the names of types and fields as we go. std::vector<TypeNames> names; @@ -373,56 +646,108 @@ struct ParseTypeDefsCtx { Index index = 0; ParseTypeDefsCtx(TypeBuilder& builder, const IndexMap& typeIndices) - : builder(builder), typeIndices(typeIndices), names(builder.size()) {} -}; + : TypeParserCtx<ParseTypeDefsCtx>(typeIndices), builder(builder), + names(builder.size()) {} -template<typename Ctx> -inline constexpr bool parsingTypeDefs = std::is_same_v<Ctx, ParseTypeDefsCtx>; + TypeT makeRefType(HeapTypeT ht, Nullability nullability) { + return builder.getTempRefType(ht, nullability); + } + + TypeT makeTupleType(const std::vector<Type> types) { + return builder.getTempTupleType(types); + } + + Result<HeapTypeT> getHeapTypeFromIdx(Index idx, ParseInput& in) { + if (idx >= builder.size()) { + return in.err("type index out of bounds"); + } + return builder[idx]; + } + + void addFuncType(SignatureT& type) { builder[index] = type; } + + void addStructType(StructT& type) { + auto& [fieldNames, str] = type; + builder[index] = str; + for (Index i = 0; i < fieldNames.size(); ++i) { + if (auto name = fieldNames[i]; name.is()) { + names[index].fieldNames[i] = name; + } + } + } + + void addArrayType(ArrayT& type) { builder[index] = type; } + + Result<> addSubtype(Index super, ParseInput& in) { + if (super >= builder.size()) { + return in.err("supertype index out of bounds"); + } + builder[index].subTypeOf(builder[super]); + return Ok{}; + } + + void finishSubtype(Name name, Index pos) { names[index++].name = name; } + + size_t getRecGroupStartIndex() { return index; } + + void addRecGroup(Index start, size_t len) { + builder.createRecGroup(start, len); + } + + void finishDeftype(Index) {} +}; // TODO: Phase 3: ParseImplicitTypeDefsCtx // Phase 4: Parse and set the types of module elements. -struct ParseModuleTypesCtx { +struct ParseModuleTypesCtx : TypeParserCtx<ParseModuleTypesCtx>, + NullInstrParserCtx { // In this phase we have constructed all the types, so we can materialize and // validate them when they are used. - using IndexT = Index; - using HeapTypeT = HeapType; - using TypeT = Type; - using ParamsT = std::vector<NameType>; - using ResultsT = std::vector<Type>; - using GlobalTypeT = GlobalType; - using InstrT = Ok; - using InstrsT = Ok; - using ExprT = Ok; + using GlobalTypeT = GlobalType; Module& wasm; const std::vector<HeapType>& types; - // Map heap type names to their indices. - const IndexMap& typeIndices; - // The index of the current type. Index index = 0; ParseModuleTypesCtx(Module& wasm, const std::vector<HeapType>& types, const IndexMap& typeIndices) - : wasm(wasm), types(types), typeIndices(typeIndices) {} -}; + : TypeParserCtx<ParseModuleTypesCtx>(typeIndices), wasm(wasm), + types(types) {} -template<typename Ctx> -inline constexpr bool parsingModuleTypes = - std::is_same_v<Ctx, ParseModuleTypesCtx>; + Result<HeapTypeT> getHeapTypeFromIdx(Index idx, ParseInput& in) { + if (idx >= types.size()) { + return in.err("type index out of bounds"); + } + return types[idx]; + } + + GlobalTypeT makeGlobalType(Mutability mutability, TypeT type) { + return {mutability, type}; + } + + Result<> addGlobal(Name, + const std::vector<Name>&, + ImportNames*, + GlobalTypeT type, + std::optional<ExprT>, + Index, + ParseInput&) { + auto& g = wasm.globals[index]; + g->mutable_ = type.mutability; + g->type = type.type; + return Ok{}; + } +}; // Phase 5: Parse module element definitions, including instructions. -struct ParseDefsCtx { - using IndexT = Index; - using TypeT = Type; - using HeapTypeT = HeapType; - // TODO: This could be Ok, but then we wouldn't be able to use RETURN_OR_OK. - using GlobalTypeT = GlobalType; +struct ParseDefsCtx : InstrParserCtx<ParseDefsCtx> { + using GlobalTypeT = Ok; using InstrT = Expression*; using InstrsT = std::vector<Expression*>; @@ -430,24 +755,42 @@ struct ParseDefsCtx { Module& wasm; - Builder builder; + // A stack of stacks of fully-parsed instructions. + std::vector<std::vector<Expression*>> instrStack; const std::vector<HeapType>& types; - // Map heap type names to their indices. - const IndexMap& typeIndices; - // The index of the current module element. Index index = 0; ParseDefsCtx(Module& wasm, const std::vector<HeapType>& types, const IndexMap& typeIndices) - : wasm(wasm), builder(wasm), types(types), typeIndices(typeIndices) {} -}; + : InstrParserCtx<ParseDefsCtx>(wasm, typeIndices), wasm(wasm), + types(types) {} -template<typename Ctx> -inline constexpr bool parsingDefs = std::is_same_v<Ctx, ParseDefsCtx>; + GlobalTypeT makeGlobalType(Mutability, TypeT) { return Ok{}; } + + Result<HeapTypeT> getHeapTypeFromIdx(Index idx, ParseInput& in) { + if (idx >= types.size()) { + return in.err("type index out of bounds"); + } + return types[idx]; + } + + Result<> addGlobal(Name, + const std::vector<Name>&, + ImportNames*, + GlobalTypeT, + std::optional<ExprT> exp, + Index, + ParseInput&) { + if (exp) { + wasm.globals[index]->init = *exp; + } + return Ok{}; + } +}; // ================ // Parser Functions @@ -678,7 +1021,7 @@ Result<typename Ctx::InstrT> makeStringAs(Ctx&, ParseInput&, StringAsOp op); // Modules template<typename Ctx> -MaybeResult<typename Ctx::IndexT> maybeTypeidx(Ctx& ctx, ParseInput& in); +MaybeResult<Index> maybeTypeidx(Ctx& ctx, ParseInput& in); template<typename Ctx> Result<typename Ctx::HeapTypeT> typeidx(Ctx&, ParseInput&); MaybeResult<ImportNames> inlineImport(ParseInput&); @@ -691,28 +1034,6 @@ template<typename Ctx> MaybeResult<> global(Ctx&, ParseInput&); MaybeResult<> modulefield(ParseDeclsCtx&, ParseInput&); Result<> module(ParseDeclsCtx&, ParseInput&); -// Utilities -template<typename T> -void applyImportNames(Importable& item, - const std::optional<ImportNames>& names); -Result<> addExports(ParseInput& in, - Module& wasm, - const Named* item, - const std::vector<Name>& exports, - ExternalKind kind); -Result<Global*> addGlobalDecl(ParseDeclsCtx& ctx, - ParseInput& in, - Name name, - std::optional<ImportNames> importNames); -Result<IndexMap> createIndexMap(std::string_view input, - const std::vector<DefPos>& defs); -std::vector<Type> getUnnamedTypes(const std::vector<NameType>& named); -template<typename Ctx> -Result<> parseDefs(Ctx& ctx, - std::string_view input, - const std::vector<DefPos>& defs, - MaybeResult<> (*parser)(Ctx&, ParseInput&)); - // ===== // Types // ===== @@ -723,22 +1044,22 @@ Result<> parseDefs(Ctx& ctx, template<typename Ctx> Result<typename Ctx::HeapTypeT> heaptype(Ctx& ctx, ParseInput& in) { if (in.takeKeyword("func"sv)) { - RETURN_OR_OK(HeapType::func); + return ctx.makeFunc(); } if (in.takeKeyword("any"sv)) { - RETURN_OR_OK(HeapType::any); + return ctx.makeAny(); } if (in.takeKeyword("extern"sv)) { - RETURN_OR_OK(HeapType::any); + return ctx.makeExtern(); } if (in.takeKeyword("eq"sv)) { - RETURN_OR_OK(HeapType::eq); + return ctx.makeEq(); } if (in.takeKeyword("i31"sv)) { - RETURN_OR_OK(HeapType::i31); + return ctx.makeI31(); } if (in.takeKeyword("data"sv)) { - RETURN_OR_OK(HeapType::data); + return ctx.makeData(); } if (in.takeKeyword("array"sv)) { return in.err("array heap type not yet supported"); @@ -759,22 +1080,22 @@ Result<typename Ctx::HeapTypeT> heaptype(Ctx& ctx, ParseInput& in) { template<typename Ctx> MaybeResult<typename Ctx::TypeT> reftype(Ctx& ctx, ParseInput& in) { if (in.takeKeyword("funcref"sv)) { - RETURN_OR_OK(Type(HeapType::func, Nullable)); + return ctx.makeRefType(ctx.makeFunc(), Nullable); } if (in.takeKeyword("externref"sv)) { - RETURN_OR_OK(Type(HeapType::any, Nullable)); + return ctx.makeRefType(ctx.makeExtern(), Nullable); } if (in.takeKeyword("anyref"sv)) { - RETURN_OR_OK(Type(HeapType::any, Nullable)); + return ctx.makeRefType(ctx.makeAny(), Nullable); } if (in.takeKeyword("eqref"sv)) { - RETURN_OR_OK(Type(HeapType::eq, Nullable)); + return ctx.makeRefType(ctx.makeEq(), Nullable); } if (in.takeKeyword("i31ref"sv)) { - RETURN_OR_OK(Type(HeapType::i31, NonNullable)); + return ctx.makeRefType(ctx.makeI31(), NonNullable); } if (in.takeKeyword("dataref"sv)) { - RETURN_OR_OK(Type(HeapType::data, NonNullable)); + return ctx.makeRefType(ctx.makeData(), NonNullable); } if (in.takeKeyword("arrayref"sv)) { return in.err("arrayref not yet supported"); @@ -784,8 +1105,7 @@ MaybeResult<typename Ctx::TypeT> reftype(Ctx& ctx, ParseInput& in) { return {}; } - [[maybe_unused]] auto nullability = - in.takeKeyword("null"sv) ? Nullable : NonNullable; + auto nullability = in.takeKeyword("null"sv) ? Nullable : NonNullable; auto type = heaptype(ctx, in); CHECK_ERR(type); @@ -794,13 +1114,7 @@ MaybeResult<typename Ctx::TypeT> reftype(Ctx& ctx, ParseInput& in) { return in.err("expected end of reftype"); } - if constexpr (parsingDecls<Ctx>) { - return Ok{}; - } else if constexpr (parsingTypeDefs<Ctx>) { - return ctx.builder.getTempRefType(*type, nullability); - } else { - return Type(*type, nullability); - } + return ctx.makeRefType(*type, nullability); } // numtype ::= 'i32' => i32 @@ -814,15 +1128,15 @@ MaybeResult<typename Ctx::TypeT> reftype(Ctx& ctx, ParseInput& in) { template<typename Ctx> Result<typename Ctx::TypeT> valtype(Ctx& ctx, ParseInput& in) { if (in.takeKeyword("i32"sv)) { - RETURN_OR_OK(Type::i32); + return ctx.makeI32(); } else if (in.takeKeyword("i64"sv)) { - RETURN_OR_OK(Type::i64); + return ctx.makeI64(); } else if (in.takeKeyword("f32"sv)) { - RETURN_OR_OK(Type::f32); + return ctx.makeF32(); } else if (in.takeKeyword("f64"sv)) { - RETURN_OR_OK(Type::f64); + return ctx.makeF64(); } else if (in.takeKeyword("v128"sv)) { - RETURN_OR_OK(Type::v128); + return ctx.makeV128(); } else if (auto type = reftype(ctx, in)) { CHECK_ERR(type); return *type; @@ -837,36 +1151,28 @@ Result<typename Ctx::TypeT> valtype(Ctx& ctx, ParseInput& in) { template<typename Ctx> MaybeResult<typename Ctx::ParamsT> params(Ctx& ctx, ParseInput& in) { bool hasAny = false; - std::vector<NameType> res; + auto res = ctx.makeParams(); while (in.takeSExprStart("param"sv)) { hasAny = true; if (auto id = in.takeID()) { // Single named param auto type = valtype(ctx, in); CHECK_ERR(type); - if (!in.takeRParen()) { return in.err("expected end of param"); } - - if constexpr (!parsingDecls<Ctx>) { - res.push_back({*id, *type}); - } + ctx.appendParam(res, *id, *type); } else { // Repeated unnamed params while (!in.takeRParen()) { auto type = valtype(ctx, in); CHECK_ERR(type); - - if constexpr (!parsingDecls<Ctx>) { - res.push_back({Name(), *type}); - } + ctx.appendParam(res, {}, *type); } } } - if (hasAny) { - RETURN_OR_OK(res); + return res; } return {}; } @@ -876,21 +1182,17 @@ MaybeResult<typename Ctx::ParamsT> params(Ctx& ctx, ParseInput& in) { template<typename Ctx> MaybeResult<typename Ctx::ResultsT> results(Ctx& ctx, ParseInput& in) { bool hasAny = false; - std::vector<Type> res; + auto res = ctx.makeResults(); while (in.takeSExprStart("result"sv)) { hasAny = true; while (!in.takeRParen()) { auto type = valtype(ctx, in); CHECK_ERR(type); - - if constexpr (!parsingDecls<Ctx>) { - res.push_back(*type); - } + ctx.appendResult(res, *type); } } - if (hasAny) { - RETURN_OR_OK(res); + return res; } return {}; } @@ -912,22 +1214,7 @@ MaybeResult<typename Ctx::SignatureT> functype(Ctx& ctx, ParseInput& in) { return in.err("expected end of functype"); } - std::vector<Type> paramTypes, resultTypes; - if constexpr (!parsingDecls<Ctx>) { - if (parsedParams) { - paramTypes = getUnnamedTypes(*parsedParams); - } - if (parsedResults) { - resultTypes = *parsedResults; - } - } - - if constexpr (parsingTypeDefs<Ctx>) { - return Signature(ctx.builder.getTempTupleType(paramTypes), - ctx.builder.getTempTupleType(resultTypes)); - } else { - RETURN_OR_OK(Signature(Type(paramTypes), Type(resultTypes))); - } + return ctx.makeFuncType(parsedParams.getPtr(), parsedResults.getPtr()); } // storagetype ::= valtype | packedtype @@ -935,35 +1222,35 @@ MaybeResult<typename Ctx::SignatureT> functype(Ctx& ctx, ParseInput& in) { template<typename Ctx> Result<typename Ctx::FieldT> storagetype(Ctx& ctx, ParseInput& in) { if (in.takeKeyword("i8"sv)) { - RETURN_OR_OK(Field(Field::i8, Immutable)); + return ctx.makeI8(); } if (in.takeKeyword("i16"sv)) { - RETURN_OR_OK(Field(Field::i16, Immutable)); + return ctx.makeI16(); } auto type = valtype(ctx, in); CHECK_ERR(type); - RETURN_OR_OK(Field(*type, Immutable)); + return ctx.makeStorageType(*type); } // fieldtype ::= t:storagetype => const t // | '(' 'mut' t:storagetype ')' => var t template<typename Ctx> Result<typename Ctx::FieldT> fieldtype(Ctx& ctx, ParseInput& in) { + auto mutability = Immutable; if (in.takeSExprStart("mut"sv)) { - auto field = storagetype(ctx, in); - CHECK_ERR(field); + mutability = Mutable; + } + + auto field = storagetype(ctx, in); + CHECK_ERR(field); + + if (mutability == Mutable) { if (!in.takeRParen()) { return in.err("expected end of field type"); } - if constexpr (parsingTypeDefs<Ctx>) { - field->mutable_ = Mutable; - return *field; - } else { - return Ok{}; - } } - return storagetype(ctx, in); + return ctx.makeFieldType(*field, mutability); } // field ::= '(' 'field' id t:fieldtype ')' => [(id, t)] @@ -971,11 +1258,10 @@ Result<typename Ctx::FieldT> fieldtype(Ctx& ctx, ParseInput& in) { // | fieldtype template<typename Ctx> Result<typename Ctx::FieldsT> fields(Ctx& ctx, ParseInput& in) { - std::vector<Name> names; - std::vector<Field> fs; + auto res = ctx.makeFields(); while (true) { if (auto t = in.peek(); !t || t->isRParen()) { - RETURN_OR_OK(std::pair(std::move(names), std::move(fs))); + return res; } if (in.takeSExprStart("field")) { if (auto id = in.takeID()) { @@ -984,27 +1270,18 @@ Result<typename Ctx::FieldsT> fields(Ctx& ctx, ParseInput& in) { if (!in.takeRParen()) { return in.err("expected end of field"); } - if constexpr (parsingTypeDefs<Ctx>) { - names.push_back(*id); - fs.push_back(*field); - } + ctx.appendField(res, *id, *field); } else { while (!in.takeRParen()) { auto field = fieldtype(ctx, in); CHECK_ERR(field); - if constexpr (parsingTypeDefs<Ctx>) { - names.push_back({}); - fs.push_back(*field); - } + ctx.appendField(res, {}, *field); } } } else { auto field = fieldtype(ctx, in); CHECK_ERR(field); - if constexpr (parsingTypeDefs<Ctx>) { - names.push_back({}); - fs.push_back(*field); - } + ctx.appendField(res, {}, *field); } } } @@ -1021,8 +1298,7 @@ MaybeResult<typename Ctx::StructT> structtype(Ctx& ctx, ParseInput& in) { return in.err("expected end of struct definition"); } - RETURN_OR_OK( - std::pair(std::move(namedFields->first), std::move(namedFields->second))); + return ctx.makeStruct(*namedFields); } // arraytype ::= '(' 'array' field ')' @@ -1037,34 +1313,29 @@ MaybeResult<typename Ctx::ArrayT> arraytype(Ctx& ctx, ParseInput& in) { return in.err("expected end of array definition"); } - if constexpr (parsingTypeDefs<Ctx>) { - if (namedFields->first.size() != 1) { - return in.err("expected exactly one field in array definition"); - } + if (auto array = ctx.makeArray(*namedFields)) { + return *array; } - - RETURN_OR_OK({namedFields->second[0]}); + return in.err("expected exactly one field in array definition"); } // globaltype ::= t:valtype => const t // | '(' 'mut' t:valtype ')' => var t template<typename Ctx> Result<typename Ctx::GlobalTypeT> globaltype(Ctx& ctx, ParseInput& in) { - // '(' 'mut' t:valtype ')' + auto mutability = Immutable; if (in.takeSExprStart("mut"sv)) { - auto type = valtype(ctx, in); - CHECK_ERR(type); - - if (!in.takeRParen()) { - return in.err("expected end of globaltype"); - } - RETURN_OR_OK((GlobalType{Mutable, *type})); + mutability = Mutable; } - // t:valtype auto type = valtype(ctx, in); CHECK_ERR(type); - RETURN_OR_OK((GlobalType{Immutable, *type})); + + if (mutability == Mutable && !in.takeRParen()) { + return in.err("expected end of globaltype"); + } + + return ctx.makeGlobalType(mutability, *type); } // ============ @@ -1087,53 +1358,27 @@ MaybeResult<typename Ctx::InstrT> instr(Ctx& ctx, ParseInput& in) { template<typename Ctx> Result<typename Ctx::InstrsT> instrs(Ctx& ctx, ParseInput& in) { - // TODO: Folded instructions. - std::vector<Expression*> insts; + auto insts = ctx.makeInstrs(); while (auto inst = instr(ctx, in)) { CHECK_ERR(inst); - if constexpr (parsingDefs<Ctx>) { - insts.push_back(*inst); - } - } - if constexpr (parsingDefs<Ctx>) { - return insts; - } else { - return Ok{}; + ctx.appendInstr(insts, *inst); } + return insts; } template<typename Ctx> Result<typename Ctx::ExprT> expr(Ctx& ctx, ParseInput& in) { auto insts = instrs(ctx, in); CHECK_ERR(insts); - if constexpr (parsingDefs<Ctx>) { - switch (insts->size()) { - case 0: - return ctx.builder.makeNop(); - case 1: - return insts->front(); - default: - return ctx.builder.makeBlock(*insts); - } - } else { - return Ok{}; - } + return ctx.makeExpr(*insts); } template<typename Ctx> Result<typename Ctx::InstrT> makeUnreachable(Ctx& ctx) { - if constexpr (parsingDefs<Ctx>) { - return ctx.builder.makeUnreachable(); - } else { - return Ok{}; - } + return ctx.makeUnreachable(); } template<typename Ctx> Result<typename Ctx::InstrT> makeNop(Ctx& ctx) { - if constexpr (parsingDefs<Ctx>) { - return ctx.builder.makeNop(); - } else { - return Ok{}; - } + return ctx.makeNop(); } template<typename Ctx> @@ -1207,38 +1452,22 @@ Result<typename Ctx::InstrT> makeConst(Ctx& ctx, ParseInput& in, Type type) { switch (type.getBasic()) { case Type::i32: if (auto c = in.takeI32()) { - if constexpr (parsingDefs<Ctx>) { - return ctx.builder.makeConst(Literal(*c)); - } else { - return Ok{}; - } + return ctx.makeI32Const(*c); } return in.err("expected i32"); case Type::i64: if (auto c = in.takeI64()) { - if constexpr (parsingDefs<Ctx>) { - return ctx.builder.makeConst(Literal(*c)); - } else { - return Ok{}; - } + return ctx.makeI64Const(*c); } return in.err("expected i64"); case Type::f32: - if (auto f = in.takeF32()) { - if constexpr (parsingDefs<Ctx>) { - return ctx.builder.makeConst(Literal(*f)); - } else { - return Ok{}; - } + if (auto c = in.takeF32()) { + return ctx.makeF32Const(*c); } return in.err("expected f32"); case Type::f64: - if (auto f = in.takeF64()) { - if constexpr (parsingDefs<Ctx>) { - return ctx.builder.makeConst(Literal(*f)); - } else { - return Ok{}; - } + if (auto c = in.takeF64()) { + return ctx.makeF64Const(*c); } return in.err("expected f64"); case Type::v128: @@ -1418,11 +1647,7 @@ template<typename Ctx> Result<typename Ctx::InstrT> makeRefNull(Ctx& ctx, ParseInput& in) { auto t = heaptype(ctx, in); CHECK_ERR(t); - if constexpr (parsingDefs<Ctx>) { - return ctx.builder.makeRefNull(*t); - } else { - return Ok{}; - } + return ctx.makeRefNull(*t); } template<typename Ctx> @@ -1676,41 +1901,24 @@ makeStringAs(Ctx& ctx, ParseInput& in, StringAsOp op) { // typeidx ::= x:u32 => x // | v:id => x (if types[x] = v) template<typename Ctx> -MaybeResult<typename Ctx::IndexT> maybeTypeidx(Ctx& ctx, ParseInput& in) { +MaybeResult<Index> maybeTypeidx(Ctx& ctx, ParseInput& in) { if (auto x = in.takeU32()) { - RETURN_OR_OK(*x); + return *x; } if (auto id = in.takeID()) { - if constexpr (parsingDecls<Ctx>) { - return Ok{}; - } else { - auto it = ctx.typeIndices.find(*id); - if (it == ctx.typeIndices.end()) { - return in.err("unknown type identifier"); - } - return it->second; - } + // TODO: Fix position to point to start of id, not next element. + auto idx = ctx.getTypeIndex(*id, in); + CHECK_ERR(idx); + return *idx; } return {}; } template<typename Ctx> Result<typename Ctx::HeapTypeT> typeidx(Ctx& ctx, ParseInput& in) { - if (auto index = maybeTypeidx(ctx, in)) { - CHECK_ERR(index); - if constexpr (parsingDecls<Ctx>) { - return Ok{}; - } else if constexpr (parsingTypeDefs<Ctx>) { - if (*index >= ctx.builder.size()) { - return in.err("type index out of bounds"); - } - return ctx.builder[*index]; - } else { - if (*index >= ctx.types.size()) { - return in.err("type index out of bounds"); - } - return ctx.types[*index]; - } + if (auto idx = maybeTypeidx(ctx, in)) { + CHECK_ERR(idx); + return ctx.getHeapTypeFromIdx(*idx, in); } return in.err("expected type index or identifier"); } @@ -1731,6 +1939,7 @@ MaybeResult<ImportNames> inlineImport(ParseInput& in) { if (!in.takeRParen()) { return in.err("expected end of import"); } + // TODO: Return Ok when parsing Decls. return {{*mod, *nm}}; } @@ -1756,29 +1965,17 @@ Result<std::vector<Name>> inlineExports(ParseInput& in) { template<typename Ctx> Result<> strtype(Ctx& ctx, ParseInput& in) { if (auto type = functype(ctx, in)) { CHECK_ERR(type); - if constexpr (parsingTypeDefs<Ctx>) { - ctx.builder[ctx.index] = *type; - } + ctx.addFuncType(*type); return Ok{}; } if (auto type = structtype(ctx, in)) { CHECK_ERR(type); - if constexpr (parsingTypeDefs<Ctx>) { - auto& [fieldNames, str] = *type; - ctx.builder[ctx.index] = str; - for (Index i = 0; i < fieldNames.size(); ++i) { - if (auto name = fieldNames[i]; name.is()) { - ctx.names[ctx.index].fieldNames[i] = name; - } - } - } + ctx.addStructType(*type); return Ok{}; } if (auto type = arraytype(ctx, in)) { CHECK_ERR(type); - if constexpr (parsingTypeDefs<Ctx>) { - ctx.builder[ctx.index] = *type; - } + ctx.addArrayType(*type); return Ok{}; } return in.err("expected type description"); @@ -1787,7 +1984,7 @@ template<typename Ctx> Result<> strtype(Ctx& ctx, ParseInput& in) { // subtype ::= '(' 'type' id? '(' 'sub' typeidx? strtype ')' ')' // | '(' 'type' id? strtype ')' template<typename Ctx> MaybeResult<> subtype(Ctx& ctx, ParseInput& in) { - [[maybe_unused]] auto pos = in.getPos(); + auto pos = in.getPos(); if (!in.takeSExprStart("type"sv)) { return {}; @@ -1796,20 +1993,12 @@ template<typename Ctx> MaybeResult<> subtype(Ctx& ctx, ParseInput& in) { Name name; if (auto id = in.takeID()) { name = *id; - if constexpr (parsingTypeDefs<Ctx>) { - ctx.names[ctx.index].name = name; - } } if (in.takeSExprStart("sub"sv)) { if (auto super = maybeTypeidx(ctx, in)) { CHECK_ERR(super); - if constexpr (parsingTypeDefs<Ctx>) { - if (*super >= ctx.builder.size()) { - return in.err("supertype index out of bounds"); - } - ctx.builder[ctx.index].subTypeOf(ctx.builder[*super]); - } + CHECK_ERR(ctx.addSubtype(*super, in)); } CHECK_ERR(strtype(ctx, in)); @@ -1825,26 +2014,17 @@ template<typename Ctx> MaybeResult<> subtype(Ctx& ctx, ParseInput& in) { return in.err("expected end of type definition"); } - if constexpr (parsingDecls<Ctx>) { - ctx.subtypeDefs.push_back({name, pos}); - } - if constexpr (parsingTypeDefs<Ctx>) { - ++ctx.index; - } - + ctx.finishSubtype(name, pos); return Ok{}; } // deftype ::= '(' 'rec' subtype* ')' // | subtype template<typename Ctx> MaybeResult<> deftype(Ctx& ctx, ParseInput& in) { - [[maybe_unused]] auto pos = in.getPos(); + auto pos = in.getPos(); if (in.takeSExprStart("rec"sv)) { - [[maybe_unused]] size_t startIndex = 0; - if constexpr (parsingTypeDefs<Ctx>) { - startIndex = ctx.index; - } + size_t startIndex = ctx.getRecGroupStartIndex(); size_t groupLen = 0; while (auto type = subtype(ctx, in)) { CHECK_ERR(type); @@ -1853,19 +2033,14 @@ template<typename Ctx> MaybeResult<> deftype(Ctx& ctx, ParseInput& in) { if (!in.takeRParen()) { return in.err("expected type definition or end of recursion group"); } - if constexpr (parsingTypeDefs<Ctx>) { - ctx.builder.createRecGroup(startIndex, groupLen); - } + ctx.addRecGroup(startIndex, groupLen); } else if (auto type = subtype(ctx, in)) { CHECK_ERR(type); } else { return {}; } - if constexpr (parsingDecls<Ctx>) { - ctx.typeDefs.push_back({{}, pos}); - } - + ctx.finishDeftype(pos); return Ok{}; } @@ -1873,7 +2048,7 @@ template<typename Ctx> MaybeResult<> deftype(Ctx& ctx, ParseInput& in) { // | '(' 'global' id? '(' 'import' mod:name nm:name ')' // gt:globaltype ')' template<typename Ctx> MaybeResult<> global(Ctx& ctx, ParseInput& in) { - [[maybe_unused]] auto pos = in.getPos(); + auto pos = in.getPos(); if (!in.takeSExprStart("global"sv)) { return {}; } @@ -1895,32 +2070,16 @@ template<typename Ctx> MaybeResult<> global(Ctx& ctx, ParseInput& in) { if (!import) { auto e = expr(ctx, in); CHECK_ERR(e); - *exp = *e; + exp = *e; } if (!in.takeRParen()) { return in.err("expected end of global"); } - if constexpr (parsingDecls<Ctx>) { - if (ctx.hasNonImport) { - return in.err("import after non-import"); - } - auto imp = import ? std::make_optional(*import) : std::nullopt; - auto g = addGlobalDecl(ctx, in, name, imp); - CHECK_ERR(g); - CHECK_ERR(addExports(in, ctx.wasm, *g, *exports, ExternalKind::Global)); - ctx.globalDefs.push_back({name, pos}); - } else if constexpr (parsingModuleTypes<Ctx>) { - auto& g = ctx.wasm.globals[ctx.index]; - g->mutable_ = type->mutability; - g->type = type->type; - } else if constexpr (parsingDefs<Ctx>) { - if (!import) { - ctx.wasm.globals[ctx.index]->init = *exp; - } - } - + // TODO: Use `pos` instead of `in` for error position. + CHECK_ERR( + ctx.addGlobal(name, *exports, import.getPtr(), *type, exp, pos, in)); return Ok{}; } @@ -1971,93 +2130,6 @@ Result<> module(ParseDeclsCtx& ctx, ParseInput& in) { return Ok{}; } -// ========= -// Utilities -// ========= - -void applyImportNames(Importable& item, - const std::optional<ImportNames>& names) { - if (names) { - item.module = names->mod; - item.base = names->nm; - } -} - -Result<> addExports(ParseInput& in, - Module& wasm, - const Named* item, - const std::vector<Name>& exports, - ExternalKind kind) { - for (auto name : exports) { - if (wasm.getExportOrNull(name)) { - // TODO: Fix error location - return in.err("repeated export name"); - } - wasm.addExport(Builder(wasm).makeExport(name, item->name, kind)); - } - return Ok{}; -} - -Result<Global*> addGlobalDecl(ParseDeclsCtx& ctx, - ParseInput& in, - Name name, - std::optional<ImportNames> importNames) { - auto g = std::make_unique<Global>(); - if (name.is()) { - if (ctx.wasm.getGlobalOrNull(name)) { - // TODO: if the existing global is not explicitly named, fix its name and - // continue. - // TODO: Fix error location to point to name. - return in.err("repeated global name"); - } - g->setExplicitName(name); - } else { - name = - (importNames ? "gimport$" : "") + std::to_string(ctx.globalCounter++); - name = Names::getValidGlobalName(ctx.wasm, name); - g->name = name; - } - applyImportNames(*g, importNames); - return ctx.wasm.addGlobal(std::move(g)); -} - -Result<IndexMap> createIndexMap(std::string_view input, - const std::vector<DefPos>& defs) { - IndexMap indices; - for (Index i = 0; i < defs.size(); ++i) { - if (defs[i].name.is()) { - if (!indices.insert({defs[i].name, i}).second) { - return ParseInput(input, defs[i].pos).err("duplicate element name"); - } - } - } - return indices; -} - -std::vector<Type> getUnnamedTypes(const std::vector<NameType>& named) { - std::vector<Type> types; - types.reserve(named.size()); - for (auto& t : named) { - types.push_back(t.type); - } - return types; -} - -template<typename Ctx> -Result<> parseDefs(Ctx& ctx, - std::string_view input, - const std::vector<DefPos>& defs, - MaybeResult<> (*parser)(Ctx&, ParseInput&)) { - for (Index i = 0; i < defs.size(); ++i) { - ctx.index = i; - ParseInput in(input, defs[i].pos); - auto parsed = parser(ctx, in); - CHECK_ERR(parsed); - assert(parsed); - } - return Ok{}; -} - } // anonymous namespace Result<> parseModule(Module& wasm, std::string_view input) { diff --git a/src/wat-parser.h b/src/wat-parser.h index 7f92de9e4..10988dbf0 100644 --- a/src/wat-parser.h +++ b/src/wat-parser.h @@ -62,6 +62,8 @@ template<typename T = Ok> struct MaybeResult { Err* getErr() { return std::get_if<Err>(&val); } T& operator*() { return *std::get_if<T>(&val); } T* operator->() { return std::get_if<T>(&val); } + + T* getPtr() { return std::get_if<T>(&val); } }; // Parse a single WAT module. |