Warning: This is a legacy page that is only available for historical reasons. I stopped maintaining this page in spring 2007 when Nicolas Pouillard announced a rewrite of Camlp5 on the OCaml list.
On this page I collected information on Camlp5 that could neither be found in the reference manual nor in the tutorial around 2007. I found out about all that during my work on a quotation system in original syntax.| Issue | Original syntax | Revised syntax |
|---|---|---|
| parenthesis required in record update | { record_expr with field = expr; ... } | { (record_expr) with field = expr; ... } |
| fun bindings in record field definitions | { field = fun a -> ... } | { field a = ... } |
| no bigarray syntax | a.{1} | Bigarray.Array1.get a 1 |
| a.{1,2,3,4} <- b | Bigarray.Genarray.set a [| 1;2;3;4 |] b | |
| nested as patterns require parenthesis | function | Some a as opt, b -> ... | function [ ((Some a as opt), b) -> ... |
| manifest types | type 'a a = 'a option = None | Some of 'a | type 'a a = 'a option == None | Some of 'a |
| top level quantification on type variables | type a = ! 'a . list 'a | |
| abstract module type specifications | module type MT | module type MT = 'a |
| class declarations | class [ 'a, 'b ] c = ... | class c [ 'a, 'b ] = ... |
| class instantiation | [ 'a, 'b ] c | c [ 'a, 'b ] |
| class valued function types | typ -> class_type | [ typ ] -> class_type |
| semicolon required after every class fields | method virtual m : int | method virtual m : int; |
| order of private and virtual | method private virtual m : int | method virtual private m : int; |
| method virtual private m : int | ||
| object instance variables | val a = ... | value a = ... |
| class constraints | constraint t1 = t2 | type t1 = t2 |
SLIST0 and SLIST1 auto-magically provide an $list:...$ anti-quotation. SOPT provides $opt:...$. However, I don't understand (yet) where these anti-quotations come from.
There are also some limitations with SLIST:
You cannot distinguish both rules with an LL(1) grammar, because you might have to parse arbitrarily long to see the difference between field = and expr with.
The Camlp5 solution looks as follows. (The following code is taken from paqo_o.ml. It originates from Daniel de Rauglaudre's pa_o.ml. However, Daniel used the revised syntax.)
(* stream_peek_nth : int -> 'a Stream.t -> 'a option *)
let stream_peek_nth n strm =
try
Some(List.nth (Stream.npeek n strm) (n-1))
with
| Failure "nth" -> None
Now we define an oracle that distinguishes the two grammar rules in question:
(* test_label_eq : unit Grammar.Entry.e *)
let test_label_eq =
Grammar.Entry.of_parser gram "test_label_eq"
(let rec test lev strm =
match stream_peek_nth lev strm with
| Some (("UIDENT", _) | ("LIDENT", _) | ("", ".")) ->
test (lev + 1) strm
| Some ("", "=") -> ()
| _ -> raise Stream.Failure
in
test 1
)
The Camlp5 grammar is now as follows:
EXTEND
expr:
[ ...
| "simple" LEFTA
[ ...
| "{"; test_label_eq; lel = lbl_expr_list; "}" -> ...
| "{"; e = expr LEVEL "."; "with"; lel = lbl_expr_list; "}" -> ...
...
EXTEND
expr:
[ ...
| "simple" LEFTA
[ ...
| "("; op = operator_rparen -> ...
...
let operator_rparen =
Grammar.Entry.of_parser gram "operator_rparen"
(fun strm ->
match Stream.npeek 2 strm with
| [("", s); ("", ")")] when is_operator s ->
begin
Stream.junk strm;
Stream.junk strm;
s
end
| _ -> raise Stream.Failure)