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:
parent
4c3036b3cc
commit
3e3d92c9e5
|
@ -501,7 +501,6 @@ dependencies. See the [[User actions][actions section]] for more details.
|
||||||
(deps (<name>.mll))
|
(deps (<name>.mll))
|
||||||
(action (chdir ${ROOT} (run ${bin:ocamllex} -q -o ${<})))))
|
(action (chdir ${ROOT} (run ${bin:ocamllex} -q -o ${<})))))
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
**** ocamlyacc
|
**** ocamlyacc
|
||||||
|
|
||||||
=(ocamlyacc (<names>))= is essentially a shorthand for:
|
=(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} ${<})))))
|
(action (chdir ${ROOT} (run ${bin:ocamlyacc} ${<})))))
|
||||||
#+end_src
|
#+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
|
**** alias
|
||||||
|
|
||||||
The =alias= stanza lets you add dependencies to an alias, or specify
|
The =alias= stanza lets you add dependencies to an alias, or specify
|
||||||
|
|
|
@ -678,6 +678,59 @@ module Rule = struct
|
||||||
let ocamlyacc_vjs = ocamlyacc_v1
|
let ocamlyacc_vjs = ocamlyacc_v1
|
||||||
end
|
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
|
module Provides = struct
|
||||||
type t =
|
type t =
|
||||||
{ name : string
|
{ name : string
|
||||||
|
@ -752,6 +805,7 @@ module Stanza = struct
|
||||||
; cstr "rule" (Rule.v1 @> nil) (fun x -> [Rule x])
|
; cstr "rule" (Rule.v1 @> nil) (fun x -> [Rule x])
|
||||||
; cstr "ocamllex" (list string @> nil) (fun x -> rules (Rule.ocamllex_v1 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 "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 "install" (Install_conf.v1 @> nil) (fun x -> [Install x])
|
||||||
; cstr "alias" (Alias_conf.v1 @> nil) (fun x -> [Alias x])
|
; cstr "alias" (Alias_conf.v1 @> nil) (fun x -> [Alias x])
|
||||||
(* Just for validation and error messages *)
|
(* Just for validation and error messages *)
|
||||||
|
|
|
@ -26,6 +26,14 @@
|
||||||
(run ${exe:run.exe} --
|
(run ${exe:run.exe} --
|
||||||
${bin:jbuilder} build -j1 @install --root . --debug-dependency-path)))))
|
${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"
|
;; This test define an installed "plop" with a "plop.ca-marche-pas"
|
||||||
;; sub-package which depend on a library that doesn't exist.
|
;; sub-package which depend on a library that doesn't exist.
|
||||||
;;
|
;;
|
||||||
|
|
|
@ -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))))
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
open Test_menhir1
|
||||||
|
}
|
||||||
|
|
||||||
|
rule lex = parse
|
||||||
|
| 'c' { TOKEN 'c' }
|
||||||
|
| eof { EOF }
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
open Test_base
|
||||||
|
}
|
||||||
|
|
||||||
|
rule lex = parse
|
||||||
|
| 'c' { TOKEN 'c' }
|
||||||
|
| eof { EOF }
|
|
@ -0,0 +1,7 @@
|
||||||
|
%start <char list> main
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
main:
|
||||||
|
| c = TOKEN EOF { [c] }
|
||||||
|
| c = TOKEN xs = main { c :: xs }
|
|
@ -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)
|
|
@ -0,0 +1,10 @@
|
||||||
|
%token <char> TOKEN
|
||||||
|
%token EOF
|
||||||
|
|
||||||
|
%start <char list> main
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
main:
|
||||||
|
| c = TOKEN EOF { [c] }
|
||||||
|
| c = TOKEN xs = main { c :: xs }
|
|
@ -0,0 +1,5 @@
|
||||||
|
%token <char> TOKEN
|
||||||
|
%token EOF
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
Loading…
Reference in New Issue