Skip to content

Commit 4f69eee

Browse files
authored
[interpreter] Switch to menhir (WebAssembly#1705)
1 parent 3be4c2f commit 4f69eee

File tree

10 files changed

+165
-130
lines changed

10 files changed

+165
-130
lines changed

interpreter/dune

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
; Wasm REPL every time in all the dependencies.
77
; We exclude the 'wast' module as it is only used for the JS build.
88
; 'smallint' is a separate test module.
9-
(modules :standard \ main wasm smallint wast))
9+
(modules :standard \ main wasm smallint wast)
10+
(libraries menhirLib))
1011

1112
(executable
1213
(public_name wasm)
@@ -43,7 +44,7 @@
4344
(chdir
4445
%{workspace_root}
4546
(run %{bin:ocamllex} -ml -q -o %{target} %{deps}))))
46-
(ocamlyacc
47+
(menhir
4748
(modules parser)))
4849

4950
(env

interpreter/dune-project

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
(name wasm)
44

55
(generate_opam_files true)
6+
(using menhir 2.1)
7+
(implicit_transitive_deps false)
68

79
(license Apache-2.0)
810

@@ -17,4 +19,5 @@
1719
(synopsis "Library to read and write WebAssembly (Wasm) files and manipulate their AST")
1820
(tags (wasm webassembly spec interpreter))
1921
(depends
20-
(ocaml (>= 4.12))))
22+
(ocaml (>= 4.12))
23+
(menhir (>= 20220210))))

interpreter/jslib/wast.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
open Wasm
55
open Js_of_ocaml
66

7-
let _ =
7+
let () =
88
Js.export "WebAssemblyText"
99
(object%js (_self)
1010

1111
method encode (s : Js.js_string Js.t) : (Typed_array.arrayBuffer Js.t) =
12-
let def = Parse.string_to_module (Js.to_string s) in
12+
let _, def = Parse.Module.from_string (Js.to_string s) in
1313
let bs =
1414
match def.Source.it with
1515
| Script.Textual m -> (Encode.encode m)

interpreter/script/js.ml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,10 @@ let rec of_definition def =
526526
| Textual m -> of_bytes (Encode.encode m)
527527
| Encoded (_, bs) -> of_bytes bs
528528
| Quoted (_, s) ->
529-
try of_definition (Parse.string_to_module s) with Parse.Syntax _ ->
529+
try
530+
let _v, m = Parse.Module.from_string s in
531+
of_definition m
532+
with Script.Syntax _ ->
530533
of_bytes "<malformed quote>"
531534

532535
let of_wrapper mods x_opt name wrap_action wrap_assertion at =
@@ -594,7 +597,7 @@ let of_command mods cmd =
594597
match def.it with
595598
| Textual m -> m
596599
| Encoded (_, bs) -> Decode.decode "binary" bs
597-
| Quoted (_, s) -> unquote (Parse.string_to_module s)
600+
| Quoted (_, s) -> unquote (snd (Parse.Module.from_string s))
598601
in bind mods x_opt (unquote def);
599602
"let " ^ current_var mods ^ " = instance(" ^ of_definition def ^ ");\n" ^
600603
(if x_opt = None then "" else

interpreter/script/run.ml

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ let input_from get_script run =
105105
true
106106
with
107107
| Decode.Code (at, msg) -> error at "decoding error" msg
108-
| Parse.Syntax (at, msg) -> error at "syntax error" msg
108+
| Syntax (at, msg) -> error at "syntax error" msg
109109
| Valid.Invalid (at, msg) -> error at "invalid module" msg
110110
| Import.Unknown (at, msg) -> error at "link failure" msg
111111
| Eval.Link (at, msg) -> error at "link failure" msg
@@ -118,17 +118,26 @@ let input_from get_script run =
118118
| Assert (at, msg) -> error at "assertion failure" msg
119119
| Abort _ -> false
120120

121-
let input_script start name lexbuf run =
122-
input_from (fun _ -> Parse.parse name lexbuf start) run
121+
let input_script name lexbuf run =
122+
input_from (fun () ->
123+
Lexing.set_filename lexbuf name;
124+
Parse.Script.from_lexbuf lexbuf)
125+
run
126+
127+
let input_script1 name lexbuf run =
128+
input_from (fun () ->
129+
Lexing.set_filename lexbuf name;
130+
Parse.Script1.from_lexbuf lexbuf)
131+
run
123132

124133
let input_sexpr name lexbuf run =
125-
input_from (fun _ ->
126-
let var_opt, def = Parse.parse name lexbuf Parse.Module in
134+
input_from (fun () ->
135+
let var_opt, def = Parse.Module.from_lexbuf lexbuf in
127136
[Module (var_opt, def) @@ no_region]) run
128137

129138
let input_binary name buf run =
130139
let open Source in
131-
input_from (fun _ ->
140+
input_from (fun () ->
132141
[Module (None, Encoded (name, buf) @@ no_region) @@ no_region]) run
133142

134143
let input_sexpr_file input file run =
@@ -162,16 +171,16 @@ let input_file file run =
162171
dispatch_file_ext
163172
input_binary_file
164173
(input_sexpr_file input_sexpr)
165-
(input_sexpr_file (input_script Parse.Script))
166-
(input_sexpr_file (input_script Parse.Script))
174+
(input_sexpr_file input_script)
175+
(input_sexpr_file input_script)
167176
input_js_file
168177
file run
169178

170179
let input_string string run =
171180
trace ("Running (\"" ^ String.escaped string ^ "\")...");
172181
let lexbuf = Lexing.from_string string in
173182
trace "Parsing...";
174-
input_script Parse.Script "string" lexbuf run
183+
input_script "string" lexbuf run
175184

176185

177186
(* Interactive *)
@@ -195,7 +204,7 @@ let lexbuf_stdin buf len =
195204
let input_stdin run =
196205
let lexbuf = Lexing.from_function lexbuf_stdin in
197206
let rec loop () =
198-
let success = input_script Parse.Script1 "stdin" lexbuf run in
207+
let success = input_script1 "stdin" lexbuf run in
199208
if not success then Lexing.flush_input lexbuf;
200209
if Lexing.(lexbuf.lex_curr_pos >= lexbuf.lex_buffer_len - 1) then
201210
continuing := false;
@@ -337,7 +346,7 @@ let rec run_definition def : Ast.module_ =
337346
Decode.decode name bs
338347
| Quoted (_, s) ->
339348
trace "Parsing quote...";
340-
let def' = Parse.string_to_module s in
349+
let _, def' = Parse.Module.from_string s in
341350
run_definition def'
342351

343352
let run_action act : Values.value list =
@@ -443,7 +452,7 @@ let run_assertion ass =
443452
trace "Asserting malformed...";
444453
(match ignore (run_definition def) with
445454
| exception Decode.Code (_, msg) -> assert_message ass.at "decoding" msg re
446-
| exception Parse.Syntax (_, msg) -> assert_message ass.at "parsing" msg re
455+
| exception Syntax (_, msg) -> assert_message ass.at "parsing" msg re
447456
| _ -> Assert.error ass.at "expected decoding/parsing error"
448457
)
449458

interpreter/text/arrange.ml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -679,21 +679,25 @@ let definition mode x_opt def =
679679
match def.it with
680680
| Textual m -> m
681681
| Encoded (_, bs) -> Decode.decode "" bs
682-
| Quoted (_, s) -> unquote (Parse.string_to_module s)
682+
| Quoted (_, s) ->
683+
let _v, m = Parse.Module.from_string s in
684+
unquote m
683685
in module_with_var_opt x_opt (unquote def)
684686
| `Binary ->
685687
let rec unquote def =
686688
match def.it with
687689
| Textual m -> Encode.encode m
688690
| Encoded (_, bs) -> Encode.encode (Decode.decode "" bs)
689-
| Quoted (_, s) -> unquote (Parse.string_to_module s)
691+
| Quoted (_, s) ->
692+
let _v, m = Parse.Module.from_string s in
693+
unquote m
690694
in binary_module_with_var_opt x_opt (unquote def)
691695
| `Original ->
692696
match def.it with
693697
| Textual m -> module_with_var_opt x_opt m
694698
| Encoded (_, bs) -> binary_module_with_var_opt x_opt bs
695699
| Quoted (_, s) -> quoted_module_with_var_opt x_opt s
696-
with Parse.Syntax _ ->
700+
with Script.Syntax _ ->
697701
quoted_module_with_var_opt x_opt "<invalid module>"
698702

699703
let access x_opt n =

interpreter/text/parse.ml

Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,60 @@
1-
type 'a start =
2-
| Module : (Script.var option * Script.definition) start
3-
| Script : Script.script start
4-
| Script1 : Script.script start
5-
6-
exception Syntax = Script.Syntax
7-
8-
let parse' name lexbuf start =
9-
lexbuf.Lexing.lex_curr_p <-
10-
{lexbuf.Lexing.lex_curr_p with Lexing.pos_fname = name};
11-
try start Lexer.token lexbuf
12-
with Syntax (region, s) ->
13-
let region' = if region <> Source.no_region then region else
14-
{Source.left = Lexer.convert_pos lexbuf.Lexing.lex_start_p;
15-
Source.right = Lexer.convert_pos lexbuf.Lexing.lex_curr_p} in
16-
raise (Syntax (region', s))
17-
18-
let parse (type a) name lexbuf : a start -> a = function
19-
| Module -> parse' name lexbuf Parser.module1
20-
| Script -> parse' name lexbuf Parser.script
21-
| Script1 -> parse' name lexbuf Parser.script1
22-
23-
let string_to start s =
24-
let lexbuf = Lexing.from_string s in
25-
parse "string" lexbuf start
26-
27-
let string_to_script s = string_to Script s
28-
let string_to_module s = snd (string_to Module s)
1+
module Make (M : sig
2+
type t
3+
4+
val rule : (Lexing.lexbuf -> Parser.token) -> Lexing.lexbuf -> t
5+
6+
end) = struct
7+
8+
type nonrec t = M.t
9+
10+
let from_lexbuf =
11+
let parser = MenhirLib.Convert.Simplified.traditional2revised M.rule in
12+
fun buf ->
13+
let provider () =
14+
let tok = Lexer.token buf in
15+
let start = Lexing.lexeme_start_p buf in
16+
let stop = Lexing.lexeme_end_p buf in
17+
tok, start, stop
18+
in
19+
try parser provider with
20+
| Parser.Error ->
21+
let left = Lexer.convert_pos buf.Lexing.lex_start_p in
22+
let right = Lexer.convert_pos buf.Lexing.lex_curr_p in
23+
let region = { Source.left; right } in
24+
raise (Script.Syntax (region, "unexpected token"))
25+
| Script.Syntax (region, s) as exn ->
26+
if region <> Source.no_region then raise exn
27+
else
28+
let region' = {
29+
Source.left = Lexer.convert_pos buf.Lexing.lex_start_p;
30+
Source.right = Lexer.convert_pos buf.Lexing.lex_curr_p }
31+
in
32+
raise (Script.Syntax (region', s))
33+
34+
let from_file filename =
35+
let chan = open_in filename in
36+
Fun.protect ~finally:(fun () -> close_in chan)
37+
(fun () ->
38+
let lb = Lexing.from_channel ~with_positions:true chan in
39+
Lexing.set_filename lb filename;
40+
from_lexbuf lb)
41+
42+
let from_string s = from_lexbuf (Lexing.from_string ~with_positions:true s)
43+
44+
let from_channel c = from_lexbuf (Lexing.from_channel ~with_positions:true c)
45+
end
46+
47+
module Module = Make (struct
48+
type t = Script.var option * Script.definition
49+
let rule = Parser.module1
50+
end)
51+
52+
module Script1 = Make (struct
53+
type t = Script.script
54+
let rule = Parser.script1
55+
end)
56+
57+
module Script = Make (struct
58+
type t = Script.script
59+
let rule = Parser.script
60+
end)

interpreter/text/parse.mli

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
1-
type 'a start =
2-
| Module : (Script.var option * Script.definition) start
3-
| Script : Script.script start
4-
| Script1 : Script.script start
1+
module Module : sig
2+
type t = Script.var option * Script.definition
3+
val from_lexbuf : Lexing.lexbuf -> t
4+
val from_file : string -> t
5+
val from_string : string -> t
6+
val from_channel : in_channel -> t
7+
end
58

6-
exception Syntax of Source.region * string
9+
module Script1 : sig
10+
type t = Script.script
11+
val from_lexbuf : Lexing.lexbuf -> t
12+
val from_file : string -> t
13+
val from_string : string -> t
14+
val from_channel : in_channel -> t
15+
end
716

8-
val parse : string -> Lexing.lexbuf -> 'a start -> 'a (* raises Syntax *)
9-
10-
val string_to_script : string -> Script.script (* raises Syntax *)
11-
val string_to_module : string -> Script.definition (* raises Syntax *)
17+
module Script : sig
18+
type t = Script.script
19+
val from_lexbuf : Lexing.lexbuf -> t
20+
val from_file : string -> t
21+
val from_string : string -> t
22+
val from_channel : in_channel -> t
23+
end

0 commit comments

Comments
 (0)