Chapter 7 |
Extending the syntax of OCaml |
|
Syntax extensions in OCaml
can be done by extending the
grammars entries of the OCaml syntax. All grammars entries are defined
in the module named Pcaml
. They all return values of types
defined in the module MLast
: nodes of these types can be
created using the predefined quotation expansion q_MLast.cmo
.
The entries in Pcaml
are:
-
expr
for expressions, returning values of type
MLast.expr
.
-
patt
for patterns, returning values of type
MLast.patt
.
-
ctyp
for types, returning values of type
MLast.ctyp
.
-
module_type
for module types, returning values of type
MLast.module_type
.
-
module_expr
for module expressions, returning values of type
MLast.module_expr
.
-
sig_item
for signature items, returning values of type
MLast.sig_item
.
-
str_item
for structure items, returning values of type
MLast.str_item
.
Most of these entries are generally defined (``extended'') with
several ``levels'' (see chapter 3). Some of them
are labelled, in order to be able to extend them or to insert other
levels.
The levels and their possible labels are not predefined. It depend on
how the syntax define them. To see which labels are defined and which
rule they contain, enter the toplevel and type for the normal syntax:
#load "camlp4o.cma";;
Grammar.Entry.print Pcaml.expr;; (* for the expressions *)
Grammar.Entry.print Pcaml.patt;; (* for the patterns *)
(* ... and so on *)
For the revised syntax, load "camlp4r.cma"
instead. If you
defined another syntax of the whole language or want to see the other
syntaxes provided, load it before, and call Grammar.Entry.print
of the desired grammar entry. Look at the manual page
(man camlp4
in the shell) to see all available syntaxes and
extensions.
Once you have the list of the grammar entry you want to extend and the
possible level label, you can do your extension.
7.2 |
Example: ``repeat until'' like in Pascal |
|
If you read all this tutorial, you are able to understand this
complete example. If you did not, just create the files and type the
indicated commands.
Write first a file named foo.ml
containing:
open Pcaml;;
EXTEND
expr: LEVEL "expr1"
[[ "repeat"; e1 = expr; "until"; e2 = expr ->
<:expr< do { $e1$; while not $e2$ do { $e1$; } } >> ]];
END;;
The compilation of this file can be done by typing under the shell
(the dollar is the shell prompt):
$ ocamlc -pp "camlp4o pa_extend.cmo q_MLast.cmo" -I +camlp4 \
-c foo.ml
Here is the file bar.ml
containing a repeat..until
statement:
let main () =
let i = ref 0 in
repeat print_int !i; incr i until !i = 10;
print_newline ()
let _ = main ()
You can compile it by typing:
$ ocamlc -pp "camlp4o ./foo.cmo" bar.ml
And run it:
$ ./a.out
0123456789
Or just pretty print the program with the expanded syntax:
$ camlp4o ./foo.cmo pr_o.cmo bar.ml
let main () =
let i = ref 0 in
begin
begin print_int !i; incr i end;
while not (!i = 10) do print_int !i; incr i done;
end;
print_newline ()
;;
main ();;
If you want to have the equivalent of a #define
of C, you can
write for example, if you want FOO
to be replaced by 54
in expressions and patterns:
open Pcaml;;
EXTEND
expr: LEVEL "simple"
[[ UIDENT "FOO" -> <:expr< 54 >> ]];
patt: LEVEL "simple"
[[ UIDENT "FOO" -> <:patt< 54 >> ]];
END;;
The compilation of this file can be done by typing:
$ ocamlc -pp "camlp4o pa_extend.cmo q_MLast.cmo" -I +camlp4 \
-c foo.ml
Here is the file bar.ml
containing FOO
constants:
FOO;;
function FOO -> 22;;
You can compile it by typing:
$ ocamlc -pp "camlp4o ./foo.cmo" bar.ml
You can just pretty print the program with the expanded syntax:
$ camlp4o ./foo.cmo pr_o.cmo bar.ml
54;;
function 54 -> 22;;
7.4 |
Example: a ``for'' loop like in C |
|
Here is an example of an syntax extension allowing to write a ``for''
loop like in C. A construction is added with the loop variable and 3
parameters, simple expressions: the first one is the initial value,
the second the test, the third the way to change the loop variable.
Note that we use here the directives #load
inside the source of
the syntax extension, allowing to parse it with camlp4o
without
having to specify these files in the command line.
File ``cloop.ml'':
#load "q_MLast.cmo";;
#load "pa_extend.cmo";;
open Pcaml
let gensym =
let cnt = ref 0 in
fun var ->
let x = incr cnt; !cnt in
var ^ "_gensym" ^ string_of_int x
let gen_for loc v iv wh nx e =
let loop_fun = gensym "iter" in
<:expr<
let rec $lid:loop_fun$ $lid:v$ =
if $wh$ then do { $e$; $lid:loop_fun$ $nx$ } else ()
in
$lid:loop_fun$ $iv$ >>
EXTEND
expr: LEVEL "expr1"
[ [ "for"; v = LIDENT; iv = expr LEVEL "simple";
wh = expr LEVEL "simple"; nx = expr LEVEL "simple";
"do"; e = expr; "done" ->
gen_for loc v iv wh nx e ] ]
;
END
Compile this file with:
$ ocamlc -pp camlp4o -I +camlp4 -c cloop.ml
Example under the toplevel:
$ ocaml
Objective Caml version 3.02+7 (2001-09-29)
# #load "camlp4o.cma";;
Camlp4 Parsing version 3.02+7 (2001-09-29)
# #load "cloop.cmo";;
# for i = 0 to 10 do print_int i; done;; (* normal loop *)
012345678910- : unit = ()
# for c 0 (c<10) (c+1) do print_int c; done;;
0123456789- : unit = ()
# for c 0 (c<10) (c+3) do print_int c; done;;
0369- : unit = ()
Exemple of compilation of a program using this construction:
$ cat foo.ml
for c 0 (c<10) (c+2) do print_int c; done
$ ocamlc -pp "camlp4o ./cloop.cmo" -c foo.ml
And if you want to see the generated program (for example to check
that the extension is correct):
$ camlp4o ./cloop.cmo pr_o.cmo foo.ml
let rec iter_gensym1 c =
if c < 10 then begin print_int c; iter_gensym1 (c + 2) end
in
iter_gensym1 0;;