/*
 * Copyright 2016 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-lexer.h"

#include <cassert>
#include <cstdio>

#include "config.h"

#include "circular-array.h"
#include "error-handler.h"
#include "lexer-source.h"
#include "wast-parser.h"

/*!max:re2c */

#define INITIAL_LEXER_BUFFER_SIZE (64 * 1024)

#define ERROR(...) parser->Error(GetLocation(), __VA_ARGS__)

#define BEGIN(c) cond = (c)
#define FILL(n)              \
  do {                       \
    if (Failed(Fill((n)))) { \
      RETURN(Eof);           \
    }                        \
  } while (0)

#define MAYBE_MALFORMED_UTF8(desc)                \
  if (!(eof_ && limit_ - cursor_ <= YYMAXFILL)) { \
    ERROR("malformed utf-8%s", desc);             \
  }                                               \
  continue

#define yytext (next_pos_)
#define yyleng (cursor_ - next_pos_)

/* p must be a pointer somewhere in the lexer buffer */
#define FILE_OFFSET(p) ((p) - (buffer_) + buffer_file_offset_)
#define COLUMN(p) (FILE_OFFSET(p) - line_file_offset_ + 1)

#define COMMENT_NESTING (comment_nesting_)
#define NEWLINE                               \
  do {                                        \
    line_++;                                  \
    line_file_offset_ = FILE_OFFSET(cursor_); \
  } while (0)

#define RETURN(token) return Token(GetLocation(), TokenType::token);

#define RETURN_LITERAL(token, literal)          \
  return Token(GetLocation(), TokenType::token, \
               MakeLiteral(LiteralType::literal))

#define RETURN_TYPE(token, type) \
  return Token(GetLocation(), TokenType::token, Type::type)

#define RETURN_OPCODE0(token) \
  return Token(GetLocation(), TokenType::token, Opcode::token)

#define RETURN_OPCODE(token, opcode) \
  return Token(GetLocation(), TokenType::token, Opcode::opcode)

#define RETURN_TEXT(token) \
  return Token(GetLocation(), TokenType::token, GetText())

#define RETURN_TEXT_AT(token, at) \
  return Token(GetLocation(), TokenType::token, GetText(at))

namespace wabt {

const char* GetTokenTypeName(TokenType token_type) {
  static const char* s_names[] = {
      "Invalid",
      "Reserved",
      "EOF",
      "(",
      ")",
      "NAT",
      "INT",
      "FLOAT",
      "TEXT",
      "VAR",
      "VALUETYPE",
      "anyfunc",
      "mut",
      "nop",
      "drop",
      "block",
      "end",
      "if",
      "then",
      "else",
      "loop",
      "br",
      "br_if",
      "br_table",
      "try",
      "catch",
      "catch_all",
      "throw",
      "rethrow",
      "call",
      "call_indirect",
      "return",
      "get_local",
      "set_local",
      "tee_local",
      "get_global",
      "set_global",
      "LOAD",
      "STORE",
      "offset=",
      "align=",
      "CONST",
      "UNARY",
      "BINARY",
      "COMPARE",
      "CONVERT",
      "select",
      "unreachable",
      "current_memory",
      "grow_memory",
      "func",
      "start",
      "type",
      "param",
      "result",
      "local",
      "global",
      "table",
      "elem",
      "memory",
      "data",
      "offset",
      "import",
      "export",
      "except",
      "module",
      "bin",
      "quote",
      "register",
      "invoke",
      "get",
      "assert_malformed",
      "assert_invalid",
      "assert_unlinkable",
      "assert_return",
      "assert_return_canonical_nan",
      "assert_return_arithmetic_nan",
      "assert_trap",
      "assert_exhaustion",
  };

  static_assert(
      WABT_ARRAY_SIZE(s_names) == WABT_ENUM_COUNT(TokenType),
      "Expected TokenType names list length to match number of TokenTypes.");

  int x = static_cast<int>(token_type);
  if (x < WABT_ENUM_COUNT(TokenType))
    return s_names[x];

  return "Invalid";
}

Token::Token(Location loc, TokenType token_type)
    : loc(loc), token_type(token_type) {}

Token::Token(Location loc, TokenType token_type, Type type)
    : loc(loc), token_type(token_type), type(type) {}

Token::Token(Location loc, TokenType token_type, StringTerminal text)
    : loc(loc), token_type(token_type), text(text) {}

Token::Token(Location loc, TokenType token_type, Opcode opcode)
    : loc(loc), token_type(token_type), opcode(opcode) {}

Token::Token(Location loc, TokenType token_type, LiteralTerminal literal)
    : loc(loc), token_type(token_type), literal(literal) {}

std::string Token::to_string() const {
  switch (token_type) {
    case TokenType::Nat:
    case TokenType::Int:
    case TokenType::Float:
      return literal.text.to_string();

    case TokenType::Reserved:
    case TokenType::Text:
    case TokenType::Var:
      return text.to_string();

    case TokenType::ValueType:
      return GetTypeName(type);

    case TokenType::Load:
    case TokenType::Store:
    case TokenType::Const:
    case TokenType::Unary:
    case TokenType::Binary:
    case TokenType::Compare:
    case TokenType::Convert:
      return opcode.GetName();

    default:
      return GetTokenTypeName(token_type);
  }
}

WastLexer::WastLexer(std::unique_ptr<LexerSource> source, const char* filename)
    : source_(std::move(source)),
      line_finder_(source_->Clone()),
      filename_(filename),
      line_(1),
      comment_nesting_(0),
      buffer_file_offset_(0),
      line_file_offset_(0),
      eof_(false),
      buffer_(nullptr),
      buffer_size_(0),
      marker_(nullptr),
      next_pos_(nullptr),
      cursor_(nullptr),
      limit_(nullptr) {}

WastLexer::~WastLexer() {
  delete[] buffer_;
}

// static
std::unique_ptr<WastLexer> WastLexer::CreateFileLexer(const char* filename) {
  std::unique_ptr<LexerSource> source(new LexerSourceFile(filename));
  return std::unique_ptr<WastLexer>(new WastLexer(std::move(source), filename));
}

// static
std::unique_ptr<WastLexer> WastLexer::CreateBufferLexer(const char* filename,
                                                        const void* data,
                                                        size_t size) {
  std::unique_ptr<LexerSource> source(new LexerSourceBuffer(data, size));
  return std::unique_ptr<WastLexer>(new WastLexer(std::move(source), filename));
}

Location WastLexer::GetLocation() {
  return Location(filename_, line_, COLUMN(next_pos_), COLUMN(cursor_));
}

LiteralTerminal WastLexer::MakeLiteral(LiteralType type) {
  return LiteralTerminal(type, GetText());
}

StringTerminal WastLexer::GetText(size_t offset) {
  return StringTerminal(yytext + offset, yyleng - offset);
}

Result WastLexer::Fill(size_t need) {
  if (eof_)
    return Result::Error;
  size_t free = next_pos_ - buffer_;
  assert(static_cast<size_t>(cursor_ - buffer_) >= free);
  // Our buffer is too small, need to realloc.
  if (free < need) {
    char* old_buffer = buffer_;
    size_t old_buffer_size = buffer_size_;
    size_t new_buffer_size =
        old_buffer_size ? old_buffer_size * 2 : INITIAL_LEXER_BUFFER_SIZE;
    // Make sure there is enough space for the bytes requested (need) and an
    // additional YYMAXFILL bytes which is needed for the re2c lexer
    // implementation when the eof is reached.
    while ((new_buffer_size - old_buffer_size) + free < need + YYMAXFILL)
      new_buffer_size *= 2;

    char* new_buffer = new char[new_buffer_size];
    if (limit_ > next_pos_)
      memmove(new_buffer, next_pos_, limit_ - next_pos_);
    buffer_ = new_buffer;
    buffer_size_ = new_buffer_size;
    next_pos_ = new_buffer + (next_pos_ - old_buffer) - free;
    marker_ = new_buffer + (marker_ - old_buffer) - free;
    cursor_ = new_buffer + (cursor_ - old_buffer) - free;
    limit_ = new_buffer + (limit_ - old_buffer) - free;
    buffer_file_offset_ += free;
    free += new_buffer_size - old_buffer_size;
    delete[] old_buffer;
  } else {
    // Shift everything down to make more room in the buffer.
    if (limit_ > next_pos_)
      memmove(buffer_, next_pos_, limit_ - next_pos_);
    next_pos_ -= free;
    marker_ -= free;
    cursor_ -= free;
    limit_ -= free;
    buffer_file_offset_ += free;
  }
  // Read the new data into the buffer.
  limit_ += source_->Fill(limit_, free);

  // If at the end of file, need to fill YYMAXFILL more characters with "fake
  // characters", that are not a lexeme nor a lexeme suffix. see
  // http://re2c.org/examples/example_03.html.
  if (limit_ < buffer_ + buffer_size_ - YYMAXFILL) {
    eof_ = true;
    // Fill with 0xff, since that is an invalid utf-8 byte.
    memset(limit_, 0xff, YYMAXFILL);
    limit_ += YYMAXFILL;
  }
  return Result::Ok;
}

Token WastLexer::GetToken(WastParser* parser) {
  /*!types:re2c*/
  YYCONDTYPE cond = YYCOND_i;  // i is the initial state.

  for (;;) {
    next_pos_ = cursor_;
    /*!re2c
      re2c:condprefix = YYCOND_;
      re2c:condenumprefix = YYCOND_;
      re2c:define:YYCTYPE = "unsigned char";
      re2c:define:YYCURSOR = cursor_;
      re2c:define:YYMARKER = marker_;
      re2c:define:YYLIMIT = limit_;
      re2c:define:YYFILL = "FILL";
      re2c:define:YYGETCONDITION = "cond";
      re2c:define:YYGETCONDITION:naked = 1;
      re2c:define:YYSETCONDITION = "BEGIN";

      digit =     [0-9];
      hexdigit =  [0-9a-fA-F];
      num =       digit ("_"? digit)*;
      hexnum =    hexdigit ("_"? hexdigit)*;
      letter =    [a-zA-Z];
      symbol =    [+\-*\\/^~=<>!?@#$%&|:`.'];
      character = [^"\\\x00-\x1f]
                | "\\" [nrt\\'"]
                | "\\" hexdigit hexdigit;
      sign =      [+-];
      nat =       num | "0x" hexnum;
      int =       sign nat;
      frac =      num;
      hexfrac =   hexnum;
      hexfloat =  sign? "0x" hexnum "." hexfrac?
               |  sign? "0x" hexnum ("." hexfrac?)? [pP] sign? num;
      infinity =  sign? "inf";
      nan =       sign? "nan"
          |       sign? "nan:0x" hexnum;
      float =     sign? num "." frac?
            |     sign? num ("." frac?)? [eE] sign? num;
      text =      '"' character* '"';
      name =      "$" (letter | digit | "_" | symbol)+;

      // Should be ([\x21-\x7e] \ [()"; ])+ , but re2c doesn't like this...
      reserved =  [\x21\x23-\x27\x2a-\x3a\x3c-\x7e]+;

      <i> "("                   { RETURN(Lpar); }
      <i> ")"                   { RETURN(Rpar); }
      <i> nat                   { RETURN_LITERAL(Nat, Int); }
      <i> int                   { RETURN_LITERAL(Int, Int); }
      <i> float                 { RETURN_LITERAL(Float, Float); }
      <i> hexfloat              { RETURN_LITERAL(Float, Hexfloat); }
      <i> infinity              { RETURN_LITERAL(Float, Infinity); }
      <i> nan                   { RETURN_LITERAL(Float, Nan); }
      <i> text                  { RETURN_TEXT(Text); }
      <i> '"' => BAD_TEXT       { continue; }
      <BAD_TEXT> character      { continue; }
      <BAD_TEXT> "\n" => i      { ERROR("newline in string");
                                  NEWLINE;
                                  continue; }
      <BAD_TEXT> "\\".          { ERROR("bad escape \"%.*s\"",
                                        static_cast<int>(yyleng), yytext);
                                  continue; }
      <BAD_TEXT> '"' => i       { RETURN_TEXT(Text); }
      <BAD_TEXT> [^]            { ERROR("illegal character in string");
                                  continue; }
      <BAD_TEXT> *              { MAYBE_MALFORMED_UTF8(" in string"); }
      <i> "i32"                 { RETURN_TYPE(ValueType, I32); }
      <i> "i64"                 { RETURN_TYPE(ValueType, I64); }
      <i> "f32"                 { RETURN_TYPE(ValueType, F32); }
      <i> "f64"                 { RETURN_TYPE(ValueType, F64); }
      <i> "anyfunc"             { RETURN(Anyfunc); }
      <i> "mut"                 { RETURN(Mut); }
      <i> "nop"                 { RETURN(Nop); }
      <i> "block"               { RETURN(Block); }
      <i> "if"                  { RETURN_OPCODE0(If); }
      <i> "then"                { RETURN(Then); }
      <i> "else"                { RETURN(Else); }
      <i> "loop"                { RETURN_OPCODE0(Loop); }
      <i> "br"                  { RETURN_OPCODE0(Br); }
      <i> "br_if"               { RETURN_OPCODE0(BrIf); }
      <i> "br_table"            { RETURN_OPCODE0(BrTable); }
      <i> "call"                { RETURN_OPCODE0(Call); }
      <i> "call_indirect"       { RETURN_OPCODE0(CallIndirect); }
      <i> "drop"                { RETURN_OPCODE0(Drop); }
      <i> "end"                 { RETURN_OPCODE0(End); }
      <i> "return"              { RETURN_OPCODE0(Return); }
      <i> "get_local"           { RETURN_OPCODE0(GetLocal); }
      <i> "set_local"           { RETURN_OPCODE0(SetLocal); }
      <i> "tee_local"           { RETURN_OPCODE0(TeeLocal); }
      <i> "get_global"          { RETURN_OPCODE0(GetGlobal); }
      <i> "set_global"          { RETURN_OPCODE0(SetGlobal); }
      <i> "i32.load"            { RETURN_OPCODE(Load, I32Load); }
      <i> "i64.load"            { RETURN_OPCODE(Load, I64Load); }
      <i> "f32.load"            { RETURN_OPCODE(Load, F32Load); }
      <i> "f64.load"            { RETURN_OPCODE(Load, F64Load); }
      <i> "i32.store"           { RETURN_OPCODE(Store, I32Store); }
      <i> "i64.store"           { RETURN_OPCODE(Store, I64Store); }
      <i> "f32.store"           { RETURN_OPCODE(Store, F32Store); }
      <i> "f64.store"           { RETURN_OPCODE(Store, F64Store); }
      <i> "i32.load8_s"         { RETURN_OPCODE(Load, I32Load8S); }
      <i> "i64.load8_s"         { RETURN_OPCODE(Load, I64Load8S); }
      <i> "i32.load8_u"         { RETURN_OPCODE(Load, I32Load8U); }
      <i> "i64.load8_u"         { RETURN_OPCODE(Load, I64Load8U); }
      <i> "i32.load16_s"        { RETURN_OPCODE(Load, I32Load16S); }
      <i> "i64.load16_s"        { RETURN_OPCODE(Load, I64Load16S); }
      <i> "i32.load16_u"        { RETURN_OPCODE(Load, I32Load16U); }
      <i> "i64.load16_u"        { RETURN_OPCODE(Load, I64Load16U); }
      <i> "i64.load32_s"        { RETURN_OPCODE(Load, I64Load32S); }
      <i> "i64.load32_u"        { RETURN_OPCODE(Load, I64Load32U); }
      <i> "i32.store8"          { RETURN_OPCODE(Store, I32Store8); }
      <i> "i64.store8"          { RETURN_OPCODE(Store, I64Store8); }
      <i> "i32.store16"         { RETURN_OPCODE(Store, I32Store16); }
      <i> "i64.store16"         { RETURN_OPCODE(Store, I64Store16); }
      <i> "i64.store32"         { RETURN_OPCODE(Store, I64Store32); }
      <i> "offset=" nat         { RETURN_TEXT_AT(OffsetEqNat, 7); }
      <i> "align=" nat          { RETURN_TEXT_AT(AlignEqNat, 6); }
      <i> "i32.const"           { RETURN_OPCODE(Const, I32Const); }
      <i> "i64.const"           { RETURN_OPCODE(Const, I64Const); }
      <i> "f32.const"           { RETURN_OPCODE(Const, F32Const); }
      <i> "f64.const"           { RETURN_OPCODE(Const, F64Const); }
      <i> "i32.eqz"             { RETURN_OPCODE(Convert, I32Eqz); }
      <i> "i64.eqz"             { RETURN_OPCODE(Convert, I64Eqz); }
      <i> "i32.clz"             { RETURN_OPCODE(Unary, I32Clz); }
      <i> "i64.clz"             { RETURN_OPCODE(Unary, I64Clz); }
      <i> "i32.ctz"             { RETURN_OPCODE(Unary, I32Ctz); }
      <i> "i64.ctz"             { RETURN_OPCODE(Unary, I64Ctz); }
      <i> "i32.popcnt"          { RETURN_OPCODE(Unary, I32Popcnt); }
      <i> "i64.popcnt"          { RETURN_OPCODE(Unary, I64Popcnt); }
      <i> "f32.neg"             { RETURN_OPCODE(Unary, F32Neg); }
      <i> "f64.neg"             { RETURN_OPCODE(Unary, F64Neg); }
      <i> "f32.abs"             { RETURN_OPCODE(Unary, F32Abs); }
      <i> "f64.abs"             { RETURN_OPCODE(Unary, F64Abs); }
      <i> "f32.sqrt"            { RETURN_OPCODE(Unary, F32Sqrt); }
      <i> "f64.sqrt"            { RETURN_OPCODE(Unary, F64Sqrt); }
      <i> "f32.ceil"            { RETURN_OPCODE(Unary, F32Ceil); }
      <i> "f64.ceil"            { RETURN_OPCODE(Unary, F64Ceil); }
      <i> "f32.floor"           { RETURN_OPCODE(Unary, F32Floor); }
      <i> "f64.floor"           { RETURN_OPCODE(Unary, F64Floor); }
      <i> "f32.trunc"           { RETURN_OPCODE(Unary, F32Trunc); }
      <i> "f64.trunc"           { RETURN_OPCODE(Unary, F64Trunc); }
      <i> "f32.nearest"         { RETURN_OPCODE(Unary, F32Nearest); }
      <i> "f64.nearest"         { RETURN_OPCODE(Unary, F64Nearest); }
      <i> "i32.add"             { RETURN_OPCODE(Binary, I32Add); }
      <i> "i64.add"             { RETURN_OPCODE(Binary, I64Add); }
      <i> "i32.sub"             { RETURN_OPCODE(Binary, I32Sub); }
      <i> "i64.sub"             { RETURN_OPCODE(Binary, I64Sub); }
      <i> "i32.mul"             { RETURN_OPCODE(Binary, I32Mul); }
      <i> "i64.mul"             { RETURN_OPCODE(Binary, I64Mul); }
      <i> "i32.div_s"           { RETURN_OPCODE(Binary, I32DivS); }
      <i> "i64.div_s"           { RETURN_OPCODE(Binary, I64DivS); }
      <i> "i32.div_u"           { RETURN_OPCODE(Binary, I32DivU); }
      <i> "i64.div_u"           { RETURN_OPCODE(Binary, I64DivU); }
      <i> "i32.rem_s"           { RETURN_OPCODE(Binary, I32RemS); }
      <i> "i64.rem_s"           { RETURN_OPCODE(Binary, I64RemS); }
      <i> "i32.rem_u"           { RETURN_OPCODE(Binary, I32RemU); }
      <i> "i64.rem_u"           { RETURN_OPCODE(Binary, I64RemU); }
      <i> "i32.and"             { RETURN_OPCODE(Binary, I32And); }
      <i> "i64.and"             { RETURN_OPCODE(Binary, I64And); }
      <i> "i32.or"              { RETURN_OPCODE(Binary, I32Or); }
      <i> "i64.or"              { RETURN_OPCODE(Binary, I64Or); }
      <i> "i32.xor"             { RETURN_OPCODE(Binary, I32Xor); }
      <i> "i64.xor"             { RETURN_OPCODE(Binary, I64Xor); }
      <i> "i32.shl"             { RETURN_OPCODE(Binary, I32Shl); }
      <i> "i64.shl"             { RETURN_OPCODE(Binary, I64Shl); }
      <i> "i32.shr_s"           { RETURN_OPCODE(Binary, I32ShrS); }
      <i> "i64.shr_s"           { RETURN_OPCODE(Binary, I64ShrS); }
      <i> "i32.shr_u"           { RETURN_OPCODE(Binary, I32ShrU); }
      <i> "i64.shr_u"           { RETURN_OPCODE(Binary, I64ShrU); }
      <i> "i32.rotl"            { RETURN_OPCODE(Binary, I32Rotl); }
      <i> "i64.rotl"            { RETURN_OPCODE(Binary, I64Rotl); }
      <i> "i32.rotr"            { RETURN_OPCODE(Binary, I32Rotr); }
      <i> "i64.rotr"            { RETURN_OPCODE(Binary, I64Rotr); }
      <i> "f32.add"             { RETURN_OPCODE(Binary, F32Add); }
      <i> "f64.add"             { RETURN_OPCODE(Binary, F64Add); }
      <i> "f32.sub"             { RETURN_OPCODE(Binary, F32Sub); }
      <i> "f64.sub"             { RETURN_OPCODE(Binary, F64Sub); }
      <i> "f32.mul"             { RETURN_OPCODE(Binary, F32Mul); }
      <i> "f64.mul"             { RETURN_OPCODE(Binary, F64Mul); }
      <i> "f32.div"             { RETURN_OPCODE(Binary, F32Div); }
      <i> "f64.div"             { RETURN_OPCODE(Binary, F64Div); }
      <i> "f32.min"             { RETURN_OPCODE(Binary, F32Min); }
      <i> "f64.min"             { RETURN_OPCODE(Binary, F64Min); }
      <i> "f32.max"             { RETURN_OPCODE(Binary, F32Max); }
      <i> "f64.max"             { RETURN_OPCODE(Binary, F64Max); }
      <i> "f32.copysign"        { RETURN_OPCODE(Binary, F32Copysign); }
      <i> "f64.copysign"        { RETURN_OPCODE(Binary, F64Copysign); }
      <i> "i32.eq"              { RETURN_OPCODE(Compare, I32Eq); }
      <i> "i64.eq"              { RETURN_OPCODE(Compare, I64Eq); }
      <i> "i32.ne"              { RETURN_OPCODE(Compare, I32Ne); }
      <i> "i64.ne"              { RETURN_OPCODE(Compare, I64Ne); }
      <i> "i32.lt_s"            { RETURN_OPCODE(Compare, I32LtS); }
      <i> "i64.lt_s"            { RETURN_OPCODE(Compare, I64LtS); }
      <i> "i32.lt_u"            { RETURN_OPCODE(Compare, I32LtU); }
      <i> "i64.lt_u"            { RETURN_OPCODE(Compare, I64LtU); }
      <i> "i32.le_s"            { RETURN_OPCODE(Compare, I32LeS); }
      <i> "i64.le_s"            { RETURN_OPCODE(Compare, I64LeS); }
      <i> "i32.le_u"            { RETURN_OPCODE(Compare, I32LeU); }
      <i> "i64.le_u"            { RETURN_OPCODE(Compare, I64LeU); }
      <i> "i32.gt_s"            { RETURN_OPCODE(Compare, I32GtS); }
      <i> "i64.gt_s"            { RETURN_OPCODE(Compare, I64GtS); }
      <i> "i32.gt_u"            { RETURN_OPCODE(Compare, I32GtU); }
      <i> "i64.gt_u"            { RETURN_OPCODE(Compare, I64GtU); }
      <i> "i32.ge_s"            { RETURN_OPCODE(Compare, I32GeS); }
      <i> "i64.ge_s"            { RETURN_OPCODE(Compare, I64GeS); }
      <i> "i32.ge_u"            { RETURN_OPCODE(Compare, I32GeU); }
      <i> "i64.ge_u"            { RETURN_OPCODE(Compare, I64GeU); }
      <i> "f32.eq"              { RETURN_OPCODE(Compare, F32Eq); }
      <i> "f64.eq"              { RETURN_OPCODE(Compare, F64Eq); }
      <i> "f32.ne"              { RETURN_OPCODE(Compare, F32Ne); }
      <i> "f64.ne"              { RETURN_OPCODE(Compare, F64Ne); }
      <i> "f32.lt"              { RETURN_OPCODE(Compare, F32Lt); }
      <i> "f64.lt"              { RETURN_OPCODE(Compare, F64Lt); }
      <i> "f32.le"              { RETURN_OPCODE(Compare, F32Le); }
      <i> "f64.le"              { RETURN_OPCODE(Compare, F64Le); }
      <i> "f32.gt"              { RETURN_OPCODE(Compare, F32Gt); }
      <i> "f64.gt"              { RETURN_OPCODE(Compare, F64Gt); }
      <i> "f32.ge"              { RETURN_OPCODE(Compare, F32Ge); }
      <i> "f64.ge"              { RETURN_OPCODE(Compare, F64Ge); }
      <i> "i64.extend_s/i32"    { RETURN_OPCODE(Convert, I64ExtendSI32); }
      <i> "i64.extend_u/i32"    { RETURN_OPCODE(Convert, I64ExtendUI32); }
      <i> "i32.wrap/i64"        { RETURN_OPCODE(Convert, I32WrapI64); }
      <i> "i32.trunc_s/f32"     { RETURN_OPCODE(Convert, I32TruncSF32); }
      <i> "i64.trunc_s/f32"     { RETURN_OPCODE(Convert, I64TruncSF32); }
      <i> "i32.trunc_s/f64"     { RETURN_OPCODE(Convert, I32TruncSF64); }
      <i> "i64.trunc_s/f64"     { RETURN_OPCODE(Convert, I64TruncSF64); }
      <i> "i32.trunc_u/f32"     { RETURN_OPCODE(Convert, I32TruncUF32); }
      <i> "i64.trunc_u/f32"     { RETURN_OPCODE(Convert, I64TruncUF32); }
      <i> "i32.trunc_u/f64"     { RETURN_OPCODE(Convert, I32TruncUF64); }
      <i> "i64.trunc_u/f64"     { RETURN_OPCODE(Convert, I64TruncUF64); }
      <i> "i32.trunc_s:sat/f32" { RETURN_OPCODE(Convert, I32TruncSSatF32); }
      <i> "i64.trunc_s:sat/f32" { RETURN_OPCODE(Convert, I64TruncSSatF32); }
      <i> "i32.trunc_s:sat/f64" { RETURN_OPCODE(Convert, I32TruncSSatF64); }
      <i> "i64.trunc_s:sat/f64" { RETURN_OPCODE(Convert, I64TruncSSatF64); }
      <i> "i32.trunc_u:sat/f32" { RETURN_OPCODE(Convert, I32TruncUSatF32); }
      <i> "i64.trunc_u:sat/f32" { RETURN_OPCODE(Convert, I64TruncUSatF32); }
      <i> "i32.trunc_u:sat/f64" { RETURN_OPCODE(Convert, I32TruncUSatF64); }
      <i> "i64.trunc_u:sat/f64" { RETURN_OPCODE(Convert, I64TruncUSatF64); }
      <i> "f32.convert_s/i32"   { RETURN_OPCODE(Convert, F32ConvertSI32); }
      <i> "f64.convert_s/i32"   { RETURN_OPCODE(Convert, F64ConvertSI32); }
      <i> "f32.convert_s/i64"   { RETURN_OPCODE(Convert, F32ConvertSI64); }
      <i> "f64.convert_s/i64"   { RETURN_OPCODE(Convert, F64ConvertSI64); }
      <i> "f32.convert_u/i32"   { RETURN_OPCODE(Convert, F32ConvertUI32); }
      <i> "f64.convert_u/i32"   { RETURN_OPCODE(Convert, F64ConvertUI32); }
      <i> "f32.convert_u/i64"   { RETURN_OPCODE(Convert, F32ConvertUI64); }
      <i> "f64.convert_u/i64"   { RETURN_OPCODE(Convert, F64ConvertUI64); }
      <i> "f64.promote/f32"     { RETURN_OPCODE(Convert, F64PromoteF32); }
      <i> "f32.demote/f64"      { RETURN_OPCODE(Convert, F32DemoteF64); }
      <i> "f32.reinterpret/i32" { RETURN_OPCODE(Convert, F32ReinterpretI32); }
      <i> "i32.reinterpret/f32" { RETURN_OPCODE(Convert, I32ReinterpretF32); }
      <i> "f64.reinterpret/i64" { RETURN_OPCODE(Convert, F64ReinterpretI64); }
      <i> "i64.reinterpret/f64" { RETURN_OPCODE(Convert, I64ReinterpretF64); }
      <i> "select"              { RETURN_OPCODE0(Select); }
      <i> "unreachable"         { RETURN_OPCODE0(Unreachable); }
      <i> "current_memory"      { RETURN_OPCODE0(CurrentMemory); }
      <i> "grow_memory"         { RETURN_OPCODE0(GrowMemory); }
      <i> "type"                { RETURN(Type); }
      <i> "func"                { RETURN(Func); }
      <i> "param"               { RETURN(Param); }
      <i> "result"              { RETURN(Result); }
      <i> "local"               { RETURN(Local); }
      <i> "global"              { RETURN(Global); }
      <i> "module"              { RETURN(Module); }
      <i> "binary"              { RETURN(Bin); }
      <i> "quote"               { RETURN(Quote); }
      <i> "table"               { RETURN(Table); }
      <i> "memory"              { RETURN(Memory); }
      <i> "start"               { RETURN(Start); }
      <i> "elem"                { RETURN(Elem); }
      <i> "data"                { RETURN(Data); }
      <i> "offset"              { RETURN(Offset); }
      <i> "import"              { RETURN(Import); }
      <i> "export"              { RETURN(Export); }
      <i> "except"              { RETURN(Except); }
      <i> "register"            { RETURN(Register); }
      <i> "invoke"              { RETURN(Invoke); }
      <i> "get"                 { RETURN(Get); }
      <i> "assert_malformed"    { RETURN(AssertMalformed); }
      <i> "assert_invalid"      { RETURN(AssertInvalid); }
      <i> "assert_unlinkable"   { RETURN(AssertUnlinkable); }
      <i> "assert_return"       { RETURN(AssertReturn); }
      <i> "assert_return_canonical_nan" { RETURN(AssertReturnCanonicalNan); }
      <i> "assert_return_arithmetic_nan" { RETURN(AssertReturnArithmeticNan); }
      <i> "assert_trap"         { RETURN(AssertTrap); }
      <i> "assert_exhaustion"   { RETURN(AssertExhaustion); }
      <i> "try"                 { RETURN_OPCODE0(Try); }
      <i> "catch"               { RETURN_OPCODE0(Catch); }
      <i> "catch_all"           { RETURN_OPCODE0(CatchAll); }
      <i> "throw"               { RETURN_OPCODE0(Throw); }
      <i> "rethrow"             { RETURN_OPCODE0(Rethrow); }
      <i> name                  { RETURN_TEXT(Var); }

      <i> ";;" => LINE_COMMENT  { continue; }
      <LINE_COMMENT> "\n" => i  { NEWLINE; continue; }
      <LINE_COMMENT> [^\n]+     { continue; }
      <i> "(;" => BLOCK_COMMENT { COMMENT_NESTING = 1; continue; }
      <BLOCK_COMMENT> "(;"      { COMMENT_NESTING++; continue; }
      <BLOCK_COMMENT> ";)"      { if (--COMMENT_NESTING == 0)
                                    BEGIN(YYCOND_i);
                                  continue; }
      <BLOCK_COMMENT> "\n"      { NEWLINE; continue; }
      <BLOCK_COMMENT> [^]       { continue; }
      <BLOCK_COMMENT> *         { MAYBE_MALFORMED_UTF8(" in block comment"); }
      <i> "\n"                  { NEWLINE; continue; }
      <i> [ \t\r]+              { continue; }
      <i> reserved              { RETURN_TEXT(Reserved); }
      <i> [^]                   { ERROR("unexpected char"); continue; }
      <*> *                     { MAYBE_MALFORMED_UTF8(""); }
     */
  }
}

}  // namespace wabt
