WebAssembly / exception-handling

Proposal to add exception handling to WebAssembly
https://webassembly.github.io/exception-handling/
Other
159 stars 34 forks source link

Grammar railroad diagram #167

Open mingodad opened 3 years ago

mingodad commented 3 years ago

I've done a experimental tool to convert bison grammars to a kind of EBNF understood by https://www.bottlecaps.de/rr/ui to generate railroad diagrams see bellow the converted interpreter/text/parser.mly and with some hand made changes to allow view it at https://www.bottlecaps.de/rr/ui the order of the rules could be changed to a better view of the railroad diagrams. Copy and paste the EBNF bellow on https://www.bottlecaps.de/rr/ui tab Edit Grammar then switch to the tab View Diagram.

/*
From https://github.com/WebAssembly/exception-handling/blob/master/interpreter/text/parser.mly
*/

name ::= /*empty*/ | STRING
string_list ::= /*empty*/ | | string_list STRING
ref_kind ::= /*empty*/ | FUNC | EXTERN
ref_type ::= /*empty*/ | FUNCREF | EXTERNREF
value_type ::= /*empty*/ | NUM_TYPE | ref_type
value_type_list ::= /*empty*/ | | value_type value_type_list
global_type ::= /*empty*/ | value_type | LPAR MUT value_type RPAR
def_type ::= /*empty*/ | LPAR FUNC func_type RPAR
func_type ::= /*empty*/ | | LPAR RESULT value_type_list RPAR func_type | LPAR PARAM value_type_list RPAR func_type | LPAR PARAM bind_var value_type RPAR func_type
table_type ::= /*empty*/ | limits ref_type
memory_type ::= /*empty*/ | limits
limits ::= /*empty*/ | NAT | NAT NAT
type_use ::= /*empty*/ | LPAR TYPE var RPAR
num ::= /*empty*/ | NAT | INT | FLOAT
var ::= /*empty*/ | NAT | VAR
var_list ::= /*empty*/ | | var var_list
bind_var_opt ::= /*empty*/ | | bind_var
bind_var ::= /*empty*/ | VAR
labeling_opt ::= /*empty*/ | | bind_var
labeling_end_opt ::= /*empty*/ | | bind_var
offset_opt ::= /*empty*/ | | OFFSET_EQ_NAT
align_opt ::= /*empty*/ | | ALIGN_EQ_NAT
instr ::= /*empty*/ | plain_instr | select_instr_instr | call_instr_instr | block_instr | expr
plain_instr ::= /*empty*/ | UNREACHABLE | NOP | DROP | BR var | BR_IF var | BR_TABLE var var_list | RETURN | CALL var | THROW var | RETHROW var | LOCAL_GET var | LOCAL_SET var | LOCAL_TEE var | GLOBAL_GET var | GLOBAL_SET var | TABLE_GET var | TABLE_SET var | TABLE_SIZE var | TABLE_GROW var | TABLE_FILL var | TABLE_COPY var var | TABLE_INIT var var | TABLE_GET | TABLE_SET | TABLE_SIZE | TABLE_GROW | TABLE_FILL | TABLE_COPY | TABLE_INIT var | ELEM_DROP var | LOAD offset_opt align_opt | STORE offset_opt align_opt | MEMORY_SIZE | MEMORY_GROW | MEMORY_FILL | MEMORY_COPY | MEMORY_INIT var | DATA_DROP var | REF_NULL ref_kind | REF_IS_NULL | REF_FUNC var | CONST num | TEST | COMPARE | UNARY | BINARY | CONVERT
select_instr ::= /*empty*/ | SELECT select_instr_results
select_instr_results ::= /*empty*/ | LPAR RESULT value_type_list RPAR select_instr_results |
select_instr_instr ::= /*empty*/ | SELECT select_instr_results_instr
select_instr_results_instr ::= /*empty*/ | LPAR RESULT value_type_list RPAR select_instr_results_instr | instr
call_instr ::= /*empty*/ | CALL_INDIRECT var call_instr_type | CALL_INDIRECT call_instr_type
call_instr_type ::= /*empty*/ | type_use call_instr_params | call_instr_params
call_instr_params ::= /*empty*/ | LPAR PARAM value_type_list RPAR call_instr_params | call_instr_results
call_instr_results ::= /*empty*/ | LPAR RESULT value_type_list RPAR call_instr_results |
call_instr_instr ::= /*empty*/ | CALL_INDIRECT var call_instr_type_instr | CALL_INDIRECT call_instr_type_instr
call_instr_type_instr ::= /*empty*/ | type_use call_instr_params_instr | call_instr_params_instr
call_instr_params_instr ::= /*empty*/ | LPAR PARAM value_type_list RPAR call_instr_params_instr | call_instr_results_instr
call_instr_results_instr ::= /*empty*/ | LPAR RESULT value_type_list RPAR call_instr_results_instr | instr
block_instr ::= /*empty*/ | BLOCK labeling_opt block END labeling_end_opt | LOOP labeling_opt block END labeling_end_opt | IF labeling_opt block END labeling_end_opt | IF labeling_opt block ELSE labeling_end_opt instr_list END labeling_end_opt | TRY labeling_opt block handler_instr | TRY labeling_opt block DELEGATE var
block ::= /*empty*/ | type_use block_param_body | block_param_body
block_param_body ::= /*empty*/ | block_result_body | LPAR PARAM value_type_list RPAR block_param_body
block_result_body ::= /*empty*/ | instr_list | LPAR RESULT value_type_list RPAR block_result_body
handler_instr ::= /*empty*/ | catch_list_instr END | catch_list_instr catch_all END | catch_all END | END
catch_list_instr ::= /*empty*/ | catch catch_list_instr | catch
handler ::= /*empty*/ | catch_list | catch_list LPAR catch_all RPAR | LPAR catch_all RPAR | LPAR DELEGATE var RPAR |
catch_list ::= /*empty*/ | catch_list LPAR catch RPAR | LPAR catch RPAR
catch ::= /*empty*/ | CATCH var instr_list
catch_all ::= /*empty*/ | CATCH_ALL instr_list
expr ::= /*empty*/ | LPAR expr1 RPAR
expr1 ::= /*empty*/ | plain_instr expr_list | SELECT select_expr_results | CALL_INDIRECT var call_expr_type | CALL_INDIRECT call_expr_type | BLOCK labeling_opt block | LOOP labeling_opt block | IF labeling_opt if_block | TRY labeling_opt try_block
select_expr_results ::= /*empty*/ | LPAR RESULT value_type_list RPAR select_expr_results | expr_list
call_expr_type ::= /*empty*/ | type_use call_expr_params | call_expr_params
call_expr_params ::= /*empty*/ | LPAR PARAM value_type_list RPAR call_expr_params | call_expr_results
call_expr_results ::= /*empty*/ | LPAR RESULT value_type_list RPAR call_expr_results | expr_list
if_block ::= /*empty*/ | type_use if_block_param_body | if_block_param_body
if_block_param_body ::= /*empty*/ | if_block_result_body | LPAR PARAM value_type_list RPAR if_block_param_body
if_block_result_body ::= /*empty*/ | if_ | LPAR RESULT value_type_list RPAR if_block_result_body
if_ ::= /*empty*/ | expr if_ | LPAR THEN instr_list RPAR LPAR ELSE instr_list RPAR | LPAR THEN instr_list RPAR
try_block ::= /*empty*/ | type_use try_block_param_body | try_block_param_body
try_block_param_body ::= /*empty*/ | try_block_result_body | LPAR PARAM value_type_list RPAR try_block_param_body
try_block_result_body ::= /*empty*/ | try_ | LPAR RESULT value_type_list RPAR try_block_result_body
try_ ::= /*empty*/ | LPAR DO instr_list RPAR handler
instr_list ::= /*empty*/ | | select_instr | call_instr | instr instr_list
expr_list ::= /*empty*/ | | expr expr_list
const_expr ::= /*empty*/ | instr_list
func ::= /*empty*/ | LPAR FUNC bind_var_opt func_fields RPAR
func_fields ::= /*empty*/ | type_use func_fields_body | func_fields_body | inline_import type_use func_fields_import | inline_import func_fields_import | inline_export func_fields
func_fields_import ::= /*empty*/ | func_fields_import_result | LPAR PARAM value_type_list RPAR func_fields_import | LPAR PARAM bind_var value_type RPAR func_fields_import
func_fields_import_result ::= /*empty*/ | | LPAR RESULT value_type_list RPAR func_fields_import_result
func_fields_body ::= /*empty*/ | func_result_body | LPAR PARAM value_type_list RPAR func_fields_body | LPAR PARAM bind_var value_type RPAR func_fields_body
func_result_body ::= /*empty*/ | func_body | LPAR RESULT value_type_list RPAR func_result_body
func_body ::= /*empty*/ | instr_list | LPAR LOCAL value_type_list RPAR func_body | LPAR LOCAL bind_var value_type RPAR func_body
table_use ::= /*empty*/ | LPAR TABLE var RPAR
memory_use ::= /*empty*/ | LPAR MEMORY var RPAR
offset ::= /*empty*/ | LPAR OFFSET const_expr RPAR | expr
elem_kind ::= /*empty*/ | FUNC
elem_expr ::= /*empty*/ | LPAR ITEM const_expr RPAR | expr
elem_expr_list ::= /*empty*/ | | elem_expr elem_expr_list
elem_var_list ::= /*empty*/ | var_list
elem_list ::= /*empty*/ | elem_kind elem_var_list | ref_type elem_expr_list
elem ::= /*empty*/ | LPAR ELEM bind_var_opt elem_list RPAR | LPAR ELEM bind_var_opt table_use offset elem_list RPAR | LPAR ELEM bind_var_opt DECLARE elem_list RPAR | LPAR ELEM bind_var_opt offset elem_list RPAR | LPAR ELEM bind_var_opt offset elem_var_list RPAR
table ::= /*empty*/ | LPAR TABLE bind_var_opt table_fields RPAR
table_fields ::= /*empty*/ | table_type | inline_import table_type | inline_export table_fields | ref_type LPAR ELEM elem_var_list RPAR | ref_type LPAR ELEM elem_expr elem_expr_list RPAR
data ::= /*empty*/ | LPAR DATA bind_var_opt string_list RPAR | LPAR DATA bind_var_opt memory_use offset string_list RPAR | LPAR DATA bind_var_opt offset string_list RPAR
memory ::= /*empty*/ | LPAR MEMORY bind_var_opt memory_fields RPAR
memory_fields ::= /*empty*/ | memory_type | inline_import memory_type | inline_export memory_fields | LPAR DATA string_list RPAR
event ::= /*empty*/ | LPAR EVENT bind_var_opt event_fields RPAR
event_fields ::= /*empty*/ | type_use func_type | func_type | inline_import type_use event_fields_import | inline_import event_fields_import | inline_export event_fields
event_fields_import ::= /*empty*/ | event_fields_import_result | LPAR PARAM value_type_list RPAR event_fields_import | LPAR PARAM bind_var value_type RPAR event_fields_import
event_fields_import_result ::= /*empty*/ | | LPAR RESULT value_type_list RPAR event_fields_import_result
global ::= /*empty*/ | LPAR GLOBAL bind_var_opt global_fields RPAR
global_fields ::= /*empty*/ | global_type const_expr | inline_import global_type | inline_export global_fields
import_desc ::= /*empty*/ | LPAR FUNC bind_var_opt type_use RPAR | LPAR FUNC bind_var_opt func_type RPAR | LPAR TABLE bind_var_opt table_type RPAR | LPAR MEMORY bind_var_opt memory_type RPAR | LPAR EVENT bind_var_opt type_use RPAR | LPAR EVENT bind_var_opt func_type RPAR | LPAR GLOBAL bind_var_opt global_type RPAR
import ::= /*empty*/ | LPAR IMPORT name name import_desc RPAR
inline_import ::= /*empty*/ | LPAR IMPORT name name RPAR
export_desc ::= /*empty*/ | LPAR FUNC var RPAR | LPAR TABLE var RPAR | LPAR MEMORY var RPAR | LPAR EVENT var RPAR | LPAR GLOBAL var RPAR
export ::= /*empty*/ | LPAR EXPORT name export_desc RPAR
inline_export ::= /*empty*/ | LPAR EXPORT name RPAR
type_ ::= /*empty*/ | def_type
type_def ::= /*empty*/ | LPAR TYPE type_ RPAR | LPAR TYPE bind_var type_ RPAR
start ::= /*empty*/ | LPAR START var RPAR
module_fields ::= /*empty*/ | | module_fields1
module_fields1 ::= /*empty*/ | type_def module_fields | global module_fields | table module_fields | memory module_fields | event module_fields | func module_fields | elem module_fields | data module_fields | start module_fields | import module_fields | export module_fields
module_var_opt ::= /*empty*/ | | VAR
module_ ::= /*empty*/ | LPAR MODULE module_var_opt module_fields RPAR
inline_module ::= /*empty*/ | module_fields
inline_module1 ::= /*empty*/ | module_fields1
script_var_opt ::= /*empty*/ | | VAR
script_module ::= /*empty*/ | module_ | LPAR MODULE module_var_opt BIN string_list RPAR | LPAR MODULE module_var_opt QUOTE string_list RPAR
action ::= /*empty*/ | LPAR INVOKE module_var_opt name const_list RPAR | LPAR GET module_var_opt name RPAR
assertion ::= /*empty*/ | LPAR ASSERT_MALFORMED script_module STRING RPAR | LPAR ASSERT_INVALID script_module STRING RPAR | LPAR ASSERT_UNLINKABLE script_module STRING RPAR | LPAR ASSERT_TRAP script_module STRING RPAR | LPAR ASSERT_RETURN action result_list RPAR | LPAR ASSERT_TRAP action STRING RPAR | LPAR ASSERT_EXCEPTION action RPAR | LPAR ASSERT_EXHAUSTION action STRING RPAR
cmd ::= /*empty*/ | action | assertion | script_module | LPAR REGISTER name module_var_opt RPAR | meta
cmd_list ::= /*empty*/ | | cmd cmd_list
meta ::= /*empty*/ | LPAR SCRIPT script_var_opt cmd_list RPAR | LPAR INPUT script_var_opt STRING RPAR | LPAR OUTPUT script_var_opt STRING RPAR | LPAR OUTPUT script_var_opt RPAR
const ::= /*empty*/ | LPAR CONST num RPAR | LPAR REF_NULL ref_kind RPAR | LPAR REF_EXTERN NAT RPAR
const_list ::= /*empty*/ | | const const_list
result ::= /*empty*/ | const | LPAR CONST NAN RPAR | LPAR REF_FUNC RPAR | LPAR REF_EXTERN RPAR
result_list ::= /*empty*/ | | result result_list
script ::= /*empty*/ | cmd_list EOF | inline_module1 EOF
script1 ::= /*empty*/ | cmd
module1 ::= /*empty*/ | module_ EOF | inline_module EOF

// Tokens

LPAR  ::= "("
RPAR  ::= ")"

EXTERN  ::= "extern"
EXTERNREF  ::= "externref"
FUNCREF  ::= "funcref"
MUT  ::= "mut"

REF_NULL  ::= "ref.null"
REF_FUNC  ::= "ref.func"
REF_EXTERN  ::= "ref.extern"
REF_IS_NULL  ::= "ref.is_null"

NOP  ::= "nop"
UNREACHABLE  ::= "unreachable"
DROP  ::= "drop"
BLOCK  ::= "block"
LOOP  ::= "loop"
END  ::= "end"
BR  ::= "br"
BR_IF  ::= "br_if"
BR_TABLE  ::= "br_table"
RETURN  ::= "return"
IF  ::= "if"
THEN  ::= "then"
ELSE  ::= "else"
SELECT  ::= "select"
CALL  ::= "call"
CALL_INDIRECT  ::= "call_indirect"

LOCAL_GET  ::= "local.get"
LOCAL_SET  ::= "local.set"
LOCAL_TEE  ::= "local.tee"
GLOBAL_GET  ::= "global.get"
GLOBAL_SET  ::= "global.set"

TABLE_GET  ::= "table.get"
TABLE_SET  ::= "table.set"
TABLE_SIZE  ::= "table.size"
TABLE_GROW  ::= "table.grow"
TABLE_FILL  ::= "table.fill"
TABLE_COPY  ::= "table.copy"
TABLE_INIT  ::= "table.init"
ELEM_DROP  ::= "elem.drop"

MEMORY_SIZE  ::= "memory.size"
MEMORY_GROW  ::= "memory.grow"
MEMORY_FILL  ::= "memory.fill"
MEMORY_COPY  ::= "memory.copy"
MEMORY_INIT  ::= "memory.init"
DATA_DROP  ::= "data.drop"

TRY  ::= "try"
DO  ::= "do"
CATCH  ::= "catch"
CATCH_ALL  ::= "catch_all"
DELEGATE  ::= "delegate"
THROW  ::= "throw"
RETHROW  ::= "rethrow"

TYPE  ::= "type"
FUNC  ::= "func"
START  ::= "start"
PARAM  ::= "param"
RESULT  ::= "result"
LOCAL  ::= "local"
GLOBAL  ::= "global"
TABLE  ::= "table"
MEMORY  ::= "memory"
EVENT  ::= "event"
ELEM  ::= "elem"
DATA  ::= "data"
DECLARE  ::= "declare"
OFFSET  ::= "offset"
ITEM  ::= "item"
IMPORT  ::= "import"
EXPORT  ::= "export"

MODULE  ::= "module"
BIN  ::= "binary"
QUOTE  ::= "quote"

SCRIPT  ::= "script"
REGISTER  ::= "register"
INVOKE  ::= "invoke"
GET  ::= "get"
ASSERT_MALFORMED  ::= "assert_malformed"
ASSERT_INVALID  ::= "assert_invalid"
ASSERT_UNLINKABLE  ::= "assert_unlinkable"
ASSERT_RETURN  ::= "assert_return"
ASSERT_TRAP  ::= "assert_trap"
ASSERT_EXCEPTION  ::= "assert_exception"
ASSERT_EXHAUSTION  ::= "assert_exhaustion"
NAN Script.CanonicalNan  ::= "nan:canonical"
NAN Script.ArithmeticNan  ::= "nan:arithmetic"
INPUT  ::= "input"
OUTPUT  ::= "output"
rossberg commented 3 years ago

Very cute!

Question: I'm a bit confused about all the /*empty*/ productions you inserted everywhere, and which seem to make the grammar incorrect. What are they for?

Also, there appears to be some oversimplification of func_type, which now allows params and results to occur in any order. Similarly with the locals in functions, which can be interleaved with instructions.

mingodad commented 3 years ago

Thank you for pointing this out /*empty*/ this is inserted by the tool I made because in bison grammars any rule that starts with | mean the first option is /*empty*/ and looking throut the grammas now I can see that this is not the case with menhir grammars ?

I'll review the menhir grammar.

mingodad commented 3 years ago

After some work I found easier to replace ::= /*empty*/ | by ::= and then replace ::= | to ::= /*empty*/ |, see the result bellow:

/*
From https://github.com/WebAssembly/exception-handling/blob/master/interpreter/text/parser.mly
*/

name ::=  STRING
string_list ::=  /* empty */ | string_list STRING
ref_kind ::=  FUNC | EXTERN
ref_type ::=  FUNCREF | EXTERNREF
value_type ::=  NUM_TYPE | ref_type
value_type_list ::=  /* empty */ | value_type value_type_list
global_type ::=  value_type | LPAR MUT value_type RPAR
def_type ::=  LPAR FUNC func_type RPAR
func_type ::=  /* empty */ | LPAR RESULT value_type_list RPAR func_type | LPAR PARAM value_type_list RPAR func_type | LPAR PARAM bind_var value_type RPAR func_type
table_type ::=  limits ref_type
memory_type ::=  limits
limits ::=  NAT | NAT NAT
type_use ::=  LPAR TYPE var RPAR
num ::=  NAT | INT | FLOAT
var ::=  NAT | VAR
var_list ::=  /* empty */ | var var_list
bind_var_opt ::=  /* empty */ | bind_var
bind_var ::=  VAR
labeling_opt ::=  /* empty */ | bind_var
labeling_end_opt ::=  /* empty */ | bind_var
offset_opt ::=  /* empty */ | OFFSET_EQ_NAT
align_opt ::=  /* empty */ | ALIGN_EQ_NAT
instr ::=  plain_instr | select_instr_instr | call_instr_instr | block_instr | expr
plain_instr ::=  UNREACHABLE | NOP | DROP | BR var | BR_IF var | BR_TABLE var var_list | RETURN | CALL var | THROW var | RETHROW var | LOCAL_GET var | LOCAL_SET var | LOCAL_TEE var | GLOBAL_GET var | GLOBAL_SET var | TABLE_GET var | TABLE_SET var | TABLE_SIZE var | TABLE_GROW var | TABLE_FILL var | TABLE_COPY var var | TABLE_INIT var var | TABLE_GET | TABLE_SET | TABLE_SIZE | TABLE_GROW | TABLE_FILL | TABLE_COPY | TABLE_INIT var | ELEM_DROP var | LOAD offset_opt align_opt | STORE offset_opt align_opt | MEMORY_SIZE | MEMORY_GROW | MEMORY_FILL | MEMORY_COPY | MEMORY_INIT var | DATA_DROP var | REF_NULL ref_kind | REF_IS_NULL | REF_FUNC var | CONST num | TEST | COMPARE | UNARY | BINARY | CONVERT
select_instr ::=  SELECT select_instr_results
select_instr_results ::=   /* empty */ | LPAR RESULT value_type_list RPAR select_instr_results
select_instr_instr ::=  SELECT select_instr_results_instr
select_instr_results_instr ::=  LPAR RESULT value_type_list RPAR select_instr_results_instr | instr
call_instr ::=  CALL_INDIRECT var call_instr_type | CALL_INDIRECT call_instr_type
call_instr_type ::=  type_use call_instr_params | call_instr_params
call_instr_params ::=  LPAR PARAM value_type_list RPAR call_instr_params | call_instr_results
call_instr_results ::=   /* empty */ | LPAR RESULT value_type_list RPAR call_instr_results
call_instr_instr ::=  CALL_INDIRECT var call_instr_type_instr | CALL_INDIRECT call_instr_type_instr
call_instr_type_instr ::=  type_use call_instr_params_instr | call_instr_params_instr
call_instr_params_instr ::=  LPAR PARAM value_type_list RPAR call_instr_params_instr | call_instr_results_instr
call_instr_results_instr ::=  LPAR RESULT value_type_list RPAR call_instr_results_instr | instr
block_instr ::=  BLOCK labeling_opt block END labeling_end_opt | LOOP labeling_opt block END labeling_end_opt | IF labeling_opt block END labeling_end_opt | IF labeling_opt block ELSE labeling_end_opt instr_list END labeling_end_opt | TRY labeling_opt block handler_instr | TRY labeling_opt block DELEGATE var
block ::=  type_use block_param_body | block_param_body
block_param_body ::=  block_result_body | LPAR PARAM value_type_list RPAR block_param_body
block_result_body ::=  instr_list | LPAR RESULT value_type_list RPAR block_result_body
handler_instr ::=  catch_list_instr END | catch_list_instr catch_all END | catch_all END | END
catch_list_instr ::=  catch catch_list_instr | catch
handler ::=   /* empty */ | catch_list | catch_list LPAR catch_all RPAR | LPAR catch_all RPAR | LPAR DELEGATE var RPAR
catch_list ::=  catch_list LPAR catch RPAR | LPAR catch RPAR
catch ::=  CATCH var instr_list
catch_all ::=  CATCH_ALL instr_list
expr ::=  LPAR expr1 RPAR
expr1 ::=  plain_instr expr_list | SELECT select_expr_results | CALL_INDIRECT var call_expr_type | CALL_INDIRECT call_expr_type | BLOCK labeling_opt block | LOOP labeling_opt block | IF labeling_opt if_block | TRY labeling_opt try_block
select_expr_results ::=  LPAR RESULT value_type_list RPAR select_expr_results | expr_list
call_expr_type ::=  type_use call_expr_params | call_expr_params
call_expr_params ::=  LPAR PARAM value_type_list RPAR call_expr_params | call_expr_results
call_expr_results ::=  LPAR RESULT value_type_list RPAR call_expr_results | expr_list
if_block ::=  type_use if_block_param_body | if_block_param_body
if_block_param_body ::=  if_block_result_body | LPAR PARAM value_type_list RPAR if_block_param_body
if_block_result_body ::=  if_ | LPAR RESULT value_type_list RPAR if_block_result_body
if_ ::=  expr if_ | LPAR THEN instr_list RPAR LPAR ELSE instr_list RPAR | LPAR THEN instr_list RPAR
try_block ::=  type_use try_block_param_body | try_block_param_body
try_block_param_body ::=  try_block_result_body | LPAR PARAM value_type_list RPAR try_block_param_body
try_block_result_body ::=  try_ | LPAR RESULT value_type_list RPAR try_block_result_body
try_ ::=  LPAR DO instr_list RPAR handler
instr_list ::=  /* empty */ | select_instr | call_instr | instr instr_list
expr_list ::=  /* empty */ | expr expr_list
const_expr ::=  instr_list
func ::=  LPAR FUNC bind_var_opt func_fields RPAR
func_fields ::=  type_use func_fields_body | func_fields_body | inline_import type_use func_fields_import | inline_import func_fields_import | inline_export func_fields
func_fields_import ::=  func_fields_import_result | LPAR PARAM value_type_list RPAR func_fields_import | LPAR PARAM bind_var value_type RPAR func_fields_import
func_fields_import_result ::=  /* empty */ | LPAR RESULT value_type_list RPAR func_fields_import_result
func_fields_body ::=  func_result_body | LPAR PARAM value_type_list RPAR func_fields_body | LPAR PARAM bind_var value_type RPAR func_fields_body
func_result_body ::=  func_body | LPAR RESULT value_type_list RPAR func_result_body
func_body ::=  instr_list | LPAR LOCAL value_type_list RPAR func_body | LPAR LOCAL bind_var value_type RPAR func_body
table_use ::=  LPAR TABLE var RPAR
memory_use ::=  LPAR MEMORY var RPAR
offset ::=  LPAR OFFSET const_expr RPAR | expr
elem_kind ::=  FUNC
elem_expr ::=  LPAR ITEM const_expr RPAR | expr
elem_expr_list ::=  /* empty */ | elem_expr elem_expr_list
elem_var_list ::=  var_list
elem_list ::=  elem_kind elem_var_list | ref_type elem_expr_list
elem ::=  LPAR ELEM bind_var_opt elem_list RPAR | LPAR ELEM bind_var_opt table_use offset elem_list RPAR | LPAR ELEM bind_var_opt DECLARE elem_list RPAR | LPAR ELEM bind_var_opt offset elem_list RPAR | LPAR ELEM bind_var_opt offset elem_var_list RPAR
table ::=  LPAR TABLE bind_var_opt table_fields RPAR
table_fields ::=  table_type | inline_import table_type | inline_export table_fields | ref_type LPAR ELEM elem_var_list RPAR | ref_type LPAR ELEM elem_expr elem_expr_list RPAR
data ::=  LPAR DATA bind_var_opt string_list RPAR | LPAR DATA bind_var_opt memory_use offset string_list RPAR | LPAR DATA bind_var_opt offset string_list RPAR
memory ::=  LPAR MEMORY bind_var_opt memory_fields RPAR
memory_fields ::=  memory_type | inline_import memory_type | inline_export memory_fields | LPAR DATA string_list RPAR
event ::=  LPAR EVENT bind_var_opt event_fields RPAR
event_fields ::=  type_use func_type | func_type | inline_import type_use event_fields_import | inline_import event_fields_import | inline_export event_fields
event_fields_import ::=  event_fields_import_result | LPAR PARAM value_type_list RPAR event_fields_import | LPAR PARAM bind_var value_type RPAR event_fields_import
event_fields_import_result ::=  /* empty */ | LPAR RESULT value_type_list RPAR event_fields_import_result
global ::=  LPAR GLOBAL bind_var_opt global_fields RPAR
global_fields ::=  global_type const_expr | inline_import global_type | inline_export global_fields
import_desc ::=  LPAR FUNC bind_var_opt type_use RPAR | LPAR FUNC bind_var_opt func_type RPAR | LPAR TABLE bind_var_opt table_type RPAR | LPAR MEMORY bind_var_opt memory_type RPAR | LPAR EVENT bind_var_opt type_use RPAR | LPAR EVENT bind_var_opt func_type RPAR | LPAR GLOBAL bind_var_opt global_type RPAR
import ::=  LPAR IMPORT name name import_desc RPAR
inline_import ::=  LPAR IMPORT name name RPAR
export_desc ::=  LPAR FUNC var RPAR | LPAR TABLE var RPAR | LPAR MEMORY var RPAR | LPAR EVENT var RPAR | LPAR GLOBAL var RPAR
export ::=  LPAR EXPORT name export_desc RPAR
inline_export ::=  LPAR EXPORT name RPAR
type_ ::=  def_type
type_def ::=  LPAR TYPE type_ RPAR | LPAR TYPE bind_var type_ RPAR
start ::=  LPAR START var RPAR
module_fields ::=  /* empty */ | module_fields1
module_fields1 ::=  type_def module_fields | global module_fields | table module_fields | memory module_fields | event module_fields | func module_fields | elem module_fields | data module_fields | start module_fields | import module_fields | export module_fields
module_var_opt ::=  /* empty */ | VAR
module_ ::=  LPAR MODULE module_var_opt module_fields RPAR
inline_module ::=  module_fields
inline_module1 ::=  module_fields1
script_var_opt ::=  /* empty */ | VAR
script_module ::=  module_ | LPAR MODULE module_var_opt BIN string_list RPAR | LPAR MODULE module_var_opt QUOTE string_list RPAR
action ::=  LPAR INVOKE module_var_opt name const_list RPAR | LPAR GET module_var_opt name RPAR
assertion ::=  LPAR ASSERT_MALFORMED script_module STRING RPAR | LPAR ASSERT_INVALID script_module STRING RPAR | LPAR ASSERT_UNLINKABLE script_module STRING RPAR | LPAR ASSERT_TRAP script_module STRING RPAR | LPAR ASSERT_RETURN action result_list RPAR | LPAR ASSERT_TRAP action STRING RPAR | LPAR ASSERT_EXCEPTION action RPAR | LPAR ASSERT_EXHAUSTION action STRING RPAR
cmd ::=  action | assertion | script_module | LPAR REGISTER name module_var_opt RPAR | meta
cmd_list ::=  /* empty */ | cmd cmd_list
meta ::=  LPAR SCRIPT script_var_opt cmd_list RPAR | LPAR INPUT script_var_opt STRING RPAR | LPAR OUTPUT script_var_opt STRING RPAR | LPAR OUTPUT script_var_opt RPAR
const ::=  LPAR CONST num RPAR | LPAR REF_NULL ref_kind RPAR | LPAR REF_EXTERN NAT RPAR
const_list ::=  /* empty */ | const const_list
result ::=  const | LPAR CONST NAN RPAR | LPAR REF_FUNC RPAR | LPAR REF_EXTERN RPAR
result_list ::=  /* empty */ | result result_list
script ::=  cmd_list EOF | inline_module1 EOF
script1 ::=  cmd
module1 ::=  module_ EOF | inline_module EOF

// Tokens

LPAR  ::= "("
RPAR  ::= ")"

EXTERN  ::= "extern"
EXTERNREF  ::= "externref"
FUNCREF  ::= "funcref"
MUT  ::= "mut"

REF_NULL  ::= "ref.null"
REF_FUNC  ::= "ref.func"
REF_EXTERN  ::= "ref.extern"
REF_IS_NULL  ::= "ref.is_null"

NOP  ::= "nop"
UNREACHABLE  ::= "unreachable"
DROP  ::= "drop"
BLOCK  ::= "block"
LOOP  ::= "loop"
END  ::= "end"
BR  ::= "br"
BR_IF  ::= "br_if"
BR_TABLE  ::= "br_table"
RETURN  ::= "return"
IF  ::= "if"
THEN  ::= "then"
ELSE  ::= "else"
SELECT  ::= "select"
CALL  ::= "call"
CALL_INDIRECT  ::= "call_indirect"

LOCAL_GET  ::= "local.get"
LOCAL_SET  ::= "local.set"
LOCAL_TEE  ::= "local.tee"
GLOBAL_GET  ::= "global.get"
GLOBAL_SET  ::= "global.set"

TABLE_GET  ::= "table.get"
TABLE_SET  ::= "table.set"
TABLE_SIZE  ::= "table.size"
TABLE_GROW  ::= "table.grow"
TABLE_FILL  ::= "table.fill"
TABLE_COPY  ::= "table.copy"
TABLE_INIT  ::= "table.init"
ELEM_DROP  ::= "elem.drop"

MEMORY_SIZE  ::= "memory.size"
MEMORY_GROW  ::= "memory.grow"
MEMORY_FILL  ::= "memory.fill"
MEMORY_COPY  ::= "memory.copy"
MEMORY_INIT  ::= "memory.init"
DATA_DROP  ::= "data.drop"

TRY  ::= "try"
DO  ::= "do"
CATCH  ::= "catch"
CATCH_ALL  ::= "catch_all"
DELEGATE  ::= "delegate"
THROW  ::= "throw"
RETHROW  ::= "rethrow"

TYPE  ::= "type"
FUNC  ::= "func"
START  ::= "start"
PARAM  ::= "param"
RESULT  ::= "result"
LOCAL  ::= "local"
GLOBAL  ::= "global"
TABLE  ::= "table"
MEMORY  ::= "memory"
EVENT  ::= "event"
ELEM  ::= "elem"
DATA  ::= "data"
DECLARE  ::= "declare"
OFFSET  ::= "offset"
ITEM  ::= "item"
IMPORT  ::= "import"
EXPORT  ::= "export"

MODULE  ::= "module"
BIN  ::= "binary"
QUOTE  ::= "quote"

SCRIPT  ::= "script"
REGISTER  ::= "register"
INVOKE  ::= "invoke"
GET  ::= "get"
ASSERT_MALFORMED  ::= "assert_malformed"
ASSERT_INVALID  ::= "assert_invalid"
ASSERT_UNLINKABLE  ::= "assert_unlinkable"
ASSERT_RETURN  ::= "assert_return"
ASSERT_TRAP  ::= "assert_trap"
ASSERT_EXCEPTION  ::= "assert_exception"
ASSERT_EXHAUSTION  ::= "assert_exhaustion"
NAN Script.CanonicalNan  ::= "nan:canonical"
NAN Script.ArithmeticNan  ::= "nan:arithmetic"
INPUT  ::= "input"
OUTPUT  ::= "output"
mingodad commented 3 years ago

Question: I'm a bit confused about all the /*empty*/ productions you inserted everywhere, and which seem to make the grammar incorrect. What are they for?

Also, there appears to be some oversimplification of func_type, which now allows params and results to occur in any order. Similarly with the locals in functions, which can be interleaved with instructions.

I did some fixes (shown on the previous message) could you have a look at it to see if you've pointed out has been resolved ?

One of the objectives of the railroad diagrams is to help visualize the whole grammar and spot possible logical errors.