This article describes how to use Camlp4 command-line tools to pre-process and pretty-print.
A review of different camlp4 commands
Camlp4 may be called using a bunch of executables:
This section deals with the differences between those executables, first by detailing the features and languages understood by each of them, then by showing where those differences come from.
Camlp4 executables functionalities
Here is a table that sums up the different executables functionalities:
- "host" language means the language outside quotations (here <<...>> here).
- "embedded" language means the language inside quotations (... <<here>> ...).
- "reflective" is true when extending the syntax of the host language will also extend the embedded one.
- "3.09 equivalent" only concerns equivalence with quotations in 3.09 version.
|camlp4r -parser rq||revised||revised||No||camlp4r q_MLast.cmo|
A few notes:
- the ending "f" means full, that is all standard extensions (parsers, grammars, quotations, macros, and list comprehensions).
- with the old camlp4 most of the libraries are written in a style understood by camlp4orf. However being able to write them in the original syntax was a very waited feature. Sadly camlp4of is still experimental since the original syntax doesn't fit well with quotations so there may be unsupported things with camlp4of.
How does it work?
Those executables are actually syntactic sugar for calling camlp4 with different modules, as can be seen using the -loaded-modules option:
$ camlp4 -loaded-modules $ camlp4o -loaded-modules Camlp4.Printers.OCaml Camlp4OCamlParser Camlp4OCamlParserParser Camlp4OCamlRevisedParserParser Camlp4RevisedParserParser $ camlp4rf -loaded-modules Camlp4.Printers.OCaml Camlp4GrammarParser Camlp4ListComprenhsion Camlp4MacroParser Camlp4OCamlRevisedParserParser Camlp4QuotationExpander Camlp4RevisedParserParser
As you can see, the camlp4 executable does not load any modules.
Camlp4 module system
Modules are searched for in the $OCAMLLIB/camlp4 folder, and loaded using the DynLink module. You may have seen that there are also native versions, which therefore cannot dynamically load code (the Natdynlink module is not (yet?) in the main CVS branch of OCaml). This is accomplished by embedding the modules in the executables in the correct order; more on that can be seen on How to create a standalone camlp4 executable.
Interestingly enough, using camlp4 in a standalone manner is not exactly the same as using it as a pre-processor. The difference is the printer module used:
- in standalone mode, camlp4* executables default to Camlp4.Printers.OCaml, which pretty-prints code in OCaml regular syntax.
- in pre-processor mode, camlp4* executables use Camlp4Printers.DumpOCamlAst, which outputs code in a format understood by the Objective Caml compiler (by marshaling an OCaml AST).
The following table gives some equivalences between camlp4* executables and camlp4 commands.
|executable||equivalent command||parsers loaded||printers loaded|
|camlp4r||camlp4 -parser r -parser rp -printer a||Camlp4RevisedParserParser, Camlp4OCamlRevisedParser||Camlp4Printers.Camlp4AutoPrinter|
|camlp4o||camlp4 -parser o -parser op -printer a||Camlp4OCamlParser, Camlp4OCamlParserParser, Camlp4OCamlRevisedParser, Camlp4OCamlRevisedParserParser||Camlp4Printers.Camlp4AutoPrinter|
A few notes
- Camlp4 model sees OCaml original (regular) syntax as a syntax extension of OCaml revised syntax, hence the Camlp4OCamlRevisedParser is always loaded.
- Modules ended by ParserParser is for the syntax for stream parsers. Here again the original syntax is an extension of the revised one.
- The list of abbreviations for the parsers is in the file Camlp4Bin.ml
- The Camlp4AutoPrinter (also abbreviated -printer a) is either Camlp4OCamlPrinter (see below) if the output is a terminal (tty) or the Camlp4OCamlAstDumper otherwise.
- Camlp4OCamlPrinter is the original syntax printer (the implementation is in Camlp4.Printers.OCaml). This printer is abbreviated as -printer o.
- Camlp4OCamlAstDumper dumps the OCaml AST as a marshaled value (this printer is abbreviated as -printer p).
- Don't confuse the OCaml AST dumper with the Camlp4 AST Dumper (Camlp4AstDumper) also abbreviated as -printer d, that dumps the Camlp4 AST as a marshaled value.
Pre-processing is necessary when one wants to compile a file which is not written in "pure" original syntax. This is the case for:
- code written in the revised syntax
- code written in the original syntax, using macros
- camlp4 syntax extensions
- files using one or more syntax extensions
Please note that pre-processing files written in "pure" original syntax is not mandatory (since the Objective Caml compiler understands it natively), however it may be damn useful when dealing with the cryptic "Syntax error" message:
Consider the following code:
let a = (5 + )
And the error messages, without and with pre-processing:
$ ocamlc error.ml File "error.ml", line 1, characters 13-14: Syntax error
$ ocamlc -pp camlp4o.opt error.ml File "error.ml", line 1, characters 9-10: Parse error: [expr] expected after [infix operator (level 2) (start with '+', '-')] (in [expr]) Preprocessor error
How to pre-process?
Using the "-pp" option, as follows:
ocamlc -pp <camlp4 command> <implementation (.ml) or interface (.mli) file>
Note that you may force parsing of a file as an implementation (using -impl) or an interface (using -intf), regardless of the file extension.
Pre-processing a file written in regular syntax:
ocamlc -pp camlp4o.opt my_file.ml
Pre-processing a file written in revised syntax:
ocamlc -pp camlp4r.opt my_file.ml
Pre-processing a syntax extension written in original syntax:
ocamlc -I +camlp4 -pp camlp4of.opt my_file.ml
Pre-processing a syntax extension written in revised syntax:
ocamlc -I +camlp4 -pp camlp4rf.opt my_file.ml