Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/wasm2c/src/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 110 kB image not shown  

Quelle  wast-parser.cc   Sprache: C

 
/*
 * 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 "wabt/wast-parser.h"

#include "wabt/binary-reader-ir.h"
#include "wabt/binary-reader.h"
#include "wabt/cast.h"
#include "wabt/expr-visitor.h"
#include "wabt/resolve-names.h"
#include "wabt/stream.h"
#include "wabt/utf8.h"
#include "wabt/validator.h"

#define WABT_TRACING 0
#include "wabt/tracing.h"

#define EXPECT(token_type) CHECK_RESULT(Expect(TokenType::token_type))

namespace wabt {

namespace {

static const size_t kMaxErrorTokenLength = 80;

bool IsPowerOfTwo(uint32_t x) {
  return x && ((x & (x - 1)) == 0);
}

template <typename OutputIter>
void RemoveEscapes(std::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;
        case 'u': {
          // The string should be validated already,
          // so this must be a valid unicode escape sequence.
          uint32_t digit;
          uint32_t scalar_value = 0;

          // Skip u and { characters.
          src += 2;

          do {
            if (Succeeded(ParseHexdigit(src[0], &digit))) {
              scalar_value = (scalar_value << 4) | digit;
            } else {
              assert(0);
            }
            src++;
          } while (src[0] != '}');

          // Maximum value of a unicode scalar value
          assert(scalar_value < 0x110000);

          // Encode the unicode scalar value as UTF8 sequence
          if (scalar_value < 0x80) {
            *dest++ = static_cast<uint8_t>(scalar_value);
          } else {
            if (scalar_value < 0x800) {
              *dest++ = static_cast<uint8_t>(0xc0 | (scalar_value >> 6));
            } else {
              if (scalar_value < 0x10000) {
                *dest++ = static_cast<uint8_t>(0xe0 | (scalar_value >> 12));
              } else {
                *dest++ = static_cast<uint8_t>(0xf0 | (scalar_value >> 18));
                *dest++ =
                    static_cast<uint8_t>(0x80 | ((scalar_value >> 12) & 0x3f));
              }

              *dest++ =
                  static_cast<uint8_t>(0x80 | ((scalar_value >> 6) & 0x3f));
            }

            *dest++ = static_cast<uint8_t>(0x80 | (scalar_value & 0x3f));
          }
          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++;
    }
  }
}

using TextVector = std::vector<std::string_view>;

template <typename OutputIter>
void RemoveEscapes(const TextVector& texts, OutputIter out) {
  for (std::string_view text : texts)
    RemoveEscapes(text, out);
}

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::ReturnCall:
    case TokenType::ReturnCallIndirect:
    case TokenType::Call:
    case TokenType::CallIndirect:
    case TokenType::CallRef:
    case TokenType::LocalGet:
    case TokenType::LocalSet:
    case TokenType::LocalTee:
    case TokenType::GlobalGet:
    case TokenType::GlobalSet:
    case TokenType::Load:
    case TokenType::Store:
    case TokenType::Const:
    case TokenType::Unary:
    case TokenType::Binary:
    case TokenType::Compare:
    case TokenType::Convert:
    case TokenType::MemoryCopy:
    case TokenType::DataDrop:
    case TokenType::MemoryFill:
    case TokenType::MemoryGrow:
    case TokenType::MemoryInit:
    case TokenType::MemorySize:
    case TokenType::TableCopy:
    case TokenType::ElemDrop:
    case TokenType::TableInit:
    case TokenType::TableGet:
    case TokenType::TableSet:
    case TokenType::TableGrow:
    case TokenType::TableSize:
    case TokenType::TableFill:
    case TokenType::Throw:
    case TokenType::Rethrow:
    case TokenType::RefFunc:
    case TokenType::RefNull:
    case TokenType::RefIsNull:
    case TokenType::AtomicLoad:
    case TokenType::AtomicStore:
    case TokenType::AtomicRmw:
    case TokenType::AtomicRmwCmpxchg:
    case TokenType::AtomicNotify:
    case TokenType::AtomicFence:
    case TokenType::AtomicWait:
    case TokenType::Ternary:
    case TokenType::SimdLaneOp:
    case TokenType::SimdLoadLane:
    case TokenType::SimdStoreLane:
    case TokenType::SimdShuffleOp:
      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 IsLparAnn(TokenTypePair pair) {
  return pair[0] == TokenType::LparAnn;
}

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::Tag:
    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::AssertException:
    case TokenType::AssertExhaustion:
    case TokenType::AssertInvalid:
    case TokenType::AssertMalformed:
    case TokenType::AssertReturn:
    case TokenType::AssertTrap:
    case TokenType::AssertUnlinkable:
    case TokenType::Get:
    case TokenType::Invoke:
    case TokenType::Input:
    case TokenType::Module:
    case TokenType::Output:
    case TokenType::Register:
      return true;
    default:
      return false;
  }
}

bool IsEmptySignature(const FuncSignature& sig) {
  return sig.result_types.empty() && sig.param_types.empty();
}

bool ResolveFuncTypeWithEmptySignature(const Module& module,
                                       FuncDeclaration* decl) {
  // Resolve func type variables where the signature was not specified
  // explicitly, e.g.: (func (type 1) ...)
  if (decl->has_func_type && IsEmptySignature(decl->sig)) {
    const FuncType* func_type = module.GetFuncType(decl->type_var);
    if (func_type) {
      decl->sig = func_type->sig;
      return true;
    }
  }
  return false;
}

void ResolveTypeName(
    const Module& module,
    Type& type,
    Index index,
    const std::unordered_map<uint32_t, std::string>& bindings) {
  if (type != Type::Reference || type.GetReferenceIndex() != kInvalidIndex) {
    return;
  }

  const auto name_iterator = bindings.find(index);
  assert(name_iterator != bindings.cend());
  const auto type_index = module.type_bindings.FindIndex(name_iterator->second);
  assert(type_index != kInvalidIndex);
  type = Type(Type::Reference, type_index);
}

void ResolveTypeNames(const Module& module, FuncDeclaration* decl) {
  assert(decl);
  auto& signature = decl->sig;

  for (uint32_t param_index = 0; param_index < signature.GetNumParams();
       ++param_index) {
    ResolveTypeName(module, signature.param_types[param_index], param_index,
                    signature.param_type_names);
  }

  for (uint32_t result_index = 0; result_index < signature.GetNumResults();
       ++result_index) {
    ResolveTypeName(module, signature.result_types[result_index], result_index,
                    signature.result_type_names);
  }
}

void ResolveImplicitlyDefinedFunctionType(const Location& loc,
                                          Module* module,
                                          const FuncDeclaration& decl) {
  // Resolve implicitly defined function types, e.g.: (func (param i32) ...)
  if (!decl.has_func_type) {
    Index func_type_index = module->GetFuncTypeIndex(decl.sig);
    if (func_type_index == kInvalidIndex) {
      auto func_type_field = std::make_unique<TypeModuleField>(loc);
      auto func_type = std::make_unique<FuncType>();
      func_type->sig = decl.sig;
      func_type_field->type = std::move(func_type);
      module->AppendField(std::move(func_type_field));
    }
  }
}

Result CheckTypeIndex(const Location& loc,
                      Type actual,
                      Type expected,
                      const char* desc,
                      Index index,
                      const char* index_kind,
                      Errors* errors) {
  // Types must match exactly; no subtyping should be allowed.
  if (actual != expected) {
    errors->emplace_back(
        ErrorLevel::Error, loc,
        StringPrintf("type mismatch for %s %" PRIindex
                     " of %s. got %s, expected %s",
                     index_kind, index, desc, actual.GetName().c_str(),
                     expected.GetName().c_str()));
    return Result::Error;
  }
  return Result::Ok;
}

Result CheckTypes(const Location& loc,
                  const TypeVector& actual,
                  const TypeVector& expected,
                  const char* desc,
                  const char* index_kind,
                  Errors* errors) {
  Result result = Result::Ok;
  if (actual.size() == expected.size()) {
    for (size_t i = 0; i < actual.size(); ++i) {
      result |= CheckTypeIndex(loc, actual[i], expected[i], desc, i, index_kind,
                               errors);
    }
  } else {
    errors->emplace_back(
        ErrorLevel::Error, loc,
        StringPrintf("expected %" PRIzd " %ss, got %" PRIzd, expected.size(),
                     index_kind, actual.size()));
    result = Result::Error;
  }
  return result;
}

Result CheckFuncTypeVarMatchesExplicit(const Location& loc,
                                       const Module& module,
                                       const FuncDeclaration& decl,
                                       Errors* errors) {
  Result result = Result::Ok;
  if (decl.has_func_type) {
    const FuncType* func_type = module.GetFuncType(decl.type_var);
    if (func_type) {
      result |=
          CheckTypes(loc, decl.sig.result_types, func_type->sig.result_types,
                     "function""result", errors);
      result |=
          CheckTypes(loc, decl.sig.param_types, func_type->sig.param_types,
                     "function""argument", errors);
    } else if (!(decl.sig.param_types.empty() &&
                 decl.sig.result_types.empty())) {
      // We want to check whether the function type at the explicit index
      // matches the given param and result types. If they were omitted then
      // they'll be resolved automatically (see
      // ResolveFuncTypeWithEmptySignature), but if they are provided then we
      // have to check. If we get here then the type var is invalid, so we
      // can't check whether they match.
      if (decl.type_var.is_index()) {
        errors->emplace_back(ErrorLevel::Error, loc,
                             StringPrintf("invalid func type index %" PRIindex,
                                          decl.type_var.index()));
      } else {
        errors->emplace_back(ErrorLevel::Error, loc,
                             StringPrintf("expected func type identifier %s",
                                          decl.type_var.name().c_str()));
      }
      result = Result::Error;
    }
  }
  return result;
}

bool IsInlinableFuncSignature(const FuncSignature& sig) {
  return sig.GetNumParams() == 0 && sig.GetNumResults() <= 1;
}

class ResolveFuncTypesExprVisitorDelegate : public ExprVisitor::DelegateNop {
 public:
  explicit ResolveFuncTypesExprVisitorDelegate(Module* module, Errors* errors)
      : module_(module), errors_(errors) {}

  void ResolveBlockDeclaration(const Location& loc, BlockDeclaration* decl) {
    ResolveTypeNames(*module_, decl);
    ResolveFuncTypeWithEmptySignature(*module_, decl);
    if (!IsInlinableFuncSignature(decl->sig)) {
      ResolveImplicitlyDefinedFunctionType(loc, module_, *decl);
    }
  }

  Result BeginBlockExpr(BlockExpr* expr) override {
    ResolveBlockDeclaration(expr->loc, &expr->block.decl);
    return CheckFuncTypeVarMatchesExplicit(expr->loc, *module_,
                                           expr->block.decl, errors_);
  }

  Result BeginIfExpr(IfExpr* expr) override {
    ResolveBlockDeclaration(expr->loc, &expr->true_.decl);
    return CheckFuncTypeVarMatchesExplicit(expr->loc, *module_,
                                           expr->true_.decl, errors_);
  }

  Result BeginLoopExpr(LoopExpr* expr) override {
    ResolveBlockDeclaration(expr->loc, &expr->block.decl);
    return CheckFuncTypeVarMatchesExplicit(expr->loc, *module_,
                                           expr->block.decl, errors_);
  }

  Result BeginTryExpr(TryExpr* expr) override {
    ResolveBlockDeclaration(expr->loc, &expr->block.decl);
    return CheckFuncTypeVarMatchesExplicit(expr->loc, *module_,
                                           expr->block.decl, errors_);
  }

  Result OnCallIndirectExpr(CallIndirectExpr* expr) override {
    ResolveFuncTypeWithEmptySignature(*module_, &expr->decl);
    ResolveImplicitlyDefinedFunctionType(expr->loc, module_, expr->decl);
    return CheckFuncTypeVarMatchesExplicit(expr->loc, *module_, expr->decl,
                                           errors_);
  }

  Result OnReturnCallIndirectExpr(ReturnCallIndirectExpr* expr) override {
    ResolveFuncTypeWithEmptySignature(*module_, &expr->decl);
    ResolveImplicitlyDefinedFunctionType(expr->loc, module_, expr->decl);
    return CheckFuncTypeVarMatchesExplicit(expr->loc, *module_, expr->decl,
                                           errors_);
  }

 private:
  Module* module_;
  Errors* errors_;
};

Result ResolveFuncTypes(Module* module, Errors* errors) {
  Result result = Result::Ok;
  for (ModuleField& field : module->fields) {
    Func* func = nullptr;
    FuncDeclaration* decl = nullptr;
    if (auto* func_field = dyn_cast<FuncModuleField>(&field)) {
      func = &func_field->func;
      decl = &func->decl;
    } else if (auto* tag_field = dyn_cast<TagModuleField>(&field)) {
      decl = &tag_field->tag.decl;
    } else if (auto* import_field = dyn_cast<ImportModuleField>(&field)) {
      if (auto* func_import =
              dyn_cast<FuncImport>(import_field->import.get())) {
        // Only check the declaration, not the function itself, since it is an
        // import.
        decl = &func_import->func.decl;
      } else if (auto* tag_import =
                     dyn_cast<TagImport>(import_field->import.get())) {
        decl = &tag_import->tag.decl;
      } else {
        continue;
      }
    } else {
      continue;
    }

    bool has_func_type_and_empty_signature = false;

    if (decl) {
      ResolveTypeNames(*module, decl);
      has_func_type_and_empty_signature =
          ResolveFuncTypeWithEmptySignature(*module, decl);
      ResolveImplicitlyDefinedFunctionType(field.loc, module, *decl);
      result |=
          CheckFuncTypeVarMatchesExplicit(field.loc, *module, *decl, errors);
    }

    if (func) {
      if (has_func_type_and_empty_signature) {
        // The call to ResolveFuncTypeWithEmptySignature may have updated the
        // function signature so there are parameters. Since parameters and
        // local variables share the same index space, we need to increment the
        // local indexes bound to a given name by the number of parameters in
        // the function.
        for (auto& [name, binding] : func->bindings) {
          binding.index += func->GetNumParams();
        }
      }

      ResolveFuncTypesExprVisitorDelegate delegate(module, errors);
      ExprVisitor visitor(&delegate);
      result |= visitor.VisitFunc(func);
    }
  }
  return result;
}

void AppendInlineExportFields(Module* module,
                              ModuleFieldList* fields,
                              Index index) {
  Location last_field_loc = module->fields.back().loc;

  for (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,
                       Errors* errors,
                       WastParseOptions* options)
    : lexer_(lexer), errors_(errors), options_(options) {}

void WastParser::Error(Location loc, const char* format, ...) {
  WABT_SNPRINTF_ALLOCA(buffer, length, format);
  errors_->emplace_back(ErrorLevel::Error, loc, buffer);
}

Token WastParser::GetToken() {
  if (tokens_.empty()) {
    tokens_.push_back(lexer_->GetToken());
  }
  return tokens_.front();
}

Location WastParser::GetLocation() {
  return GetToken().loc;
}

TokenType WastParser::Peek(size_t n) {
  assert(n <= 1);
  while (tokens_.size() <= n) {
    Token cur = lexer_->GetToken();
    if (cur.token_type() != TokenType::LparAnn) {
      tokens_.push_back(cur);
    } else {
      // Custom annotation. For now, discard until matching Rpar, unless it is
      // a code metadata annotation or custom section. In those cases, we know
      // how to parse it.
      if (!options_->features.annotations_enabled()) {
        Error(cur.loc, "annotations not enabled: %s", cur.to_string().c_str());
        tokens_.push_back(Token(cur.loc, TokenType::Invalid));
        continue;
      }
      if ((options_->features.code_metadata_enabled() &&
           cur.text().find("metadata.code.") == 0) ||
          cur.text() == "custom") {
        tokens_.push_back(cur);
        continue;
      }
      int indent = 1;
      while (indent > 0) {
        cur = lexer_->GetToken();
        switch (cur.token_type()) {
          case TokenType::Lpar:
          case TokenType::LparAnn:
            indent++;
            break;

          case TokenType::Rpar:
            indent--;
            break;

          case TokenType::Eof:
            indent = 0;
            Error(cur.loc, "unterminated annotation");
            break;

          default:
            break;
        }
      }
    }
  }
  return tokens_.at(n).token_type();
}

TokenTypePair WastParser::PeekPair() {
  return TokenTypePair{{Peek(), Peek(1)}};
}

bool WastParser::PeekMatch(TokenType type, size_t n) {
  return Peek(n) == type;
}

bool WastParser::PeekMatchLpar(TokenType type) {
  return Peek() == TokenType::Lpar && Peek(1) == type;
}

bool WastParser::PeekMatchExpr() {
  return IsExpr(PeekPair());
}

bool WastParser::PeekMatchRefType() {
  return (options_->features.function_references_enabled() ||
          options_->features.exceptions_enabled()) &&
         PeekMatchLpar(TokenType::Ref);
}

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)) {
    Token token = Consume();
    Error(token.loc, "unexpected token %s, expected %s.",
          token.to_string_clamp(kMaxErrorTokenLength).c_str(),
          GetTokenTypeName(type));
    return Result::Error;
  }

  return Result::Ok;
}

Token WastParser::Consume() {
  assert(!tokens_.empty());
  Token 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;
    }

    Token token = Consume();
    if (token.token_type() == TokenType::Reserved) {
      Error(token.loc, "unexpected token %s.",
            token.to_string_clamp(kMaxErrorTokenLength).c_str());
    }
  }

  return Result::Error;
}

void WastParser::ErrorUnlessOpcodeEnabled(const Token& token) {
  Opcode opcode = token.opcode();
  if (!opcode.IsEnabled(options_->features)) {
    Error(token.loc, "opcode not allowed: %s", opcode.GetName());
  }
}

Result WastParser::ErrorExpected(const std::vector<std::string>& expected,
                                 const char* example) {
  GetToken();
  Token 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_clamp(kMaxErrorTokenLength).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;
}

bool WastParser::ParseBindVarOpt(std::string* name) {
  WABT_TRACE(ParseBindVarOpt);
  if (!PeekMatch(TokenType::Var)) {
    return false;
  }
  Token token = Consume();
  *name = std::string(token.text());
  return true;
}

Result WastParser::ParseVar(Var* out_var) {
  WABT_TRACE(ParseVar);
  if (PeekMatch(TokenType::Nat)) {
    Token token = Consume();
    std::string_view sv = token.literal().text;
    uint64_t index = kInvalidIndex;
    if (Failed(ParseUint64(sv, &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)) {
    Token token = Consume();
    *out_var = Var(token.text(), 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 (!ParseOffsetExprOpt(out_expr_list)) {
    return ErrorExpected({"an offset expr"}, "(i32.const 123)");
  }
  return Result::Ok;
}

bool WastParser::ParseOffsetExprOpt(ExprList* out_expr_list) {
  WABT_TRACE(ParseOffsetExprOpt);
  if (MatchLpar(TokenType::Offset)) {
    CHECK_RESULT(ParseTerminatingInstrList(out_expr_list));
    EXPECT(Rpar);
    return true;
  } else if (PeekMatchExpr()) {
    CHECK_RESULT(ParseExpr(out_expr_list));
    return true;
  } else {
    return false;
  }
}

Result WastParser::ParseTextList(std::vector<uint8_t>* out_data) {
  WABT_TRACE(ParseTextList);
  if (!ParseTextListOpt(out_data)) {
    // TODO(binji): Add error message here.
    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());

  RemoveEscapes(texts, std::back_inserter(*out_data));
  return !texts.empty();
}

Result WastParser::ParseVarList(VarVector* out_var_list) {
  WABT_TRACE(ParseVarList);
  Var var;
  while (ParseVarOpt(&var)) {
    out_var_list->emplace_back(var);
  }
  if (out_var_list->empty()) {
    return ErrorExpected({"a var"}, "12 or $foo");
  } else {
    return Result::Ok;
  }
}

bool WastParser::ParseElemExprOpt(ExprList* out_elem_expr) {
  WABT_TRACE(ParseElemExprOpt);
  bool item = MatchLpar(TokenType::Item);
  ExprList exprs;
  if (item) {
    if (ParseTerminatingInstrList(&exprs) != Result::Ok) {
      return false;
    }
    EXPECT(Rpar);
  } else {
    if (!IsExpr(PeekPair()) || ParseExpr(&exprs) != Result::Ok) {
      return false;
    }
  }
  if (!exprs.size()) {
    return false;
  }
  *out_elem_expr = std::move(exprs);
  return true;
}

bool WastParser::ParseElemExprListOpt(ExprListVector* out_list) {
  ExprList elem_expr;
  while (ParseElemExprOpt(&elem_expr)) {
    out_list->push_back(std::move(elem_expr));
  }
  return !out_list->empty();
}

bool WastParser::ParseElemExprVarListOpt(ExprListVector* out_list) {
  WABT_TRACE(ParseElemExprVarListOpt);
  Var var;
  ExprList init_expr;
  while (ParseVarOpt(&var)) {
    init_expr.push_back(std::make_unique<RefFuncExpr>(var));
    out_list->push_back(std::move(init_expr));
  }
  return !out_list->empty();
}

Result WastParser::ParseValueType(Var* out_type) {
  WABT_TRACE(ParseValueType);

  const bool is_ref_type = PeekMatchRefType();
  const bool is_value_type = PeekMatch(TokenType::ValueType);

  if (!is_value_type && !is_ref_type) {
    return ErrorExpected(
        {"i32""i64""f32""f64""v128""externref""funcref"});
  }

  if (is_ref_type) {
    EXPECT(Lpar);
    EXPECT(Ref);
    CHECK_RESULT(ParseVar(out_type));
    EXPECT(Rpar);
    return Result::Ok;
  }

  Token token = Consume();
  Type type = token.type();
  bool is_enabled;
  switch (type) {
    case Type::V128:
      is_enabled = options_->features.simd_enabled();
      break;
    case Type::FuncRef:
    case Type::ExternRef:
      is_enabled = options_->features.reference_types_enabled();
      break;
    case Type::ExnRef:
      is_enabled = options_->features.exceptions_enabled();
      break;
    default:
      is_enabled = true;
      break;
  }

  if (!is_enabled) {
    Error(token.loc, "value type not allowed: %s", type.GetName().c_str());
    return Result::Error;
  }

  *out_type = Var(type, GetLocation());
  return Result::Ok;
}

Result WastParser::ParseValueTypeList(
    TypeVector* out_type_list,
    std::unordered_map<uint32_t, std::string>* type_names) {
  WABT_TRACE(ParseValueTypeList);
  while (true) {
    if (!PeekMatchRefType() && !PeekMatch(TokenType::ValueType)) {
      break;
    }

    Var type;
    CHECK_RESULT(ParseValueType(&type));

    if (type.is_index()) {
      out_type_list->push_back(Type(type.index()));
    } else {
      assert(type.is_name());
      assert(options_->features.function_references_enabled());
      type_names->emplace(out_type_list->size(), type.name());
      out_type_list->push_back(Type(Type::Reference, kInvalidIndex));
    }
  }

  return Result::Ok;
}

Result WastParser::ParseRefKind(Type* out_type) {
  WABT_TRACE(ParseRefKind);
  if (!IsTokenTypeRefKind(Peek())) {
    return ErrorExpected({"func""extern""exn"});
  }

  Token token = Consume();
  Type type = token.type();

  if ((type == Type::ExternRef &&
       !options_->features.reference_types_enabled()) ||
      ((type == Type::Struct || type == Type::Array) &&
       !options_->features.gc_enabled())) {
    Error(token.loc, "value type not allowed: %s", type.GetName().c_str());
    return Result::Error;
  }

  *out_type = type;
  return Result::Ok;
}

Result WastParser::ParseRefType(Type* out_type) {
  WABT_TRACE(ParseRefType);
  if (!PeekMatch(TokenType::ValueType)) {
    return ErrorExpected({"funcref""externref"});
  }

  Token token = Consume();
  Type type = token.type();
  if (type == Type::ExternRef &&
      !options_->features.reference_types_enabled()) {
    Error(token.loc, "value type not allowed: %s", type.GetName().c_str());
    return Result::Error;
  }

  *out_type = type;
  return Result::Ok;
}

bool WastParser::ParseRefTypeOpt(Type* out_type) {
  WABT_TRACE(ParseRefTypeOpt);
  if (!PeekMatch(TokenType::ValueType)) {
    return false;
  }

  Token token = Consume();
  Type type = token.type();
  if (type == Type::ExternRef &&
      !options_->features.reference_types_enabled()) {
    return false;
  }

  *out_type = type;
  return true;
}

Result WastParser::ParseQuotedText(std::string* text, bool check_utf8) {
  WABT_TRACE(ParseQuotedText);
  if (!PeekMatch(TokenType::Text)) {
    return ErrorExpected({"a quoted string"}, "\"foo\"");
  }

  Token token = Consume();
  RemoveEscapes(token.text(), std::back_inserter(*text));
  if (check_utf8 && !IsValidUtf8(text->data(), text->length())) {
    Error(token.loc, "quoted string has an invalid utf-8 encoding");
  }
  return Result::Ok;
}

bool WastParser::ParseOffsetOpt(Address* out_offset) {
  WABT_TRACE(ParseOffsetOpt);
  if (PeekMatch(TokenType::OffsetEqNat)) {
    Token token = Consume();
    uint64_t offset64;
    std::string_view sv = token.text();
    if (Failed(ParseInt64(sv, &offset64, ParseIntType::SignedAndUnsigned))) {
      Error(token.loc, "invalid offset \"" PRIstringview "\"",
            WABT_PRINTF_STRING_VIEW_ARG(sv));
    }
    // With memory64, offsets > UINT32_MAX for i32 memories are no longer
    // malformed (just invalid)
    if ((!options_->features.memory64_enabled()) && (offset64 > UINT32_MAX)) {
      Error(token.loc, "offset must be less than or equal to 0xffffffff");
    }
    *out_offset = offset64;
    return true;
  } else {
    *out_offset = 0;
    return false;
  }
}

bool WastParser::ParseAlignOpt(Address* out_align) {
  WABT_TRACE(ParseAlignOpt);
  if (PeekMatch(TokenType::AlignEqNat)) {
    Token token = Consume();
    std::string_view sv = token.text();
    if (Failed(ParseInt64(sv, 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::ParseMemidx(Location loc, Var* out_memidx) {
  WABT_TRACE(ParseMemidx);
  if (PeekMatchLpar(TokenType::Memory)) {
    if (!options_->features.multi_memory_enabled()) {
      Error(loc, "Specifying memory variable is not allowed");
      return Result::Error;
    }
    EXPECT(Lpar);
    EXPECT(Memory);
    CHECK_RESULT(ParseVar(out_memidx));
    EXPECT(Rpar);
  } else {
    if (ParseVarOpt(out_memidx, Var(0, loc)) &&
        !options_->features.multi_memory_enabled()) {
      Error(loc, "Specifying memory variable is not allowed");
      return Result::Error;
    }
  }
  return Result::Ok;
}

Result WastParser::ParseLimitsIndex(Limits* out_limits) {
  WABT_TRACE(ParseLimitsIndex);

  if (PeekMatch(TokenType::ValueType)) {
    if (GetToken().type() == Type::I64) {
      Consume();
      out_limits->is_64 = true;
    } else if (GetToken().type() == Type::I32) {
      Consume();
      out_limits->is_64 = false;
    }
  }

  return Result::Ok;
}

Result WastParser::ParseLimits(Limits* out_limits) {
  WABT_TRACE(ParseLimits);

  CHECK_RESULT(ParseNat(&out_limits->initial, out_limits->is_64));
  if (PeekMatch(TokenType::Nat)) {
    CHECK_RESULT(ParseNat(&out_limits->max, out_limits->is_64));
    out_limits->has_max = true;
  } else {
    out_limits->has_max = false;
  }

  if (Match(TokenType::Shared)) {
    out_limits->is_shared = true;
  }

  return Result::Ok;
}

Result WastParser::ParsePageSize(uint32_t* out_page_size) {
  WABT_TRACE(ParsePageSize);

  Result result = Result::Ok;

  if (PeekMatchLpar(TokenType::PageSize)) {
    if (!options_->features.custom_page_sizes_enabled()) {
      Error(GetLocation(), "Specifying memory page size is not allowed");
      return Result::Error;
    }
    EXPECT(Lpar);
    EXPECT(PageSize);
    auto token = GetToken();
    if (!token.HasLiteral()) {
      Error(GetLocation(), "malformed custom page size");
      return Result::Error;
    }
    auto sv = token.literal().text;
    result |= ParseInt32(sv, out_page_size, ParseIntType::UnsignedOnly);
    if (*out_page_size > UINT32_MAX || *out_page_size <= 0 ||
        (*out_page_size & (*out_page_size - 1))) {
      Error(GetLocation(), "malformed custom page size");
      return Result::Error;
    }
    Consume();
    EXPECT(Rpar);
  }

  return result;
}

Result WastParser::ParseNat(uint64_t* out_nat, bool is_64) {
  WABT_TRACE(ParseNat);
  if (!PeekMatch(TokenType::Nat)) {
    return ErrorExpected({"a natural number"}, "123");
  }

  Token token = Consume();
  std::string_view sv = token.literal().text;
  if (Failed(ParseUint64(sv, out_nat)) || (!is_64 && *out_nat > 0xffffffffu)) {
    Error(token.loc, "invalid int \"" PRIstringview "\"",
          WABT_PRINTF_STRING_VIEW_ARG(sv));
  }

  return Result::Ok;
}

Result WastParser::ParseModule(std::unique_ptr<Module>* out_module) {
  WABT_TRACE(ParseModule);
  auto module = std::make_unique<Module>();

  if (PeekMatchLpar(TokenType::Module)) {
    // Starts with "(module". Allow text and binary modules, but no quoted
    // modules.
    CommandPtr command;
    CHECK_RESULT(ParseModuleCommand(nullptr, &command));
    if (isa<ModuleCommand>(command.get())) {
      auto module_command = cast<ModuleCommand>(std::move(command));
      *module = std::move(module_command->module);
    } else {
      assert(isa<ScriptModuleCommand>(command.get()));
      auto module_command = cast<ScriptModuleCommand>(std::move(command));
      *module = std::move(module_command->module);
    }
  } else if (IsModuleField(PeekPair()) || PeekIsCustom()) {
    // Parse an inline module (i.e. one with no surrounding (module)).
    CHECK_RESULT(ParseModuleFieldList(module.get()));
  } else if (PeekMatch(TokenType::Eof)) {
    errors_->emplace_back(ErrorLevel::Warning, GetLocation(), "empty module");
  } else {
    ConsumeIfLpar();
    ErrorExpected({"a module field""a module"});
  }

  EXPECT(Eof);
  if (!HasError()) {
    *out_module = std::move(module);
    return Result::Ok;
  } else {
    return Result::Error;
  }
}

Result WastParser::ParseScript(std::unique_ptr<Script>* out_script) {
  WABT_TRACE(ParseScript);
  auto script = std::make_unique<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()) || PeekIsCustom()) {
    // Parse an inline module (i.e. one with no surrounding (module)).
    auto command = std::make_unique<ModuleCommand>();
    command->module.loc = GetLocation();
    CHECK_RESULT(ParseModuleFieldList(&command->module));
    script->commands.emplace_back(std::move(command));
  } else if (IsCommand(PeekPair())) {
    CHECK_RESULT(ParseCommandList(script.get(), &script->commands));
  } else if (PeekMatch(TokenType::Eof)) {
    errors_->emplace_back(ErrorLevel::Warning, GetLocation(), "empty script");
  } else {
    ConsumeIfLpar();
    ErrorExpected({"a module field""a command"});
  }

  EXPECT(Eof);
  if (!HasError()) {
    *out_script = std::move(script);
    return Result::Ok;
  } else {
    return Result::Error;
  }
}

Result WastParser::ParseCustomSectionAnnotation(Module* module) {
  WABT_TRACE(ParseCustomSectionAnnotation);
  Location loc = GetLocation();
  Token token = Consume();
  if (token.text() != "custom") {
    assert(
        !"ParseCustomSectionAnnotation should only be called if PeekIsCustom() is true");
    return Result::Error;
  }
  std::string section_name;
  CHECK_RESULT(ParseQuotedText(§ion_name));
  if (Match(TokenType::Lpar)) {
    if (!PeekMatch(TokenType::After) && !PeekMatch(TokenType::Before)) {
      return ErrorExpected({"before""after"});
    }
    Consume();
    switch (Peek()) {
      case TokenType::Function:
      case TokenType::Type:
      case TokenType::Import:
      case TokenType::Export:
      case TokenType::Table:
      case TokenType::Global:
      case TokenType::Elem:
      case TokenType::Data:
      case TokenType::Memory:
      case TokenType::Code:
      case TokenType::Start: {
        Consume();
        break;
      }
      default: {
        return ErrorExpected({"type""import""function""table""memory",
                              "global""export""start""elem""code",
                              "data"});
      }
    }
    EXPECT(Rpar);
  }
  std::vector<uint8_t> data;
  CHECK_RESULT(ParseTextList(&data));
  EXPECT(Rpar);

  Custom custom = Custom(loc, section_name, data);
  module->customs.push_back(custom);

  return Result::Ok;
}

bool WastParser::PeekIsCustom() {
  // If IsLparAnn succeeds, tokens_.front() must have text, as it is an LparAnn
  // token.
  return options_->features.annotations_enabled() && IsLparAnn(PeekPair()) &&
         tokens_.front().text() == "custom";
}

Result WastParser::ParseModuleFieldList(Module* module) {
  WABT_TRACE(ParseModuleFieldList);
  while (IsModuleField(PeekPair()) || PeekIsCustom()) {
    if (PeekIsCustom()) {
      CHECK_RESULT(ParseCustomSectionAnnotation(module));
      continue;
    }
    if (Failed(ParseModuleField(module))) {
      CHECK_RESULT(Synchronize(IsModuleField));
    }
  }
  CHECK_RESULT(ResolveFuncTypes(module, errors_));
  CHECK_RESULT(ResolveNamesModule(module, errors_));
  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::Tag:    return ParseTagModuleField(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);
  Location loc = GetLocation();
  EXPECT(Data);
  std::string name;
  ParseBindVarOpt(&name);
  auto field = std::make_unique<DataSegmentModuleField>(loc, name);

  if (PeekMatchLpar(TokenType::Memory)) {
    EXPECT(Lpar);
    EXPECT(Memory);
    CHECK_RESULT(ParseVar(&field->data_segment.memory_var));
    EXPECT(Rpar);
    CHECK_RESULT(ParseOffsetExpr(&field->data_segment.offset));
  } else if (ParseVarOpt(&field->data_segment.memory_var, Var(0, loc))) {
    CHECK_RESULT(ParseOffsetExpr(&field->data_segment.offset));
  } else if (!ParseOffsetExprOpt(&field->data_segment.offset)) {
    if (!options_->features.bulk_memory_enabled()) {
      Error(loc, "passive data segments are not allowed");
      return Result::Error;
    }

    field->data_segment.kind = SegmentKind::Passive;
  }

  ParseTextListOpt(&field->data_segment.data);
  EXPECT(Rpar);
  module->AppendField(std::move(field));
  return Result::Ok;
}

Result WastParser::ParseElemModuleField(Module* module) {
  WABT_TRACE(ParseElemModuleField);
  EXPECT(Lpar);
  Location loc = GetLocation();
  EXPECT(Elem);

  // With MVP text format the name here was intended to refer to the table
  // that the elem segment was part of, but we never did anything with this name
  // since there was only one table anyway.
  // With bulk-memory enabled this introduces a new name for the particular
  // elem segment.
  std::string initial_name;
  bool has_name = ParseBindVarOpt(&initial_name);

  std::string segment_name = initial_name;
  if (!options_->features.bulk_memory_enabled()) {
    segment_name = "";
  }
  auto field = std::make_unique<ElemSegmentModuleField>(loc, segment_name);
  if (options_->features.reference_types_enabled() &&
      Match(TokenType::Declare)) {
    field->elem_segment.kind = SegmentKind::Declared;
  }

  // Optional table specifier
  if (options_->features.bulk_memory_enabled()) {
    if (PeekMatchLpar(TokenType::Table)) {
      EXPECT(Lpar);
      EXPECT(Table);
      CHECK_RESULT(ParseVar(&field->elem_segment.table_var));
      EXPECT(Rpar);
    } else {
      ParseVarOpt(&field->elem_segment.table_var, Var(0, loc));
    }
  } else {
    if (has_name) {
      field->elem_segment.table_var = Var(initial_name, loc);
    } else {
      ParseVarOpt(&field->elem_segment.table_var, Var(0, loc));
    }
  }

  // Parse offset expression, if not declared/passive segment.
  if (options_->features.bulk_memory_enabled()) {
    if (field->elem_segment.kind != SegmentKind::Declared &&
        !ParseOffsetExprOpt(&field->elem_segment.offset)) {
      field->elem_segment.kind = SegmentKind::Passive;
    }
  } else {
    CHECK_RESULT(ParseOffsetExpr(&field->elem_segment.offset));
  }

  if (ParseRefTypeOpt(&field->elem_segment.elem_type)) {
    ParseElemExprListOpt(&field->elem_segment.elem_exprs);
  } else {
    field->elem_segment.elem_type = Type::FuncRef;
    if (PeekMatch(TokenType::Func)) {
      EXPECT(Func);
    }
    ParseElemExprVarListOpt(&field->elem_segment.elem_exprs);
  }
  EXPECT(Rpar);
  module->AppendField(std::move(field));
  return Result::Ok;
}

Result WastParser::ParseTagModuleField(Module* module) {
  WABT_TRACE(ParseTagModuleField);
  if (!options_->features.exceptions_enabled()) {
    Error(Consume().loc, "tag not allowed");
    return Result::Error;
  }
  EXPECT(Lpar);
  EXPECT(Tag);
  Location loc = GetLocation();

  std::string name;
  ParseBindVarOpt(&name);

  ModuleFieldList export_fields;
  CHECK_RESULT(ParseInlineExports(&export_fields, ExternalKind::Tag));

  if (PeekMatchLpar(TokenType::Import)) {
    CheckImportOrdering(module);
    auto import = std::make_unique<TagImport>(name);
    Tag& tag = import->tag;
    CHECK_RESULT(ParseInlineImport(import.get()));
    CHECK_RESULT(ParseTypeUseOpt(&tag.decl));
    CHECK_RESULT(ParseUnboundFuncSignature(&tag.decl.sig));
    CHECK_RESULT(ErrorIfLpar({"type""param""result"}));
    auto field =
        std::make_unique<ImportModuleField>(std::move(import), GetLocation());
    module->AppendField(std::move(field));
  } else {
    auto field = std::make_unique<TagModuleField>(loc, name);
    CHECK_RESULT(ParseTypeUseOpt(&field->tag.decl));
    CHECK_RESULT(ParseUnboundFuncSignature(&field->tag.decl.sig));
    module->AppendField(std::move(field));
  }

  AppendInlineExportFields(module, &export_fields, module->tags.size() - 1);

  EXPECT(Rpar);
  return Result::Ok;
}

Result WastParser::ParseExportModuleField(Module* module) {
  WABT_TRACE(ParseExportModuleField);
  EXPECT(Lpar);
  auto field = std::make_unique<ExportModuleField>(GetLocation());
  EXPECT(Export);
  CHECK_RESULT(ParseQuotedText(&field->export_.name));
  CHECK_RESULT(ParseExportDesc(&field->export_));
  EXPECT(Rpar);
  module->AppendField(std::move(field));
  return Result::Ok;
}

Result WastParser::ParseFuncModuleField(Module* module) {
  WABT_TRACE(ParseFuncModuleField);
  EXPECT(Lpar);
  Location loc = GetLocation();
  EXPECT(Func);
  std::string name;
  ParseBindVarOpt(&name);

  ModuleFieldList export_fields;
  CHECK_RESULT(ParseInlineExports(&export_fields, ExternalKind::Func));

  if (PeekMatchLpar(TokenType::Import)) {
    CheckImportOrdering(module);
    auto import = std::make_unique<FuncImport>(name);
    Func& func = import->func;
    CHECK_RESULT(ParseInlineImport(import.get()));
    CHECK_RESULT(ParseTypeUseOpt(&func.decl));
    CHECK_RESULT(ParseFuncSignature(&func.decl.sig, &func.bindings));
    CHECK_RESULT(ErrorIfLpar({"type""param""result"}));
    auto field =
        std::make_unique<ImportModuleField>(std::move(import), GetLocation());
    module->AppendField(std::move(field));
  } else {
    auto field = std::make_unique<FuncModuleField>(loc, name);
    Func& func = field->func;
    func.loc = GetLocation();
    CHECK_RESULT(ParseTypeUseOpt(&func.decl));
    CHECK_RESULT(ParseFuncSignature(&func.decl.sig, &func.bindings));
    TypeVector local_types;
    CHECK_RESULT(ParseBoundValueTypeList(
        TokenType::Local, &local_types, &func.bindings,
        &func.decl.sig.param_type_names, func.GetNumParams()));
    func.local_types.Set(local_types);
    CHECK_RESULT(ParseTerminatingInstrList(&func.exprs));
    module->AppendField(std::move(field));
  }

  AppendInlineExportFields(module, &export_fields, module->funcs.size() - 1);

  EXPECT(Rpar);
  return Result::Ok;
}

Result WastParser::ParseTypeModuleField(Module* module) {
  WABT_TRACE(ParseTypeModuleField);
  EXPECT(Lpar);
  auto field = std::make_unique<TypeModuleField>(GetLocation());
  EXPECT(Type);

  std::string name;
  ParseBindVarOpt(&name);
  EXPECT(Lpar);
  Location loc = GetLocation();

  if (Match(TokenType::Func)) {
    auto func_type = std::make_unique<FuncType>(name);
    BindingHash bindings;
    CHECK_RESULT(ParseFuncSignature(&func_type->sig, &bindings));
    CHECK_RESULT(ErrorIfLpar({"param""result"}));
    field->type = std::move(func_type);
  } else if (Match(TokenType::Struct)) {
    if (!options_->features.gc_enabled()) {
      Error(loc, "struct not allowed");
      return Result::Error;
    }
    auto struct_type = std::make_unique<StructType>(name);
    CHECK_RESULT(ParseFieldList(&struct_type->fields));
    field->type = std::move(struct_type);
  } else if (Match(TokenType::Array)) {
    if (!options_->features.gc_enabled()) {
      Error(loc, "array type not allowed");
    }
    auto array_type = std::make_unique<ArrayType>(name);
    CHECK_RESULT(ParseField(&array_type->field));
    field->type = std::move(array_type);
  } else {
    return ErrorExpected({"func""struct""array"});
  }

  EXPECT(Rpar);
  EXPECT(Rpar);
  module->AppendField(std::move(field));
  return Result::Ok;
}

Result WastParser::ParseField(Field* field) {
  WABT_TRACE(ParseField);
  auto parse_mut_valuetype = [&]() -> Result {
    // TODO: Share with ParseGlobalType?
    if (MatchLpar(TokenType::Mut)) {
      field->mutable_ = true;
      Var type;
      CHECK_RESULT(ParseValueType(&type));
      field->type = Type(type.index());
      EXPECT(Rpar);
    } else {
      field->mutable_ = false;
      Var type;
      CHECK_RESULT(ParseValueType(&type));
      field->type = Type(type.index());
    }
    return Result::Ok;
  };

  if (MatchLpar(TokenType::Field)) {
    ParseBindVarOpt(&field->name);
    CHECK_RESULT(parse_mut_valuetype());
    EXPECT(Rpar);
  } else {
    CHECK_RESULT(parse_mut_valuetype());
  }

  return Result::Ok;
}

Result WastParser::ParseFieldList(std::vector<Field>* fields) {
  WABT_TRACE(ParseFieldList);
  while (PeekMatch(TokenType::ValueType) || PeekMatch(TokenType::Lpar)) {
    Field field;
    CHECK_RESULT(ParseField(&field));
    fields->push_back(field);
  }
  return Result::Ok;
}

Result WastParser::ParseGlobalModuleField(Module* module) {
  WABT_TRACE(ParseGlobalModuleField);
  EXPECT(Lpar);
  Location loc = GetLocation();
  EXPECT(Global);
  std::string name;
  ParseBindVarOpt(&name);

  ModuleFieldList export_fields;
  CHECK_RESULT(ParseInlineExports(&export_fields, ExternalKind::Global));

  if (PeekMatchLpar(TokenType::Import)) {
    CheckImportOrdering(module);
    auto import = std::make_unique<GlobalImport>(name);
    CHECK_RESULT(ParseInlineImport(import.get()));
    CHECK_RESULT(ParseGlobalType(&import->global));
    auto field =
        std::make_unique<ImportModuleField>(std::move(import), GetLocation());
    module->AppendField(std::move(field));
  } else {
    auto field = std::make_unique<GlobalModuleField>(loc, name);
    CHECK_RESULT(ParseGlobalType(&field->global));
    CHECK_RESULT(ParseTerminatingInstrList(&field->global.init_expr));
    module->AppendField(std::move(field));
  }

  AppendInlineExportFields(module, &export_fields, module->globals.size() - 1);

  EXPECT(Rpar);
  return Result::Ok;
}

Result WastParser::ParseImportModuleField(Module* module) {
  WABT_TRACE(ParseImportModuleField);
  EXPECT(Lpar);
  Location loc = GetLocation();
  CheckImportOrdering(module);
  EXPECT(Import);
  std::string module_name;
  std::string field_name;
  CHECK_RESULT(ParseQuotedText(&module_name));
  CHECK_RESULT(ParseQuotedText(&field_name));
  EXPECT(Lpar);

  std::unique_ptr<ImportModuleField> field;
  std::string name;

  switch (Peek()) {
    case TokenType::Func: {
      Consume();
      ParseBindVarOpt(&name);
      auto import = std::make_unique<FuncImport>(name);
      CHECK_RESULT(ParseTypeUseOpt(&import->func.decl));
      CHECK_RESULT(
          ParseFuncSignature(&import->func.decl.sig, &import->func.bindings));
      CHECK_RESULT(ErrorIfLpar({"param""result"}));
      EXPECT(Rpar);
      field = std::make_unique<ImportModuleField>(std::move(import), loc);
      break;
    }

    case TokenType::Table: {
      Consume();
      ParseBindVarOpt(&name);
      auto import = std::make_unique<TableImport>(name);
      CHECK_RESULT(ParseLimitsIndex(&import->table.elem_limits));
      CHECK_RESULT(ParseLimits(&import->table.elem_limits));
      CHECK_RESULT(ParseRefType(&import->table.elem_type));
      EXPECT(Rpar);
      field = std::make_unique<ImportModuleField>(std::move(import), loc);
      break;
    }

    case TokenType::Memory: {
      Consume();
      ParseBindVarOpt(&name);
      auto import = std::make_unique<MemoryImport>(name);
      import->memory.page_size = WABT_DEFAULT_PAGE_SIZE;
      CHECK_RESULT(ParseLimitsIndex(&import->memory.page_limits));
      CHECK_RESULT(ParseLimits(&import->memory.page_limits));
      CHECK_RESULT(ParsePageSize(&import->memory.page_size));
      EXPECT(Rpar);
      field = std::make_unique<ImportModuleField>(std::move(import), loc);
      break;
    }

    case TokenType::Global: {
      Consume();
      ParseBindVarOpt(&name);
      auto import = std::make_unique<GlobalImport>(name);
      CHECK_RESULT(ParseGlobalType(&import->global));
      EXPECT(Rpar);
      field = std::make_unique<ImportModuleField>(std::move(import), loc);
      break;
    }

    case TokenType::Tag: {
      Consume();
      ParseBindVarOpt(&name);
      auto import = std::make_unique<TagImport>(name);
      CHECK_RESULT(ParseTypeUseOpt(&import->tag.decl));
      CHECK_RESULT(ParseUnboundFuncSignature(&import->tag.decl.sig));
      EXPECT(Rpar);
      field = std::make_unique<ImportModuleField>(std::move(import), loc);
      break;
    }

    default:
      return ErrorExpected({"an external kind"});
  }

  field->import->module_name = module_name;
  field->import->field_name = field_name;

  module->AppendField(std::move(field));
  EXPECT(Rpar);
  return Result::Ok;
}

Result WastParser::ParseMemoryModuleField(Module* module) {
  WABT_TRACE(ParseMemoryModuleField);
  EXPECT(Lpar);
  Location loc = GetLocation();
  EXPECT(Memory);
  std::string name;
  ParseBindVarOpt(&name);

  ModuleFieldList export_fields;
  CHECK_RESULT(ParseInlineExports(&export_fields, ExternalKind::Memory));

  if (PeekMatchLpar(TokenType::Import)) {
    CheckImportOrdering(module);
    auto import = std::make_unique<MemoryImport>(name);
    import->memory.page_size = WABT_DEFAULT_PAGE_SIZE;
    CHECK_RESULT(ParseInlineImport(import.get()));
    CHECK_RESULT(ParseLimitsIndex(&import->memory.page_limits));
    CHECK_RESULT(ParseLimits(&import->memory.page_limits));
    CHECK_RESULT(ParsePageSize(&import->memory.page_size));
    auto field =
        std::make_unique<ImportModuleField>(std::move(import), GetLocation());
    module->AppendField(std::move(field));
  } else {
    auto field = std::make_unique<MemoryModuleField>(loc, name);
    field->memory.page_size = WABT_DEFAULT_PAGE_SIZE;
    CHECK_RESULT(ParseLimitsIndex(&field->memory.page_limits));
    if (PeekMatchLpar(TokenType::PageSize)) {
      // this is the data abbreviation (no limits)
      CHECK_RESULT(ParsePageSize(&field->memory.page_size));
      if (!PeekMatchLpar(TokenType::Data)) {
        ConsumeIfLpar();
        return ErrorExpected({"inline data segment"});
      }
    }
    if (MatchLpar(TokenType::Data)) {
      auto data_segment_field = std::make_unique<DataSegmentModuleField>(loc);
      DataSegment& data_segment = data_segment_field->data_segment;
      data_segment.memory_var = Var(module->memories.size(), GetLocation());
      data_segment.offset.push_back(std::make_unique<ConstExpr>(
          field->memory.page_limits.is_64 ? Const::I64(0) : Const::I32(0)));
      data_segment.offset.back().loc = loc;
      ParseTextListOpt(&data_segment.data);
      EXPECT(Rpar);

      uint32_t num_pages = WABT_BYTES_TO_MIN_PAGES(data_segment.data.size(),
                                                   field->memory.page_size);
      field->memory.page_limits.initial = num_pages;
      field->memory.page_limits.max = num_pages;
      field->memory.page_limits.has_max = true;

      module->AppendField(std::move(field));
      module->AppendField(std::move(data_segment_field));
    } else {
      CHECK_RESULT(ParseLimits(&field->memory.page_limits));
      CHECK_RESULT(ParsePageSize(&field->memory.page_size));
      module->AppendField(std::move(field));
    }
  }

  AppendInlineExportFields(module, &export_fields, module->memories.size() - 1);

  EXPECT(Rpar);
  return Result::Ok;
}

Result WastParser::ParseStartModuleField(Module* module) {
  WABT_TRACE(ParseStartModuleField);
  EXPECT(Lpar);
  Location loc = GetLocation();
  if (module->starts.size() > 0) {
    Error(loc, "multiple start sections");
    return Result::Error;
  }
  EXPECT(Start);
  Var var;
  CHECK_RESULT(ParseVar(&var));
  EXPECT(Rpar);
  module->AppendField(std::make_unique<StartModuleField>(var, loc));
  return Result::Ok;
}

Result WastParser::ParseTableModuleField(Module* module) {
  WABT_TRACE(ParseTableModuleField);
  EXPECT(Lpar);
  Location loc = GetLocation();
  EXPECT(Table);
  std::string name;
  ParseBindVarOpt(&name);

  ModuleFieldList export_fields;
  CHECK_RESULT(ParseInlineExports(&export_fields, ExternalKind::Table));

  if (PeekMatchLpar(TokenType::Import)) {
    CheckImportOrdering(module);
    auto import = std::make_unique<TableImport>(name);
    CHECK_RESULT(ParseInlineImport(import.get()));
    CHECK_RESULT(ParseLimitsIndex(&import->table.elem_limits));
    CHECK_RESULT(ParseLimits(&import->table.elem_limits));
    CHECK_RESULT(ParseRefType(&import->table.elem_type));
    auto field =
        std::make_unique<ImportModuleField>(std::move(import), GetLocation());
    module->AppendField(std::move(field));
  } else {
    auto field = std::make_unique<TableModuleField>(loc, name);
    auto& table = field->table;
    CHECK_RESULT(ParseLimitsIndex(&table.elem_limits));
    if (PeekMatch(TokenType::ValueType)) {
      Type elem_type;
      CHECK_RESULT(ParseRefType(&elem_type));

      EXPECT(Lpar);
      EXPECT(Elem);

      auto elem_segment_field = std::make_unique<ElemSegmentModuleField>(loc);
      ElemSegment& elem_segment = elem_segment_field->elem_segment;
      elem_segment.table_var = Var(module->tables.size(), GetLocation());
      auto offset = table.elem_limits.is_64 ? Const::I64(0) : Const::I32(0);
      elem_segment.offset.push_back(std::make_unique<ConstExpr>(offset));
      elem_segment.offset.back().loc = loc;
      elem_segment.elem_type = elem_type;
      // Syntax is either an optional list of var (legacy), or a non-empty list
      // of elem expr.
      ExprList elem_expr;
      if (ParseElemExprOpt(&elem_expr)) {
        elem_segment.elem_exprs.push_back(std::move(elem_expr));
        // Parse the rest.
        ParseElemExprListOpt(&elem_segment.elem_exprs);
      } else {
        ParseElemExprVarListOpt(&elem_segment.elem_exprs);
      }
      EXPECT(Rpar);

      table.elem_limits.initial = elem_segment.elem_exprs.size();
      table.elem_limits.max = elem_segment.elem_exprs.size();
      table.elem_limits.has_max = true;
      table.elem_type = elem_type;
      module->AppendField(std::move(field));
      module->AppendField(std::move(elem_segment_field));
    } else {
      CHECK_RESULT(ParseLimits(&table.elem_limits));
      CHECK_RESULT(ParseRefType(&table.elem_type));
      module->AppendField(std::move(field));
    }
  }

  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::Tag:    export_->kind = ExternalKind::Tag; 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 field = std::make_unique<ExportModuleField>(GetLocation());
    field->export_.kind = kind;
    EXPECT(Export);
    CHECK_RESULT(ParseQuotedText(&field->export_.name));
    EXPECT(Rpar);
    fields->push_back(std::move(field));
  }
  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, &sig->param_type_names));
  CHECK_RESULT(ParseResultList(&sig->result_types, &sig->result_type_names));
  return Result::Ok;
}

Result WastParser::ParseUnboundFuncSignature(FuncSignature* sig) {
  WABT_TRACE(ParseUnboundFuncSignature);
  CHECK_RESULT(ParseUnboundValueTypeList(TokenType::Param, &sig->param_types,
                                         &sig->param_type_names));
  CHECK_RESULT(ParseResultList(&sig->result_types, &sig->result_type_names));
  return Result::Ok;
}

Result WastParser::ParseBoundValueTypeList(
    TokenType token,
    TypeVector* types,
    BindingHash* bindings,
    std::unordered_map<uint32_t, std::string>* type_names,
    Index binding_index_offset) {
  WABT_TRACE(ParseBoundValueTypeList);
  while (MatchLpar(token)) {
    if (PeekMatch(TokenType::Var)) {
      std::string name;
      Var type;
      Location loc = GetLocation();
      ParseBindVarOpt(&name);
      CHECK_RESULT(ParseValueType(&type));
      bindings->emplace(name,
                        Binding(loc, binding_index_offset + types->size()));
      if (type.is_index()) {
        types->push_back(Type(type.index()));
      } else {
        assert(type.is_name());
        assert(options_->features.function_references_enabled());
        type_names->emplace(binding_index_offset + types->size(), type.name());
        types->push_back(Type(Type::Reference, kInvalidIndex));
      }
    } else {
      CHECK_RESULT(ParseValueTypeList(types, type_names));
    }
    EXPECT(Rpar);
  }
  return Result::Ok;
}

Result WastParser::ParseUnboundValueTypeList(
    TokenType token,
    TypeVector* types,
    std::unordered_map<uint32_t, std::string>* type_names) {
  WABT_TRACE(ParseUnboundValueTypeList);
  while (MatchLpar(token)) {
    CHECK_RESULT(ParseValueTypeList(types, type_names));
    EXPECT(Rpar);
  }
  return Result::Ok;
}

Result WastParser::ParseResultList(
    TypeVector* result_types,
    std::unordered_map<uint32_t, std::string>* type_names) {
  WABT_TRACE(ParseResultList);
  return ParseUnboundValueTypeList(TokenType::Result, result_types, type_names);
}

Result WastParser::ParseInstrList(ExprList* exprs) {
  WABT_TRACE(ParseInstrList);
  ExprList new_exprs;
  while (true) {
    auto pair = PeekPair();
    if (IsInstr(pair)) {
      if (Succeeded(ParseInstr(&new_exprs))) {
        exprs->splice(exprs->end(), new_exprs);
      } else {
        CHECK_RESULT(Synchronize(IsInstr));
      }
    } else if (IsLparAnn(pair)) {
      if (Succeeded(ParseCodeMetadataAnnotation(&new_exprs))) {
        exprs->splice(exprs->end(), new_exprs);
      } else {
        CHECK_RESULT(Synchronize(IsLparAnn));
      }
    } else {
      break;
    }
  }
  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(std::move(expr));
    return Result::Ok;
  } else if (IsBlockInstr(Peek())) {
    std::unique_ptr<Expr> expr;
    CHECK_RESULT(ParseBlockInstr(&expr));
    exprs->push_back(std::move(expr));
    return Result::Ok;
  } else if (PeekMatchExpr()) {
    return ParseExpr(exprs);
  } else {
    assert(!"ParseInstr should only be called when IsInstr() is true");
    return Result::Error;
  }
}

Result WastParser::ParseCodeMetadataAnnotation(ExprList* exprs) {
  WABT_TRACE(ParseCodeMetadataAnnotation);
  Token tk = Consume();
  std::string_view name = tk.text();
  name.remove_prefix(sizeof("metadata.code.") - 1);
  std::string data_text;
  CHECK_RESULT(ParseQuotedText(&data_text, false));
  std::vector<uint8_t> data(data_text.begin(), data_text.end());
  exprs->push_back(std::make_unique<CodeMetadataExpr>(name, std::move(data)));
  EXPECT(Rpar);
  return Result::Ok;
}

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

template <typename T>
Result WastParser::ParseMemoryInstrVar(Location loc,
                                       std::unique_ptr<Expr>* out_expr) {
  Var memidx;
  Var var;
  if (PeekMatchLpar(TokenType::Memory)) {
    if (!options_->features.multi_memory_enabled()) {
      Error(loc, "Specifying memory variable is not allowed");
      return Result::Error;
    }
    CHECK_RESULT(ParseMemidx(loc, &memidx));
    CHECK_RESULT(ParseVar(&var));
    out_expr->reset(new T(var, memidx, loc));
  } else {
    CHECK_RESULT(ParseVar(&memidx));
    if (ParseVarOpt(&var, Var(0, loc))) {
      if (!options_->features.multi_memory_enabled()) {
        Error(loc, "Specifiying memory variable is not allowed");
        return Result::Error;
      }
      out_expr->reset(new T(var, memidx, loc));
    } else {
      out_expr->reset(new T(memidx, var, loc));
    }
  }
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=93 G=94

¤ Dauer der Verarbeitung: 0.11 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.