Error handling
diff --git a/src/wasm/wasm-io.cpp b/src/wasm/wasm-io.cpp index 31a83f1..7acee3c 100644 --- a/src/wasm/wasm-io.cpp +++ b/src/wasm/wasm-io.cpp
@@ -38,8 +38,9 @@ static void readTextData(std::string& input, Module& wasm, IRProfile profile) { if (std::getenv("BINARYEN_NEW_WAT_PARSER")) { - if (!WATParser::parseModule(wasm, std::string_view(input.c_str()))) { - Fatal() << "Parse failure"; + std::string_view in(input.c_str()); + if (auto err = WATParser::parseModule(wasm, in).getErr()) { + Fatal() << err->msg; } } else { SExpressionParser parser(const_cast<char*>(input.c_str()));
diff --git a/src/wasm/wat-parser.cpp b/src/wasm/wat-parser.cpp index 5c721fd..c913a5a 100644 --- a/src/wasm/wat-parser.cpp +++ b/src/wasm/wat-parser.cpp
@@ -58,6 +58,25 @@ return val; \ } +#define RETURN_OR_TRUE(val) \ + if constexpr (parsingDecls<Ctx>) { \ + return true; \ + } else { \ + return val; \ + } + +#define RETURN_NONE() \ + if constexpr (parsingDecls<Ctx>) { \ + return false; \ + } else { \ + return {std::nullopt}; \ + } + +#define CHECK_ERR(val) \ + if (auto err = val.getErr()) { \ + return *err; \ + } + using namespace std::string_view_literals; namespace wasm::WATParser { @@ -250,6 +269,17 @@ auto curr = getPos(); return std::string_view(prev.pos, curr.pos - prev.pos); } + + [[nodiscard]] Err err(std::string reason) { + std::stringstream msg; + if (auto t = peek()) { + msg << lexer.position(*t); + } else { + msg << lexer.position(lexer.next()); + } + msg << ": error: " << reason; + return Err{msg.str()}; + } }; // =================== @@ -268,7 +298,7 @@ std::optional<uint32_t> max; }; -struct DefinedTableType { +struct TableType { Limits limits; Type type; }; @@ -283,16 +313,16 @@ Name name; }; -struct DefinedTypeUse { - HeapType type; - std::vector<IndexedName> ids; -}; - struct ImportNames { Name mod; Name nm; }; +struct TypeUse { + HeapType type; + std::vector<NameType> ids; +}; + // =============== // Parser Contexts // =============== @@ -304,20 +334,29 @@ // 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. - using RefType = Ok; + using RefType = bool; using ValType = Ok; - using TypeIdx = Ok; - using Params = Ok; - using Results = Ok; - using FuncType = Ok; - using TableType = Ok; + using Params = bool; + using Results = bool; + using FuncType = bool; + using TableTypeT = Ok; using GlobalType = Ok; - using TypeUse = Ok; - using Locals = Ok; + using TypeUseT = Ok; + using Locals = bool; using Instrs = Ok; using DataStr = Ok; + using TypeIdx = Ok; + using FuncIdx = Ok; + using TableIdx = Ok; + using MemIdx = Ok; + using GlobalIdx = Ok; + using ElemIdx = Ok; + using DataIdx = Ok; + using LocalIdx = Ok; + using LabelIdx = Ok; + // Declared module elements are inserted into the module, but their bodies are // not filled out until later parsing phases. Module& wasm; @@ -348,12 +387,12 @@ // A parsing context used while parsing explicit type definitions. struct ParseTypesCtx { - using RefType = Type; + using RefType = std::optional<Type>; using ValType = Type; + using Params = std::optional<std::vector<NameType>>; + using Results = std::optional<std::vector<Type>>; + using FuncType = std::optional<Signature>; using TypeIdx = HeapType; - using Params = std::vector<NameType>; - using Results = std::vector<Type>; - using FuncType = Signature; // We update slots in this builder as we parse type definitions. TypeBuilder& builder; @@ -366,24 +405,17 @@ ParseTypesCtx(TypeBuilder& builder, const IndexMap& typeIndices, Index index) : builder(builder), typeIndices(typeIndices), index(index) {} - - std::optional<HeapType> getHeapType(Index index) { - if (index >= builder.size()) { - return {}; - } - return builder[index]; - } }; // A parsing context used while parsing type uses to find implicit type // definitions. struct ParseImplicitTypesCtx { - using RefType = Type; + using RefType = std::optional<Type>; using ValType = Type; + using Params = std::optional<std::vector<NameType>>; + using Results = std::optional<std::vector<Type>>; + using TypeUseT = Signature; using TypeIdx = HeapType; - using Params = std::vector<NameType>; - using Results = std::vector<Type>; - using TypeUse = Signature; // Map heap type names to their indices. const IndexMap& typeIndices; @@ -392,33 +424,35 @@ ParseImplicitTypesCtx(const IndexMap& typeIndices, const std::vector<HeapType>& types) : typeIndices(typeIndices), types(types) {} - - std::optional<HeapType> getHeapType(Index index) { - if (index >= types.size()) { - return {}; - } - return types[index]; - } }; // A parsing context used while parsing module elements besides types. struct ParseDefsCtx { // In this phase we have constructed all the types, so we can materialize and // validate them when they are used. - using RefType = Type; + using RefType = std::optional<Type>; using ValType = Type; - using TypeIdx = HeapType; - using Params = std::vector<NameType>; - using Results = std::vector<Type>; - using FuncType = Signature; - using TypeUse = DefinedTypeUse; - using TableType = DefinedTableType; + using Params = std::optional<std::vector<NameType>>; + using Results = std::optional<std::vector<Type>>; + using FuncType = std::optional<Signature>; + using TypeUseT = TypeUse; + using TableTypeT = TableType; using GlobalType = DefinedGlobalType; - using Locals = std::vector<NameType>; + using Locals = std::optional<std::vector<NameType>>; using Instrs = Expression*; using DataStr = std::vector<char>; + using TypeIdx = HeapType; + using FuncIdx = Name; + using TableIdx = Name; + using MemIdx = Name; + using GlobalIdx = Name; + using ElemIdx = Name; + using DataIdx = Name; + using LocalIdx = Index; + using LabelIdx = Name; + Module& wasm; // The index of the current element in its index space. @@ -455,13 +489,6 @@ funcIndices(funcIndices), memIndices(memIndices), tableIndices(tableIndices), globalIndices(globalIndices), elemIndices(elemIndices), dataIndices(dataIndices) {} - - std::optional<HeapType> getHeapType(Index index) { - if (index >= types.size()) { - return {}; - } - return types[index]; - } }; template<typename Ctx> @@ -491,14 +518,14 @@ Result<typename Ctx::TypeIdx> heaptype(Ctx&, ParseInput&); template<typename Ctx> Result<typename Ctx::RefType> reftype(Ctx&, ParseInput&); template<typename Ctx> Result<typename Ctx::ValType> valtype(Ctx&, ParseInput&); -template<typename Ctx> Result<typename Ctx::Params> param(Ctx&, ParseInput&); -template<typename Ctx> Result<typename Ctx::Results> result(Ctx&, ParseInput&); +template<typename Ctx> Result<typename Ctx::Params> params(Ctx&, ParseInput&); +template<typename Ctx> Result<typename Ctx::Results> results(Ctx&, ParseInput&); template<typename Ctx> Result<typename Ctx::FuncType> functype(Ctx&, ParseInput&); Result<Limits> limits(ParseInput&); Result<Limits> memtype(ParseInput&); template<typename Ctx> -Result<typename Ctx::TableType> tabletype(Ctx&, ParseInput&); +Result<typename Ctx::TableTypeT> tabletype(Ctx&, ParseInput&); template<typename Ctx> Result<typename Ctx::GlobalType> globaltype(Ctx&, ParseInput&); @@ -508,34 +535,44 @@ // Modules template<typename Ctx> Result<typename Ctx::TypeIdx> typeidx(Ctx&, ParseInput&); -[[maybe_unused]] Result<Name> funcidx(ParseDefsCtx&, ParseInput&); -[[maybe_unused]] Result<Name> tableidx(ParseDefsCtx&, ParseInput&); -[[maybe_unused]] Result<Memory*> memidx(ParseDefsCtx&, ParseInput&); -[[maybe_unused]] Result<Name> globalidx(ParseDefsCtx&, ParseInput&); -[[maybe_unused]] Result<Name> elemidx(ParseDefsCtx&, ParseInput&); -[[maybe_unused]] Result<Name> dataidx(ParseDefsCtx&, ParseInput&); -[[maybe_unused]] Result<Index> localidx(FuncCtx&, ParseInput&); -[[maybe_unused]] Result<Name> labelidx(FuncCtx&, ParseInput&); -template<typename Ctx> Result<> type(Ctx&, ParseInput&); -template<typename Ctx> Result<typename Ctx::TypeUse> typeuse(Ctx&, ParseInput&); -template<typename Ctx> Result<> import(Ctx&, ParseInput&); +template<typename Ctx> +Result<typename Ctx::FuncIdx> funcidx(ParseDefsCtx&, ParseInput&); +template<typename Ctx> +Result<typename Ctx::TableIdx> tableidx(ParseDefsCtx&, ParseInput&); +template<typename Ctx> +Result<typename Ctx::MemIdx> memidx(ParseDefsCtx&, ParseInput&); +template<typename Ctx> +Result<typename Ctx::GlobalIdx> globalidx(ParseDefsCtx&, ParseInput&); +template<typename Ctx> +Result<typename Ctx::ElemIdx> elemidx(ParseDefsCtx&, ParseInput&); +template<typename Ctx> +Result<typename Ctx::DataIdx> dataidx(ParseDefsCtx&, ParseInput&); +template<typename Ctx> +Result<typename Ctx::LocalIdx> localidx(FuncCtx&, ParseInput&); +template<typename Ctx> +Result<typename Ctx::LabelIdx> labelidx(FuncCtx&, ParseInput&); +template<typename Ctx> Result<bool> type(Ctx&, ParseInput&); +template<typename Ctx> +Result<typename Ctx::TypeUseT> typeuse(Ctx&, ParseInput&); +template<typename Ctx> Result<bool> import(Ctx&, ParseInput&); +// TODO: parse *all* locals, also params and results template<typename Ctx> Result<typename Ctx::Locals> local(Ctx&, ParseInput&); -Result<ImportNames> inlineImport(ParseInput&); +Result<std::optional<ImportNames>> inlineImport(ParseInput&); Result<std::vector<Name>> inlineExports(ParseInput&); -template<typename Ctx> Result<> func(Ctx&, ParseInput&); -template<typename Ctx> Result<> table(Ctx&, ParseInput&); +template<typename Ctx> Result<bool> func(Ctx&, ParseInput&); template<typename Ctx> Result<typename Ctx::DataStr> datastring(Ctx&, ParseInput&); -template<typename Ctx> Result<> table(Ctx&, ParseInput&); -template<typename Ctx> Result<> mem(Ctx&, ParseInput&); -template<typename Ctx> Result<> global(Ctx&, ParseInput&); -Result<> modulefield(ParseDeclsCtx&, ParseInput&); +template<typename Ctx> Result<bool> table(Ctx&, ParseInput&); +template<typename Ctx> Result<bool> mem(Ctx&, ParseInput&); +template<typename Ctx> Result<bool> global(Ctx&, ParseInput&); +Result<bool> modulefield(ParseDeclsCtx&, ParseInput&); Result<> module(ParseDeclsCtx&, ParseInput&); // Utilities void applyImportNames(Importable& item, const std::optional<ImportNames>& names); -Result<> addExports(Module& wasm, +Result<> addExports(ParseInput& in, + Module& wasm, const Named* item, const std::vector<Name>& exports, ExternalKind kind); @@ -552,10 +589,9 @@ if (in.takeKeyword("extern"sv)) { RETURN_OR_OK(HeapType::any); } - if (auto type = typeidx(ctx, in)) { - return *type; - } - return {}; + auto type = typeidx(ctx, in); + CHECK_ERR(type); + return *type; } // reftype ::= 'funcref' => funcref @@ -564,29 +600,27 @@ template<typename Ctx> Result<typename Ctx::RefType> reftype(Ctx& ctx, ParseInput& in) { if (in.takeKeyword("funcref"sv)) { - RETURN_OR_OK(Type(HeapType::func, Nullable)); + RETURN_OR_TRUE(Type(HeapType::func, Nullable)); } if (in.takeKeyword("externref"sv)) { - RETURN_OR_OK(Type(HeapType::func, Nullable)); + RETURN_OR_TRUE(Type(HeapType::func, Nullable)); } if (!in.takeSExprStart("ref"sv)) { - return {}; + RETURN_NONE(); } auto nullability = in.takeKeyword("null"sv) ? Nullable : NonNullable; auto type = heaptype(ctx, in); - if (!type) { - return {}; - } + CHECK_ERR(type); if (!in.takeRParen()) { - return {}; + return in.err("expected end of reftype"); } if constexpr (parsingDecls<Ctx>) { - return Ok{}; + return true; } else if constexpr (parsingTypes<Ctx>) { return ctx.builder.getTempRefType(*type, nullability); } else { @@ -606,112 +640,133 @@ Result<typename Ctx::ValType> valtype(Ctx& ctx, ParseInput& in) { if (in.takeKeyword("i32"sv)) { RETURN_OR_OK(Type::i32); - } - if (in.takeKeyword("i64"sv)) { + } else if (in.takeKeyword("i64"sv)) { RETURN_OR_OK(Type::i64); - } - if (in.takeKeyword("f32"sv)) { + } else if (in.takeKeyword("f32"sv)) { RETURN_OR_OK(Type::f32); - } - if (in.takeKeyword("f64"sv)) { + } else if (in.takeKeyword("f64"sv)) { RETURN_OR_OK(Type::f64); - } - if (in.takeKeyword("v128"sv)) { + } else if (in.takeKeyword("v128"sv)) { RETURN_OR_OK(Type::v128); + } else if (auto type = reftype(ctx, in)) { + CHECK_ERR(type); + RETURN_OR_OK(**type); + } else { + return in.err("expected valtype"); } - if (auto type = reftype(ctx, in)) { - return *type; - } - return {}; } -// param ::= '(' 'param id? t:valtype ')' => [t] -// | '(' 'param t*:valtype* ')' => [t*] +// param ::= '(' 'param id? t:valtype ')' => [t] +// | '(' 'param t*:valtype* ')' => [t*] +// params ::= param* template<typename Ctx> -Result<typename Ctx::Params> param(Ctx& ctx, ParseInput& in) { - if (!in.takeSExprStart("param"sv)) { - return {}; - } +Result<typename Ctx::Params> params(Ctx& ctx, ParseInput& in) { + bool hasAny = false; + std::vector<NameType> res; + while (in.takeSExprStart("param"sv)) { + hasAny = true; + if (auto id = in.takeID()) { + // Single named param + auto type = valtype(ctx, in); + CHECK_ERR(type); - // Single named param - if (auto id = in.takeID()) { - auto type = valtype(ctx, in); - if (!type) { - return {}; - } - if (!in.takeRParen()) { - return {}; - } - if constexpr (parsingDecls<Ctx>) { - return Ok{}; + if (!in.takeRParen()) { + return in.err("expected end of param"); + } + + if constexpr (!parsingDecls<Ctx>) { + res.push_back({*id, *type}); + } } else { - return {{{*id, *type}}}; + // Repeated unnamed params + while (!in.takeRParen()) { + auto type = valtype(ctx, in); + CHECK_ERR(type); + + if constexpr (!parsingDecls<Ctx>) { + res.push_back({Name(), *type}); + } + } } } - // Repeated unnamed params - std::vector<NameType> params; - while (!in.takeRParen()) { - auto type = valtype(ctx, in); - if (!type) { - return {}; + if constexpr (parsingDecls<Ctx>) { + return hasAny; + } else { + if (hasAny) { + return {res}; } - if constexpr (parsingTypes<Ctx>) { - params.push_back({Name(), *type}); - } + return std::nullopt; } - - RETURN_OR_OK(params); } -// result ::= '(' 'result' t*:valtype ')' => [t*] +// result ::= '(' 'result' t*:valtype ')' => [t*] +// results ::= result* template<typename Ctx> -Result<typename Ctx::Results> result(Ctx& ctx, ParseInput& in) { - if (!in.takeSExprStart("result"sv)) { - return {}; - } +Result<typename Ctx::Results> results(Ctx& ctx, ParseInput& in) { + bool hasAny = false; + std::vector<Type> res; + while (in.takeSExprStart("result"sv)) { + hasAny = true; + while (!in.takeRParen()) { + auto type = valtype(ctx, in); + CHECK_ERR(type); - std::vector<Type> results; - while (!in.takeRParen()) { - auto type = valtype(ctx, in); - if (!type) { - return {}; - } - if constexpr (!parsingDecls<Ctx>) { - results.push_back(*type); + if constexpr (!parsingDecls<Ctx>) { + res.push_back(*type); + } } } - RETURN_OR_OK(results); + if constexpr (parsingDecls<Ctx>) { + return hasAny; + } else { + if (hasAny) { + return {res}; + } + return std::nullopt; + } } // functype ::= '(' 'func' t1*:vec(param) t2*:vec(result) ')' => [t1*] -> [t2*] template<typename Ctx> Result<typename Ctx::FuncType> functype(Ctx& ctx, ParseInput& in) { if (!in.takeSExprStart("func"sv)) { - return {}; - } - - std::vector<Type> params, results; - while (auto newParams = param(ctx, in)) { - if constexpr (!parsingDecls<Ctx>) { - for (auto& p : *newParams) { - params.push_back(p.type); - } + if constexpr (parsingDecls<Ctx>) { + return false; + } else { + return std::nullopt; } } - while (auto newResults = result(ctx, in)) { - if constexpr (!parsingDecls<Ctx>) { - results.insert(results.end(), newResults->begin(), newResults->end()); - } - } + auto namedParams = params(ctx, in); + CHECK_ERR(namedParams); + + auto resultTypes = results(ctx, in); + CHECK_ERR(resultTypes); if (!in.takeRParen()) { - return {}; + return in.err("expected end of functype"); } - RETURN_OR_OK(Signature(Type(params), Type(results))); + std::vector<Type> paramTypes; + if constexpr (!parsingDecls<Ctx>) { + if (*namedParams) { + paramTypes.reserve((*namedParams)->size()); + for (auto& param : **namedParams) { + paramTypes.push_back(param.type); + } + } + if (!resultTypes) { + resultTypes = std::vector<Type>(); + } + } + + if constexpr (parsingDecls<Ctx>) { + return true; + } else { + return Signature(Type(paramTypes), Type(**resultTypes)); + } } // limits ::= n:u32 => { min n, max _ } @@ -719,9 +774,8 @@ Result<Limits> limits(ParseInput& in) { auto min = in.takeU32(); if (!min) { - return {}; + return in.err("expected limits minimum"); } - return {{*min, in.takeU32()}}; } @@ -730,22 +784,15 @@ // tabletype ::= lim:limits et:reftype => lim et template<typename Ctx> -Result<typename Ctx::TableType> tabletype(Ctx& ctx, ParseInput& in) { +Result<typename Ctx::TableTypeT> tabletype(Ctx& ctx, ParseInput& in) { auto lim = limits(in); - if (!lim) { - return {}; - } - + CHECK_ERR(lim); auto type = reftype(ctx, in); + CHECK_ERR(type); if (!type) { - return {}; + return in.err("expected reftype"); } - - if constexpr (parsingDecls<Ctx>) { - return Ok{}; - } else if constexpr (parsingDefs<Ctx>) { - return {{*lim, *type}}; - } + RETURN_OR_OK((TableType{*lim, **type})); } // globaltype ::= t:valtype => const t @@ -753,227 +800,231 @@ template<typename Ctx> Result<typename Ctx::GlobalType> globaltype(Ctx& ctx, ParseInput& in) { // t:valtype - if (auto type = valtype(ctx, in)) { - if constexpr (parsingDecls<Ctx>) { - return Ok{}; - } else if constexpr (parsingDefs<Ctx>) { - return {{Immutable, *type}}; - } + auto type = valtype(ctx, in); + if (type.ok()) { + RETURN_OR_OK((DefinedGlobalType{Immutable, *type})); } // '(' 'mut' t:valtype ')' if (!in.takeSExprStart("mut"sv)) { - return {}; + return *type.getErr(); } - auto type = valtype(ctx, in); - if (!type) { - return {}; - } + auto mutType = valtype(ctx, in); + CHECK_ERR(mutType); + if (!in.takeRParen()) { - return {}; + return in.err("expected end of globaltype"); } - if constexpr (parsingDecls<Ctx>) { - return Ok{}; - } else if constexpr (parsingDefs<Ctx>) { - return {{Mutable, *type}}; - } + RETURN_OR_OK((DefinedGlobalType{Mutable, *mutType})); } // TODO -template<typename Ctx> Result<typename Ctx::Instrs> instrs(Ctx&, ParseInput&) { - return {}; +template<typename Ctx> +Result<typename Ctx::Instrs> instrs(Ctx& ctx, ParseInput& in) { + return in.err("TODO: instrs"); } // expr ::= (in:instr)* => in* end -template<typename Ctx> Result<typename Ctx::Instrs> expr(Ctx&, ParseInput&) { +template<typename Ctx> +Result<typename Ctx::Instrs> expr(Ctx& ctx, ParseInput& in) { // TODO - return {}; + return instrs(ctx, in); } // typeidx ::= x:u32 => x // | v:id => x (if types[x] = v) template<typename Ctx> Result<typename Ctx::TypeIdx> typeidx(Ctx& ctx, ParseInput& in) { - if constexpr (parsingDecls<Ctx>) { - if (in.takeU32() || in.takeID()) { - return Ok{}; - } - return {}; - } else { - Index index; - if (auto x = in.takeU32()) { - index = *x; - } else if (auto id = in.takeID()) { + Index index; + if (auto x = in.takeU32()) { + index = *x; + } else if (auto id = in.takeID()) { + if constexpr (!parsingDecls<Ctx>) { auto it = ctx.typeIndices.find(*id); if (it == ctx.typeIndices.end()) { - return {}; + return in.err("unknown type identifier"); } index = it->second; - } else { - return {}; } - if (auto type = ctx.getHeapType(index)) { - return *type; - } - return {}; + } else { + return in.err("expected type index or identifier"); } + + if constexpr (parsingDecls<Ctx>) { + return Ok{}; + } else if constexpr (parsingTypes<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]; + } +} + +template<typename Elems> +Result<Name> getModuleElementByIdx(ParseInput& in, + const Elems& elements, + Index i, + std::string kind) { + if (i < elements.size()) { + return elements[i]; + } + return in.err(kind + " index out of bounds"); +} + +template<typename Elems> +Result<Name> getModuleElementByID(ParseInput& in, + const Elems& elements, + const IndexMap& indices, + Name id, + std::string kind) { + if (auto it = indices.find(id); it != indices.end()) { + return elements[it->second]->name; + } + return in.err("unknown " + kind + " identifier"); } // funcidx ::= x:u32 => x // | v:id => x (if funcs[x] = v) -Result<Name> funcidx(ParseDefsCtx& ctx, ParseInput& in) { - if (auto x = in.takeU32()) { - if (*x > ctx.wasm.functions.size()) { - return {}; - } - return ctx.wasm.functions[*x]->name; +template<typename Ctx> +Result<typename Ctx::FuncIdx> funcidx(ParseDefsCtx& ctx, ParseInput& in) { + if (auto i = in.takeU32()) { + RETURN_OR_OK(getModuleElementByIdx(in, ctx.wasm.functions, *i, "function")); } if (auto id = in.takeID()) { - if (auto func = ctx.wasm.getFunctionOrNull(*id)) { - return func->name; - } - return {}; + RETURN_OR_OK(getModuleElementByID( + in, ctx.wasm.functions, ctx.funcIndices, *id, "function")); } - return {}; + return in.err("expected function index or identifier"); } // tableidx ::= x:u32 => x // | v:id => x (if tables[x] = v) -Result<Name> tableidx(ParseDefsCtx& ctx, ParseInput& in) { - if (auto x = in.takeU32()) { - if (*x > ctx.wasm.tables.size()) { - return {}; - } - return ctx.wasm.tables[*x]->name; +template<typename Ctx> +Result<typename Ctx::TableIdx> tableidx(ParseDefsCtx& ctx, ParseInput& in) { + if (auto i = in.takeU32()) { + RETURN_OR_OK(getModuleElementByIdx(in, ctx.wasm.tables, *i, "table")); } if (auto id = in.takeID()) { - if (auto table = ctx.wasm.getTableOrNull(*id)) { - return table->name; - } - return {}; + RETURN_OR_OK(getModuleElementByID( + in, ctx.wasm.tables, ctx.tableIndices, *id, "table")); } - return {}; + return in.err("expected table index or identifier"); } // memidx ::= x:u32 => x // | v:id => x (if mems[x] = v) -Result<Memory*> memidx(ParseDefsCtx& ctx, ParseInput& in) { +template<typename Ctx> +Result<typename Ctx::MemIdx> memidx(ParseDefsCtx& ctx, ParseInput& in) { if (auto x = in.takeU32()) { - if (ctx.wasm.memory.exists && *x == 0) { - return &ctx.wasm.memory; + if constexpr (parsingDecls<Ctx>) { + return Ok{}; + } else { + if (ctx.wasm.memory.exists && *x == 0) { + return &ctx.wasm.memory; + } + return in.err("memory index out of bounds"); } - return {}; } + if (auto id = in.takeID()) { - if (ctx.wasm.memory.exists && ctx.wasm.memory.name == *id) { - return &ctx.wasm.memory; + if constexpr (parsingDecls<Ctx>) { + return Ok{}; + } else { + if (ctx.wasm.memory.exists && ctx.wasm.memory.name == *id) { + return &ctx.wasm.memory; + } + return in.err("unknown memory identifier"); } - return {}; } - return {}; + + return in.err("expected memory index or identifier"); } // globalidx ::= x:u32 => x // | v:id => x (if globals[x] = v) -Result<Name> globalidx(ParseDefsCtx& ctx, ParseInput& in) { - if (auto x = in.takeU32()) { - if (*x > ctx.wasm.globals.size()) { - return {}; - } - return ctx.wasm.globals[*x]->name; +template<typename Ctx> +Result<typename Ctx::GlobalIdx> globalidx(ParseDefsCtx& ctx, ParseInput& in) { + if (auto i = in.takeU32()) { + RETURN_OR_OK(getModuleElementByIdx(in, ctx.wasm.globals, *i, "global")); } if (auto id = in.takeID()) { - if (auto global = ctx.wasm.getGlobalOrNull(*id)) { - return global->name; - } - return {}; + RETURN_OR_OK(getModuleElementByID( + in, ctx.wasm.globals, ctx.globalIndices, *id, "global")); } - return {}; + return in.err("expected global index or identifier"); } // elemidx ::= x:u32 => x // | v:id => x (if elem[x] = v) -Result<Name> elemidx(ParseDefsCtx& ctx, ParseInput& in) { - if (auto x = in.takeU32()) { - if (*x > ctx.wasm.elementSegments.size()) { - return {}; - } - return ctx.wasm.elementSegments[*x]->name; +template<typename Ctx> +Result<typename Ctx::ElemIdx> elemidx(ParseDefsCtx& ctx, ParseInput& in) { + if (auto i = in.takeU32()) { + RETURN_OR_OK( + getModuleElementByIdx(in, ctx.wasm.elementSegments, *i, "elem segment")); } + if (auto id = in.takeID()) { - if (auto elem = ctx.wasm.getElementSegmentOrNull(*id)) { - return elem->name; - } - return {}; + RETURN_OR_OK(getModuleElementByID( + in, ctx.wasm.elementSegments, ctx.elemIndices, *id, "elem segment")); } - return {}; + return in.err("expected elem segment index or identifier"); } // dataidx ::= x:u32 => x // | v:id => x (if data[x] = v) -Result<Name> dataidx(ParseDefsCtx& ctx, ParseInput& in) { - if (auto x = in.takeU32()) { - if (*x > ctx.wasm.memory.segments.size()) { - return {}; - } - return ctx.wasm.memory.segments[*x].name; - } - if (auto id = in.takeID()) { - for (auto& seg : ctx.wasm.memory.segments) { - if (seg.name == *id) { - return seg.name; +template<typename Ctx> +Result<typename Ctx::DataIdx> dataidx(ParseDefsCtx& ctx, ParseInput& in) { + if (auto i = in.takeU32()) { + if constexpr (parsingDecls<Ctx>) { + return Ok{}; + } else { + if (*i < ctx.wasm.memory.segments.size()) { + return *i; } + return in.err("data segment index out of bounds"); } - return {}; } - return {}; + + if (auto id = in.takeID()) { + if constexpr (parsingDecls<Ctx>) { + return Ok{}; + } else { + if (auto it = ctx.dataIndices.find(*id); it != ctx.dataIndices.end()) { + return it->second; + } + return in.err("unknown data segment identifier"); + } + } + + return in.err("expected data segment index or identifier"); } // localidx ::= x:u32 => x // | v:id => x (if locals[x] = v) -Result<Index> localidx(FuncCtx& ctx, ParseInput& in) { - if (auto x = in.takeU32()) { - if (*x > ctx.func.getNumLocals()) { - return {}; - } - return *x; - } - if (auto id = in.takeID()) { - auto it = ctx.func.localIndices.find(*id); - if (it == ctx.func.localIndices.end()) { - return {}; - } - return it->second; - } - return {}; +template<typename Ctx> +Result<typename Ctx::LocalIdx> localidx(FuncCtx& ctx, ParseInput& in) { + return in.err("TODO: localidx"); } // labelidx ::= x:u32 => x // | v:id => x (if labels[x] = v) -Result<Name> labelidx(FuncCtx& ctx, ParseInput& in) { - if (auto x = in.takeU32()) { - if (*x > ctx.labels.labelStack.size()) { - return {}; - } - return *(ctx.labels.labelStack.end() - *x); - } - if (auto id = in.takeID()) { - auto it = ctx.labels.labelMappings.find(*id); - if (it == ctx.labels.labelMappings.end()) { - return {}; - } - return it->second.back(); - } - return {}; +template<typename Ctx> +Result<typename Ctx::LabelIdx> labelidx(FuncCtx& ctx, ParseInput& in) { + return in.err("TODO: labelidx"); } // type ::= '(' 'type' id? ft:functype ')' => ft -template<typename Ctx> Result<> type(Ctx& ctx, ParseInput& in) { +template<typename Ctx> Result<bool> type(Ctx& ctx, ParseInput& in) { auto start = in.getPos(); if (!in.takeSExprStart("type"sv)) { - return {}; + return false; } Name name; @@ -981,22 +1032,23 @@ name = *id; } - auto type = functype(ctx, in); - if (!type) { - return {}; + if (auto type = functype(ctx, in)) { + CHECK_ERR(type); + if constexpr (parsingTypes<Ctx>) { + ctx.builder[ctx.index] = **type; + } + } else { + return in.err("expected type description"); } if (!in.takeRParen()) { - return {}; + return in.err("expected end of type definition"); } if constexpr (parsingDecls<Ctx>) { ctx.explicitTypeDefs.push_back({name, in.getSpanSince(start)}); - return Ok{}; - } else if constexpr (parsingTypes<Ctx>) { - ctx.builder[ctx.index] = *type; - return Ok{}; } + return true; } // typeuse ::= '(' 'type' x:typeidx ')' => x, [] @@ -1006,66 +1058,66 @@ // | ((t1,IDs):param)* (t2:result)* => x, IDs // (if x is minimum s.t. typedefs[x] = [t1*] -> [t2*]) template<typename Ctx> -Result<typename Ctx::TypeUse> typeuse(Ctx& ctx, ParseInput& in) { +Result<typename Ctx::TypeUseT> typeuse(Ctx& ctx, ParseInput& in) { auto start = in.getPos(); std::optional<typename Ctx::TypeIdx> type; if (in.takeSExprStart("type"sv)) { auto x = typeidx(ctx, in); - if (!x) { - return {}; - } + CHECK_ERR(x); + if (!in.takeRParen()) { - return {}; + return in.err("expected end of type use"); } + type = *x; } - bool hasSig = !type; - std::vector<Type> params; - std::vector<IndexedName> ids; - Index index = 0; - while (auto nametypes = param(ctx, in)) { - hasSig = true; - if constexpr (!parsingDecls<Ctx>) { - for (auto& nametype : *nametypes) { - if (nametype.name.is()) { - ids.push_back({index, nametype.name}); - } - params.push_back(nametype.type); - ++index; - } - } - } + auto namedParams = params(ctx, in); + CHECK_ERR(namedParams); - std::vector<Type> results; - while (auto types = result(ctx, in)) { - hasSig = true; - if constexpr (!parsingDecls<Ctx>) { - results.insert(results.end(), types->begin(), types->end()); - } - } + auto resultTypes = results(ctx, in); + CHECK_ERR(resultTypes); + + bool hasSig = !type || namedParams || resultTypes; if constexpr (parsingDecls<Ctx>) { if (hasSig) { ctx.implicitTypeDefs.push_back(in.getSpanSince(start)); } return Ok{}; - } else if constexpr (parsingImplicitTypes<Ctx>) { - return Signature(Type(params), Type(results)); - } else if constexpr (parsingDefs<Ctx>) { - Signature sig{Type(params), Type(results)}; - if (type) { - if (!type->isSignature()) { - return {}; + } else { + std::vector<Type> paramTypes; + if (namedParams) { + paramTypes.reserve((*namedParams)->size()); + for (auto& param : **namedParams) { + paramTypes.push_back(param.type); } - if (hasSig) { - if (type->getSignature() != sig) { - return {}; - } - } - return {{*type, ids}}; } - return {{ctx.types[ctx.signatureIndices.at(sig)], ids}}; + if (!resultTypes) { + resultTypes = std::vector<Type>{}; + } + Signature sig{Type(paramTypes), Type(**resultTypes)}; + if constexpr (parsingImplicitTypes<Ctx>) { + return sig; + } else if constexpr (parsingDefs<Ctx>) { + if (!*namedParams) { + *namedParams = std::vector<NameType>{}; + } + if (type) { + if (!type->isSignature()) { + // TODO: Fix error position to be `start` + return in.err("type is not a signature"); + } + if (hasSig) { + if (type->getSignature() != sig) { + // TODO: Fix error position to be `start` + return in.err("type does not match signature"); + } + } + return {{*type, **namedParams}}; + } + return {{ctx.types[ctx.signatureIndices.at(sig)], **namedParams}}; + } } } @@ -1075,35 +1127,34 @@ // | '(' 'table' id? tt:tabletype ')' => table tt // | '(' 'memory' id? mt:memtype ')' => mem mt // | '(' 'global' id? gt:globaltype ')' => global gt -template<typename Ctx> Result<> import(Ctx& ctx, ParseInput& in) { +template<typename Ctx> Result<bool> import(Ctx& ctx, ParseInput& in) { auto start = in.getPos(); if (!in.takeSExprStart("import"sv)) { - return {}; + return false; } if constexpr (parsingDecls<Ctx>) { if (ctx.hasNonImport) { - return {}; + return in.err("import after non-import"); } } auto mod = in.takeName(); if (!mod) { - return {}; + return in.err("expected import module"); } auto nm = in.takeName(); if (!nm) { - return {}; + return in.err("expected import name"); } if (in.takeSExprStart("func"sv)) { [[maybe_unused]] auto id = in.takeID(); auto type = typeuse(ctx, in); - if (!type) { - return {}; - } + CHECK_ERR(type); + if constexpr (parsingDecls<Ctx>) { // TODO: Insert import. } else { @@ -1112,9 +1163,8 @@ } else if (in.takeSExprStart("table"sv)) { [[maybe_unused]] auto id = in.takeID(); auto type = tabletype(ctx, in); - if (!type) { - return {}; - } + CHECK_ERR(type); + if constexpr (parsingDecls<Ctx>) { // TODO: Insert import. } else { @@ -1123,9 +1173,8 @@ } else if (in.takeSExprStart("memory"sv)) { [[maybe_unused]] auto id = in.takeID(); auto type = memtype(in); - if (!type) { - return {}; - } + CHECK_ERR(type); + if constexpr (parsingDecls<Ctx>) { // TODO: Insert import. } else { @@ -1134,90 +1183,55 @@ } else if (in.takeSExprStart("global"sv)) { [[maybe_unused]] auto id = in.takeID(); auto type = globaltype(ctx, in); - if (!type) { - return {}; - } + CHECK_ERR(type); + if constexpr (parsingDecls<Ctx>) { // TODO: Insert import. } else { // TODO: Fill out import. } } else { - return {}; + return in.err("expected import description"); } if (!in.takeRParen()) { - return {}; + return in.err("expected end of import description"); } if (!in.takeRParen()) { - return {}; + return in.err("expected end of import"); } if constexpr (parsingDecls<Ctx>) { ctx.importDefs.push_back({{}, in.getSpanSince(start)}); } - return Ok{}; + return true; } // local ::= '(' 'local' id t:valtype ')' => [(t, id)] // | '(' 'local' (t:valtype)* ')' => [t*] template<typename Ctx> Result<typename Ctx::Locals> local(Ctx& ctx, ParseInput& in) { - if (!in.takeSExprStart("local"sv)) { - return {}; - } - - if (auto id = in.takeID()) { - auto type = valtype(ctx, in); - if (!type) { - return {}; - } - if (!in.takeRParen()) { - return {}; - } - if constexpr (parsingDecls<Ctx>) { - return Ok{}; - } else if constexpr (parsingDefs<Ctx>) { - return {{{*id, *type}}}; - } - } - - std::vector<NameType> locals; - while (!in.takeRParen()) { - auto type = valtype(ctx, in); - if (!type) { - return {}; - } - if constexpr (parsingDefs<Ctx>) { - locals.push_back({Name(), *type}); - } - } - if constexpr (parsingDecls<Ctx>) { - return Ok{}; - } else if constexpr (parsingDefs<Ctx>) { - return locals; - } + return in.err("TODO: local"); } -Result<ImportNames> inlineImport(ParseInput& in) { +Result<std::optional<ImportNames>> inlineImport(ParseInput& in) { if (!in.takeSExprStart("import"sv)) { - // TODO: Differentiate between absence and errors at call sites. - return {}; + return std::nullopt; } auto mod = in.takeName(); if (!mod) { - return {}; + return in.err("expected import module"); } auto nm = in.takeName(); if (!nm) { - return {}; + return in.err("expected import name"); } if (!in.takeRParen()) { - return {}; + return in.err("expected end of import"); } - return {{*mod, *nm}}; + return {{{*mod, *nm}}}; } Result<std::vector<Name>> inlineExports(ParseInput& in) { @@ -1225,10 +1239,10 @@ while (in.takeSExprStart("export"sv)) { auto name = in.takeName(); if (!name) { - return {}; + return in.err("expected export name"); } if (!in.takeRParen()) { - return {}; + return in.err("expected end of import"); } exports.push_back(*name); } @@ -1239,30 +1253,27 @@ // x:typeuse (t:local)* (in:instr)* ')' // ::= '(' 'func' id? ('(' 'export' name ')')* // '(' 'import' mod:name nm:name ')' x:typeuse ')' -template<typename Ctx> Result<> func(Ctx& ctx, ParseInput& in) { +template<typename Ctx> Result<bool> func(Ctx& ctx, ParseInput& in) { auto start = in.getPos(); if (!in.takeSExprStart("func"sv)) { - return {}; + return false; } [[maybe_unused]] auto id = in.takeID(); auto exports = inlineExports(in); - if (!exports) { - return {}; - } + CHECK_ERR(exports); auto import = inlineImport(in); + CHECK_ERR(import); auto type = typeuse(ctx, in); - if (!type) { - return {}; - } + CHECK_ERR(type); if (import) { if (!in.takeRParen()) { - return {}; + return in.err("expected end of function"); } if constexpr (parsingDecls<Ctx>) { ctx.funcDefs.push_back({{}, in.getSpanSince(start)}); @@ -1271,20 +1282,19 @@ } else if constexpr (parsingDefs<Ctx>) { // TODO: Set import type } - return Ok{}; + return true; } while (auto locs = local(ctx, in)) { + CHECK_ERR(locs); // TODO: collect and install locals } auto body = instrs(ctx, in); - if (!body) { - return {}; - } + CHECK_ERR(body); if (!in.takeRParen()) { - return {}; + return in.err("expected end of function"); } if constexpr (parsingDecls<Ctx>) { @@ -1294,7 +1304,7 @@ } else if constexpr (parsingDefs<Ctx>) { // TODO: Set function type, params, locals, body } - return Ok{}; + return true; } // table ::= '(' 'table' id? ('(' 'export' name ')')* tt:tabletype ')' @@ -1304,20 +1314,9 @@ // '(' 'elem' (f:funcidx)* ')' ')' // | '(' 'table' id? ('(' 'export' name ')')* // '(' 'import' mod:name nm:name ')' tt:tabletype ')' -template<typename Ctx> Result<> table(Ctx& ctx, ParseInput& in) { - // if (!in.takeSExprStart("table"sv)) { - // return {}; - // } - - // auto id = in.takeID(); - - // auto exports = inlineExports(in); - // if (!exports) { - // return {}; - // } - +template<typename Ctx> Result<bool> table(Ctx& ctx, ParseInput& in) { // TODO - return {}; + return false; } // datastring ::= (b:string)* => concat(b*) @@ -1329,11 +1328,7 @@ data.insert(data.end(), str->begin(), str->end()); } } - if constexpr (parsingDecls<Ctx>) { - return Ok{}; - } else if constexpr (parsingDefs<Ctx>) { - return data; - } + RETURN_OR_OK(data); } // mem ::= '(' 'memory' id? ('(' 'export' name ')')* mt:memtype ')' @@ -1341,71 +1336,55 @@ // '(' 'data' b:datastring ')' ')' // | '(' 'memory' id? ('(' 'export' name ')')* // '(' 'import' mod:name nm:name ')' mt:memtype ')' -template<typename Ctx> Result<> mem(Ctx& ctx, ParseInput& in) { +template<typename Ctx> Result<bool> mem(Ctx& ctx, ParseInput& in) { auto start = in.getPos(); if (!in.takeSExprStart("memory"sv)) { - return {}; + return false; } [[maybe_unused]] auto id = in.takeID(); auto exports = inlineExports(in); - if (!exports) { - return {}; - } + CHECK_ERR(exports); auto import = inlineImport(in); + CHECK_ERR(import); - auto type = memtype(in); - - if (import) { - if (!type) { - return {}; - } - if (!in.takeRParen()) { - return {}; - } - if constexpr (parsingDecls<Ctx>) { - ctx.memDefs.push_back({{}, in.getSpanSince(start)}); - // TODO: Insert import - // TODO: Add exports - } else if constexpr (parsingDefs<Ctx>) { - // TODO: Set import type - } - return Ok{}; - } - - if (type) { - if (!in.takeRParen()) { - return {}; - } - if constexpr (parsingDecls<Ctx>) { - ctx.memDefs.push_back({{}, in.getSpanSince(start)}); - // TODO: Insert mem - } - } else { - if (!in.takeSExprStart("data"sv)) { - return {}; - } + if (!import && in.takeSExprStart("data"sv)) { [[maybe_unused]] auto data = datastring(ctx, in); if (!in.takeRParen()) { - return {}; + return in.err("expected end of data"); } if (!in.takeRParen()) { - return {}; + return in.err("expected end of memory"); } if constexpr (parsingDecls<Ctx>) { ctx.memDefs.push_back({{}, in.getSpanSince(start)}); // TODO: Insert mem - // TODO: Add exports; + // TODO: Add exports } else if constexpr (parsingDefs<Ctx>) { // TODO: Add data } } - return Ok{}; + + auto type = memtype(in); + CHECK_ERR(type); + + if (!in.takeRParen()) { + return in.err("expected end of memory"); + } + + if constexpr (parsingDecls<Ctx>) { + ctx.memDefs.push_back({{}, in.getSpanSince(start)}); + // TODO: Insert possibly-imported memory + // TODO: Add exports + } + + return true; } Result<Global*> addGlobalDecl(ParseDeclsCtx& ctx, + ParseInput& in, Name name, std::optional<ImportNames> importNames) { auto g = std::make_unique<Global>(); @@ -1413,7 +1392,8 @@ if (ctx.wasm.getGlobalOrNull(name)) { // TDOO: if the existing global is not explicitly named, fix its name and // continue. - return {}; + // TODO: Fix error location to point to name. + return in.err("repeated global name"); } g->setExplicitName(name); } else { @@ -1435,10 +1415,10 @@ // global ::= '(' 'global' id? ('(' 'export' name ')')* gt:globaltype e:expr ')' // | '(' 'global' id? '(' 'import' mod:name nm:name ')' // gt:globaltype ')' -template<typename Ctx> Result<> global(Ctx& ctx, ParseInput& in) { +template<typename Ctx> Result<bool> global(Ctx& ctx, ParseInput& in) { auto start = in.getPos(); if (!in.takeSExprStart("global"sv)) { - return {}; + return false; } Name name; @@ -1447,59 +1427,52 @@ } auto exports = inlineExports(in); - if (!exports) { - return {}; - } + CHECK_ERR(exports); auto import = inlineImport(in); + CHECK_ERR(import); auto gtype = globaltype(ctx, in); - if (!gtype) { - return {}; - } + CHECK_ERR(gtype); if (import) { if (!in.takeRParen()) { - return {}; + return in.err("expected end of global"); } if constexpr (parsingDecls<Ctx>) { if (ctx.hasNonImport) { - return {}; + return in.err("import after non-import"); } - auto g = addGlobalDecl(ctx, name, *import); - if (!g) { - return {}; - } - if (!addExports(ctx.wasm, *g, *exports, ExternalKind::Global)) { - return {}; - } + auto g = addGlobalDecl(ctx, in, name, *import); + CHECK_ERR(g); + + auto added = addExports(in, ctx.wasm, *g, *exports, ExternalKind::Global); + CHECK_ERR(added); + ctx.globalDefs.push_back({name, in.getSpanSince(start)}); } else { finishGlobalDef(*ctx.wasm.globals[ctx.index], *gtype, nullptr); } - return Ok{}; + return true; } auto exp = expr(ctx, in); - if (!exp) { - return {}; - } + CHECK_ERR(exp); if (!in.takeRParen()) { - return {}; + return in.err("expected end of global"); } if constexpr (parsingDecls<Ctx>) { ctx.hasNonImport = true; - if (!addGlobalDecl(ctx, name, {})) { - return {}; - } + auto g = addGlobalDecl(ctx, in, name, {}); + CHECK_ERR(g); ctx.globalDefs.push_back({name, in.getSpanSince(start)}); } else { finishGlobalDef(*ctx.wasm.globals[ctx.index], *gtype, *exp); } - return Ok{}; + return true; } // export ::= '(' 'export' nm:name d:exportdesc ')' @@ -1533,38 +1506,51 @@ // | start // | elem // | data -Result<> modulefield(ParseDeclsCtx& ctx, ParseInput& in) { - if (type(ctx, in)) { - return Ok{}; +Result<bool> modulefield(ParseDeclsCtx& ctx, ParseInput& in) { + if (auto t = in.peek(); !t || t->isRParen()) { + return false; } - if (import(ctx, in)) { - return Ok{}; + if (auto res = type(ctx, in)) { + CHECK_ERR(res); + return true; } - if (func(ctx, in)) { - return Ok{}; + if (auto res = import(ctx, in)) { + CHECK_ERR(res); + return true; } - if (table(ctx, in)) { - return Ok{}; + if (auto res = func(ctx, in)) { + CHECK_ERR(res); + return true; } - if (mem(ctx, in)) { - return Ok{}; + if (auto res = table(ctx, in)) { + CHECK_ERR(res); + return true; } - if (global(ctx, in)) { - return Ok{}; + if (auto res = mem(ctx, in)) { + CHECK_ERR(res); + return true; } - // if (export(ctx, in)) { - // return Ok{}; + if (auto res = global(ctx, in)) { + CHECK_ERR(res); + return true; + } + // if (auto res = export(ctx, in)) { + // CHECK_ERR(res); + // return true; // } - // if (start(ctx, in)) { - // return Ok{}; + // if (auto res = start(ctx, in)) { + // CHECK_ERR(res); + // return true; // } - // if (elem(ctx, in)) { - // return Ok{}; + // if (auto res = elem(ctx, in)) { + // CHECK_ERR(res); + // return true; // } - // if (data(ctx, in)) { - // return Ok{}; + // if (auto res = data(ctx, in)) { + // CHECK_ERR(res); + // return true; // } - return {}; + return in.err("unrecognized module field"); } // module ::= '(' 'module' id? (m:modulefield)* ')' @@ -1578,11 +1564,12 @@ } } - while (modulefield(ctx, in)) { + while (auto field = modulefield(ctx, in)) { + CHECK_ERR(field); } if (outer && !in.takeRParen()) { - return {}; + return in.err("expected end of module"); } return Ok{}; @@ -1596,13 +1583,15 @@ } } -Result<> addExports(Module& wasm, +Result<> addExports(ParseInput& in, + Module& wasm, const Named* item, const std::vector<Name>& exports, ExternalKind kind) { for (auto name : exports) { if (wasm.getExportOrNull(name)) { - return {}; + // TODO: Fix error location + return in.err("repeated export name"); } wasm.addExport(Builder(wasm).makeExport(name, item->name, kind)); } @@ -1615,7 +1604,8 @@ if (defs[i].name.is()) { bool inserted = indices.insert({defs[i].name, i}).second; if (!inserted) { - return {}; + // TODO: improve this message. + return Err{"error: duplicate element"}; } } } @@ -1629,18 +1619,14 @@ ParseDeclsCtx decls(wasm); { ParseInput in(input); - if (!module(decls, in)) { - return {}; - } + CHECK_ERR(module(decls, in)); if (!in.empty()) { - return {}; + return in.err("Unexpected tokens after module"); } } auto typeIndices = createIndexMap(decls.explicitTypeDefs); - if (!typeIndices) { - return {}; - } + CHECK_ERR(typeIndices); // Parse type definitions. std::vector<HeapType> types; @@ -1650,16 +1636,16 @@ for (Index i = 0; i < decls.explicitTypeDefs.size(); ++i) { ParseTypesCtx ctx(builder, *typeIndices, i); ParseInput in(decls.explicitTypeDefs[i].span); - if (!type(ctx, in)) { - return {}; - } + auto def = type(ctx, in); + CHECK_ERR(def); if (HeapType t = builder[i]; t.isSignature()) { signatureIndices.insert({t.getSignature(), i}); } } auto built = builder.build(); if (!built) { - return {}; + // TODO: Improve this message. + return Err{"error: could not build types"}; } types = *built; // Now that we have built the explicit types, parse type uses that might @@ -1669,9 +1655,8 @@ ParseImplicitTypesCtx ctx(*typeIndices, types); ParseInput in(span); auto sig = typeuse(ctx, in); - if (!sig) { - return {}; - } + CHECK_ERR(sig); + if (signatureIndices.insert({*sig, types.size()}).second) { types.push_back(HeapType(*sig)); } @@ -1685,29 +1670,22 @@ // Map names to indices for each index space. auto funcIndices = createIndexMap(decls.funcDefs); - if (!funcIndices) { - return {}; - } + CHECK_ERR(funcIndices); + auto memIndices = createIndexMap(decls.memDefs); - if (!memIndices) { - return {}; - } + CHECK_ERR(memIndices); + auto tableIndices = createIndexMap(decls.tableDefs); - if (!tableIndices) { - return {}; - } + CHECK_ERR(tableIndices); + auto globalIndices = createIndexMap(decls.globalDefs); - if (!globalIndices) { - return {}; - } + CHECK_ERR(globalIndices); + auto elemIndices = createIndexMap(decls.elemDefs); - if (!elemIndices) { - return {}; - } + CHECK_ERR(elemIndices); + auto dataIndices = createIndexMap(decls.dataDefs); - if (!dataIndices) { - return {}; - } + CHECK_ERR(dataIndices); // Parse definitions // TODO: Parallelize these! To do so, we would have to parse and install @@ -1725,38 +1703,37 @@ *dataIndices); auto parseDefs = - [&](auto& defs, Result<> (*parse)(ParseDefsCtx&, ParseInput&)) -> Result<> { + [&](auto& defs, + Result<bool> (*parse)(ParseDefsCtx&, ParseInput&)) -> Result<> { for (Index i = 0; i < defs.size(); ++i) { ctx.index = i; ParseInput in(defs[i].span); - if (!parse(ctx, in)) { - return {}; - } + auto parsed = parse(ctx, in); + CHECK_ERR(parsed); } return Ok{}; }; - if (!parseDefs(decls.importDefs, import)) { - return {}; - } - if (!parseDefs(decls.funcDefs, func)) { - return {}; - } - if (!parseDefs(decls.memDefs, mem)) { - return {}; - } - if (!parseDefs(decls.tableDefs, table)) { - return {}; - } - if (!parseDefs(decls.globalDefs, global)) { - return {}; - } - // if (!parseDefs(decls.elemDefs, elem)) { - // return {}; - // } - // if (!parseDefs(decls.dataDefs, data)) { - // return {}; - // } + auto imports = parseDefs(decls.importDefs, import); + CHECK_ERR(imports); + + auto funcs = parseDefs(decls.funcDefs, func); + CHECK_ERR(funcs); + + auto mems = parseDefs(decls.memDefs, mem); + CHECK_ERR(mems); + + auto tables = parseDefs(decls.tableDefs, table); + CHECK_ERR(tables); + + auto globals = parseDefs(decls.globalDefs, global); + CHECK_ERR(globals); + + // auto elems = parseDefs(decls.elemDefs, elem); + // CHECK_ERR(elems); + + // auto datas = parseDefs(decls.dataDefs, data); + // CHECK_ERR(datas); return Ok{}; }
diff --git a/src/wat-parser.h b/src/wat-parser.h index c5bab27..13e5067 100644 --- a/src/wat-parser.h +++ b/src/wat-parser.h
@@ -26,8 +26,39 @@ struct Ok {}; -// TODO: Support error variants as well. -template<typename T = Ok> using Result = std::optional<T>; +// struct None {}; + +struct Err { + std::string msg; +}; + +template<typename T = Ok> struct Result { + // std::variant<T, None, Err> val; + std::variant<T, Err> val; + + // Result() : val(None{}) {} + Result(Err& e) : val(std::in_place_type<Err>, e) {} + Result(Err&& e) : val(std::in_place_type<Err>, std::move(e)) {} + template<typename U = T> + Result(U&& u) : val(std::in_place_type<T>, std::forward<U>(u)) {} + + bool ok() const { return std::holds_alternative<T>(val); } + + // Whether we have an error or a truthy value. Useful for assignment in loops + // and if conditions where errors should not get lost. + operator bool() const { return !ok() || bool(*std::get_if<T>(&val)); } + + std::optional<Err> getErr() { + if (auto* err = std::get_if<Err>(&val)) { + return *err; + } + return {}; + } + + T& operator*() { return *std::get_if<T>(&val); } + + T* operator->() { return std::get_if<T>(&val); } +}; // Parse a single WAT module. Result<> parseModule(Module& wasm, std::string_view in);