Add menhir support (#42)

Adds the menhir stanza which desugars into a Rule.t with Support for:
* ocamlyacc like use
* modular parsers with --base
* passing extra flags

Also add tests and documentation.
This commit is contained in:
Rudi Grinberg 2017-04-03 11:25:43 -04:00 committed by Jérémie Dimino
parent 4c3036b3cc
commit 3e3d92c9e5
10 changed files with 146 additions and 1 deletions

View File

@ -501,7 +501,6 @@ dependencies. See the [[User actions][actions section]] for more details.
(deps (<name>.mll))
(action (chdir ${ROOT} (run ${bin:ocamllex} -q -o ${<})))))
#+end_src
**** ocamlyacc
=(ocamlyacc (<names>))= is essentially a shorthand for:
@ -513,6 +512,32 @@ dependencies. See the [[User actions][actions section]] for more details.
(action (chdir ${ROOT} (run ${bin:ocamlyacc} ${<})))))
#+end_src
**** menhir
The basic form for defining menhir parsers (analogous to ocamlyacc):
#+begin_src scheme
(menhir
((modules (<parser1> <parser2> ...))))
#+end_src
Modular parsers (--base flag) can be defined by adding the =merge_into= option.
With this option, a single parser named =base_name= is generated.
#+begin_src scheme
(menhir
((merge_into <base_name>)
(modules (<parser1> <parser2> ...))))
#+end_src
Extra flags can be passed to menhir using the =flags= option:
#+begin_src scheme
(menhir
((flags (<option1> <option2> ...))
(modules (<parser1> <parser2> ...))))
#+end_src
**** alias
The =alias= stanza lets you add dependencies to an alias, or specify

View File

@ -678,6 +678,59 @@ module Rule = struct
let ocamlyacc_vjs = ocamlyacc_v1
end
module Menhir = struct
type t =
{ base : string option
; flags : String_with_vars.t list
; modules: string list
}
let v1 =
record
(field_o "merge_into" string >>= fun base ->
field "flags" (list String_with_vars.t) ~default:[] >>= fun flags ->
field "modules" (list string) >>= fun modules ->
return
{ base
; flags
; modules
}
)
let v1_to_rule t =
let str s = String_with_vars.of_string s in
let targets n = [n ^ ".ml"; n ^ ".mli"] in
match t.base with
| None ->
List.map t.modules ~f:(fun name ->
let src = name ^ ".mly" in
{ Rule.
targets = targets name
; deps = [Dep_conf.File (str src)]
; action =
Chdir
(str "${ROOT}",
Run (str "${bin:menhir}",
t.flags @ [str "${<}"]))
})
| Some base ->
let mly m = str (m ^ ".mly") in
[{ Rule.
targets = targets base
; deps = List.map ~f:(fun m -> Dep_conf.File (mly m)) t.modules
; action =
Chdir
(str "${ROOT}",
Run (str "${bin:menhir}",
[ str "--base"
; str base
]
@ t.flags
@ (List.map ~f:mly t.modules))
)
}]
end
module Provides = struct
type t =
{ name : string
@ -752,6 +805,7 @@ module Stanza = struct
; cstr "rule" (Rule.v1 @> nil) (fun x -> [Rule x])
; cstr "ocamllex" (list string @> nil) (fun x -> rules (Rule.ocamllex_v1 x))
; cstr "ocamlyacc" (list string @> nil) (fun x -> rules (Rule.ocamlyacc_v1 x))
; cstr "menhir" (Menhir.v1 @> nil) (fun x -> rules (Menhir.v1_to_rule x))
; cstr "install" (Install_conf.v1 @> nil) (fun x -> [Install x])
; cstr "alias" (Alias_conf.v1 @> nil) (fun x -> [Alias x])
(* Just for validation and error messages *)

View File

@ -26,6 +26,14 @@
(run ${exe:run.exe} --
${bin:jbuilder} build -j1 @install --root . --debug-dependency-path)))))
(alias
((name runtest)
(deps ((files_recursively_in workspaces/menhir)))
(action
(chdir workspaces/menhir
(run ${exe:run.exe} --
${bin:jbuilder} build -j1 test.exe --root . --debug-dependency-path)))))
;; This test define an installed "plop" with a "plop.ca-marche-pas"
;; sub-package which depend on a library that doesn't exist.
;;

View File

@ -0,0 +1,14 @@
(jbuild_version 1)
(ocamllex (lexer1 lexer2))
(menhir
((modules (test_menhir1))))
(menhir
((merge_into test_base)
(flags (--explain))
(modules (tokens parser))))
(executables
((names (test))))

View File

@ -0,0 +1,7 @@
{
open Test_menhir1
}
rule lex = parse
| 'c' { TOKEN 'c' }
| eof { EOF }

View File

@ -0,0 +1,7 @@
{
open Test_base
}
rule lex = parse
| 'c' { TOKEN 'c' }
| eof { EOF }

View File

@ -0,0 +1,7 @@
%start <char list> main
%%
main:
| c = TOKEN EOF { [c] }
| c = TOKEN xs = main { c :: xs }

View File

@ -0,0 +1,8 @@
let s = "foo bar baz"
let () =
let lex1 = Lexing.from_string s in
let lex2 = Lexing.from_string s in
ignore (Test_menhir1.main Lexer1.lex lex1);
ignore (Test_base.main Lexer2.lex lex2)

View File

@ -0,0 +1,10 @@
%token <char> TOKEN
%token EOF
%start <char list> main
%%
main:
| c = TOKEN EOF { [c] }
| c = TOKEN xs = main { c :: xs }

View File

@ -0,0 +1,5 @@
%token <char> TOKEN
%token EOF
%%