Camlp4 no longer keeps a generated directory of pure OCaml sources in order to start the build. It starts from to OCaml files (boot/
and camlp4boot
.mlboot/Camlp4.ml
). With these two files we build a single bytecode camlp4
program called camlp4boot
. This camlp4boot
is a full-featured Camlp4 engine able to parse all Camlp4 sources. With this new system developers who have to change the Camlp4 side of the OCaml distribution shouldn't be hurt by the bootstrap process. More details about the bootstrap can be found here:
The new Camlp4 version is much more modular and abstract than the previous one. As a first benefit Camlp4 no more pollutes the global namespace, all Camlp4 modules are packed inside modules that begins with Camlp4.
Camlp4 makes heavy use of functors to factor and abstract different parts of the system but -don't be afraid- a pre-casted module is waiting for you as a default Camlp4 structure.
The (partial) module tree can be found below.Locations are now abstract, and therefore can be replaced by any representation that fits with the module signature, in particular regular OCaml locations, fake locations (for those that don't need them). Furthermore locations are now generally handled in a functional way, instead of imperatively.
ocamllex
and gains the following features:
[^ ... ^]
, {% ... %}
, ... Camlp4 can be used as a translator in between two languages. For example you can translate your OCaml sources (foo.ml
) into the revised syntax with this command:
$ camlp4 -parser OCaml -printer OCamlr foo.ml # equivalent to: # * camlp4 pa_o.cmo pr_r.cmo foo.ml # * camlp4 Camlp4Parsers/OCaml.cmo Camlp4Printers/OCamlr.cmo foo.ml
The parser reads the source, the printer writes it, but nothing can be inserted between. Camlp4Filters fill this hole by providing a way to transform an AST into another one by registering transformation functions.
$ camlp4 -parser OCaml -filter ExceptionTracer -printer OCamlr foo.ml # equivalent to: # * camlp4 Camlp4Parsers/OCaml.cmo Camlp4Filters/ExceptionTracer.cmo Camlp4Printers/OCamlr.cmo foo.ml # * camlp4o -filter ExceptionTracer pr_r.cmo foo.mlThe Camlp4 standard distribution contains some AST transformations described in the module tree below.
For that purpose you write a functor that takes a module Syntax as argument and returns an extended module Syntax.
For dynamic loading and integration in the camlp4
binary reasons, one can register an extension to makes it usable at command line.
The Camlp4 pretty-printing technology is being redesigned falling back to more classical tools (mutually recursive functions and the Format
module).
Camlp4
: PreCast
: This module is a good start for most of Camlp4 users. Sig
: The Sig module contains all signatures used by Camlp4. It's like an interface repository. You can use this module to learn what is possible to do with a module. Loc
: Abstract location values. Camlp4Ast
: The OCaml abstract syntax tree together with mapping class and functions. Quotation
: Quotations and quotation expanders. Token
, Camlp4Token
: Abstract tokens, and OCaml tokens. Lexer
: The signature of lexing functions. Grammar.{Static,Dynamic}
: The signature of grammars and entries manipulation. Syntax
, Camlp4Syntax
: The syntax signature contains most of needed things in order to make an extension. The Camlp4Syntax signature is more OCaml specific. (see also AntiquotSyntax, SyntaxExtension) Parser
, Printer
: Minimal parser and printer signatures. AstFilters
: The signature for registering and call AST filters. Ast
, Error
, DynLoader
, Type
, Id
, Options
Struct
: Implementations of these signatures. Only useful when you want to build a customized Camlp4 and reuse modules. (For Jedis only :) ). Register
: This module allows to register syntax extensions, parsers, printers, and filters. Config
, ErrorHandler
and Debug
: at top level for visibility purpose. Camlp4Parsers
: Camlp4OCamlParser
: The original OCaml syntax. Camlp4OCamlRevisedParser
: The revised OCaml syntax. Camlp4OCamlReloadedParser
: An alternative syntax that can be plugged on top of original syntax or the revised one. Camlp4QuotationExpander
: Add quotations in a reflective way (same syntax as the base language). Camlp4OCamlOriginalQuotationExpander
: OCaml quotations in the original syntax. Camlp4OCamlRevisedQuotationExpander
: OCaml quotations in the revised syntax. Camlp4OCamlParserParser
, Camlp4OCamlRevisedParserParser
: The parser and stream syntax. Camlp4GrammarParser
: The syntax of extensible grammars (EXTEND...END
). Camlp4ListComprehension
: List comprehension as a syntactic sugar. Camlp4MacroParser
: Macros like IFDEF
, DEFINE
, INCLUDE
, __FILE__
... Camlp4DebugParser
: A debug keyword for auto-removable debugging info. Camlp4AstLoader
: Load an Camlp4Ast
marshalled value. Camlp4Printers
(in fact the real implementation of them is in Camlp4.Printers): Camlp4OCamlPrinter
: A re-write from scratch OCaml original syntax pretty-printer. Camlp4OCamlRevisedPrinter
: Revised pretty-printer. Camlp4AstDumper
, Camlp4OCamlAstDumper
: Marshalling functions of trees for the OCaml compiler and Camlp4 itself. Camlp4NullDumper
: Print nothing. Camlp4AutoPrinter
: Choose either Camlp4OCamlPrinter
or Camlp4OCamlAstDumper
depending on the tty. Camlp4Filters
: Camlp4ExceptionTracer
: Add "try with" blocks around the body of functions that prints (with the location) and re-raise any exception (useful for debugging). Camlp4MapGenerator
: Grab type definitions and generate map functions to traverse these structures. Camlp4FoldGenerator
: Grab type definitions and generate fold functions to traverse these structures. Camlp4MetaGenerator
: Grab type definitions and generate functions to lift your structure to the syntax of it. Camlp4AstLifter
: Associate to a tree the program that produces it. (f 42 ~~> App (Id "f") (Int "42") == <:expr< f 42 >>) Camlp4LocationStripper
: Replace all location information by the ghost location. Camlp4Tracer
: Trace all function calls. Camlp4Profiler
: Generates call to a counting function. Camlp4TrashRemover
: Removes the Camlp4Trash module. Camlp4Top
: Top
: The main top-level plugin for the OCaml top-level. Rprint
: The revised pretty-printer for values. dynamic_old_syntax.ml
and static_old_syntax.ml
.
dynamic_old_syntax.ml
type t1 = A | B type t2 = Foo of string * t1 open Pcaml let foo = Entry.mk gram "foo" let bar = Entry.mk gram "bar" EXTEND GLOBAL: foo bar; foo: [ [ "foo"; i = LIDENT; b = bar -> Foo(i, b) ] ]; bar: [ [ "?" -> A | "." -> B ] ]; END;; Entry.parse foo (Stream.of_string "foo x?") = Foo("x", A) DELETE_RULE foo: "foo"; LIDENT; bar END
static_old_syntax.ml
type t1 = A | B type t2 = Foo of string * t1 module Gram = Grammar.GMake(...) let foo = Gram.Entry.mk "foo" let bar = Gram.Entry.mk "bar" GEXTEND Gram GLOBAL: foo bor; foo: [ [ "foo"; i = LIDENT; b = bar -> Foo(i, b) ] ]; bar: [ [ "?" -> A | "." -> B ] ]; END;; Gram.Entry.parse foo (Stream.of_string "foo x?") = Foo("x", A) GDELETE_RULE Gram foo: "foo"; LIDENT; bar END
Camlp4.PreCast
modulequick_non_extensible_example.ml
(* This scheme only works when the grammar value is not really used for other things than entry creation. In fact grammars are now static by default. *) type t1 = A | B type t2 = Foo of string * t1 open Camlp4.PreCast open Syntax let foo = Gram.Entry.mk "foo" let bar = Gram.Entry.mk "bar" EXTEND Gram GLOBAL: foo bar; foo: [ [ "foo"; i = LIDENT; b = bar -> Foo(i, b) ] ]; bar: [ [ "?" -> A | "." -> B ] ]; END;; Gram.parse_string foo (Loc.mk "<string>") "foo x?" = Foo("x", A) DELETE_RULE Gram foo: "foo"; LIDENT; bar END
dynamic_functor_example.ml
type t1 = A | B type t2 = Foo of string * t1 open Camlp4 module Id = struct (* Information for dynamic loading *) let name = "My_extension" let version = "$Id$" end (* An extension is just a functor: Syntax -> Syntax or Camlp4Syntax -> Camlp4Syntax *) module Make (Syntax : Sig.Camlp4Syntax) = struct include Syntax let foo = Gram.Entry.mk "foo" let bar = Gram.Entry.mk "bar" open Camlp4.Sig EXTEND Gram GLOBAL: foo bar; foo: [ [ "foo"; i = LIDENT; b = bar -> Foo(i, b) ] ]; bar: [ [ "?" -> A | "." -> B ] ]; END;; Gram.parse_string foo (Loc.mk "<string>") "foo x?" = Foo("x", A) DELETE_RULE Gram foo: "foo"; LIDENT; bar END end (* Register it to make it usable via the camlp4 binary. *) module M = Register.OCamlSyntaxExtension(Id)(Make)
static_functor_example.ml
type t1 = A | B type t2 = Foo of string * t1 open Camlp4.PreCast module Lexer = struct ... if you need a different lexer ... end module Gram = MakeGram(Lexer) let foo = Gram.Entry.mk "foo" let foo = Gram.Entry.mk "foo" EXTEND Gram GLOBAL: foo; foo: [ [ "foo"; i = LIDENT; b = bar -> Foo(i, b) ] ]; bar: [ [ "?" -> A | "." -> B ] ]; END;; Gram.parse_string foo (Loc.mk "<string>") "foo x?" = Foo("x", A) DELETE_RULE Gram foo: "foo"; LIDENT; bar END
without_patterns_example.ml
expr: [ [ ... | "-"; s = INT -> Ast.Int (- int_of_string s) | ... (* or worse: here `s' is a quotation token serialized as one string *) | s = QUOTATION -> let name = String.index ':' ... in let loc_name = ... in let code = ... in let quot = { name = name; loc_name = loc_name; code = code } in expand_quotatiton quot ] ]This one can now be written as:
with_patterns_example.ml
expr: [ [ ... | "-"; `INT(i, _) -> Ast.Int (-i) | ... | `QUOTATION quot -> expand_quotatiton quot ] ]But one can also be more specific:
specific_patterns_example.ml
[ [ `INT ((0 | 2) as i, _); ... -> ... | `INT (42, _); ... -> ... | `INT (i, s); ... -> ... ] ]
In order to support a nice pretty-printing the Camlp4 token type hold both versions of the value. For instance in INT(i, s)
, i
is the integer value and s
is the source string without processing.
`PATTERN
, the $`int:...$ antiquotationIn a quotation <:expr< $int:"42"$ >> the type of this antiquotation contents is a string. With the pattern support in grammars one can now directly get the integer value of an INT token but sometimes you're constrained to write <:expr< $int:string_of_int i$ >>. To get rid of this one can write <:expr< $`int:i$ >> (thanks to Alain Frish for this idea).
Here a simple equivalence table:
$`int:i$ -> $int:string_of_int i$ $`int32:i$ -> $int32:Int32.to_string i$ $`int64:i$ -> $int64:Int64.to_string i$ $`nativeint:i$ -> $nativeint:Nativeint.to_string i$ $`flo:f$ -> $flo:string_of_float f$ $`str:s$ -> $str:String.escaped s$ $`chr:c$ -> $chr:Char.escaped c$
Here a list of common errors encountered during the migration to this new version of Camlp4.
[<:expr< 42 >>]
becomes [ <:expr< 42 >> ]
. let _loc = (Lexing.dummy_pos, Lexing.dummy_pos)
or let loc = (0,0)
now becomes let _loc = Loc.ghost
. Of course the Loc module must be accessible (in Camlp4.PreCast for example). Grammar.gcreate ...
becomes module Gram = MakeGram Lexer
after opening Camlp4.PreCast
. Grammar.Entry.create gram "foo"
becomes Gram.Entry.mk "foo"
. Grammar.parse entry (Stream.of_string s)
becomes Gram.parse_string (Loc.mk "filename") s
Grammar.parse entry strm
becomes Gram.parse (Loc.mk "filename") strm
EOI
becomes `EOI
(because the EOI token have no arguments). LIST0
, LIST1
, OPT
) no longer accept a single token. LIST0 STRING
can be replaced by lIST0 [ x = STRING -> x ]
??
for error handling instead of ?
like in the orginal syntax. A new OCaml pretty-printer is under design and has the following features: