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);