diff options
-rw-r--r-- | src/wasm/wat-parser.cpp | 189 | ||||
-rw-r--r-- | test/lit/wat-kitchen-sink.wast | 70 |
2 files changed, 246 insertions, 13 deletions
diff --git a/src/wasm/wat-parser.cpp b/src/wasm/wat-parser.cpp index caeccfd76..54aa1dc7d 100644 --- a/src/wasm/wat-parser.cpp +++ b/src/wasm/wat-parser.cpp @@ -301,6 +301,10 @@ struct ParseDeclsCtx { using ParamsT = Ok; using ResultsT = Ok; using SignatureT = Ok; + using FieldT = Ok; + using FieldsT = Ok; + using StructT = Ok; + using ArrayT = Ok; using GlobalTypeT = Ok; // Declared module elements are inserted into the module, but their bodies are @@ -330,6 +334,10 @@ struct ParseTypeDefsCtx { using ParamsT = std::vector<NameType>; using ResultsT = std::vector<Type>; using SignatureT = Signature; + using FieldT = Field; + using FieldsT = std::pair<std::vector<Name>, std::vector<Field>>; + using StructT = std::pair<std::vector<Name>, Struct>; + using ArrayT = Array; // We update slots in this builder as we parse type definitions. TypeBuilder& builder; @@ -337,11 +345,14 @@ struct ParseTypeDefsCtx { // Map heap type names to their indices. const IndexMap& typeIndices; + // Parse the names of types and fields as we go. + std::vector<TypeNames> names; + // The index of the type definition we are parsing. Index index = 0; ParseTypeDefsCtx(TypeBuilder& builder, const IndexMap& typeIndices) - : builder(builder), typeIndices(typeIndices) {} + : builder(builder), typeIndices(typeIndices), names(builder.size()) {} }; template<typename Ctx> @@ -397,13 +408,22 @@ template<typename Ctx> MaybeResult<typename Ctx::ResultsT> results(Ctx&, ParseInput&); template<typename Ctx> MaybeResult<typename Ctx::SignatureT> functype(Ctx&, ParseInput&); +template<typename Ctx> +Result<typename Ctx::FieldT> storagetype(Ctx&, ParseInput&); +template<typename Ctx> +Result<typename Ctx::FieldT> fieldtype(Ctx&, ParseInput&); +template<typename Ctx> Result<typename Ctx::FieldsT> fields(Ctx&, ParseInput&); +template<typename Ctx> +MaybeResult<typename Ctx::StructT> structtype(Ctx&, ParseInput&); +template<typename Ctx> +MaybeResult<typename Ctx::ArrayT> arraytype(Ctx&, ParseInput&); // Modules template<typename Ctx> Result<typename Ctx::HeapTypeT> typeidx(Ctx&, ParseInput&); MaybeResult<ImportNames> inlineImport(ParseInput&); Result<std::vector<Name>> inlineExports(ParseInput&); -template<typename Ctx> MaybeResult<> type(Ctx&, ParseInput&); +template<typename Ctx> MaybeResult<> strtype(Ctx&, ParseInput&); template<typename Ctx> MaybeResult<> global(Ctx&, ParseInput&); MaybeResult<> modulefield(ParseDeclsCtx&, ParseInput&); Result<> module(ParseDeclsCtx&, ParseInput&); @@ -647,6 +667,122 @@ MaybeResult<typename Ctx::SignatureT> functype(Ctx& ctx, ParseInput& in) { } } +// storagetype ::= valtype | packedtype +// packedtype ::= i8 | i16 +template<typename Ctx> +Result<typename Ctx::FieldT> storagetype(Ctx& ctx, ParseInput& in) { + if (in.takeKeyword("i8"sv)) { + RETURN_OR_OK(Field(Field::i8, Immutable)); + } + if (in.takeKeyword("i16"sv)) { + RETURN_OR_OK(Field(Field::i16, Immutable)); + } + auto type = valtype(ctx, in); + CHECK_ERR(type); + RETURN_OR_OK(Field(*type, Immutable)); +} + +// fieldtype ::= t:storagetype => const t +// | '(' 'mut' t:storagetype ')' => var t +template<typename Ctx> +Result<typename Ctx::FieldT> fieldtype(Ctx& ctx, ParseInput& in) { + if (in.takeSExprStart("mut"sv)) { + auto field = storagetype(ctx, in); + CHECK_ERR(field); + if (!in.takeRParen()) { + return in.err("expected end of field type"); + } + if constexpr (parsingTypeDefs<Ctx>) { + field->mutable_ = Mutable; + return *field; + } else { + return Ok{}; + } + } + + return storagetype(ctx, in); +} + +// field ::= '(' 'field' id t:fieldtype ')' => [(id, t)] +// | '(' 'field' t*:fieldtype* ')' => [(_, t*)*] +// | fieldtype +template<typename Ctx> +Result<typename Ctx::FieldsT> fields(Ctx& ctx, ParseInput& in) { + std::vector<Name> names; + std::vector<Field> fs; + while (true) { + if (auto t = in.peek(); !t || t->isRParen()) { + RETURN_OR_OK(std::pair(std::move(names), std::move(fs))); + } + if (in.takeSExprStart("field")) { + if (auto id = in.takeID()) { + auto field = fieldtype(ctx, in); + CHECK_ERR(field); + if (!in.takeRParen()) { + return in.err("expected end of field"); + } + if constexpr (parsingTypeDefs<Ctx>) { + names.push_back(*id); + fs.push_back(*field); + } + } else { + while (!in.takeRParen()) { + auto field = fieldtype(ctx, in); + CHECK_ERR(field); + if constexpr (parsingTypeDefs<Ctx>) { + names.push_back({}); + fs.push_back(*field); + } + } + } + } else { + auto field = fieldtype(ctx, in); + CHECK_ERR(field); + if constexpr (parsingTypeDefs<Ctx>) { + names.push_back({}); + fs.push_back(*field); + } + } + } +} + +// structtype ::= '(' 'struct' field* ')' +template<typename Ctx> +MaybeResult<typename Ctx::StructT> structtype(Ctx& ctx, ParseInput& in) { + if (!in.takeSExprStart("struct"sv)) { + return {}; + } + auto namedFields = fields(ctx, in); + CHECK_ERR(namedFields); + if (!in.takeRParen()) { + return in.err("expected end of struct definition"); + } + + RETURN_OR_OK( + std::pair(std::move(namedFields->first), std::move(namedFields->second))); +} + +// arraytype ::= '(' 'array' field ')' +template<typename Ctx> +MaybeResult<typename Ctx::ArrayT> arraytype(Ctx& ctx, ParseInput& in) { + if (!in.takeSExprStart("array"sv)) { + return {}; + } + auto namedFields = fields(ctx, in); + CHECK_ERR(namedFields); + if (!in.takeRParen()) { + return in.err("expected end of array definition"); + } + + if constexpr (parsingTypeDefs<Ctx>) { + if (namedFields->first.size() != 1) { + return in.err("expected exactly one field in array definition"); + } + } + + RETURN_OR_OK({namedFields->second[0]}); +} + // globaltype ::= t:valtype => const t // | '(' 'mut' t:valtype ')' => var t template<typename Ctx> @@ -741,8 +877,10 @@ Result<std::vector<Name>> inlineExports(ParseInput& in) { return exports; } -// type ::= '(' 'type' id? ft:functype ')' => ft -template<typename Ctx> MaybeResult<> type(Ctx& ctx, ParseInput& in) { +// strtype ::= '(' 'type' id? ft:functype ')' => ft +// | '(' 'type' id? st:structtype ')' => st +// | '(' 'type' id? at:arraytype ')' => at +template<typename Ctx> MaybeResult<> strtype(Ctx& ctx, ParseInput& in) { [[maybe_unused]] auto start = in.getPos(); if (!in.takeSExprStart("type"sv)) { @@ -752,14 +890,32 @@ template<typename Ctx> MaybeResult<> type(Ctx& ctx, ParseInput& in) { Name name; if (auto id = in.takeID()) { name = *id; + if constexpr (parsingTypeDefs<Ctx>) { + ctx.names[ctx.index].name = name; + } } - // TODO: GC types if (auto type = functype(ctx, in)) { CHECK_ERR(type); if constexpr (parsingTypeDefs<Ctx>) { ctx.builder[ctx.index] = *type; } + } else if (auto type = structtype(ctx, in)) { + CHECK_ERR(type); + if constexpr (parsingTypeDefs<Ctx>) { + auto& [fieldNames, str] = *type; + ctx.builder[ctx.index] = str; + for (Index i = 0; i < fieldNames.size(); ++i) { + if (auto name = fieldNames[i]; name.is()) { + ctx.names[ctx.index].fieldNames[i] = name; + } + } + } + } else if (auto type = arraytype(ctx, in)) { + CHECK_ERR(type); + if constexpr (parsingTypeDefs<Ctx>) { + ctx.builder[ctx.index] = *type; + } } else { return in.err("expected type description"); } @@ -774,6 +930,14 @@ template<typename Ctx> MaybeResult<> type(Ctx& ctx, ParseInput& in) { return Ok{}; } +// subtype ::= '(' 'sub' typeidx? strtype ')' +// | strtype +// TODO + +// deftype ::= '(' 'rec' subtype* ')' +// | subtype +// TODO: + // global ::= '(' 'global' id? ('(' 'export' name ')')* gt:globaltype e:expr ')' // | '(' 'global' id? '(' 'import' mod:name nm:name ')' // gt:globaltype ')' @@ -820,7 +984,7 @@ template<typename Ctx> MaybeResult<> global(Ctx& ctx, ParseInput& in) { return in.err("TODO: non-imported globals"); } -// modulefield ::= type +// modulefield ::= deftype // | import // | func // | table @@ -834,7 +998,8 @@ MaybeResult<> modulefield(ParseDeclsCtx& ctx, ParseInput& in) { if (auto t = in.peek(); !t || t->isRParen()) { return {}; } - if (auto res = type(ctx, in)) { + // TODO: Replace strtype with deftype. + if (auto res = strtype(ctx, in)) { CHECK_ERR(res); return Ok{}; } @@ -975,7 +1140,7 @@ Result<> parseModule(Module& wasm, std::string_view input) { { TypeBuilder builder(decls.typeDefs.size()); ParseTypeDefsCtx ctx(builder, *typeIndices); - CHECK_ERR(parseDefs(ctx, input, decls.typeDefs, type)); + CHECK_ERR(parseDefs(ctx, input, decls.typeDefs, strtype)); auto built = builder.build(); if (auto* err = built.getError()) { std::stringstream msg; @@ -985,11 +1150,9 @@ Result<> parseModule(Module& wasm, std::string_view input) { types = *built; // Record type names on the module. for (size_t i = 0; i < types.size(); ++i) { - if (auto name = decls.typeDefs[i].name; name.is()) { - // TODO: struct field names - bool inserted = wasm.typeNames.insert({types[i], {name, {}}}).second; - WASM_UNUSED(inserted); - assert(inserted); + auto& names = ctx.names[i]; + if (names.name.is() || names.fieldNames.size()) { + wasm.typeNames.insert({types[i], names}); } } } diff --git a/test/lit/wat-kitchen-sink.wast b/test/lit/wat-kitchen-sink.wast index 4b5ec740b..1520003a6 100644 --- a/test/lit/wat-kitchen-sink.wast +++ b/test/lit/wat-kitchen-sink.wast @@ -4,17 +4,61 @@ (module $parse ;; types + ;; CHECK: (type $s0 (struct )) + ;; CHECK: (type $void (func)) (type $void (func)) ;; CHECK: (type $many (func (param i32 i64 f32 f64) (result anyref (ref func)))) (type $many (func (param $x i32) (param i64 f32) (param) (param $y f64) (result anyref (ref func)))) + (type $s0 (struct)) + (type $s1 (struct (field))) + ;; CHECK: (type $s2 (struct (field i32))) + (type $s2 (struct i32)) + ;; CHECK: (type $s3 (struct (field i64))) + (type $s3 (struct (field i64))) + ;; CHECK: (type $s4 (struct (field $x f32))) + (type $s4 (struct (field $x f32))) + ;; CHECK: (type $s5 (struct (field i32) (field i64))) + (type $s5 (struct i32 i64)) + ;; CHECK: (type $s6 (struct (field i64) (field f32))) + (type $s6 (struct (field i64 f32))) + ;; CHECK: (type $s7 (struct (field $x f32) (field $y f64))) + (type $s7 (struct (field $x f32) (field $y f64))) + ;; CHECK: (type $s8 (struct (field i32) (field i64) (field $z f32) (field f64) (field (mut i32)))) + (type $s8 (struct i32 (field) i64 (field $z f32) (field f64 (mut i32)))) + + ;; CHECK: (type $a0 (array i32)) + (type $a0 (array i32)) + ;; CHECK: (type $a1 (array i64)) + (type $a1 (array (field i64))) + ;; CHECK: (type $a2 (array (mut f32))) + (type $a2 (array (mut f32))) + ;; CHECK: (type $a3 (array (mut f64))) + (type $a3 (array (field $x (mut f64)))) + ;; globals (global $g1 (export "g1") (export "g1.1") (import "mod" "g1") i32) (global $g2 (import "mod" "g2") (mut i64)) (global (import "" "g3") (ref 0)) (global (import "mod" "") (ref null $many)) + + ;; uninteresting globals just to use the types + ;; TODO: replace these with a function. + (global $s0 (import "mod" "s0") (mut (ref $s0))) + (global $s1 (import "mod" "s1") (mut (ref $s1))) + (global $s2 (import "mod" "s2") (mut (ref $s2))) + (global $s3 (import "mod" "s3") (mut (ref $s3))) + (global $s4 (import "mod" "s4") (mut (ref $s4))) + (global $s5 (import "mod" "s5") (mut (ref $s5))) + (global $s6 (import "mod" "s6") (mut (ref $s6))) + (global $s7 (import "mod" "s7") (mut (ref $s7))) + (global $s8 (import "mod" "s8") (mut (ref $s8))) + (global $a0 (import "mod" "a0") (mut (ref $a0))) + (global $a1 (import "mod" "a1") (mut (ref $a1))) + (global $a2 (import "mod" "a2") (mut (ref $a2))) + (global $a3 (import "mod" "a3") (mut (ref $a3))) ) ;; CHECK: (import "mod" "g1" (global $g1 i32)) @@ -24,6 +68,32 @@ ;; CHECK: (import "mod" "" (global $gimport$1 (ref null $many))) +;; CHECK: (import "mod" "s0" (global $s0 (mut (ref $s0)))) + +;; CHECK: (import "mod" "s1" (global $s1 (mut (ref $s0)))) + +;; CHECK: (import "mod" "s2" (global $s2 (mut (ref $s2)))) + +;; CHECK: (import "mod" "s3" (global $s3 (mut (ref $s3)))) + +;; CHECK: (import "mod" "s4" (global $s4 (mut (ref $s4)))) + +;; CHECK: (import "mod" "s5" (global $s5 (mut (ref $s5)))) + +;; CHECK: (import "mod" "s6" (global $s6 (mut (ref $s6)))) + +;; CHECK: (import "mod" "s7" (global $s7 (mut (ref $s7)))) + +;; CHECK: (import "mod" "s8" (global $s8 (mut (ref $s8)))) + +;; CHECK: (import "mod" "a0" (global $a0 (mut (ref $a0)))) + +;; CHECK: (import "mod" "a1" (global $a1 (mut (ref $a1)))) + +;; CHECK: (import "mod" "a2" (global $a2 (mut (ref $a2)))) + +;; CHECK: (import "mod" "a3" (global $a3 (mut (ref $a3)))) + ;; CHECK: (export "g1" (global $g1)) ;; CHECK: (export "g1.1" (global $g1)) |