summaryrefslogtreecommitdiff
path: root/src/wast-parser.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/wast-parser.cc')
-rw-r--r--src/wast-parser.cc2010
1 files changed, 2010 insertions, 0 deletions
diff --git a/src/wast-parser.cc b/src/wast-parser.cc
new file mode 100644
index 00000000..5174d4a4
--- /dev/null
+++ b/src/wast-parser.cc
@@ -0,0 +1,2010 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wast-parser.h"
+
+#include "binary-reader.h"
+#include "binary-reader-ir.h"
+#include "cast.h"
+#include "error-handler.h"
+#include "wast-parser-lexer-shared.h"
+
+#define WABT_TRACING 0
+#include "tracing.h"
+
+#define CHECK_RESULT(expr) \
+ do { \
+ if (Failed(expr)) \
+ return Result::Error; \
+ } while (0)
+
+#define EXPECT(token_type) CHECK_RESULT(Expect(TokenType::token_type))
+
+namespace wabt {
+
+namespace {
+
+bool IsPowerOfTwo(uint32_t x) {
+ return x && ((x & (x - 1)) == 0);
+}
+
+template <typename OutputIter>
+void RemoveEscapes(string_view text, OutputIter dest) {
+ // Remove surrounding quotes; if any. This may be empty if the string was
+ // invalid (e.g. if it contained a bad escape sequence).
+ if (text.size() <= 2)
+ return;
+
+ text = text.substr(1, text.size() - 2);
+
+ const char* src = text.data();
+ const char* end = text.data() + text.size();
+
+ while (src < end) {
+ if (*src == '\\') {
+ src++;
+ switch (*src) {
+ case 'n':
+ *dest++ = '\n';
+ break;
+ case 'r':
+ *dest++ = '\r';
+ break;
+ case 't':
+ *dest++ = '\t';
+ break;
+ case '\\':
+ *dest++ = '\\';
+ break;
+ case '\'':
+ *dest++ = '\'';
+ break;
+ case '\"':
+ *dest++ = '\"';
+ break;
+ default: {
+ // The string should be validated already, so we know this is a hex
+ // sequence.
+ uint32_t hi;
+ uint32_t lo;
+ if (Succeeded(ParseHexdigit(src[0], &hi)) &&
+ Succeeded(ParseHexdigit(src[1], &lo))) {
+ *dest++ = (hi << 4) | lo;
+ } else {
+ assert(0);
+ }
+ src++;
+ break;
+ }
+ }
+ src++;
+ } else {
+ *dest++ = *src++;
+ }
+ }
+}
+
+typedef std::vector<std::string> TextVector;
+
+template <typename OutputIter>
+void RemoveEscapes(const TextVector& texts, OutputIter out) {
+ for (const std::string& text: texts)
+ RemoveEscapes(text, out);
+}
+
+class BinaryErrorHandlerModule : public ErrorHandler {
+ public:
+ BinaryErrorHandlerModule(Location* loc, WastParser* parser);
+ bool OnError(const Location&,
+ const std::string& error,
+ const std::string& source_line,
+ size_t source_line_column_offset) override;
+
+ // Unused.
+ size_t source_line_max_length() const override { return 0; }
+
+ private:
+ Location* loc_;
+ WastParser* parser_;
+};
+
+BinaryErrorHandlerModule::BinaryErrorHandlerModule(Location* loc,
+ WastParser* parser)
+ : ErrorHandler(Location::Type::Binary), loc_(loc), parser_(parser) {}
+
+bool BinaryErrorHandlerModule::OnError(const Location& binary_loc,
+ const std::string& error,
+ const std::string& source_line,
+ size_t source_line_column_offset) {
+ if (binary_loc.offset == kInvalidOffset) {
+ parser_->Error(*loc_, "error in binary module: %s", error.c_str());
+ } else {
+ parser_->Error(*loc_, "error in binary module: @0x%08" PRIzx ": %s",
+ binary_loc.offset, error.c_str());
+ }
+ return true;
+}
+
+bool IsPlainInstr(TokenType token_type) {
+ switch (token_type) {
+ case TokenType::Unreachable:
+ case TokenType::Nop:
+ case TokenType::Drop:
+ case TokenType::Select:
+ case TokenType::Br:
+ case TokenType::BrIf:
+ case TokenType::BrTable:
+ case TokenType::Return:
+ case TokenType::Call:
+ case TokenType::CallIndirect:
+ case TokenType::GetLocal:
+ case TokenType::SetLocal:
+ case TokenType::TeeLocal:
+ case TokenType::GetGlobal:
+ case TokenType::SetGlobal:
+ case TokenType::Load:
+ case TokenType::Store:
+ case TokenType::Const:
+ case TokenType::Unary:
+ case TokenType::Binary:
+ case TokenType::Compare:
+ case TokenType::Convert:
+ case TokenType::CurrentMemory:
+ case TokenType::GrowMemory:
+ case TokenType::Throw:
+ case TokenType::Rethrow:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool IsBlockInstr(TokenType token_type) {
+ switch (token_type) {
+ case TokenType::Block:
+ case TokenType::Loop:
+ case TokenType::If:
+ case TokenType::Try:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool IsPlainOrBlockInstr(TokenType token_type) {
+ return IsPlainInstr(token_type) || IsBlockInstr(token_type);
+}
+
+bool IsExpr(TokenTypePair pair) {
+ return pair[0] == TokenType::Lpar && IsPlainOrBlockInstr(pair[1]);
+}
+
+bool IsInstr(TokenTypePair pair) {
+ return IsPlainOrBlockInstr(pair[0]) || IsExpr(pair);
+}
+
+bool IsCatch(TokenType token_type) {
+ return token_type == TokenType::Catch || token_type == TokenType::CatchAll;
+}
+
+bool IsModuleField(TokenTypePair pair) {
+ if (pair[0] != TokenType::Lpar)
+ return false;
+
+ switch (pair[1]) {
+ case TokenType::Data:
+ case TokenType::Elem:
+ case TokenType::Except:
+ case TokenType::Export:
+ case TokenType::Func:
+ case TokenType::Type:
+ case TokenType::Global:
+ case TokenType::Import:
+ case TokenType::Memory:
+ case TokenType::Start:
+ case TokenType::Table:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool IsCommand(TokenTypePair pair) {
+ if (pair[0] != TokenType::Lpar)
+ return false;
+
+ switch (pair[1]) {
+ case TokenType::AssertExhaustion:
+ case TokenType::AssertInvalid:
+ case TokenType::AssertMalformed:
+ case TokenType::AssertReturn:
+ case TokenType::AssertReturnArithmeticNan:
+ case TokenType::AssertReturnCanonicalNan:
+ case TokenType::AssertTrap:
+ case TokenType::AssertUnlinkable:
+ case TokenType::Get:
+ case TokenType::Invoke:
+ case TokenType::Module:
+ case TokenType::Register:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool IsEmptySignature(const FuncSignature* sig) {
+ return sig->result_types.empty() && sig->param_types.empty();
+}
+
+void ResolveFuncTypes(Module* module) {
+ for (ModuleField& field : module->fields) {
+ Func* func = nullptr;
+ if (field.type == ModuleFieldType::Func) {
+ func = dyn_cast<FuncModuleField>(&field)->func;
+ } else if (field.type == ModuleFieldType::Import) {
+ Import* import = dyn_cast<ImportModuleField>(&field)->import;
+ if (import->kind == ExternalKind::Func) {
+ func = import->func;
+ } else {
+ continue;
+ }
+ } else {
+ continue;
+ }
+
+ // Resolve func type variables where the signature was not specified
+ // explicitly, e.g.: (func (type 1) ...)
+ if (func->decl.has_func_type && IsEmptySignature(&func->decl.sig)) {
+ FuncType* func_type = module->GetFuncType(func->decl.type_var);
+ if (func_type) {
+ func->decl.sig = func_type->sig;
+ }
+ }
+
+ // Resolve implicitly defined function types, e.g.: (func (param i32) ...)
+ if (!func->decl.has_func_type) {
+ Index func_type_index = module->GetFuncTypeIndex(func->decl.sig);
+ if (func_type_index == kInvalidIndex) {
+ auto func_type = new FuncType();
+ func_type->sig = func->decl.sig;
+ module->AppendField(new FuncTypeModuleField(func_type, field.loc));
+ }
+ }
+ }
+}
+
+void AppendInlineExportFields(Module* module,
+ ModuleFieldList* fields,
+ Index index) {
+ Location last_field_loc = module->fields.back().loc;
+
+ for (const ModuleField& field: *fields) {
+ auto* export_field = cast<ExportModuleField>(&field);
+ export_field->export_->var = Var(index, last_field_loc);
+ }
+
+ module->AppendFields(fields);
+}
+
+} // End of anonymous namespace
+
+WastParser::WastParser(WastLexer* lexer,
+ ErrorHandler* error_handler,
+ WastParseOptions* options)
+ : lexer_(lexer), error_handler_(error_handler), options_(options) {}
+
+void WastParser::Error(Location loc, const char* format, ...) {
+ errors_++;
+ va_list args;
+ va_start(args, format);
+ WastFormatError(error_handler_, &loc, lexer_, format, args);
+ va_end(args);
+}
+
+Token WastParser::GetToken() {
+ if (tokens_.empty())
+ tokens_.push_back(lexer_->GetToken(this));
+ return tokens_.front();
+}
+
+Location WastParser::GetLocation() {
+ return GetToken().loc;
+}
+
+TokenType WastParser::Peek(size_t n) {
+ while (tokens_.size() <= n)
+ tokens_.push_back(lexer_->GetToken(this));
+ return tokens_.at(n).token_type;
+}
+
+TokenTypePair WastParser::PeekPair() {
+ return TokenTypePair{{Peek(), Peek(1)}};
+}
+
+bool WastParser::PeekMatch(TokenType type) {
+ return Peek() == type;
+}
+
+bool WastParser::PeekMatchLpar(TokenType type) {
+ return Peek() == TokenType::Lpar && Peek(1) == type;
+}
+
+bool WastParser::PeekMatchExpr() {
+ return IsExpr(PeekPair());
+}
+
+bool WastParser::Match(TokenType type) {
+ if (PeekMatch(type)) {
+ Consume();
+ return true;
+ }
+ return false;
+}
+
+bool WastParser::MatchLpar(TokenType type) {
+ if (PeekMatchLpar(type)) {
+ Consume();
+ Consume();
+ return true;
+ }
+ return false;
+}
+
+Result WastParser::Expect(TokenType type) {
+ if (!Match(type)) {
+ auto token = Consume();
+ Error(token.loc, "unexpected token %s, expected %s.",
+ token.to_string().c_str(), GetTokenTypeName(type));
+ return Result::Error;
+ }
+
+ return Result::Ok;
+}
+
+Token WastParser::Consume() {
+ assert(!tokens_.empty());
+ auto token = tokens_.front();
+ tokens_.pop_front();
+ return token;
+}
+
+Result WastParser::Synchronize(SynchronizeFunc func) {
+ static const int kMaxConsumed = 10;
+ for (int i = 0; i < kMaxConsumed; ++i) {
+ if (func(PeekPair()))
+ return Result::Ok;
+
+ auto token = Consume();
+ if (token.token_type == TokenType::Reserved) {
+ Error(token.loc, "unexpected token %s.", token.to_string().c_str());
+ }
+ }
+
+ return Result::Error;
+}
+
+void WastParser::ErrorUnlessExceptionsAllowed() {
+ if (!options_->allow_future_exceptions) {
+ Error(GetLocation(), "opcode not allowed: %s",
+ GetToken().to_string().c_str());
+ }
+}
+
+Result WastParser::ErrorExpected(const std::vector<std::string>& expected,
+ const char* example) {
+ auto token = Consume();
+ std::string expected_str;
+ if (!expected.empty()) {
+ expected_str = ", expected ";
+ for (size_t i = 0; i < expected.size(); ++i) {
+ if (i != 0) {
+ if (i == expected.size() - 1)
+ expected_str += " or ";
+ else
+ expected_str += ", ";
+ }
+
+ expected_str += expected[i];
+ }
+
+ if (example) {
+ expected_str += " (e.g. ";
+ expected_str += example;
+ expected_str += ")";
+ }
+ }
+
+ Error(token.loc, "unexpected token \"%s\"%s.", token.to_string().c_str(),
+ expected_str.c_str());
+ return Result::Error;
+}
+
+Result WastParser::ErrorIfLpar(const std::vector<std::string>& expected,
+ const char* example) {
+ if (Match(TokenType::Lpar))
+ return ErrorExpected(expected, example);
+ return Result::Ok;
+}
+
+void WastParser::ParseBindVarOpt(std::string* name) {
+ WABT_TRACE(ParseBindVarOpt);
+ if (PeekMatch(TokenType::Var)) {
+ auto token = Consume();
+ *name = token.text.to_string();
+ }
+}
+
+Result WastParser::ParseVar(Var* out_var) {
+ WABT_TRACE(ParseVar);
+ if (PeekMatch(TokenType::Nat)) {
+ auto token = Consume();
+ string_view sv = token.literal.text.to_string_view();
+ uint64_t index = kInvalidIndex;
+ if (Failed(ParseUint64(sv.begin(), sv.end(), &index))) {
+ // Print an error, but don't fail parsing.
+ Error(token.loc, "invalid int \"" PRIstringview "\"",
+ WABT_PRINTF_STRING_VIEW_ARG(sv));
+ }
+
+ *out_var = Var(index, token.loc);
+ return Result::Ok;
+ } else if (PeekMatch(TokenType::Var)) {
+ auto token = Consume();
+ *out_var = Var(token.text.to_string_view(), token.loc);
+ return Result::Ok;
+ } else {
+ return ErrorExpected({"a numeric index", "a name"}, "12 or $foo");
+ }
+}
+
+bool WastParser::ParseVarOpt(Var* out_var, Var default_var) {
+ WABT_TRACE(ParseVarOpt);
+ if (PeekMatch(TokenType::Nat) || PeekMatch(TokenType::Var)) {
+ Result result = ParseVar(out_var);
+ // Should always succeed, the only way it could fail is if the token
+ // doesn't match.
+ assert(Succeeded(result));
+ WABT_USE(result);
+ return true;
+ } else {
+ *out_var = default_var;
+ return false;
+ }
+}
+
+Result WastParser::ParseOffsetExpr(ExprList* out_expr_list) {
+ WABT_TRACE(ParseOffsetExpr);
+ if (MatchLpar(TokenType::Offset)) {
+ CHECK_RESULT(ParseTerminatingInstrList(out_expr_list));
+ EXPECT(Rpar);
+ } else if (PeekMatchExpr()) {
+ CHECK_RESULT(ParseExpr(out_expr_list));
+ } else {
+ return ErrorExpected({"an offset expr"}, "(i32.const 123)");
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseTextList(std::vector<uint8_t>* out_data) {
+ WABT_TRACE(ParseTextList);
+ if (!ParseTextListOpt(out_data)) {
+ return Result::Error;
+ }
+
+ return Result::Ok;
+}
+
+bool WastParser::ParseTextListOpt(std::vector<uint8_t>* out_data) {
+ WABT_TRACE(ParseTextListOpt);
+ TextVector texts;
+ while (PeekMatch(TokenType::Text))
+ texts.push_back(Consume().text.to_string());
+
+ RemoveEscapes(texts, std::back_inserter(*out_data));
+ return !texts.empty();
+}
+
+Result WastParser::ParseVarList(VarVector* out_var_list) {
+ WABT_TRACE(ParseVarList);
+ if (!ParseVarListOpt(out_var_list)) {
+ return Result::Error;
+ }
+
+ return Result::Ok;
+}
+
+bool WastParser::ParseVarListOpt(VarVector* out_var_list) {
+ WABT_TRACE(ParseVarListOpt);
+ Var var;
+ while (ParseVarOpt(&var))
+ out_var_list->push_back(var);
+
+ return !out_var_list->empty();
+}
+
+Result WastParser::ParseValueType(Type* out_type) {
+ WABT_TRACE(ParseValueType);
+ if (!PeekMatch(TokenType::ValueType))
+ return ErrorExpected({"i32", "i64", "f32", "f64"});
+
+ *out_type = Consume().type;
+ return Result::Ok;
+}
+
+Result WastParser::ParseValueTypeList(TypeVector* out_type_list) {
+ WABT_TRACE(ParseValueTypeList);
+ while (PeekMatch(TokenType::ValueType))
+ out_type_list->push_back(Consume().type);
+
+ CHECK_RESULT(ErrorIfLpar({"i32", "i64", "f32", "f64"}));
+ return Result::Ok;
+}
+
+Result WastParser::ParseQuotedText(std::string* text) {
+ WABT_TRACE(ParseQuotedText);
+ if (!PeekMatch(TokenType::Text))
+ return ErrorExpected({"a quoted string"}, "\"foo\"");
+
+ RemoveEscapes(Consume().text.to_string_view(), std::back_inserter(*text));
+ return Result::Ok;
+}
+
+bool WastParser::ParseOffsetOpt(uint32_t* out_offset) {
+ WABT_TRACE(ParseOffsetOpt);
+ if (PeekMatch(TokenType::OffsetEqNat)) {
+ auto token = Consume();
+ uint64_t offset64;
+ string_view sv = token.text.to_string_view();
+ if (Failed(ParseInt64(sv.begin(), sv.end(), &offset64,
+ ParseIntType::SignedAndUnsigned))) {
+ Error(token.loc, "invalid offset \"" PRIstringview "\"",
+ WABT_PRINTF_STRING_VIEW_ARG(sv));
+ }
+ if (offset64 > UINT32_MAX) {
+ Error(token.loc, "offset must be less than or equal to 0xffffffff");
+ }
+
+ *out_offset = static_cast<uint32_t>(offset64);
+ return true;
+ } else {
+ *out_offset = 0;
+ return false;
+ }
+}
+
+bool WastParser::ParseAlignOpt(uint32_t* out_align) {
+ WABT_TRACE(ParseAlignOpt);
+ if (PeekMatch(TokenType::AlignEqNat)) {
+ auto token = Consume();
+ string_view sv = token.text.to_string_view();
+ if (Failed(ParseInt32(sv.begin(), sv.end(), out_align,
+ ParseIntType::UnsignedOnly))) {
+ Error(token.loc, "invalid alignment \"" PRIstringview "\"",
+ WABT_PRINTF_STRING_VIEW_ARG(sv));
+ }
+
+ if (!IsPowerOfTwo(*out_align)) {
+ Error(token.loc, "alignment must be power-of-two");
+ }
+
+ return true;
+ } else {
+ *out_align = WABT_USE_NATURAL_ALIGNMENT;
+ return false;
+ }
+}
+
+Result WastParser::ParseLimits(Limits* out_limits) {
+ WABT_TRACE(ParseLimits);
+ CHECK_RESULT(ParseNat(&out_limits->initial));
+
+ if (PeekMatch(TokenType::Nat)) {
+ CHECK_RESULT(ParseNat(&out_limits->max));
+ out_limits->has_max = true;
+ } else {
+ out_limits->has_max = false;
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseNat(uint64_t* out_nat) {
+ WABT_TRACE(ParseNat);
+ if (!PeekMatch(TokenType::Nat))
+ return ErrorExpected({"a natural number"}, "123");
+
+ auto token = Consume();
+ string_view sv = token.literal.text.to_string_view();
+ if (Failed(ParseUint64(sv.begin(), sv.end(), out_nat))) {
+ Error(token.loc, "invalid int \"" PRIstringview "\"",
+ WABT_PRINTF_STRING_VIEW_ARG(sv));
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseScript(Script* script) {
+ WABT_TRACE(ParseScript);
+ script_ = script;
+
+ // Don't consume the Lpar yet, even though it is required. This way the
+ // sub-parser functions (e.g. ParseFuncModuleField) can consume it and keep
+ // the parsing structure more regular.
+ if (IsModuleField(PeekPair())) {
+ // Parse an inline module (i.e. one with no surrounding (module)).
+ auto module = make_unique<Module>();
+ module->loc = GetLocation();
+ CHECK_RESULT(ParseModuleFieldList(module.get()));
+ script->commands.emplace_back(make_unique<ModuleCommand>(module.release()));
+ } else if (IsCommand(PeekPair())) {
+ CHECK_RESULT(ParseCommandList(&script->commands));
+ } else {
+ ConsumeIfLpar();
+ ErrorExpected({"a module field", "a command"});
+ }
+
+ EXPECT(Eof);
+ return errors_ == 0 ? Result::Ok : Result::Error;
+}
+
+Result WastParser::ParseModuleFieldList(Module* module) {
+ WABT_TRACE(ParseModuleFieldList);
+ while (PeekMatch(TokenType::Lpar)) {
+ if (Failed(ParseModuleField(module))) {
+ CHECK_RESULT(Synchronize(IsModuleField));
+ }
+ }
+ ResolveFuncTypes(module);
+ return Result::Ok;
+}
+
+Result WastParser::ParseModuleField(Module* module) {
+ WABT_TRACE(ParseModuleField);
+ switch (Peek(1)) {
+ case TokenType::Data: return ParseDataModuleField(module);
+ case TokenType::Elem: return ParseElemModuleField(module);
+ case TokenType::Except: return ParseExceptModuleField(module);
+ case TokenType::Export: return ParseExportModuleField(module);
+ case TokenType::Func: return ParseFuncModuleField(module);
+ case TokenType::Type: return ParseTypeModuleField(module);
+ case TokenType::Global: return ParseGlobalModuleField(module);
+ case TokenType::Import: return ParseImportModuleField(module);
+ case TokenType::Memory: return ParseMemoryModuleField(module);
+ case TokenType::Start: return ParseStartModuleField(module);
+ case TokenType::Table: return ParseTableModuleField(module);
+ default:
+ assert(
+ !"ParseModuleField should only be called if IsModuleField() is true");
+ return Result::Error;
+ }
+}
+
+Result WastParser::ParseDataModuleField(Module* module) {
+ WABT_TRACE(ParseDataModuleField);
+ EXPECT(Lpar);
+ auto loc = GetLocation();
+ EXPECT(Data);
+ auto data_segment = make_unique<DataSegment>();
+ ParseVarOpt(&data_segment->memory_var, Var(0, loc));
+ CHECK_RESULT(ParseOffsetExpr(&data_segment->offset));
+ ParseTextListOpt(&data_segment->data);
+ EXPECT(Rpar);
+ module->AppendField(new DataSegmentModuleField(data_segment.release(), loc));
+ return Result::Ok;
+}
+
+Result WastParser::ParseElemModuleField(Module* module) {
+ WABT_TRACE(ParseElemModuleField);
+ EXPECT(Lpar);
+ auto loc = GetLocation();
+ EXPECT(Elem);
+ auto elem_segment = make_unique<ElemSegment>();
+ ParseVarOpt(&elem_segment->table_var, Var(0, loc));
+ CHECK_RESULT(ParseOffsetExpr(&elem_segment->offset));
+ ParseVarListOpt(&elem_segment->vars);
+ EXPECT(Rpar);
+ module->AppendField(new ElemSegmentModuleField(elem_segment.release(), loc));
+ return Result::Ok;
+}
+
+Result WastParser::ParseExceptModuleField(Module* module) {
+ WABT_TRACE(ParseExceptModuleField);
+ EXPECT(Lpar);
+ auto loc = GetLocation();
+ EXPECT(Except);
+ auto exception = make_unique<Exception>();
+ ParseBindVarOpt(&exception->name);
+ ParseValueTypeList(&exception->sig);
+ EXPECT(Rpar);
+ module->AppendField(new ExceptionModuleField(exception.release(), loc));
+ return Result::Ok;
+}
+
+Result WastParser::ParseExportModuleField(Module* module) {
+ WABT_TRACE(ParseExportModuleField);
+ EXPECT(Lpar);
+ auto loc = GetLocation();
+ EXPECT(Export);
+ auto export_ = make_unique<Export>();
+ CHECK_RESULT(ParseQuotedText(&export_->name));
+ CHECK_RESULT(ParseExportDesc(export_.get()));
+ EXPECT(Rpar);
+ module->AppendField(new ExportModuleField(export_.release(), loc));
+ return Result::Ok;
+}
+
+Result WastParser::ParseFuncModuleField(Module* module) {
+ WABT_TRACE(ParseFuncModuleField);
+ EXPECT(Lpar);
+ auto loc = GetLocation();
+ EXPECT(Func);
+ auto func = make_unique<Func>();
+ ParseBindVarOpt(&func->name);
+
+ ModuleFieldList export_fields;
+ CHECK_RESULT(ParseInlineExports(&export_fields, ExternalKind::Func));
+
+ if (PeekMatchLpar(TokenType::Import)) {
+ CheckImportOrdering(module);
+ auto import_loc = GetLocation();
+ auto import = make_unique<Import>();
+ import->kind = ExternalKind::Func;
+ import->func = func.release();
+ CHECK_RESULT(ParseInlineImport(import.get()));
+ CHECK_RESULT(ParseTypeUseOpt(&import->func->decl));
+ CHECK_RESULT(ParseFuncSignature(&import->func->decl.sig,
+ &import->func->param_bindings));
+ CHECK_RESULT(ErrorIfLpar({"type", "param", "result"}));
+ module->AppendField(new ImportModuleField(import.release(), import_loc));
+ } else {
+ CHECK_RESULT(ParseTypeUseOpt(&func->decl));
+ CHECK_RESULT(ParseFuncSignature(&func->decl.sig, &func->param_bindings));
+ CHECK_RESULT(ParseBoundValueTypeList(TokenType::Local, &func->local_types,
+ &func->local_bindings));
+ CHECK_RESULT(ParseTerminatingInstrList(&func->exprs));
+ module->AppendField(new FuncModuleField(func.release(), loc));
+ }
+
+ AppendInlineExportFields(module, &export_fields, module->funcs.size() - 1);
+
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseTypeModuleField(Module* module) {
+ WABT_TRACE(ParseTypeModuleField);
+ EXPECT(Lpar);
+ auto loc = GetLocation();
+ EXPECT(Type);
+ auto func_type = make_unique<FuncType>();
+ ParseBindVarOpt(&func_type->name);
+ EXPECT(Lpar);
+ EXPECT(Func);
+ BindingHash bindings;
+ CHECK_RESULT(ParseFuncSignature(&func_type->sig, &bindings));
+ CHECK_RESULT(ErrorIfLpar({"param", "result"}));
+ EXPECT(Rpar);
+ EXPECT(Rpar);
+ module->AppendField(new FuncTypeModuleField(func_type.release(), loc));
+ return Result::Ok;
+}
+
+Result WastParser::ParseGlobalModuleField(Module* module) {
+ WABT_TRACE(ParseGlobalModuleField);
+ EXPECT(Lpar);
+ auto loc = GetLocation();
+ EXPECT(Global);
+ auto global = make_unique<Global>();
+ ParseBindVarOpt(&global->name);
+
+ ModuleFieldList export_fields;
+ CHECK_RESULT(ParseInlineExports(&export_fields, ExternalKind::Global));
+
+ if (PeekMatchLpar(TokenType::Import)) {
+ CheckImportOrdering(module);
+ auto import_loc = GetLocation();
+ auto import = make_unique<Import>();
+ import->kind = ExternalKind::Global;
+ import->global = global.release();
+ CHECK_RESULT(ParseInlineImport(import.get()));
+ CHECK_RESULT(ParseGlobalType(import->global));
+ module->AppendField(new ImportModuleField(import.release(), import_loc));
+ } else {
+ CHECK_RESULT(ParseGlobalType(global.get()));
+ CHECK_RESULT(ParseTerminatingInstrList(&global->init_expr));
+ module->AppendField(new GlobalModuleField(global.release(), loc));
+ }
+
+ AppendInlineExportFields(module, &export_fields, module->globals.size() - 1);
+
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseImportModuleField(Module* module) {
+ WABT_TRACE(ParseImportModuleField);
+ EXPECT(Lpar);
+ auto loc = GetLocation();
+ CheckImportOrdering(module);
+ EXPECT(Import);
+ auto import = make_unique<Import>();
+ CHECK_RESULT(ParseQuotedText(&import->module_name));
+ CHECK_RESULT(ParseQuotedText(&import->field_name));
+ EXPECT(Lpar);
+ switch (Peek()) {
+ case TokenType::Func:
+ Consume();
+ import->kind = ExternalKind::Func;
+ import->func = new Func();
+ ParseBindVarOpt(&import->func->name);
+ if (PeekMatchLpar(TokenType::Type)) {
+ import->func->decl.has_func_type = true;
+ CHECK_RESULT(ParseTypeUseOpt(&import->func->decl));
+ EXPECT(Rpar);
+ } else {
+ CHECK_RESULT(ParseFuncSignature(&import->func->decl.sig,
+ &import->func->param_bindings));
+ CHECK_RESULT(ErrorIfLpar({"param", "result"}));
+ EXPECT(Rpar);
+ }
+ break;
+
+ case TokenType::Table:
+ Consume();
+ import->kind = ExternalKind::Table;
+ import->table = new Table();
+ ParseBindVarOpt(&import->table->name);
+ CHECK_RESULT(ParseLimits(&import->table->elem_limits));
+ EXPECT(Anyfunc);
+ EXPECT(Rpar);
+ break;
+
+ case TokenType::Memory:
+ Consume();
+ import->kind = ExternalKind::Memory;
+ import->memory = new Memory();
+ ParseBindVarOpt(&import->memory->name);
+ CHECK_RESULT(ParseLimits(&import->memory->page_limits));
+ EXPECT(Rpar);
+ break;
+
+ case TokenType::Global:
+ Consume();
+ import->kind = ExternalKind::Global;
+ import->global = new Global();
+ ParseBindVarOpt(&import->global->name);
+ CHECK_RESULT(ParseGlobalType(import->global));
+ EXPECT(Rpar);
+ break;
+
+ case TokenType::Except:
+ Consume();
+ import->kind = ExternalKind::Except;
+ import->except = new Exception();
+ ParseBindVarOpt(&import->except->name);
+ ParseValueTypeList(&import->except->sig);
+ EXPECT(Rpar);
+ break;
+
+ default:
+ return ErrorExpected({"an external kind"});
+ }
+
+ module->AppendField(new ImportModuleField(import.release(), loc));
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseMemoryModuleField(Module* module) {
+ WABT_TRACE(ParseMemoryModuleField);
+ EXPECT(Lpar);
+ auto loc = GetLocation();
+ EXPECT(Memory);
+ auto memory = make_unique<Memory>();
+ ParseBindVarOpt(&memory->name);
+
+ ModuleFieldList export_fields;
+ CHECK_RESULT(ParseInlineExports(&export_fields, ExternalKind::Memory));
+
+ if (PeekMatchLpar(TokenType::Import)) {
+ CheckImportOrdering(module);
+ auto import_loc = GetLocation();
+ auto import = make_unique<Import>();
+ import->kind = ExternalKind::Memory;
+ import->memory = memory.release();
+ CHECK_RESULT(ParseInlineImport(import.get()));
+ CHECK_RESULT(ParseLimits(&import->memory->page_limits));
+ module->AppendField(new ImportModuleField(import.release(), import_loc));
+ } else if (MatchLpar(TokenType::Data)) {
+ auto data_segment = make_unique<DataSegment>();
+ data_segment->memory_var = Var(module->memories.size());
+ data_segment->offset.push_back(new ConstExpr(Const(Const::I32(), 0)));
+ data_segment->offset.back().loc = loc;
+ ParseTextListOpt(&data_segment->data);
+ EXPECT(Rpar);
+
+ uint32_t byte_size = WABT_ALIGN_UP_TO_PAGE(data_segment->data.size());
+ uint32_t page_size = WABT_BYTES_TO_PAGES(byte_size);
+ memory->page_limits.initial = page_size;
+ memory->page_limits.max = page_size;
+ memory->page_limits.has_max = true;
+
+ module->AppendField(new MemoryModuleField(memory.release(), loc));
+ module->AppendField(
+ new DataSegmentModuleField(data_segment.release(), loc));
+ } else {
+ CHECK_RESULT(ParseLimits(&memory->page_limits));
+ module->AppendField(new MemoryModuleField(memory.release(), loc));
+ }
+
+ AppendInlineExportFields(module, &export_fields, module->memories.size() - 1);
+
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseStartModuleField(Module* module) {
+ WABT_TRACE(ParseStartModuleField);
+ EXPECT(Lpar);
+ auto loc = GetLocation();
+ EXPECT(Start);
+ Var var;
+ CHECK_RESULT(ParseVar(&var));
+ EXPECT(Rpar);
+ module->AppendField(new StartModuleField(var, loc));
+ return Result::Ok;
+}
+
+Result WastParser::ParseTableModuleField(Module* module) {
+ WABT_TRACE(ParseTableModuleField);
+ EXPECT(Lpar);
+ auto loc = GetLocation();
+ EXPECT(Table);
+ auto table = make_unique<Table>();
+ ParseBindVarOpt(&table->name);
+
+ ModuleFieldList export_fields;
+ CHECK_RESULT(ParseInlineExports(&export_fields, ExternalKind::Table));
+
+ if (PeekMatchLpar(TokenType::Import)) {
+ CheckImportOrdering(module);
+ auto import_loc = GetLocation();
+ auto import = make_unique<Import>();
+ import->kind = ExternalKind::Table;
+ import->table = table.release();
+ CHECK_RESULT(ParseInlineImport(import.get()));
+ CHECK_RESULT(ParseLimits(&import->table->elem_limits));
+ EXPECT(Anyfunc);
+ module->AppendField(new ImportModuleField(import.release(), import_loc));
+ } else if (Match(TokenType::Anyfunc)) {
+ EXPECT(Lpar);
+ EXPECT(Elem);
+
+ auto elem_segment = make_unique<ElemSegment>();
+ elem_segment->table_var = Var(module->tables.size());
+ elem_segment->offset.push_back(new ConstExpr(Const(Const::I32(), 0)));
+ elem_segment->offset.back().loc = loc;
+ CHECK_RESULT(ParseVarList(&elem_segment->vars));
+ EXPECT(Rpar);
+
+ table->elem_limits.initial = elem_segment->vars.size();
+ table->elem_limits.max = elem_segment->vars.size();
+ table->elem_limits.has_max = true;
+ module->AppendField(new TableModuleField(table.release(), loc));
+ module->AppendField(
+ new ElemSegmentModuleField(elem_segment.release(), loc));
+ } else {
+ CHECK_RESULT(ParseLimits(&table->elem_limits));
+ EXPECT(Anyfunc);
+ module->AppendField(new TableModuleField(table.release(), loc));
+ }
+
+ AppendInlineExportFields(module, &export_fields, module->tables.size() - 1);
+
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseExportDesc(Export* export_) {
+ WABT_TRACE(ParseExportDesc);
+ EXPECT(Lpar);
+ switch (Peek()) {
+ case TokenType::Func: export_->kind = ExternalKind::Func; break;
+ case TokenType::Table: export_->kind = ExternalKind::Table; break;
+ case TokenType::Memory: export_->kind = ExternalKind::Memory; break;
+ case TokenType::Global: export_->kind = ExternalKind::Global; break;
+ case TokenType::Except: export_->kind = ExternalKind::Except; break;
+ default:
+ return ErrorExpected({"an external kind"});
+ }
+ Consume();
+ CHECK_RESULT(ParseVar(&export_->var));
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseInlineExports(ModuleFieldList* fields,
+ ExternalKind kind) {
+ WABT_TRACE(ParseInlineExports);
+ while (PeekMatchLpar(TokenType::Export)) {
+ EXPECT(Lpar);
+ auto loc = GetLocation();
+ EXPECT(Export);
+ auto export_ = make_unique<Export>();
+ export_->kind = kind;
+ CHECK_RESULT(ParseQuotedText(&export_->name));
+ EXPECT(Rpar);
+ fields->push_back(new ExportModuleField(export_.release(), loc));
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseInlineImport(Import* import) {
+ WABT_TRACE(ParseInlineImport);
+ EXPECT(Lpar);
+ EXPECT(Import);
+ CHECK_RESULT(ParseQuotedText(&import->module_name));
+ CHECK_RESULT(ParseQuotedText(&import->field_name));
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseTypeUseOpt(FuncDeclaration* decl) {
+ WABT_TRACE(ParseTypeUseOpt);
+ if (MatchLpar(TokenType::Type)) {
+ decl->has_func_type = true;;
+ CHECK_RESULT(ParseVar(&decl->type_var));
+ EXPECT(Rpar);
+ } else {
+ decl->has_func_type = false;
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseFuncSignature(FuncSignature* sig,
+ BindingHash* param_bindings) {
+ WABT_TRACE(ParseFuncSignature);
+ CHECK_RESULT(ParseBoundValueTypeList(TokenType::Param, &sig->param_types,
+ param_bindings));
+ CHECK_RESULT(ParseResultList(&sig->result_types));
+ return Result::Ok;
+}
+
+Result WastParser::ParseBoundValueTypeList(TokenType token,
+ TypeVector* types,
+ BindingHash* bindings) {
+ WABT_TRACE(ParseBoundValueTypeList);
+ while (MatchLpar(token)) {
+ if (PeekMatch(TokenType::Var)) {
+ std::string name;
+ Type type;
+ auto loc = GetLocation();
+ ParseBindVarOpt(&name);
+ CHECK_RESULT(ParseValueType(&type));
+ bindings->emplace(name, Binding(loc, types->size()));
+ types->push_back(type);
+ } else {
+ ParseValueTypeList(types);
+ }
+ EXPECT(Rpar);
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseResultList(TypeVector* result_types) {
+ WABT_TRACE(ParseResultList);
+ while (MatchLpar(TokenType::Result)) {
+ ParseValueTypeList(result_types);
+ EXPECT(Rpar);
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseInstrList(ExprList* exprs) {
+ WABT_TRACE(ParseInstrList);
+ ExprList new_exprs;
+ while (IsInstr(PeekPair())) {
+ if (Succeeded(ParseInstr(&new_exprs))) {
+ exprs->splice(exprs->end(), new_exprs);
+ } else {
+ CHECK_RESULT(Synchronize(IsInstr));
+ }
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseTerminatingInstrList(ExprList* exprs) {
+ WABT_TRACE(ParseTerminatingInstrList);
+ Result result = ParseInstrList(exprs);
+ // An InstrList often has no further Lpar following it, because it would have
+ // gobbled it up. So if there is a following Lpar it is an error. If we
+ // handle it here we can produce a nicer error message.
+ CHECK_RESULT(ErrorIfLpar({"an instr"}));
+ return result;
+}
+
+Result WastParser::ParseInstr(ExprList* exprs) {
+ WABT_TRACE(ParseInstr);
+ if (IsPlainInstr(Peek())) {
+ std::unique_ptr<Expr> expr;
+ CHECK_RESULT(ParsePlainInstr(&expr));
+ exprs->push_back(expr.release());
+ return Result::Ok;
+ } else if (IsBlockInstr(Peek())) {
+ std::unique_ptr<Expr> expr;
+ CHECK_RESULT(ParseBlockInstr(&expr));
+ exprs->push_back(expr.release());
+ return Result::Ok;
+ } else if (PeekMatchExpr()) {
+ return ParseExpr(exprs);
+ } else {
+ assert(!"ParseInstr should only be called when IsInstr() is true");
+ return Result::Error;
+ }
+}
+
+template <typename T>
+Result WastParser::ParsePlainInstrVar(Location loc,
+ std::unique_ptr<Expr>* out_expr) {
+ Var var;
+ CHECK_RESULT(ParseVar(&var));
+ out_expr->reset(new T(var, loc));
+ return Result::Ok;
+}
+
+Result WastParser::ParsePlainInstr(std::unique_ptr<Expr>* out_expr) {
+ WABT_TRACE(ParsePlainInstr);
+ auto loc = GetLocation();
+ switch (Peek()) {
+ case TokenType::Unreachable:
+ Consume();
+ out_expr->reset(new UnreachableExpr(loc));
+ break;
+
+ case TokenType::Nop:
+ Consume();
+ out_expr->reset(new NopExpr(loc));
+ break;
+
+ case TokenType::Drop:
+ Consume();
+ out_expr->reset(new DropExpr(loc));
+ break;
+
+ case TokenType::Select:
+ Consume();
+ out_expr->reset(new SelectExpr(loc));
+ break;
+
+ case TokenType::Br:
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<BrExpr>(loc, out_expr));
+ break;
+
+ case TokenType::BrIf:
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<BrIfExpr>(loc, out_expr));
+ break;
+
+ case TokenType::BrTable: {
+ Consume();
+ auto var_list = make_unique<VarVector>();
+ CHECK_RESULT(ParseVarList(var_list.get()));
+ Var var = var_list->back();
+ var_list->pop_back();
+ out_expr->reset(new BrTableExpr(var_list.release(), var, loc));
+ break;
+ }
+
+ case TokenType::Return:
+ Consume();
+ out_expr->reset(new ReturnExpr(loc));
+ break;
+
+ case TokenType::Call:
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<CallExpr>(loc, out_expr));
+ break;
+
+ case TokenType::CallIndirect:
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<CallIndirectExpr>(loc, out_expr));
+ break;
+
+ case TokenType::GetLocal:
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<GetLocalExpr>(loc, out_expr));
+ break;
+
+ case TokenType::SetLocal:
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<SetLocalExpr>(loc, out_expr));
+ break;
+
+ case TokenType::TeeLocal:
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<TeeLocalExpr>(loc, out_expr));
+ break;
+
+ case TokenType::GetGlobal:
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<GetGlobalExpr>(loc, out_expr));
+ break;
+
+ case TokenType::SetGlobal:
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<SetGlobalExpr>(loc, out_expr));
+ break;
+
+ case TokenType::Load: {
+ Opcode opcode = Consume().opcode;
+ uint32_t offset;
+ uint32_t align;
+ ParseOffsetOpt(&offset);
+ ParseAlignOpt(&align);
+ out_expr->reset(new LoadExpr(opcode, align, offset, loc));
+ break;
+ }
+
+ case TokenType::Store: {
+ Opcode opcode = Consume().opcode;
+ uint32_t offset;
+ uint32_t align;
+ ParseOffsetOpt(&offset);
+ ParseAlignOpt(&align);
+ out_expr->reset(new StoreExpr(opcode, align, offset, loc));
+ break;
+ }
+
+ case TokenType::Const: {
+ Const const_;
+ CHECK_RESULT(ParseConst(&const_));
+ out_expr->reset(new ConstExpr(const_, loc));
+ break;
+ }
+
+ case TokenType::Unary:
+ out_expr->reset(new UnaryExpr(Consume().opcode, loc));
+ break;
+
+ case TokenType::Binary:
+ out_expr->reset(new BinaryExpr(Consume().opcode, loc));
+ break;
+
+ case TokenType::Compare:
+ out_expr->reset(new CompareExpr(Consume().opcode, loc));
+ break;
+
+ case TokenType::Convert:
+ out_expr->reset(new ConvertExpr(Consume().opcode, loc));
+ break;
+
+ case TokenType::CurrentMemory:
+ Consume();
+ out_expr->reset(new CurrentMemoryExpr(loc));
+ break;
+
+ case TokenType::GrowMemory:
+ Consume();
+ out_expr->reset(new GrowMemoryExpr(loc));
+ break;
+
+ case TokenType::Throw:
+ ErrorUnlessExceptionsAllowed();
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<ThrowExpr>(loc, out_expr));
+ break;
+
+ case TokenType::Rethrow:
+ ErrorUnlessExceptionsAllowed();
+ Consume();
+ CHECK_RESULT(ParsePlainInstrVar<RethrowExpr>(loc, out_expr));
+ break;
+
+ default:
+ assert(
+ !"ParsePlainInstr should only be called when IsPlainInstr() is true");
+ return Result::Error;
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseConst(Const* const_) {
+ WABT_TRACE(ParseConst);
+ auto opcode = Consume().opcode;
+ LiteralTerminal literal;
+
+ auto loc = GetLocation();
+
+ switch (Peek()) {
+ case TokenType::Nat:
+ case TokenType::Int:
+ case TokenType::Float:
+ literal = Consume().literal;
+ break;
+
+ default:
+ return ErrorExpected({"a numeric literal"}, "123, -45, 6.7e8");
+ }
+
+ string_view sv = literal.text.to_string_view();
+ const char* s = sv.begin();
+ const char* end = sv.end();
+
+ Result result;
+ switch (opcode) {
+ case Opcode::I32Const:
+ const_->type = Type::I32;
+ result =
+ ParseInt32(s, end, &const_->u32, ParseIntType::SignedAndUnsigned);
+ break;
+
+ case Opcode::I64Const:
+ const_->type = Type::I64;
+ result =
+ ParseInt64(s, end, &const_->u64, ParseIntType::SignedAndUnsigned);
+ break;
+
+ case Opcode::F32Const:
+ const_->type = Type::F32;
+ result = ParseFloat(literal.type, s, end, &const_->f32_bits);
+ break;
+
+ case Opcode::F64Const:
+ const_->type = Type::F64;
+ result = ParseDouble(literal.type, s, end, &const_->f64_bits);
+ break;
+
+ default:
+ assert(!"ParseConst called with invalid opcode");
+ return Result::Error;
+ }
+
+ if (Failed(result)) {
+ Error(loc, "invalid literal \"%s\"", literal.text.to_string().c_str());
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseConstList(ConstVector* consts) {
+ WABT_TRACE(ParseConstList);
+ while (PeekMatchLpar(TokenType::Const)) {
+ Consume();
+ Const const_;
+ CHECK_RESULT(ParseConst(&const_));
+ EXPECT(Rpar);
+ consts->push_back(const_);
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseBlockInstr(std::unique_ptr<Expr>* out_expr) {
+ WABT_TRACE(ParseBlockInstr);
+ auto loc = GetLocation();
+
+ switch (Peek()) {
+ case TokenType::Block: {
+ Consume();
+ auto block = make_unique<Block>();
+ CHECK_RESULT(ParseLabelOpt(&block->label));
+ CHECK_RESULT(ParseBlock(block.get()));
+ EXPECT(End);
+ CHECK_RESULT(ParseEndLabelOpt(block->label));
+ out_expr->reset(new BlockExpr(block.release(), loc));
+ break;
+ }
+
+ case TokenType::Loop: {
+ Consume();
+ auto block = make_unique<Block>();
+ CHECK_RESULT(ParseLabelOpt(&block->label));
+ CHECK_RESULT(ParseBlock(block.get()));
+ EXPECT(End);
+ CHECK_RESULT(ParseEndLabelOpt(block->label));
+ out_expr->reset(new LoopExpr(block.release(), loc));
+ break;
+ }
+
+ case TokenType::If: {
+ Consume();
+ auto true_ = make_unique<Block>();
+ ExprList false_;
+ CHECK_RESULT(ParseLabelOpt(&true_->label));
+ CHECK_RESULT(ParseBlock(true_.get()));
+ if (Match(TokenType::Else)) {
+ CHECK_RESULT(ParseEndLabelOpt(true_->label));
+ CHECK_RESULT(ParseTerminatingInstrList(&false_));
+ }
+ EXPECT(End);
+ CHECK_RESULT(ParseEndLabelOpt(true_->label));
+ out_expr->reset(new IfExpr(true_.release(), std::move(false_), loc));
+ break;
+ }
+
+ case TokenType::Try: {
+ ErrorUnlessExceptionsAllowed();
+ Consume();
+ auto expr = make_unique<TryExpr>(loc);
+ expr->block = new Block();
+ CatchVector catches;
+ CHECK_RESULT(ParseLabelOpt(&expr->block->label));
+ CHECK_RESULT(ParseBlock(expr->block));
+ CHECK_RESULT(ParseCatchInstrList(&expr->catches));
+ CHECK_RESULT(ErrorIfLpar({"a catch expr"}));
+ EXPECT(End);
+ CHECK_RESULT(ParseEndLabelOpt(expr->block->label));
+ *out_expr = std::move(expr);
+ break;
+ }
+
+ default:
+ assert(
+ !"ParseBlockInstr should only be called when IsBlockInstr() is true");
+ return Result::Error;
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseLabelOpt(std::string* out_label) {
+ WABT_TRACE(ParseLabelOpt);
+ if (PeekMatch(TokenType::Var)) {
+ *out_label = Consume().text.to_string();
+ } else {
+ out_label->clear();
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseEndLabelOpt(const std::string& begin_label) {
+ WABT_TRACE(ParseEndLabelOpt);
+ auto loc = GetLocation();
+ std::string end_label;
+ CHECK_RESULT(ParseLabelOpt(&end_label));
+ if (!end_label.empty()) {
+ if (begin_label.empty()) {
+ Error(loc, "unexpected label \"%s\"", end_label.c_str());
+ } else if (begin_label != end_label) {
+ Error(loc, "mismatching label \"%s\" != \"%s\"", begin_label.c_str(),
+ end_label.c_str());
+ }
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseBlock(Block* block) {
+ WABT_TRACE(ParseBlock);
+ CHECK_RESULT(ParseResultList(&block->sig));
+ CHECK_RESULT(ParseInstrList(&block->exprs));
+ return Result::Ok;
+}
+
+Result WastParser::ParseExprList(ExprList* exprs) {
+ WABT_TRACE(ParseExprList);
+ ExprList new_exprs;
+ while (PeekMatchExpr()) {
+ if (Succeeded(ParseExpr(&new_exprs))) {
+ exprs->splice(exprs->end(), new_exprs);
+ } else {
+ CHECK_RESULT(Synchronize(IsExpr));
+ }
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseExpr(ExprList* exprs) {
+ WABT_TRACE(ParseExpr);
+ if (!PeekMatch(TokenType::Lpar))
+ return Result::Error;
+
+ if (IsPlainInstr(Peek(1))) {
+ Consume();
+ std::unique_ptr<Expr> expr;
+ CHECK_RESULT(ParsePlainInstr(&expr));
+ CHECK_RESULT(ParseExprList(exprs));
+ CHECK_RESULT(ErrorIfLpar({"an expr"}));
+ exprs->push_back(expr.release());
+ } else {
+ auto loc = GetLocation();
+
+ switch (Peek(1)) {
+ case TokenType::Block: {
+ Consume();
+ Consume();
+ auto block = make_unique<Block>();
+ CHECK_RESULT(ParseLabelOpt(&block->label));
+ CHECK_RESULT(ParseBlock(block.get()));
+ exprs->push_back(new BlockExpr(block.release(), loc));
+ break;
+ }
+
+ case TokenType::Loop: {
+ Consume();
+ Consume();
+ auto block = make_unique<Block>();
+ CHECK_RESULT(ParseLabelOpt(&block->label));
+ CHECK_RESULT(ParseBlock(block.get()));
+ exprs->push_back(new LoopExpr(block.release(), loc));
+ break;
+ }
+
+ case TokenType::If: {
+ Consume();
+ Consume();
+ auto true_ = make_unique<Block>();
+ ExprList false_;
+
+ CHECK_RESULT(ParseLabelOpt(&true_->label));
+ CHECK_RESULT(ParseResultList(&true_->sig));
+
+ if (PeekMatchExpr()) {
+ ExprList cond;
+ CHECK_RESULT(ParseExpr(&cond));
+ exprs->splice(exprs->end(), cond);
+ }
+
+ if (MatchLpar(TokenType::Then)) {
+ CHECK_RESULT(ParseTerminatingInstrList(&true_->exprs));
+ EXPECT(Rpar);
+
+ if (MatchLpar(TokenType::Else)) {
+ CHECK_RESULT(ParseTerminatingInstrList(&false_));
+ EXPECT(Rpar);
+ } else if (PeekMatchExpr()) {
+ CHECK_RESULT(ParseExpr(&false_));
+ }
+ } else if (PeekMatchExpr()) {
+ CHECK_RESULT(ParseExpr(&true_->exprs));
+ if (PeekMatchExpr()) {
+ CHECK_RESULT(ParseExpr(&false_));
+ }
+ } else {
+ ConsumeIfLpar();
+ return ErrorExpected({"then block"}, "(then ...)");
+ }
+
+ exprs->push_back(new IfExpr(true_.release(), std::move(false_), loc));
+ break;
+ }
+
+ case TokenType::Try: {
+ Consume();
+ ErrorUnlessExceptionsAllowed();
+ Consume();
+
+ auto block = make_unique<Block>();
+ CHECK_RESULT(ParseLabelOpt(&block->label));
+ CHECK_RESULT(ParseResultList(&block->sig));
+ CHECK_RESULT(ParseInstrList(&block->exprs));
+ auto try_ = make_unique<TryExpr>(loc);
+ try_->block = block.release();
+ CHECK_RESULT(ParseCatchExprList(&try_->catches));
+ CHECK_RESULT(ErrorIfLpar({"a catch expr"}));
+
+ exprs->push_back(try_.release());
+ break;
+ }
+
+ default:
+ assert(!"ParseExpr should only be called when IsExpr() is true");
+ return Result::Error;
+ }
+ }
+
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseCatchInstrList(CatchVector* catches) {
+ WABT_TRACE(ParseCatchInstrList);
+ while (IsCatch(Peek())) {
+ auto catch_ = make_unique<Catch>();
+ catch_->loc = GetLocation();
+
+ if (Consume().token_type == TokenType::Catch)
+ CHECK_RESULT(ParseVar(&catch_->var));
+
+ CHECK_RESULT(ParseInstrList(&catch_->exprs));
+ catches->push_back(catch_.release());
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseCatchExprList(CatchVector* catches) {
+ WABT_TRACE(ParseCatchExprList);
+ while (PeekMatch(TokenType::Lpar) && IsCatch(Peek(1))) {
+ Consume();
+ auto catch_ = make_unique<Catch>();
+ catch_->loc = GetLocation();
+
+ if (Consume().token_type == TokenType::Catch)
+ CHECK_RESULT(ParseVar(&catch_->var));
+
+ CHECK_RESULT(ParseTerminatingInstrList(&catch_->exprs));
+ EXPECT(Rpar);
+ catches->push_back(catch_.release());
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseGlobalType(Global* global) {
+ WABT_TRACE(ParseGlobalType);
+ if (MatchLpar(TokenType::Mut)) {
+ global->mutable_ = true;
+ CHECK_RESULT(ParseValueType(&global->type));
+ CHECK_RESULT(ErrorIfLpar({"i32", "i64", "f32", "f64"}));
+ EXPECT(Rpar);
+ } else {
+ CHECK_RESULT(ParseValueType(&global->type));
+ }
+
+ return Result::Ok;
+}
+
+Result WastParser::ParseCommandList(CommandPtrVector* commands) {
+ WABT_TRACE(ParseCommandList);
+ while (PeekMatch(TokenType::Lpar)) {
+ CommandPtr command;
+ if (Succeeded(ParseCommand(&command))) {
+ commands->push_back(std::move(command));
+ } else {
+ CHECK_RESULT(Synchronize(IsCommand));
+ }
+ }
+ return Result::Ok;
+}
+
+Result WastParser::ParseCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseCommand);
+ switch (Peek(1)) {
+ case TokenType::AssertExhaustion:
+ return ParseAssertExhaustionCommand(out_command);
+
+ case TokenType::AssertInvalid:
+ return ParseAssertInvalidCommand(out_command);
+
+ case TokenType::AssertMalformed:
+ return ParseAssertMalformedCommand(out_command);
+
+ case TokenType::AssertReturn:
+ return ParseAssertReturnCommand(out_command);
+
+ case TokenType::AssertReturnArithmeticNan:
+ return ParseAssertReturnArithmeticNanCommand(out_command);
+
+ case TokenType::AssertReturnCanonicalNan:
+ return ParseAssertReturnCanonicalNanCommand(out_command);
+
+ case TokenType::AssertTrap:
+ return ParseAssertTrapCommand(out_command);
+
+ case TokenType::AssertUnlinkable:
+ return ParseAssertUnlinkableCommand(out_command);
+
+ case TokenType::Get:
+ case TokenType::Invoke:
+ return ParseActionCommand(out_command);
+
+ case TokenType::Module:
+ return ParseModuleCommand(out_command);
+
+ case TokenType::Register:
+ return ParseRegisterCommand(out_command);
+
+ default:
+ assert(!"ParseCommand should only be called when IsCommand() is true");
+ return Result::Error;
+ }
+}
+
+Result WastParser::ParseAssertExhaustionCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertExhaustionCommand);
+ return ParseAssertActionTextCommand<AssertExhaustionCommand>(
+ TokenType::AssertExhaustion, out_command);
+}
+
+Result WastParser::ParseAssertInvalidCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertInvalidCommand);
+ return ParseAssertScriptModuleCommand<AssertInvalidCommand>(
+ TokenType::AssertInvalid, out_command);
+}
+
+Result WastParser::ParseAssertMalformedCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertMalformedCommand);
+ return ParseAssertScriptModuleCommand<AssertMalformedCommand>(
+ TokenType::AssertMalformed, out_command);
+}
+
+Result WastParser::ParseAssertReturnCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertReturnCommand);
+ EXPECT(Lpar);
+ EXPECT(AssertReturn);
+ auto action = make_unique<Action>();
+ auto consts = make_unique<ConstVector>();
+ CHECK_RESULT(ParseAction(action.get()));
+ CHECK_RESULT(ParseConstList(consts.get()));
+ EXPECT(Rpar);
+ out_command->reset(
+ new AssertReturnCommand(action.release(), consts.release()));
+ return Result::Ok;
+}
+
+Result WastParser::ParseAssertReturnArithmeticNanCommand(
+ CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertReturnArithmeticNanCommand);
+ return ParseAssertActionCommand<AssertReturnArithmeticNanCommand>(
+ TokenType::AssertReturnArithmeticNan, out_command);
+}
+
+Result WastParser::ParseAssertReturnCanonicalNanCommand(
+ CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertReturnCanonicalNanCommand);
+ return ParseAssertActionCommand<AssertReturnCanonicalNanCommand>(
+ TokenType::AssertReturnCanonicalNan, out_command);
+}
+
+Result WastParser::ParseAssertTrapCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertTrapCommand);
+ EXPECT(Lpar);
+ EXPECT(AssertTrap);
+ if (PeekMatchLpar(TokenType::Module)) {
+ std::unique_ptr<ScriptModule> module;
+ std::string text;
+ CHECK_RESULT(ParseScriptModule(&module));
+ CHECK_RESULT(ParseQuotedText(&text));
+ out_command->reset(new AssertUninstantiableCommand(module.release(), text));
+ } else {
+ auto action = make_unique<Action>();
+ std::string text;
+ CHECK_RESULT(ParseAction(action.get()));
+ CHECK_RESULT(ParseQuotedText(&text));
+ out_command->reset(new AssertTrapCommand(action.release(), text));
+ }
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseAssertUnlinkableCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertUnlinkableCommand);
+ return ParseAssertScriptModuleCommand<AssertUnlinkableCommand>(
+ TokenType::AssertUnlinkable, out_command);
+}
+
+Result WastParser::ParseActionCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseActionCommand);
+ auto action = make_unique<Action>();
+ CHECK_RESULT(ParseAction(action.get()));
+ out_command->reset(new ActionCommand(action.release()));
+ return Result::Ok;
+}
+
+Result WastParser::ParseModuleCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseModuleCommand);
+ std::unique_ptr<ScriptModule> script_module;
+ CHECK_RESULT(ParseScriptModule(&script_module));
+
+ std::unique_ptr<Module> module;
+
+ switch (script_module->type) {
+ case ScriptModule::Type::Text:
+ module.reset(script_module->text);
+ script_module->text = nullptr;
+ break;
+
+ case ScriptModule::Type::Binary: {
+ module.reset(new Module());
+ ReadBinaryOptions options;
+ BinaryErrorHandlerModule error_handler(&script_module->binary.loc, this);
+ const char* filename = "<text>";
+ ReadBinaryIr(filename, script_module->binary.data.data(),
+ script_module->binary.data.size(), &options, &error_handler,
+ module.get());
+ module->name = script_module->binary.name;
+ module->loc = script_module->binary.loc;
+ break;
+ }
+
+ case ScriptModule::Type::Quoted:
+ return ErrorExpected({"a binary module", "a text module"});
+ }
+
+ Index command_index = script_->commands.size();
+
+ if (!module->name.empty()) {
+ script_->module_bindings.emplace(module->name,
+ Binding(module->loc, command_index));
+ }
+
+ last_module_index_ = command_index;
+
+ out_command->reset(new ModuleCommand(module.release()));
+ return Result::Ok;
+}
+
+Result WastParser::ParseRegisterCommand(CommandPtr* out_command) {
+ WABT_TRACE(ParseRegisterCommand);
+ EXPECT(Lpar);
+ auto loc = GetLocation();
+ EXPECT(Register);
+ std::string text;
+ Var var;
+ CHECK_RESULT(ParseQuotedText(&text));
+ ParseVarOpt(&var, Var(last_module_index_, loc));
+ EXPECT(Rpar);
+ out_command->reset(new RegisterCommand(text, var));
+ return Result::Ok;
+}
+
+Result WastParser::ParseAction(Action* out_action) {
+ WABT_TRACE(ParseAction);
+ EXPECT(Lpar);
+ auto loc = GetLocation();
+
+ switch (Peek()) {
+ case TokenType::Invoke:
+ out_action->loc = loc;
+ out_action->type = ActionType::Invoke;
+ out_action->invoke = new ActionInvoke();
+
+ Consume();
+ ParseVarOpt(&out_action->module_var, Var(last_module_index_, loc));
+ CHECK_RESULT(ParseQuotedText(&out_action->name));
+ CHECK_RESULT(ParseConstList(&out_action->invoke->args));
+ break;
+
+ case TokenType::Get:
+ out_action->loc = loc;
+ out_action->type = ActionType::Get;
+
+ Consume();
+ ParseVarOpt(&out_action->module_var, Var(last_module_index_, loc));
+ CHECK_RESULT(ParseQuotedText(&out_action->name));
+ break;
+
+ default:
+ return ErrorExpected({"invoke", "get"});
+ }
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+Result WastParser::ParseScriptModule(
+ std::unique_ptr<ScriptModule>* out_module) {
+ WABT_TRACE(ParseScriptModule);
+ EXPECT(Lpar);
+ auto loc = GetLocation();
+ EXPECT(Module);
+ std::string name;
+ ParseBindVarOpt(&name);
+
+ std::unique_ptr<ScriptModule> script_module;
+
+ switch (Peek()) {
+ case TokenType::Bin: {
+ Consume();
+ std::vector<uint8_t> data;
+ CHECK_RESULT(ParseTextList(&data));
+
+ script_module = make_unique<ScriptModule>(ScriptModule::Type::Binary);
+ script_module->binary.name = name;
+ script_module->binary.loc = loc;
+ script_module->binary.data = std::move(data);
+ break;
+ }
+
+ case TokenType::Quote: {
+ Consume();
+ std::vector<uint8_t> data;
+ CHECK_RESULT(ParseTextList(&data));
+
+ script_module = make_unique<ScriptModule>(ScriptModule::Type::Quoted);
+ script_module->quoted.name = name;
+ script_module->quoted.loc = loc;
+ script_module->quoted.data = std::move(data);
+ break;
+ }
+
+ default: {
+ script_module = make_unique<ScriptModule>(ScriptModule::Type::Text);
+ auto module = make_unique<Module>();
+ module->loc = loc;
+ module->name = name;
+ CHECK_RESULT(ParseModuleFieldList(module.get()));
+ script_module->text = module.release();
+ break;
+ }
+ }
+
+ *out_module = std::move(script_module);
+
+ EXPECT(Rpar);
+ return Result::Ok;
+}
+
+template <typename T>
+Result WastParser::ParseAssertActionCommand(TokenType token_type,
+ CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertActionCommand);
+ EXPECT(Lpar);
+ CHECK_RESULT(Expect(token_type));
+ auto action = make_unique<Action>();
+ CHECK_RESULT(ParseAction(action.get()));
+ EXPECT(Rpar);
+ out_command->reset(new T(action.release()));
+ return Result::Ok;
+}
+
+template <typename T>
+Result WastParser::ParseAssertActionTextCommand(TokenType token_type,
+ CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertActionTextCommand);
+ EXPECT(Lpar);
+ CHECK_RESULT(Expect(token_type));
+ auto action = make_unique<Action>();
+ std::string text;
+ CHECK_RESULT(ParseAction(action.get()));
+ CHECK_RESULT(ParseQuotedText(&text));
+ EXPECT(Rpar);
+ out_command->reset(new T(action.release(), text));
+ return Result::Ok;
+}
+
+template <typename T>
+Result WastParser::ParseAssertScriptModuleCommand(TokenType token_type,
+ CommandPtr* out_command) {
+ WABT_TRACE(ParseAssertScriptModuleCommand);
+ EXPECT(Lpar);
+ CHECK_RESULT(Expect(token_type));
+ std::unique_ptr<ScriptModule> module;
+ std::string text;
+ CHECK_RESULT(ParseScriptModule(&module));
+ CHECK_RESULT(ParseQuotedText(&text));
+ EXPECT(Rpar);
+ out_command->reset(new T(module.release(), text));
+ return Result::Ok;
+}
+
+void WastParser::CheckImportOrdering(Module* module) {
+ if (module->funcs.size() != module->num_func_imports ||
+ module->tables.size() != module->num_table_imports ||
+ module->memories.size() != module->num_memory_imports ||
+ module->globals.size() != module->num_global_imports ||
+ module->excepts.size() != module->num_except_imports) {
+ Error(GetLocation(),
+ "imports must occur before all non-import definitions");
+ }
+}
+
+Result ParseWast(WastLexer* lexer,
+ Script** out_script,
+ ErrorHandler* error_handler,
+ WastParseOptions* options) {
+ auto script = make_unique<Script>();
+ WastParser parser(lexer, error_handler, options);
+ Result result = parser.ParseScript(script.get());
+
+ if (out_script)
+ *out_script = script.release();
+
+ return result;
+}
+
+} // namespace wabt