Commit 0eecba45 authored by POTTIER Francois's avatar POTTIER Francois

Implement --infer-write-query and --infer-read-reply.

Remove the warning that recommends setting --infer when %inline is used.
parent 328721f1
......@@ -115,17 +115,12 @@ let () =
(* ------------------------------------------------------------------------- *)
(* If [--infer] was specified on the command line, perform type inference.
The OCaml type of every nonterminal symbol is then known. *)
(* If [--depend] or [--raw-depend] was specified on the command line,
perform dependency analysis and stop. *)
let () =
match Settings.depend with
| Settings.OMRaw
| Settings.OMPostprocess ->
Infer.depend grammar (* never returns *)
| Settings.OMNone ->
()
(* The purpose of [--depend] and [--raw-depend] is to support [--infer].
Indeed, [--infer] is implemented by producing a mock [.ml] file (which
contains just the semantic actions) and invoking [ocamlc]. This requires
......@@ -135,35 +130,33 @@ let () =
[.mli] file, even though in principle it should be unnecessary -- see
comment in [nonterminalType.mli]. *)
(* ------------------------------------------------------------------------- *)
(* If some flags imply that we will NOT produce an OCaml parser, then there
is no need to perform type inference, so we act as if --infer was absent.
This saves time and dependency nightmares. *)
let skipping_parser_generation =
Settings.coq ||
Settings.compile_errors <> None ||
Settings.interpret_error ||
Settings.list_errors ||
Settings.compare_errors <> None ||
Settings.update_errors <> None ||
Settings.echo_errors <> None ||
false
(* maybe also: [preprocess_mode <> PMNormal] *)
(* ------------------------------------------------------------------------- *)
(* If [--infer] was specified on the command line, perform type inference.
The OCaml type of every nonterminal is then known. *)
let grammar =
if Settings.infer && not skipping_parser_generation then
let grammar = Infer.infer grammar in
Time.tick "Inferring types for nonterminals";
grammar
else
grammar
(* If [--infer-write-query] was specified on the command line, write a
mock [.ml] file and stop. It is then up to the user (or build system)
to invoke [ocamlc -i] on this file, so as to do type inference. *)
(* If [--infer-read-reply] was specified on the command line, read the
inferred [.mli] file. The OCaml type of every nonterminal symbol is
then known, just as with [--infer]. *)
let grammar, ocaml_types_have_been_checked =
Settings.(match infer with
| IMNone ->
grammar, false
| IMInfer ->
let grammar = Infer.infer grammar in
Time.tick "Inferring types for nonterminals";
grammar, true
| IMDependRaw ->
Infer.depend false grammar (* never returns *)
| IMDependPostprocess ->
Infer.depend true grammar (* never returns *)
| IMWriteQuery filename ->
Infer.write_query filename grammar (* never returns *)
| IMReadReply filename ->
let grammar = Infer.read_reply filename grammar in
Time.tick "Reading inferred types for nonterminals";
grammar, true
)
(* ------------------------------------------------------------------------- *)
......@@ -179,13 +172,10 @@ let grammar =
let grammar =
if Settings.inline then begin
let grammar, inlined =
NonTerminalDefinitionInlining.inline grammar
in
if not Settings.infer && inlined && not skipping_parser_generation then
Error.warning []
"you are using the standard library and/or the %%inline keyword. We\n\
recommend switching on --infer in order to avoid obscure type error messages.";
let grammar, (_ : bool) = NonTerminalDefinitionInlining.inline grammar in
(* 2018/05/23 Removed the warning that was issued when %inline was used
but --infer was turned off. Most people should use ocamlbuild or dune
anyway. *)
Time.tick "Inlining";
grammar
end
......
......@@ -20,3 +20,12 @@
val grammar: UnparameterizedSyntax.grammar
(* This flag tells whether the semantic actions have been type-checked. It is
set if and only if either [--infer] or [--infer-read-reply] is in use. Note
that the presence of a %type declaration for every nonterminal symbol is
*not* sufficient for this flag to be set. Note also that, when
[--infer-read-reply] is set, it could be the case that we have an
out-of-date inferred [.mli] file, so the semantic actions could still be
ill-typed. (The user is then at fault.) *)
val ocaml_types_have_been_checked: bool
......@@ -37,7 +37,8 @@ let encode symbol =
let decode s =
let n = String.length s in
assert (n >= 3 && String.sub s 0 3 = "xv_");
if not (n >= 3 && String.sub s 0 3 = "xv_") then
Lexmli.fail();
String.sub s 3 (n - 3)
(* The name of the temporary file. *)
......@@ -207,11 +208,11 @@ let program grammar =
(* ------------------------------------------------------------------------- *)
(* Writing the program associated with a grammar to a file. *)
let write grammar () =
let ml = open_out mlname in
let write grammar filename () =
let ml = open_out filename in
let module P = Printer.Make (struct
let f = ml
let locate_stretches = Some mlname
let locate_stretches = Some filename
end) in
P.program (program grammar);
close_out ml
......@@ -225,7 +226,7 @@ type entry =
type line =
entry (* target *) * entry list (* dependencies *)
let depend grammar =
let depend postprocess grammar =
(* Create an [.ml] file and an [.mli] file, then invoke ocamldep to
compute dependencies for us. *)
......@@ -244,7 +245,7 @@ let depend grammar =
Option.project (
IO.moving_away mlname (fun () ->
IO.moving_away mliname (fun () ->
IO.with_file mlname (write grammar) (fun () ->
IO.with_file mlname (write grammar mlname) (fun () ->
IO.with_file mliname (Interface.write grammar) (fun () ->
IO.invoke ocamldep_command
)))))
......@@ -259,49 +260,42 @@ let depend grammar =
postprocessing of [ocamldep]'s output. For normal [make] users, who use
[--depend], some postprocessing is required, which is performed below. *)
begin match Settings.depend with
| Settings.OMNone ->
assert false (* we wouldn't be here in the first place *)
| Settings.OMRaw ->
()
| Settings.OMPostprocess ->
(* Make sense out of ocamldep's output. *)
let lexbuf = Lexing.from_string output in
let lines : line list =
try
Lexdep.main lexbuf
with Lexdep.Error msg ->
(* Echo the error message, followed with ocamldep's output. *)
Error.error [] "%s" (msg ^ output)
in
(* Look for the line that concerns the [.cmo] target, and echo a
modified version of this line, where the [.cmo] target is
replaced with [.ml] and [.mli] targets, and where the dependency
over the [.cmi] file is dropped.
In doing so, we assume that the user's [Makefile] supports
bytecode compilation, so that it makes sense to request [bar.cmo]
to be built, as opposed to [bar.cmx]. This is not optimal, but
will do. [camldep] exhibits the same behavior. *)
(* TEMPORARY allow ocamldep to be called with flag -native. *)
List.iter (fun ((_, target_filename), dependencies) ->
if Filename.check_suffix target_filename ".cmo" then
let dependencies = List.filter (fun (basename, _) ->
basename <> base
) dependencies in
if List.length dependencies > 0 then begin
Printf.printf "%s.ml %s.mli:" base base;
List.iter (fun (_basename, filename) ->
Printf.printf " %s" filename
) dependencies;
Printf.printf "\n%!"
end
) lines
if postprocess then begin
(* Make sense out of ocamldep's output. *)
let lexbuf = Lexing.from_string output in
let lines : line list =
try
Lexdep.main lexbuf
with Lexdep.Error msg ->
(* Echo the error message, followed with ocamldep's output. *)
Error.error [] "%s" (msg ^ output)
in
(* Look for the line that concerns the [.cmo] target, and echo a
modified version of this line, where the [.cmo] target is
replaced with [.ml] and [.mli] targets, and where the dependency
over the [.cmi] file is dropped.
In doing so, we assume that the user's [Makefile] supports
bytecode compilation, so that it makes sense to request [bar.cmo]
to be built, as opposed to [bar.cmx]. This is not optimal, but
will do. [camldep] exhibits the same behavior. *)
List.iter (fun ((_, target_filename), dependencies) ->
if Filename.check_suffix target_filename ".cmo" then
let dependencies = List.filter (fun (basename, _) ->
basename <> base
) dependencies in
if List.length dependencies > 0 then begin
Printf.printf "%s.ml %s.mli:" base base;
List.iter (fun (_basename, filename) ->
Printf.printf " %s" filename
) dependencies;
Printf.printf "\n%!"
end
) lines
end;
......@@ -310,30 +304,11 @@ let depend grammar =
exit 0
(* ------------------------------------------------------------------------- *)
(* Inferring types for a grammar's nonterminals. *)
let infer grammar =
(* Invoke ocamlc to do type inference for us. *)
let ocamlc_command =
Printf.sprintf "%s -c -i %s" Settings.ocamlc (Filename.quote mlname)
in
(* Augmenting a grammar with inferred type information. *)
let output =
write grammar ();
match IO.invoke ocamlc_command with
| Some result ->
Sys.remove mlname;
result
| None ->
(* 2015/10/05: intentionally do not remove the [.ml] file if [ocamlc]
fails. (Or if an exception is thrown.) We cannot understand why
[ocaml] complains if we can't see the [.ml] file. *)
exit 1
in
(* The parameter [output] is supposed to contain the output of [ocamlc -i]. *)
(* Make sense out of ocamlc's output. *)
let read_reply (output : string) grammar =
let env : (string * int * int) list =
Lexmli.main (Lexing.from_string output)
......@@ -353,7 +328,10 @@ let infer grammar =
try
List.assoc (Misc.normalize symbol) env
with Not_found ->
assert false
(* No type information was inferred for this symbol.
Perhaps the mock [.ml] file or the inferred [.mli] file
are out of date. Fail gracefully. *)
Error.error [] "found no inferred type for %s." symbol
in
if StringMap.mem symbol grammar.types then
(* If there was a declared type, keep it. *)
......@@ -365,3 +343,42 @@ let infer grammar =
in
{ grammar with types = types }
(* ------------------------------------------------------------------------- *)
(* Inferring types for a grammar's nonterminals. *)
let infer grammar =
(* Invoke ocamlc to do type inference for us. *)
let ocamlc_command =
Printf.sprintf "%s -c -i %s" Settings.ocamlc (Filename.quote mlname)
in
let output =
write grammar mlname ();
match IO.invoke ocamlc_command with
| Some result ->
Sys.remove mlname;
result
| None ->
(* 2015/10/05: intentionally do not remove the [.ml] file if [ocamlc]
fails. (Or if an exception is thrown.) *)
exit 1
in
(* Make sense out of ocamlc's output. *)
read_reply output grammar
(* ------------------------------------------------------------------------- *)
let write_query filename grammar =
write grammar filename ();
exit 0
(* ------------------------------------------------------------------------- *)
let read_reply filename grammar =
read_reply (IO.read_whole_file filename) grammar
......@@ -11,6 +11,8 @@
(* *)
(******************************************************************************)
open UnparameterizedSyntax
(* [ntvar symbol] is the name of the type variable associated with a
nonterminal symbol. *)
......@@ -20,11 +22,24 @@ val ntvar: string -> string
grammar, augmented with a [%type] declaration for every nonterminal
symbol. The [ocamlc] compiler is used to infer types. *)
val infer: UnparameterizedSyntax.grammar -> UnparameterizedSyntax.grammar
val infer: grammar -> grammar
(* [depend postprocess grammar] prints (on the standard output channel) the
OCaml dependencies induced by the semantic actions. If [postprocess] is
[true], then ocamldep's output is postprocessed, otherwise it is echoed
unchanged. This function does not return; it terminates the program. *)
val depend: bool -> grammar -> 'never_returns
(* [write_query filename grammar] writes the grammar's semantic actions to a
mock [.ml] file named [filename]. This file can then be submitted to
[ocamlc] for type inference. See [--infer-write-query <filename>] in the
manual. *)
(* [depend grammar] prints (on the standard output channel) the
OCaml dependencies induced by the semantic actions.
Then, it exits the program. *)
val write_query: string -> grammar -> 'never_returns
val depend: UnparameterizedSyntax.grammar -> 'a
(* [read_reply filename grammar] reads the types inferred by OCaml for the
mock [.ml] file described above, and returns a new grammar, augmented with
a [%type] declaration for every nonterminal symbol. *)
val read_reply: string -> grammar -> grammar
......@@ -186,20 +186,19 @@ let inline_valdefs (defs : valdef list) : valdef list =
(* The heart of the inliner: rewriting a function call to a [let]
expression.
If there was a type annotation at the function definition site,
it is dropped, provided [--infer] was enabled. Otherwise, it is
kept, because, due to the presence of [EMagic] expressions in the
code, dropping a type annotation could cause an ill-typed program
to become apparently well-typed. Keeping a type annotation
requires taking a fresh instance of the type scheme, because
OCaml doesn't have support for locally and existentially bound
type variables. Yuck. *)
If there was a type annotation at the function definition site, it is
dropped, provided the semantic actions have been type-checked. Otherwise,
it is kept, because, due to the presence of [EMagic] expressions in the
code, dropping a type annotation could cause an ill-typed program to
become apparently well-typed. Keeping a type annotation requires taking a
fresh instance of the type scheme, because OCaml doesn't have support for
locally and existentially bound type variables. Yuck. *)
let inline formals actuals body oscheme =
assert (List.length actuals = List.length formals);
match oscheme with
| Some scheme
when not Settings.infer ->
when not Front.ocaml_types_have_been_checked ->
let formals, body = annotate formals body (instance scheme) in
mlet formals actuals body
......@@ -320,4 +319,3 @@ let inline (p : program) : program =
inline_structure p
else
p
......@@ -70,17 +70,28 @@ let nonterminalgadtdef grammar =
is an approximation of the real [.mli] file. When we are
not in [--(raw)-depend] mode, though, this is a problem.
We display an error message and stop. *)
match Settings.depend with
| Settings.OMRaw
| Settings.OMPostprocess ->
Settings.(match infer with
| IMDependRaw
| IMDependPostprocess ->
"The indexed type of nonterminal symbols (mock!).",
[]
| Settings.OMNone ->
| IMNone ->
Error.error [] "\
the type of the nonterminal symbol %s is unknown.\n\
When --inspection is set, the type of every nonterminal symbol must be known.\n\
Please use --infer or specify the type of every symbol via %%type declarations."
Please enable type inference (see --infer and --infer-read-reply)\n\
or specify the type of every symbol via %%type declarations."
nt
| IMInfer
| IMReadReply _ ->
(* This should not happen: when [--infer] or [--infer-read-reply]
is set, the types of all nonterminal symbols should be known. *)
assert false
| IMWriteQuery _ ->
(* This should not happen: when [--infer-write-query] is set, we
write a mock [.ml] file, but no [.mli] file, so this function
should never be called. *)
assert false)
in
[
......
......@@ -99,19 +99,70 @@ let v () =
dump := true;
explain := true
let infer =
ref false
let inline =
ref true
type ocamldep_mode =
| OMNone (* do not invoke ocamldep *)
| OMRaw (* invoke ocamldep and echo its raw output *)
| OMPostprocess (* invoke ocamldep and postprocess its output *)
type infer_mode =
(* Perform no type inference. This is the default mode. *)
| IMNone
(* Perform type inference by invoking ocamlc directly. *)
| IMInfer (* --infer *)
| IMDependRaw (* --raw-depend *)
| IMDependPostprocess (* --depend *)
(* Perform type inference by writing a mock .ml file and
reading the corresponding inferred .mli file. *)
| IMWriteQuery of string (* --infer-write-query <filename> *)
| IMReadReply of string (* --infer-read-reply <filename> *)
let show_infer_mode = function
| IMNone ->
""
| IMInfer ->
"--infer"
| IMDependRaw ->
"--raw-depend"
| IMDependPostprocess ->
"--depend"
| IMWriteQuery _ ->
"--infer-write-query"
| IMReadReply _ ->
"--infer-read-reply"
let infer =
ref IMNone
let set_infer_mode mode2 =
let mode1 = !infer in
match mode1, mode2 with
| IMNone, _ ->
infer := mode2
(* It is valid to specify [--infer] in conjunction with [--depend] or
[--raw-depend]. The latter command then takes precedence. This is
for compatibility with Menhir prior to 2018/05/23. *)
| IMInfer, (IMInfer | IMDependRaw | IMDependPostprocess) ->
infer := mode2
| (IMDependRaw | IMDependPostprocess), IMInfer ->
()
| _, _ ->
fprintf stderr "Error: you cannot use both %s and %s.\n"
(show_infer_mode mode1)
(show_infer_mode mode2);
exit 1
let enable_infer () =
set_infer_mode IMInfer
let enable_depend () =
set_infer_mode IMDependPostprocess
let enable_raw_depend () =
set_infer_mode IMDependRaw
let enable_write_query filename =
set_infer_mode (IMWriteQuery filename)
let depend =
ref OMNone
let enable_read_reply filename =
set_infer_mode (IMReadReply filename)
let code_inlining =
ref true
......@@ -241,12 +292,12 @@ let options = Arg.align [
"--canonical", Arg.Unit (fun () -> construction_mode := ModeCanonical), " Construct a canonical Knuth LR(1) automaton";
"--cmly", Arg.Set cmly, " Write a .cmly file";
"--comment", Arg.Set comment, " Include comments in the generated code";
"--compare-errors", Arg.String add_compare_errors, "<filename> (used twice) Compare two .messages files.";
"--compile-errors", Arg.String set_compile_errors, "<filename> Compile a .messages file to OCaml code.";
"--compare-errors", Arg.String add_compare_errors, "<filename> (used twice) Compare two .messages files";
"--compile-errors", Arg.String set_compile_errors, "<filename> Compile a .messages file to OCaml code";
"--coq", Arg.Set coq, " Generate a formally verified parser, in Coq";
"--coq-no-complete", Arg.Set coq_no_complete, " Do not generate a proof of completeness";
"--coq-no-actions", Arg.Set coq_no_actions, " Ignore semantic actions in the Coq output";
"--depend", Arg.Unit (fun () -> depend := OMPostprocess), " Invoke ocamldep and display dependencies";
"--depend", Arg.Unit enable_depend, " Invoke ocamldep and display dependencies";
"--dump", Arg.Set dump, " Write an .automaton file";
"--echo-errors", Arg.String set_echo_errors, "<filename> Echo the sentences in a .messages file";
"--error-recovery", Arg.Set recovery, " (no longer supported)";
......@@ -255,7 +306,9 @@ let options = Arg.align [
"--fixed-exception", Arg.Set fixedexc, " Declares Error = Parsing.Parse_error";
"--follow-construction", Arg.Set follow, " (undocumented)";
"--graph", Arg.Set graph, " Write a dependency graph to a .dot file";
"--infer", Arg.Set infer, " Invoke ocamlc to do type inference";
"--infer", Arg.Unit enable_infer, " Invoke ocamlc to do type inference";
"--infer-write-query", Arg.String enable_write_query, "<filename> Write mock .ml file";
"--infer-read-reply", Arg.String enable_read_reply, "<filename> Read inferred .mli file";
"--inspection", Arg.Set inspection, " Generate the inspection API";
"--interpret", Arg.Set interpret, " Interpret the sentences provided on stdin";
"--interpret-show-cst", Arg.Set interpret_show_cst, " Show a concrete syntax tree upon acceptance";
......@@ -266,7 +319,7 @@ let options = Arg.align [
"--log-code", Arg.Set_int logC, "<level> Log information about the generated code";
"--log-grammar", Arg.Set_int logG, "<level> Log information about the grammar";
"--no-code-inlining", Arg.Clear code_inlining, " (undocumented)";
"--no-inline", Arg.Clear inline, " Ignore the %inline keyword.";
"--no-inline", Arg.Clear inline, " Ignore the %inline keyword";
"--no-pager", Arg.Unit (fun () -> if !construction_mode = ModePager then construction_mode := ModeInclusionOnly), " (undocumented)";
"--no-prefix", Arg.Set noprefix, " (undocumented)";
"--no-stdlib", Arg.Set no_stdlib, " Do not load the standard library";
......@@ -281,7 +334,7 @@ let options = Arg.align [
"--only-preprocess-uu", Arg.Unit (fun () -> preprocess_mode := PMOnlyPreprocess (PrintUnitActions true)),
" Print grammar with unit actions & tokens";
"--only-tokens", Arg.Unit tokentypeonly, " Generate token type definition only, no code";
"--raw-depend", Arg.Unit (fun () -> depend := OMRaw), " Invoke ocamldep and echo its raw output";
"--raw-depend", Arg.Unit enable_raw_depend, " Invoke ocamldep and echo its raw output";
"--stdlib", Arg.Set_string stdlib_path, "<directory> Specify where the standard library lies";
"--strict", Arg.Set strict, " Warnings about the grammar are errors";
"--suggest-comp-flags", Arg.Unit (fun () -> suggestion := SuggestCompFlags),
......@@ -429,15 +482,9 @@ let () =
let noprefix =
!noprefix
let infer =
!infer
let code_inlining =
!code_inlining
let depend =
!depend
let inline =
!inline
......@@ -530,3 +577,28 @@ let echo_errors =
let cmly =
!cmly
let infer =
!infer
(* If some flags imply that we will NOT produce an OCaml parser, then there is
no need to perform type inference, so [--infer] is ignored. This saves time
and dependency nightmares. *)
let skipping_parser_generation =
coq ||
compile_errors <> None ||
interpret_error ||
list_errors ||
compare_errors <> None ||
update_errors <> None ||
echo_errors <> None ||
false
(* maybe also: [preprocess_mode <> PMNormal] *)
let infer =
match infer with
| IMInfer when skipping_parser_generation ->
IMNone
| _ ->
infer
......@@ -73,26 +73,28 @@ type preprocess_mode =
val preprocess_mode: preprocess_mode
(* Whether one should invoke ocamlc in order to infer types for all
nonterminals. *)
val infer: bool
(* Whether and how OCaml type inference (for semantic actions and nonterminal
symbols) should be performed. See the manual for details. *)
type infer_mode =
(* Perform no type inference. This is the default mode. *)
| IMNone
(* Perform type inference by invoking ocamlc directly. *)
| IMInfer (* --infer *)
| IMDependRaw (* --raw-depend *)
| IMDependPostprocess (* --depend *)
(* Perform type inference by writing a mock .ml file and
reading the corresponding inferred .mli file. *)
| IMWriteQuery of string (* --infer-write-query <filename> *)
| IMReadReply of string (* --infer-read-reply <filename> *)
val infer: infer_mode
(* Whether one should inline the non terminal definitions marked
with the %inline keyword. *)
val inline: bool
(* Whether and how one should invoke ocamldep in order to compute and
display dependencies. *)
type ocamldep_mode =
| OMNone (* do not invoke ocamldep *)
| OMRaw (* invoke ocamldep and echo its raw output *)
| OMPostprocess (* invoke ocamldep and postprocess its output *)
val depend: ocamldep_mode
(* Whether comments should be printed or discarded. *)
val comment: bool
......
Warning: you are using the standard library and/or the %inline keyword. We
recommend switching on --infer in order to avoid obscure type error messages.
Grammar has 3 nonterminal symbols, among which 1 start symbols.
Grammar has 5 terminal symbols.
Grammar has 6 productions.
......
Warning: you are using the standard library and/or the %inline keyword. We
recommend switching on --infer in order to avoid obscure type error messages.
%start phrase
%token <int> A
%token <int> B
......
Warning: you are using the standard library and/or the %inline keyword. We
recommend switching on --infer in order to avoid obscure type error messages.
Grammar has 5 nonterminal symbols, among which 2 start symbols.
Grammar has 5 terminal symbols.
Grammar has 9 productions.
......
Warning: you are using the standard library and/or the %inline keyword. We
recommend switching on --infer in order to avoid obscure type error messages.
%{ type ('a, 'b) either = Left of 'a | Right of 'b %}
%start other
%start phrase
......
Warning: you are using the standard library and/or the %inline keyword. We
recommend switching on --infer in order to avoid obscure type error messages.
Grammar has 5 nonterminal symbols, among which 1 start symbols.
Grammar has 5 terminal symbols.
Grammar has 9 productions.
......
Warning: you are using the standard library and/or the %inline keyword. We
recommend switching on --infer in order to avoid obscure type error messages.
%{ type ('a, 'b) either = Left of 'a | Right of 'b %}
%start phrase
%token <int> A
......
Warning: you are using the standard library and/or the %inline keyword. We
recommend switching on --infer in order to avoid obscure type error messages.
Grammar has 3 nonterminal symbols, among which 1 start symbols.
Grammar has 5 terminal symbols.
Grammar has 7 productions.
......
Warning: you are using the standard library and/or the %inline keyword. We
recommend switching on --infer in order to avoid obscure type error messages.
%start phrase
%token <int> A
%token <int> B
......
Warning: you are using the standard library and/or the %inline keyword. We
recommend switching on --infer in order to avoid obscure type error messages.
Grammar has 9 nonterminal symbols, among which 1 start symbols.
Grammar has 8 terminal symbols.
Grammar has 16 productions.
......
Warning: you are using the standard library and/or the %inline keyword. We
recommend switching on --infer in order to avoid obscure type error messages.
%{
open Prolog
%}
......