blob: 7971886ff3fce30b06c959291358bce801e4af7c [file] [log] [blame] [edit]
/*
* 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 <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <utility>
#include "binary-error-handler.h"
#include "binary-reader.h"
#include "binary-reader-ir.h"
#include "literal.h"
#include "wast-parser.h"
#include "wast-parser-lexer-shared.h"
#define INVALID_VAR_INDEX (-1)
#define RELOCATE_STACK(type, array, stack_base, old_size, new_size) \
do { \
type* new_stack = new type[new_size](); \
std::move((stack_base), (stack_base) + (old_size), (new_stack)); \
if ((stack_base) != (array)) { \
delete[](stack_base); \
} else { \
for (size_t i = 0; i < (old_size); ++i) { \
(stack_base)[i].~type(); \
} \
} \
/* Cache the pointer in the parser struct to be deleted later. */ \
parser->array = (stack_base) = new_stack; \
} while (0)
#define yyoverflow(message, ss, ss_size, vs, vs_size, ls, ls_size, new_size) \
do { \
size_t old_size = *(new_size); \
*(new_size) *= 2; \
RELOCATE_STACK(yytype_int16, yyssa, *(ss), old_size, *(new_size)); \
RELOCATE_STACK(YYSTYPE, yyvsa, *(vs), old_size, *(new_size)); \
RELOCATE_STACK(YYLTYPE, yylsa, *(ls), old_size, *(new_size)); \
} while (0)
#define DUPTEXT(dst, src) \
(dst).start = wabt_strndup((src).start, (src).length); \
(dst).length = (src).length
#define YYLLOC_DEFAULT(Current, Rhs, N) \
do \
if (N) { \
(Current).filename = YYRHSLOC(Rhs, 1).filename; \
(Current).line = YYRHSLOC(Rhs, 1).line; \
(Current).first_column = YYRHSLOC(Rhs, 1).first_column; \
(Current).last_column = YYRHSLOC(Rhs, N).last_column; \
} else { \
(Current).filename = nullptr; \
(Current).line = YYRHSLOC(Rhs, 0).line; \
(Current).first_column = (Current).last_column = \
YYRHSLOC(Rhs, 0).last_column; \
} \
while (0)
#define APPEND_FIELD_TO_LIST(module, field, Kind, kind, loc_, item) \
do { \
field = append_module_field(module); \
field->loc = loc_; \
field->type = ModuleFieldType::Kind; \
field->kind = item; \
} while (0)
#define APPEND_ITEM_TO_VECTOR(module, kinds, item_ptr) \
(module)->kinds.push_back(item_ptr)
#define INSERT_BINDING(module, kind, kinds, loc_, name) \
do \
if ((name).start) { \
(module)->kind##_bindings.emplace( \
string_slice_to_string(name), \
Binding(loc_, (module)->kinds.size() - 1)); \
} \
while (0)
#define APPEND_INLINE_EXPORT(module, Kind, loc_, value, index_) \
do \
if ((value)->export_.has_export) { \
ModuleField* export_field; \
APPEND_FIELD_TO_LIST(module, export_field, Export, export_, loc_, \
(value)->export_.export_.release()); \
export_field->export_->kind = ExternalKind::Kind; \
export_field->export_->var.loc = loc_; \
export_field->export_->var.index = index_; \
APPEND_ITEM_TO_VECTOR(module, exports, export_field->export_); \
INSERT_BINDING(module, export, exports, export_field->loc, \
export_field->export_->name); \
} \
while (0)
#define CHECK_IMPORT_ORDERING(module, kind, kinds, loc_) \
do { \
if ((module)->kinds.size() != (module)->num_##kind##_imports) { \
wast_parser_error( \
&loc_, lexer, parser, \
"imports must occur before all non-import definitions"); \
} \
} while (0)
#define CHECK_END_LABEL(loc, begin_label, end_label) \
do { \
if (!string_slice_is_empty(&(end_label))) { \
if (string_slice_is_empty(&(begin_label))) { \
wast_parser_error(&loc, lexer, parser, \
"unexpected label \"" PRIstringslice "\"", \
WABT_PRINTF_STRING_SLICE_ARG(end_label)); \
} else if (!string_slices_are_equal(&(begin_label), &(end_label))) { \
wast_parser_error(&loc, lexer, parser, \
"mismatching label \"" PRIstringslice \
"\" != \"" PRIstringslice "\"", \
WABT_PRINTF_STRING_SLICE_ARG(begin_label), \
WABT_PRINTF_STRING_SLICE_ARG(end_label)); \
} \
destroy_string_slice(&(end_label)); \
} \
} while (0)
#define YYMALLOC(size) new char [size]
#define YYFREE(p) delete [] (p)
#define USE_NATURAL_ALIGNMENT (~0)
namespace wabt {
ExprList join_exprs1(Location* loc, Expr* expr1);
ExprList join_exprs2(Location* loc,
ExprList* expr1,
Expr* expr2);
Result parse_const(Type type,
LiteralType literal_type,
const char* s,
const char* end,
Const* out);
void dup_text_list(TextList* text_list, char** out_data, size_t* out_size);
bool is_empty_signature(const FuncSignature* sig);
void append_implicit_func_declaration(Location*,
Module*,
FuncDeclaration*);
class BinaryErrorHandlerModule : public BinaryErrorHandler {
public:
BinaryErrorHandlerModule(Location* loc, WastLexer* lexer, WastParser* parser);
bool OnError(uint32_t offset, const std::string& error) override;
private:
Location* loc_;
WastLexer* lexer_;
WastParser* parser_;
};
#define wabt_wast_parser_lex wast_lexer_lex
#define wabt_wast_parser_error wast_parser_error
%}
%define api.prefix {wabt_wast_parser_}
%define api.pure true
%define api.value.type {::wabt::Token}
%define api.token.prefix {WABT_TOKEN_TYPE_}
%define parse.error verbose
%lex-param {::wabt::WastLexer* lexer} {::wabt::WastParser* parser}
%parse-param {::wabt::WastLexer* lexer} {::wabt::WastParser* parser}
%locations
%token LPAR "("
%token RPAR ")"
%token NAT INT FLOAT TEXT VAR VALUE_TYPE ANYFUNC MUT
%token NOP DROP BLOCK END IF THEN ELSE LOOP BR BR_IF BR_TABLE
%token CALL CALL_INDIRECT RETURN
%token GET_LOCAL SET_LOCAL TEE_LOCAL GET_GLOBAL SET_GLOBAL
%token LOAD STORE OFFSET_EQ_NAT ALIGN_EQ_NAT
%token CONST UNARY BINARY COMPARE CONVERT SELECT
%token UNREACHABLE CURRENT_MEMORY GROW_MEMORY
%token FUNC START TYPE PARAM RESULT LOCAL GLOBAL
%token MODULE TABLE ELEM MEMORY DATA OFFSET IMPORT EXPORT
%token REGISTER INVOKE GET
%token ASSERT_MALFORMED ASSERT_INVALID ASSERT_UNLINKABLE
%token ASSERT_RETURN ASSERT_RETURN_CANONICAL_NAN ASSERT_RETURN_ARITHMETIC_NAN
%token ASSERT_TRAP ASSERT_EXHAUSTION
%token EOF 0 "EOF"
%type<opcode> BINARY COMPARE CONVERT LOAD STORE UNARY
%type<text> ALIGN_EQ_NAT OFFSET_EQ_NAT TEXT VAR
%type<type> SELECT
%type<type> CONST VALUE_TYPE
%type<literal> NAT INT FLOAT
%type<action> action
%type<block> block
%type<command> assertion cmd
%type<commands> cmd_list
%type<const_> const
%type<consts> const_list
%type<data_segment> data
%type<elem_segment> elem
%type<export_> export export_kind
%type<exported_func> func
%type<exported_global> global
%type<exported_table> table
%type<exported_memory> memory
%type<expr> plain_instr block_instr
%type<expr_list> instr instr_list expr expr1 expr_list if_ const_expr offset
%type<func_fields> func_fields func_body
%type<func> func_info
%type<func_sig> func_sig func_type
%type<func_type> type_def
%type<global> global_type
%type<import> import import_kind inline_import
%type<limits> limits
%type<memory> memory_sig
%type<module> module module_fields
%type<optional_export> inline_export inline_export_opt
%type<raw_module> raw_module
%type<literal> literal
%type<script> script
%type<table> table_sig
%type<text> bind_var bind_var_opt labeling_opt quoted_text
%type<text_list> non_empty_text_list text_list
%type<types> value_type_list
%type<u32> align_opt
%type<u64> nat offset_opt
%type<vars> var_list
%type<var> start type_use var script_var_opt
/* These non-terminals use the types below that have destructors, but the
* memory is shared with the lexer, so should not be destroyed. */
%destructor {} ALIGN_EQ_NAT OFFSET_EQ_NAT TEXT VAR NAT INT FLOAT
%destructor { destroy_string_slice(&$$); } <text>
%destructor { destroy_string_slice(&$$.text); } <literal>
%destructor { delete $$; } <action>
%destructor { delete $$; } <block>
%destructor { delete $$; } <command>
%destructor { delete $$; } <commands>
%destructor { delete $$; } <consts>
%destructor { delete $$; } <data_segment>
%destructor { delete $$; } <elem_segment>
%destructor { delete $$; } <export_>
%destructor { delete $$; } <exported_func>
%destructor { delete $$; } <exported_memory>
%destructor { delete $$; } <exported_table>
%destructor { delete $$; } <expr>
%destructor { destroy_expr_list($$.first); } <expr_list>
%destructor { destroy_func_fields($$); } <func_fields>
%destructor { delete $$; } <func>
%destructor { delete $$; } <func_sig>
%destructor { delete $$; } <func_type>
%destructor { delete $$; } <global>
%destructor { delete $$; } <import>
%destructor { delete $$; } <optional_export>
%destructor { delete $$; } <memory>
%destructor { delete $$; } <module>
%destructor { delete $$; } <raw_module>
%destructor { delete $$; } <script>
%destructor { destroy_text_list(&$$); } <text_list>
%destructor { delete $$; } <types>
%destructor { destroy_var(&$$); } <var>
%destructor { delete $$; } <vars>
%nonassoc LOW
%nonassoc VAR
%start script_start
%%
/* Auxiliaries */
non_empty_text_list :
TEXT {
TextListNode* node = new TextListNode();
DUPTEXT(node->text, $1);
node->next = nullptr;
$$.first = $$.last = node;
}
| non_empty_text_list TEXT {
$$ = $1;
TextListNode* node = new TextListNode();
DUPTEXT(node->text, $2);
node->next = nullptr;
$$.last->next = node;
$$.last = node;
}
;
text_list :
/* empty */ { $$.first = $$.last = nullptr; }
| non_empty_text_list
;
quoted_text :
TEXT {
TextListNode node;
node.text = $1;
node.next = nullptr;
TextList text_list;
text_list.first = &node;
text_list.last = &node;
char* data;
size_t size;
dup_text_list(&text_list, &data, &size);
$$.start = data;
$$.length = size;
}
;
/* Types */
value_type_list :
/* empty */ { $$ = new TypeVector(); }
| value_type_list VALUE_TYPE {
$$ = $1;
$$->push_back($2);
}
;
elem_type :
ANYFUNC {}
;
global_type :
VALUE_TYPE {
$$ = new Global();
$$->type = $1;
$$->mutable_ = false;
}
| LPAR MUT VALUE_TYPE RPAR {
$$ = new Global();
$$->type = $3;
$$->mutable_ = true;
}
;
func_type :
LPAR FUNC func_sig RPAR { $$ = $3; }
;
func_sig :
/* empty */ { $$ = new FuncSignature(); }
| LPAR PARAM value_type_list RPAR {
$$ = new FuncSignature();
$$->param_types = std::move(*$3);
delete $3;
}
| LPAR PARAM value_type_list RPAR LPAR RESULT value_type_list RPAR {
$$ = new FuncSignature();
$$->param_types = std::move(*$3);
delete $3;
$$->result_types = std::move(*$7);
delete $7;
}
| LPAR RESULT value_type_list RPAR {
$$ = new FuncSignature();
$$->result_types = std::move(*$3);
delete $3;
}
;
table_sig :
limits elem_type {
$$ = new Table();
$$->elem_limits = $1;
}
;
memory_sig :
limits {
$$ = new Memory();
$$->page_limits = $1;
}
;
limits :
nat {
$$.has_max = false;
$$.initial = $1;
$$.max = 0;
}
| nat nat {
$$.has_max = true;
$$.initial = $1;
$$.max = $2;
}
;
type_use :
LPAR TYPE var RPAR { $$ = $3; }
;
/* Expressions */
nat :
NAT {
if (WABT_FAILED(parse_uint64($1.text.start,
$1.text.start + $1.text.length, &$$))) {
wast_parser_error(&@1, lexer, parser,
"invalid int " PRIstringslice "\"",
WABT_PRINTF_STRING_SLICE_ARG($1.text));
}
}
;
literal :
NAT {
$$.type = $1.type;
DUPTEXT($$.text, $1.text);
}
| INT {
$$.type = $1.type;
DUPTEXT($$.text, $1.text);
}
| FLOAT {
$$.type = $1.type;
DUPTEXT($$.text, $1.text);
}
;
var :
nat {
$$.loc = @1;
$$.type = VarType::Index;
$$.index = $1;
}
| VAR {
$$.loc = @1;
$$.type = VarType::Name;
DUPTEXT($$.name, $1);
}
;
var_list :
/* empty */ { $$ = new VarVector(); }
| var_list var {
$$ = $1;
$$->push_back($2);
}
;
bind_var_opt :
/* empty */ { WABT_ZERO_MEMORY($$); }
| bind_var
;
bind_var :
VAR { DUPTEXT($$, $1); }
;
labeling_opt :
/* empty */ %prec LOW { WABT_ZERO_MEMORY($$); }
| bind_var
;
offset_opt :
/* empty */ { $$ = 0; }
| OFFSET_EQ_NAT {
if (WABT_FAILED(parse_int64($1.start, $1.start + $1.length, &$$,
ParseIntType::SignedAndUnsigned))) {
wast_parser_error(&@1, lexer, parser,
"invalid offset \"" PRIstringslice "\"",
WABT_PRINTF_STRING_SLICE_ARG($1));
}
}
;
align_opt :
/* empty */ { $$ = USE_NATURAL_ALIGNMENT; }
| ALIGN_EQ_NAT {
if (WABT_FAILED(parse_int32($1.start, $1.start + $1.length, &$$,
ParseIntType::UnsignedOnly))) {
wast_parser_error(&@1, lexer, parser,
"invalid alignment \"" PRIstringslice "\"",
WABT_PRINTF_STRING_SLICE_ARG($1));
}
}
;
instr :
plain_instr { $$ = join_exprs1(&@1, $1); }
| block_instr { $$ = join_exprs1(&@1, $1); }
| expr { $$ = $1; }
;
plain_instr :
UNREACHABLE {
$$ = Expr::CreateUnreachable();
}
| NOP {
$$ = Expr::CreateNop();
}
| DROP {
$$ = Expr::CreateDrop();
}
| SELECT {
$$ = Expr::CreateSelect();
}
| BR var {
$$ = Expr::CreateBr($2);
}
| BR_IF var {
$$ = Expr::CreateBrIf($2);
}
| BR_TABLE var_list var {
$$ = Expr::CreateBrTable($2, $3);
}
| RETURN {
$$ = Expr::CreateReturn();
}
| CALL var {
$$ = Expr::CreateCall($2);
}
| CALL_INDIRECT var {
$$ = Expr::CreateCallIndirect($2);
}
| GET_LOCAL var {
$$ = Expr::CreateGetLocal($2);
}
| SET_LOCAL var {
$$ = Expr::CreateSetLocal($2);
}
| TEE_LOCAL var {
$$ = Expr::CreateTeeLocal($2);
}
| GET_GLOBAL var {
$$ = Expr::CreateGetGlobal($2);
}
| SET_GLOBAL var {
$$ = Expr::CreateSetGlobal($2);
}
| LOAD offset_opt align_opt {
$$ = Expr::CreateLoad($1, $3, $2);
}
| STORE offset_opt align_opt {
$$ = Expr::CreateStore($1, $3, $2);
}
| CONST literal {
Const const_;
WABT_ZERO_MEMORY(const_);
const_.loc = @1;
if (WABT_FAILED(parse_const($1, $2.type, $2.text.start,
$2.text.start + $2.text.length, &const_))) {
wast_parser_error(&@2, lexer, parser,
"invalid literal \"" PRIstringslice "\"",
WABT_PRINTF_STRING_SLICE_ARG($2.text));
}
delete [] $2.text.start;
$$ = Expr::CreateConst(const_);
}
| UNARY {
$$ = Expr::CreateUnary($1);
}
| BINARY {
$$ = Expr::CreateBinary($1);
}
| COMPARE {
$$ = Expr::CreateCompare($1);
}
| CONVERT {
$$ = Expr::CreateConvert($1);
}
| CURRENT_MEMORY {
$$ = Expr::CreateCurrentMemory();
}
| GROW_MEMORY {
$$ = Expr::CreateGrowMemory();
}
;
block_instr :
BLOCK labeling_opt block END labeling_opt {
$$ = Expr::CreateBlock($3);
$$->block->label = $2;
CHECK_END_LABEL(@5, $$->block->label, $5);
}
| LOOP labeling_opt block END labeling_opt {
$$ = Expr::CreateLoop($3);
$$->loop->label = $2;
CHECK_END_LABEL(@5, $$->loop->label, $5);
}
| IF labeling_opt block END labeling_opt {
$$ = Expr::CreateIf($3, nullptr);
$$->if_.true_->label = $2;
CHECK_END_LABEL(@5, $$->if_.true_->label, $5);
}
| IF labeling_opt block ELSE labeling_opt instr_list END labeling_opt {
$$ = Expr::CreateIf($3, $6.first);
$$->if_.true_->label = $2;
CHECK_END_LABEL(@5, $$->if_.true_->label, $5);
CHECK_END_LABEL(@8, $$->if_.true_->label, $8);
}
;
block :
value_type_list instr_list {
$$ = new Block();
$$->sig = std::move(*$1);
delete $1;
$$->first = $2.first;
}
;
expr :
LPAR expr1 RPAR { $$ = $2; }
;
expr1 :
plain_instr expr_list {
$$ = join_exprs2(&@1, &$2, $1);
}
| BLOCK labeling_opt block {
Expr* expr = Expr::CreateBlock($3);
expr->block->label = $2;
$$ = join_exprs1(&@1, expr);
}
| LOOP labeling_opt block {
Expr* expr = Expr::CreateLoop($3);
expr->loop->label = $2;
$$ = join_exprs1(&@1, expr);
}
| IF labeling_opt value_type_list if_ {
$$ = $4;
Expr* if_ = $4.last;
assert(if_->type == ExprType::If);
if_->if_.true_->label = $2;
if_->if_.true_->sig = std::move(*$3);
delete $3;
}
;
if_ :
LPAR THEN instr_list RPAR LPAR ELSE instr_list RPAR {
Expr* expr = Expr::CreateIf(new Block($3.first), $7.first);
$$ = join_exprs1(&@1, expr);
}
| LPAR THEN instr_list RPAR {
Expr* expr = Expr::CreateIf(new Block($3.first), nullptr);
$$ = join_exprs1(&@1, expr);
}
| expr LPAR THEN instr_list RPAR LPAR ELSE instr_list RPAR {
Expr* expr = Expr::CreateIf(new Block($4.first), $8.first);
$$ = join_exprs2(&@1, &$1, expr);
}
| expr LPAR THEN instr_list RPAR {
Expr* expr = Expr::CreateIf(new Block($4.first), nullptr);
$$ = join_exprs2(&@1, &$1, expr);
}
| expr expr expr {
Expr* expr = Expr::CreateIf(new Block($2.first), $3.first);
$$ = join_exprs2(&@1, &$1, expr);
}
| expr expr {
Expr* expr = Expr::CreateIf(new Block($2.first), nullptr);
$$ = join_exprs2(&@1, &$1, expr);
}
;
instr_list :
/* empty */ { WABT_ZERO_MEMORY($$); }
| instr instr_list {
$$.first = $1.first;
$1.last->next = $2.first;
$$.last = $2.last ? $2.last : $1.last;
$$.size = $1.size + $2.size;
}
;
expr_list :
/* empty */ { WABT_ZERO_MEMORY($$); }
| expr expr_list {
$$.first = $1.first;
$1.last->next = $2.first;
$$.last = $2.last ? $2.last : $1.last;
$$.size = $1.size + $2.size;
}
const_expr :
instr_list
;
/* Functions */
func_fields :
func_body
| LPAR RESULT value_type_list RPAR func_body {
$$ = new FuncField();
$$->type = FuncFieldType::ResultTypes;
$$->types = $3;
$$->next = $5;
}
| LPAR PARAM value_type_list RPAR func_fields {
$$ = new FuncField();
$$->type = FuncFieldType::ParamTypes;
$$->types = $3;
$$->next = $5;
}
| LPAR PARAM bind_var VALUE_TYPE RPAR func_fields {
$$ = new FuncField();
$$->type = FuncFieldType::BoundParam;
$$->bound_type.loc = @2;
$$->bound_type.name = $3;
$$->bound_type.type = $4;
$$->next = $6;
}
;
func_body :
instr_list {
$$ = new FuncField();
$$->type = FuncFieldType::Exprs;
$$->first_expr = $1.first;
$$->next = nullptr;
}
| LPAR LOCAL value_type_list RPAR func_body {
$$ = new FuncField();
$$->type = FuncFieldType::LocalTypes;
$$->types = $3;
$$->next = $5;
}
| LPAR LOCAL bind_var VALUE_TYPE RPAR func_body {
$$ = new FuncField();
$$->type = FuncFieldType::BoundLocal;
$$->bound_type.loc = @2;
$$->bound_type.name = $3;
$$->bound_type.type = $4;
$$->next = $6;
}
;
func_info :
func_fields {
$$ = new Func();
FuncField* field = $1;
while (field) {
FuncField* next = field->next;
switch (field->type) {
case FuncFieldType::Exprs:
$$->first_expr = field->first_expr;
field->first_expr = nullptr;
break;
case FuncFieldType::ParamTypes:
case FuncFieldType::LocalTypes: {
TypeVector& types = field->type == FuncFieldType::ParamTypes
? $$->decl.sig.param_types
: $$->local_types;
types.insert(types.end(), field->types->begin(),
field->types->end());
break;
}
case FuncFieldType::BoundParam:
case FuncFieldType::BoundLocal: {
TypeVector* types;
BindingHash* bindings;
if (field->type == FuncFieldType::BoundParam) {
types = &$$->decl.sig.param_types;
bindings = &$$->param_bindings;
} else {
types = &$$->local_types;
bindings = &$$->local_bindings;
}
types->push_back(field->bound_type.type);
bindings->emplace(
string_slice_to_string(field->bound_type.name),
Binding(field->bound_type.loc, types->size() - 1));
break;
}
case FuncFieldType::ResultTypes:
$$->decl.sig.result_types = std::move(*field->types);
break;
}
delete field;
field = next;
}
}
;
func :
LPAR FUNC bind_var_opt inline_export type_use func_info RPAR {
$$ = new ExportedFunc();
$$->func.reset($6);
$$->func->decl.has_func_type = true;
$$->func->decl.type_var = $5;
$$->func->name = $3;
$$->export_ = std::move(*$4);
delete $4;
}
/* Duplicate above for empty inline_export_opt to avoid LR(1) conflict. */
| LPAR FUNC bind_var_opt type_use func_info RPAR {
$$ = new ExportedFunc();
$$->func.reset($5);
$$->func->decl.has_func_type = true;
$$->func->decl.type_var = $4;
$$->func->name = $3;
}
| LPAR FUNC bind_var_opt inline_export func_info RPAR {
$$ = new ExportedFunc();
$$->func.reset($5);
$$->func->name = $3;
$$->export_ = std::move(*$4);
delete $4;
}
/* Duplicate above for empty inline_export_opt to avoid LR(1) conflict. */
| LPAR FUNC bind_var_opt func_info RPAR {
$$ = new ExportedFunc();
$$->func.reset($4);
$$->func->name = $3;
}
;
/* Tables & Memories */
offset :
LPAR OFFSET const_expr RPAR {
$$ = $3;
}
| expr
;
elem :
LPAR ELEM var offset var_list RPAR {
$$ = new ElemSegment();
$$->table_var = $3;
$$->offset = $4.first;
$$->vars = std::move(*$5);
delete $5;
}
| LPAR ELEM offset var_list RPAR {
$$ = new ElemSegment();
$$->table_var.loc = @2;
$$->table_var.type = VarType::Index;
$$->table_var.index = 0;
$$->offset = $3.first;
$$->vars = std::move(*$4);
delete $4;
}
;
table :
LPAR TABLE bind_var_opt inline_export_opt table_sig RPAR {
$$ = new ExportedTable();
$$->table.reset($5);
$$->table->name = $3;
$$->has_elem_segment = false;
$$->export_ = std::move(*$4);
delete $4;
}
| LPAR TABLE bind_var_opt inline_export_opt elem_type
LPAR ELEM var_list RPAR RPAR {
Expr* expr = Expr::CreateConst(Const(Const::I32(), 0));
expr->loc = @2;
$$ = new ExportedTable();
$$->table.reset(new Table());
$$->table->name = $3;
$$->table->elem_limits.initial = $8->size();
$$->table->elem_limits.max = $8->size();
$$->table->elem_limits.has_max = true;
$$->has_elem_segment = true;
$$->elem_segment.reset(new ElemSegment());
$$->elem_segment->offset = expr;
$$->elem_segment->vars = std::move(*$8);
delete $8;
$$->export_ = std::move(*$4);
delete $4;
}
;
data :
LPAR DATA var offset text_list RPAR {
$$ = new DataSegment();
$$->memory_var = $3;
$$->offset = $4.first;
dup_text_list(&$5, &$$->data, &$$->size);
destroy_text_list(&$5);
}
| LPAR DATA offset text_list RPAR {
$$ = new DataSegment();
$$->memory_var.loc = @2;
$$->memory_var.type = VarType::Index;
$$->memory_var.index = 0;
$$->offset = $3.first;
dup_text_list(&$4, &$$->data, &$$->size);
destroy_text_list(&$4);
}
;
memory :
LPAR MEMORY bind_var_opt inline_export_opt memory_sig RPAR {
$$ = new ExportedMemory();
$$->memory.reset($5);
$$->memory->name = $3;
$$->has_data_segment = false;
$$->export_ = std::move(*$4);
delete $4;
}
| LPAR MEMORY bind_var_opt inline_export LPAR DATA text_list RPAR RPAR {
Expr* expr = Expr::CreateConst(Const(Const::I32(), 0));
expr->loc = @2;
$$ = new ExportedMemory();
$$->has_data_segment = true;
$$->data_segment.reset(new DataSegment());
$$->data_segment->offset = expr;
dup_text_list(&$7, &$$->data_segment->data, &$$->data_segment->size);
destroy_text_list(&$7);
uint32_t byte_size = WABT_ALIGN_UP_TO_PAGE($$->data_segment->size);
uint32_t page_size = WABT_BYTES_TO_PAGES(byte_size);
$$->memory.reset(new Memory());
$$->memory->name = $3;
$$->memory->page_limits.initial = page_size;
$$->memory->page_limits.max = page_size;
$$->memory->page_limits.has_max = true;
$$->export_ = std::move(*$4);
delete $4;
}
/* Duplicate above for empty inline_export_opt to avoid LR(1) conflict. */
| LPAR MEMORY bind_var_opt LPAR DATA text_list RPAR RPAR {
Expr* expr = Expr::CreateConst(Const(Const::I32(), 0));
expr->loc = @2;
$$ = new ExportedMemory();
$$->has_data_segment = true;
$$->data_segment.reset(new DataSegment());
$$->data_segment->offset = expr;
dup_text_list(&$6, &$$->data_segment->data, &$$->data_segment->size);
destroy_text_list(&$6);
uint32_t byte_size = WABT_ALIGN_UP_TO_PAGE($$->data_segment->size);
uint32_t page_size = WABT_BYTES_TO_PAGES(byte_size);
$$->memory.reset(new Memory());
$$->memory->name = $3;
$$->memory->page_limits.initial = page_size;
$$->memory->page_limits.max = page_size;
$$->memory->page_limits.has_max = true;
$$->export_.has_export = false;
}
;
global :
LPAR GLOBAL bind_var_opt inline_export global_type const_expr RPAR {
$$ = new ExportedGlobal();
$$->global.reset($5);
$$->global->name = $3;
$$->global->init_expr = $6.first;
$$->export_ = std::move(*$4);
delete $4;
}
| LPAR GLOBAL bind_var_opt global_type const_expr RPAR {
$$ = new ExportedGlobal();
$$->global.reset($4);
$$->global->name = $3;
$$->global->init_expr = $5.first;
$$->export_.has_export = false;
}
;
/* Imports & Exports */
import_kind :
LPAR FUNC bind_var_opt type_use RPAR {
$$ = new Import();
$$->kind = ExternalKind::Func;
$$->func = new Func();
$$->func->name = $3;
$$->func->decl.has_func_type = true;
$$->func->decl.type_var = $4;
}
| LPAR FUNC bind_var_opt func_sig RPAR {
$$ = new Import();
$$->kind = ExternalKind::Func;
$$->func = new Func();
$$->func->name = $3;
$$->func->decl.sig = std::move(*$4);
delete $4;
}
| LPAR TABLE bind_var_opt table_sig RPAR {
$$ = new Import();
$$->kind = ExternalKind::Table;
$$->table = $4;
$$->table->name = $3;
}
| LPAR MEMORY bind_var_opt memory_sig RPAR {
$$ = new Import();
$$->kind = ExternalKind::Memory;
$$->memory = $4;
$$->memory->name = $3;
}
| LPAR GLOBAL bind_var_opt global_type RPAR {
$$ = new Import();
$$->kind = ExternalKind::Global;
$$->global = $4;
$$->global->name = $3;
}
;
import :
LPAR IMPORT quoted_text quoted_text import_kind RPAR {
$$ = $5;
$$->module_name = $3;
$$->field_name = $4;
}
| LPAR FUNC bind_var_opt inline_import type_use RPAR {
$$ = $4;
$$->kind = ExternalKind::Func;
$$->func = new Func();
$$->func->name = $3;
$$->func->decl.has_func_type = true;
$$->func->decl.type_var = $5;
}
| LPAR FUNC bind_var_opt inline_import func_sig RPAR {
$$ = $4;
$$->kind = ExternalKind::Func;
$$->func = new Func();
$$->func->name = $3;
$$->func->decl.sig = std::move(*$5);
delete $5;
}
| LPAR TABLE bind_var_opt inline_import table_sig RPAR {
$$ = $4;
$$->kind = ExternalKind::Table;
$$->table = $5;
$$->table->name = $3;
}
| LPAR MEMORY bind_var_opt inline_import memory_sig RPAR {
$$ = $4;
$$->kind = ExternalKind::Memory;
$$->memory = $5;
$$->memory->name = $3;
}
| LPAR GLOBAL bind_var_opt inline_import global_type RPAR {
$$ = $4;
$$->kind = ExternalKind::Global;
$$->global = $5;
$$->global->name = $3;
}
;
inline_import :
LPAR IMPORT quoted_text quoted_text RPAR {
$$ = new Import();
$$->module_name = $3;
$$->field_name = $4;
}
;
export_kind :
LPAR FUNC var RPAR {
$$ = new Export();
$$->kind = ExternalKind::Func;
$$->var = $3;
}
| LPAR TABLE var RPAR {
$$ = new Export();
$$->kind = ExternalKind::Table;
$$->var = $3;
}
| LPAR MEMORY var RPAR {
$$ = new Export();
$$->kind = ExternalKind::Memory;
$$->var = $3;
}
| LPAR GLOBAL var RPAR {
$$ = new Export();
$$->kind = ExternalKind::Global;
$$->var = $3;
}
;
export :
LPAR EXPORT quoted_text export_kind RPAR {
$$ = $4;
$$->name = $3;
}
;
inline_export_opt :
/* empty */ {
$$ = new OptionalExport();
$$->has_export = false;
}
| inline_export
;
inline_export :
LPAR EXPORT quoted_text RPAR {
$$ = new OptionalExport();
$$->has_export = true;
$$->export_.reset(new Export());
$$->export_->name = $3;
}
;
/* Modules */
type_def :
LPAR TYPE func_type RPAR {
$$ = new FuncType();
$$->sig = std::move(*$3);
delete $3;
}
| LPAR TYPE bind_var func_type RPAR {
$$ = new FuncType();
$$->name = $3;
$$->sig = std::move(*$4);
delete $4;
}
;
start :
LPAR START var RPAR { $$ = $3; }
;
module_fields :
/* empty */ {
$$ = new Module();
}
| module_fields type_def {
$$ = $1;
ModuleField* field;
APPEND_FIELD_TO_LIST($$, field, FuncType, func_type, @2, $2);
APPEND_ITEM_TO_VECTOR($$, func_types, field->func_type);
INSERT_BINDING($$, func_type, func_types, @2, $2->name);
}
| module_fields global {
$$ = $1;
ModuleField* field;
APPEND_FIELD_TO_LIST($$, field, Global, global, @2, $2->global.release());
APPEND_ITEM_TO_VECTOR($$, globals, field->global);
INSERT_BINDING($$, global, globals, @2, field->global->name);
APPEND_INLINE_EXPORT($$, Global, @2, $2, $$->globals.size() - 1);
delete $2;
}
| module_fields table {
$$ = $1;
ModuleField* field;
APPEND_FIELD_TO_LIST($$, field, Table, table, @2, $2->table.release());
APPEND_ITEM_TO_VECTOR($$, tables, field->table);
INSERT_BINDING($$, table, tables, @2, field->table->name);
APPEND_INLINE_EXPORT($$, Table, @2, $2, $$->tables.size() - 1);
if ($2->has_elem_segment) {
ModuleField* elem_segment_field;
APPEND_FIELD_TO_LIST($$, elem_segment_field, ElemSegment, elem_segment,
@2, $2->elem_segment.release());
APPEND_ITEM_TO_VECTOR($$, elem_segments,
elem_segment_field->elem_segment);
}
delete $2;
}
| module_fields memory {
$$ = $1;
ModuleField* field;
APPEND_FIELD_TO_LIST($$, field, Memory, memory, @2, $2->memory.release());
APPEND_ITEM_TO_VECTOR($$, memories, field->memory);
INSERT_BINDING($$, memory, memories, @2, field->memory->name);
APPEND_INLINE_EXPORT($$, Memory, @2, $2, $$->memories.size() - 1);
if ($2->has_data_segment) {
ModuleField* data_segment_field;
APPEND_FIELD_TO_LIST($$, data_segment_field, DataSegment, data_segment,
@2, $2->data_segment.release());
APPEND_ITEM_TO_VECTOR($$, data_segments,
data_segment_field->data_segment);
}
delete $2;
}
| module_fields func {
$$ = $1;
ModuleField* field;
// Append the implicit func declaration first so it occurs before the
// func definition when serialized out to the text format.
append_implicit_func_declaration(&@2, $$, &$2->func->decl);
APPEND_FIELD_TO_LIST($$, field, Func, func, @2, $2->func.release());
APPEND_ITEM_TO_VECTOR($$, funcs, field->func);
INSERT_BINDING($$, func, funcs, @2, field->func->name);
APPEND_INLINE_EXPORT($$, Func, @2, $2, $$->funcs.size() - 1);
delete $2;
}
| module_fields elem {
$$ = $1;
ModuleField* field;
APPEND_FIELD_TO_LIST($$, field, ElemSegment, elem_segment, @2, $2);
APPEND_ITEM_TO_VECTOR($$, elem_segments, field->elem_segment);
}
| module_fields data {
$$ = $1;
ModuleField* field;
APPEND_FIELD_TO_LIST($$, field, DataSegment, data_segment, @2, $2);
APPEND_ITEM_TO_VECTOR($$, data_segments, field->data_segment);
}
| module_fields start {
$$ = $1;
ModuleField* field;
APPEND_FIELD_TO_LIST($$, field, Start, start, @2, $2);
$$->start = &field->start;
}
| module_fields import {
$$ = $1;
ModuleField* field;
APPEND_FIELD_TO_LIST($$, field, Import, import, @2, $2);
CHECK_IMPORT_ORDERING($$, func, funcs, @2);
CHECK_IMPORT_ORDERING($$, table, tables, @2);
CHECK_IMPORT_ORDERING($$, memory, memories, @2);
CHECK_IMPORT_ORDERING($$, global, globals, @2);
switch ($2->kind) {
case ExternalKind::Func:
append_implicit_func_declaration(&@2, $$, &field->import->func->decl);
APPEND_ITEM_TO_VECTOR($$, funcs, field->import->func);
INSERT_BINDING($$, func, funcs, @2, field->import->func->name);
$$->num_func_imports++;
break;
case ExternalKind::Table:
APPEND_ITEM_TO_VECTOR($$, tables, field->import->table);
INSERT_BINDING($$, table, tables, @2, field->import->table->name);
$$->num_table_imports++;
break;
case ExternalKind::Memory:
APPEND_ITEM_TO_VECTOR($$, memories, field->import->memory);
INSERT_BINDING($$, memory, memories, @2, field->import->memory->name);
$$->num_memory_imports++;
break;
case ExternalKind::Global:
APPEND_ITEM_TO_VECTOR($$, globals, field->import->global);
INSERT_BINDING($$, global, globals, @2, field->import->global->name);
$$->num_global_imports++;
break;
}
APPEND_ITEM_TO_VECTOR($$, imports, field->import);
}
| module_fields export {
$$ = $1;
ModuleField* field;
APPEND_FIELD_TO_LIST($$, field, Export, export_, @2, $2);
APPEND_ITEM_TO_VECTOR($$, exports, field->export_);
INSERT_BINDING($$, export, exports, @2, field->export_->name);
}
;
raw_module :
LPAR MODULE bind_var_opt module_fields RPAR {
$$ = new RawModule();
$$->type = RawModuleType::Text;
$$->text = $4;
$$->text->name = $3;
$$->text->loc = @2;
/* resolve func type variables where the signature was not specified
* explicitly */
for (Func* func: $4->funcs) {
if (decl_has_func_type(&func->decl) &&
is_empty_signature(&func->decl.sig)) {
FuncType* func_type =
get_func_type_by_var($4, &func->decl.type_var);
if (func_type) {
func->decl.sig = func_type->sig;
}
}
}
}
| LPAR MODULE bind_var_opt non_empty_text_list RPAR {
$$ = new RawModule();
$$->type = RawModuleType::Binary;
$$->binary.name = $3;
$$->binary.loc = @2;
dup_text_list(&$4, &$$->binary.data, &$$->binary.size);
destroy_text_list(&$4);
}
;
module :
raw_module {
if ($1->type == RawModuleType::Text) {
$$ = $1->text;
$1->text = nullptr;
} else {
assert($1->type == RawModuleType::Binary);
$$ = new Module();
ReadBinaryOptions options = WABT_READ_BINARY_OPTIONS_DEFAULT;
BinaryErrorHandlerModule error_handler(&$1->binary.loc, lexer, parser);
read_binary_ir($1->binary.data, $1->binary.size, &options,
&error_handler, $$);
$$->name = $1->binary.name;
$$->loc = $1->binary.loc;
WABT_ZERO_MEMORY($1->binary.name);
}
delete $1;
}
;
/* Scripts */
script_var_opt :
/* empty */ {
WABT_ZERO_MEMORY($$);
$$.type = VarType::Index;
$$.index = INVALID_VAR_INDEX;
}
| VAR {
WABT_ZERO_MEMORY($$);
$$.type = VarType::Name;
DUPTEXT($$.name, $1);
}
;
action :
LPAR INVOKE script_var_opt quoted_text const_list RPAR {
$$ = new Action();
$$->loc = @2;
$$->module_var = $3;
$$->type = ActionType::Invoke;
$$->name = $4;
$$->invoke = new ActionInvoke();
$$->invoke->args = std::move(*$5);
delete $5;
}
| LPAR GET script_var_opt quoted_text RPAR {
$$ = new Action();
$$->loc = @2;
$$->module_var = $3;
$$->type = ActionType::Get;
$$->name = $4;
}
;
assertion :
LPAR ASSERT_MALFORMED raw_module quoted_text RPAR {
$$ = new Command();
$$->type = CommandType::AssertMalformed;
$$->assert_malformed.module = $3;
$$->assert_malformed.text = $4;
}
| LPAR ASSERT_INVALID raw_module quoted_text RPAR {
$$ = new Command();
$$->type = CommandType::AssertInvalid;
$$->assert_invalid.module = $3;
$$->assert_invalid.text = $4;
}
| LPAR ASSERT_UNLINKABLE raw_module quoted_text RPAR {
$$ = new Command();
$$->type = CommandType::AssertUnlinkable;
$$->assert_unlinkable.module = $3;
$$->assert_unlinkable.text = $4;
}
| LPAR ASSERT_TRAP raw_module quoted_text RPAR {
$$ = new Command();
$$->type = CommandType::AssertUninstantiable;
$$->assert_uninstantiable.module = $3;
$$->assert_uninstantiable.text = $4;
}
| LPAR ASSERT_RETURN action const_list RPAR {
$$ = new Command();
$$->type = CommandType::AssertReturn;
$$->assert_return.action = $3;
$$->assert_return.expected = $4;
}
| LPAR ASSERT_RETURN_CANONICAL_NAN action RPAR {
$$ = new Command();
$$->type = CommandType::AssertReturnCanonicalNan;
$$->assert_return_canonical_nan.action = $3;
}
| LPAR ASSERT_RETURN_ARITHMETIC_NAN action RPAR {
$$ = new Command();
$$->type = CommandType::AssertReturnArithmeticNan;
$$->assert_return_arithmetic_nan.action = $3;
}
| LPAR ASSERT_TRAP action quoted_text RPAR {
$$ = new Command();
$$->type = CommandType::AssertTrap;
$$->assert_trap.action = $3;
$$->assert_trap.text = $4;
}
| LPAR ASSERT_EXHAUSTION action quoted_text RPAR {
$$ = new Command();
$$->type = CommandType::AssertExhaustion;
$$->assert_trap.action = $3;
$$->assert_trap.text = $4;
}
;
cmd :
action {
$$ = new Command();
$$->type = CommandType::Action;
$$->action = $1;
}
| assertion
| module {
$$ = new Command();
$$->type = CommandType::Module;
$$->module = $1;
}
| LPAR REGISTER quoted_text script_var_opt RPAR {
$$ = new Command();
$$->type = CommandType::Register;
$$->register_.module_name = $3;
$$->register_.var = $4;
$$->register_.var.loc = @4;
}
;
cmd_list :
/* empty */ { $$ = new CommandPtrVector(); }
| cmd_list cmd {
$$ = $1;
$$->emplace_back($2);
}
;
const :
LPAR CONST literal RPAR {
$$.loc = @2;
if (WABT_FAILED(parse_const($2, $3.type, $3.text.start,
$3.text.start + $3.text.length, &$$))) {
wast_parser_error(&@3, lexer, parser,
"invalid literal \"" PRIstringslice "\"",
WABT_PRINTF_STRING_SLICE_ARG($3.text));
}
delete [] $3.text.start;
}
;
const_list :
/* empty */ { $$ = new ConstVector(); }
| const_list const {
$$ = $1;
$$->push_back($2);
}
;
script :
cmd_list {
$$ = new Script();
$$->commands = std::move(*$1);
delete $1;
int last_module_index = -1;
for (size_t i = 0; i < $$->commands.size(); ++i) {
Command& command = *$$->commands[i].get();
Var* module_var = nullptr;
switch (command.type) {
case CommandType::Module: {
last_module_index = i;
/* Wire up module name bindings. */
Module* module = command.module;
if (module->name.length == 0)
continue;
$$->module_bindings.emplace(string_slice_to_string(module->name),
Binding(module->loc, i));
break;
}
case CommandType::AssertReturn:
module_var = &command.assert_return.action->module_var;
goto has_module_var;
case CommandType::AssertReturnCanonicalNan:
module_var =
&command.assert_return_canonical_nan.action->module_var;
goto has_module_var;
case CommandType::AssertReturnArithmeticNan:
module_var =
&command.assert_return_arithmetic_nan.action->module_var;
goto has_module_var;
case CommandType::AssertTrap:
case CommandType::AssertExhaustion:
module_var = &command.assert_trap.action->module_var;
goto has_module_var;
case CommandType::Action:
module_var = &command.action->module_var;
goto has_module_var;
case CommandType::Register:
module_var = &command.register_.var;
goto has_module_var;
has_module_var: {
/* Resolve actions with an invalid index to use the preceding
* module. */
if (module_var->type == VarType::Index &&
module_var->index == INVALID_VAR_INDEX) {
module_var->index = last_module_index;
}
break;
}
default:
break;
}
}
parser->script = $$;
}
;
/* bison destroys the start symbol even on a successful parse. We want to keep
script from being destroyed, so create a dummy start symbol. */
script_start :
script
;
%%
void append_expr_list(ExprList* expr_list, ExprList* expr) {
if (!expr->first)
return;
if (expr_list->last)
expr_list->last->next = expr->first;
else
expr_list->first = expr->first;
expr_list->last = expr->last;
expr_list->size += expr->size;
}
void append_expr(ExprList* expr_list, Expr* expr) {
if (expr_list->last)
expr_list->last->next = expr;
else
expr_list->first = expr;
expr_list->last = expr;
expr_list->size++;
}
ExprList join_exprs1(Location* loc, Expr* expr1) {
ExprList result;
WABT_ZERO_MEMORY(result);
append_expr(&result, expr1);
expr1->loc = *loc;
return result;
}
ExprList join_exprs2(Location* loc, ExprList* expr1, Expr* expr2) {
ExprList result;
WABT_ZERO_MEMORY(result);
append_expr_list(&result, expr1);
append_expr(&result, expr2);
expr2->loc = *loc;
return result;
}
Result parse_const(Type type,
LiteralType literal_type,
const char* s,
const char* end,
Const* out) {
out->type = type;
switch (type) {
case Type::I32:
return parse_int32(s, end, &out->u32, ParseIntType::SignedAndUnsigned);
case Type::I64:
return parse_int64(s, end, &out->u64, ParseIntType::SignedAndUnsigned);
case Type::F32:
return parse_float(literal_type, s, end, &out->f32_bits);
case Type::F64:
return parse_double(literal_type, s, end, &out->f64_bits);
default:
assert(0);
break;
}
return Result::Error;
}
size_t copy_string_contents(StringSlice* text, char* dest) {
const char* src = text->start + 1;
const char* end = text->start + text->length - 1;
char* dest_start = dest;
while (src < end) {
if (*src == '\\') {
src++;
switch (*src) {
case 'n':
*dest++ = '\n';
break;
case 't':
*dest++ = '\t';
break;
case '\\':
*dest++ = '\\';
break;
case '\'':
*dest++ = '\'';
break;
case '\"':
*dest++ = '\"';
break;
default: {
/* The string should be validated already, so we know this is a hex
* sequence */
uint32_t hi;
uint32_t lo;
if (WABT_SUCCEEDED(parse_hexdigit(src[0], &hi)) &&
WABT_SUCCEEDED(parse_hexdigit(src[1], &lo))) {
*dest++ = (hi << 4) | lo;
} else {
assert(0);
}
src++;
break;
}
}
src++;
} else {
*dest++ = *src++;
}
}
/* return the data length */
return dest - dest_start;
}
void dup_text_list(TextList* text_list, char** out_data, size_t* out_size) {
/* walk the linked list to see how much total space is needed */
size_t total_size = 0;
for (TextListNode* node = text_list->first; node; node = node->next) {
/* Always allocate enough space for the entire string including the escape
* characters. It will only get shorter, and this way we only have to
* iterate through the string once. */
const char* src = node->text.start + 1;
const char* end = node->text.start + node->text.length - 1;
size_t size = (end > src) ? (end - src) : 0;
total_size += size;
}
char* result = new char [total_size];
char* dest = result;
for (TextListNode* node = text_list->first; node; node = node->next) {
size_t actual_size = copy_string_contents(&node->text, dest);
dest += actual_size;
}
*out_data = result;
*out_size = dest - result;
}
bool is_empty_signature(const FuncSignature* sig) {
return sig->result_types.empty() && sig->param_types.empty();
}
void append_implicit_func_declaration(Location* loc,
Module* module,
FuncDeclaration* decl) {
if (decl_has_func_type(decl))
return;
int sig_index = get_func_type_index_by_decl(module, decl);
if (sig_index == -1) {
append_implicit_func_type(loc, module, &decl->sig);
} else {
decl->sig = module->func_types[sig_index]->sig;
}
}
Result parse_wast(WastLexer* lexer, Script** out_script,
SourceErrorHandler* error_handler) {
WastParser parser;
WABT_ZERO_MEMORY(parser);
parser.error_handler = error_handler;
int result = wabt_wast_parser_parse(lexer, &parser);
delete [] parser.yyssa;
delete [] parser.yyvsa;
delete [] parser.yylsa;
*out_script = parser.script;
return result == 0 && parser.errors == 0 ? Result::Ok : Result::Error;
}
BinaryErrorHandlerModule::BinaryErrorHandlerModule(
Location* loc, WastLexer* lexer, WastParser* parser)
: loc_(loc), lexer_(lexer), parser_(parser) {}
bool BinaryErrorHandlerModule::OnError(uint32_t offset,
const std::string& error) {
if (offset == WABT_UNKNOWN_OFFSET) {
wast_parser_error(loc_, lexer_, parser_, "error in binary module: %s",
error.c_str());
} else {
wast_parser_error(loc_, lexer_, parser_,
"error in binary module: @0x%08x: %s", offset,
error.c_str());
}
return true;
}
} // namespace wabt