diff --git a/doc/manual.org b/doc/manual.org index 7e8de4dd..68ecc024 100644 --- a/doc/manual.org +++ b/doc/manual.org @@ -501,7 +501,6 @@ dependencies. See the [[User actions][actions section]] for more details. (deps (.mll)) (action (chdir ${ROOT} (run ${bin:ocamllex} -q -o ${<}))))) #+end_src - **** ocamlyacc =(ocamlyacc ())= 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 ( ...)))) +#+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 ) + (modules ( ...)))) +#+end_src + +Extra flags can be passed to menhir using the =flags= option: + +#+begin_src scheme +(menhir + ((flags ( ...)) + (modules ( ...)))) +#+end_src + **** alias The =alias= stanza lets you add dependencies to an alias, or specify diff --git a/src/jbuild_types.ml b/src/jbuild_types.ml index 15894e8d..d2f29be4 100644 --- a/src/jbuild_types.ml +++ b/src/jbuild_types.ml @@ -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 *) diff --git a/test/jbuild b/test/jbuild index 63a86135..03827928 100644 --- a/test/jbuild +++ b/test/jbuild @@ -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. ;; diff --git a/test/workspaces/menhir/jbuild b/test/workspaces/menhir/jbuild new file mode 100644 index 00000000..3853ca55 --- /dev/null +++ b/test/workspaces/menhir/jbuild @@ -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)))) \ No newline at end of file diff --git a/test/workspaces/menhir/lexer1.mll b/test/workspaces/menhir/lexer1.mll new file mode 100644 index 00000000..bddb6a0b --- /dev/null +++ b/test/workspaces/menhir/lexer1.mll @@ -0,0 +1,7 @@ +{ +open Test_menhir1 +} + +rule lex = parse + | 'c' { TOKEN 'c' } + | eof { EOF } diff --git a/test/workspaces/menhir/lexer2.mll b/test/workspaces/menhir/lexer2.mll new file mode 100644 index 00000000..7f20f175 --- /dev/null +++ b/test/workspaces/menhir/lexer2.mll @@ -0,0 +1,7 @@ +{ +open Test_base +} + +rule lex = parse + | 'c' { TOKEN 'c' } + | eof { EOF } diff --git a/test/workspaces/menhir/parser.mly b/test/workspaces/menhir/parser.mly new file mode 100644 index 00000000..a5691165 --- /dev/null +++ b/test/workspaces/menhir/parser.mly @@ -0,0 +1,7 @@ +%start main + +%% + +main: +| c = TOKEN EOF { [c] } +| c = TOKEN xs = main { c :: xs } diff --git a/test/workspaces/menhir/test.ml b/test/workspaces/menhir/test.ml new file mode 100644 index 00000000..c43e2fca --- /dev/null +++ b/test/workspaces/menhir/test.ml @@ -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) diff --git a/test/workspaces/menhir/test_menhir1.mly b/test/workspaces/menhir/test_menhir1.mly new file mode 100644 index 00000000..8f5bb7ee --- /dev/null +++ b/test/workspaces/menhir/test_menhir1.mly @@ -0,0 +1,10 @@ +%token TOKEN +%token EOF + +%start main + +%% + +main: +| c = TOKEN EOF { [c] } +| c = TOKEN xs = main { c :: xs } diff --git a/test/workspaces/menhir/tokens.mly b/test/workspaces/menhir/tokens.mly new file mode 100644 index 00000000..dbc3e6e1 --- /dev/null +++ b/test/workspaces/menhir/tokens.mly @@ -0,0 +1,5 @@ +%token TOKEN +%token EOF + +%% +