Version française
Home     About     Download     Resources     Contact us    
Browse thread
Quotations in Caml-Special-Light and Caml-Light
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: -- (:)
From: Daniel de Rauglaudre <ddr@p...>
Subject: Quotations in Caml-Special-Light and Caml-Light

There is a way to have macros in Caml-Light and Caml-Special-Light. I
implemented them in my private version of Caml-Special-Light. This mail
explains how these macros work.

You can include, in your source, "quotation" expressions and patterns:
quotations are tokens bracketed by << and >> holding any sequence of
characters, just like strings.

These quotations are expanded at parse time by a user function
previously loaded by the compiler. This function, the "expander", gets
as parameter the string of the sequence of characters of the quotation
and returns a new string, the source code converted, which is parsed by
the compiler.

For example, this is a definition of an expander that will transform
<<MAX>> into the value 350 and <<MIN>> to the value 8:
        let expander = function
            "MAX" -> "350"
          | "MIN" -> "8"
          | _ -> raise Parsing.Parse_error
        ;;
After having loaded this function in the compiler as a quotation
expander, you can use quotations. For example, here is a possible
toplevel session:
         #<<MAX>>;;
	 - : int = 350
	 #<<MIN>>;;
	 - : int = 8
	 #<<GRR>>;;
	 Uncaught exception: Parse_error
	 Raised while expanding quotation
         #match 350 with <<MAX>> -> 12 | x -> x + 3;;
         - : int = 12
         #match 351 with <<MAX>> -> 12 | x -> x + 3;;
         - : int = 354

Now how to load an expander? There is a new function, named
"add_expander" in a new module named "quotation" in the compiler.

For example, to load the function "expander" defined above:
         Quotation.add_expander "oops" expander;;

Quotation.add_expander is of type "string -> (string -> string) -> unit".
It guaranties that the expander is well typed (string -> string). The
first parameter is a name for the quotation (the usage of this name is
not described in this message).

In the toplevel, the order of operations is:
        load the expander function
        call Quotation.add_expander
        use quotations

Using the compiler, you have 2 steps: first make a file containing
the expander code and the call to Quotation.add_expander
(e.g. "foo.ml"):
        let expander =
          function
	    "MAX" -> "350"
	  | "MIN" -> "8"
	  | _ -> raise Parsing.Parse_error
	;;
	Quotation.add_expander "oops" expander;;

Just do the compiling phase (using the -c option):
        cslc -c foo.ml

Now, as a second step, you can use the "oops" quotations in
Caml-Special-Light source files. For example this one, "test.ml":
 	let f =
 	  function
 	    <<MAX>> -> "it is max"
 	  | <<MIN>> -> "it is min"
 	  | x -> "it is " ^ string_of_int x
 	;;
 	let test x = Printf.printf "%d: %s\n" x (f x); flush stdout in
 	test 350;
 	test <<MAX>>;
 	test 8;
 	test <<MIN>>;
 	test 25;;

Compile it with the option "-L foo.cmo". This is a new option telling
the compiler to load "foo.cmo" before starting the compilation:
        cslc -L foo.cmo test.ml

The execution of this test gives:
	350: it is max
	350: it is max
	8: it is min
	8: it is min
	25: it is 25

Remarks:

   * The quotation expanders are just of type "string -> string",
     independantly from the parsing technology. You can use lex/yacc,
     streams, or, like here, simple string pattern matchings. Etc.
   * The function Quotation.add_expander, although visible in sources
     like library interfaces, is local to the compiler. It is not
     possible to link it:
        $cslc foo.cmo
        Error while linking foo.cmo: Reference to undefined global `Quotation'
     its only usage is after the -L option, or in the toplevel.
   * The values defined in expanders are not visible in the compiled files
     using quotations: they need not to be. Only the expander code, loaded
     by the compiler is indirectly visible, since it is run when expanding
     quotations. In the example, you cannot access the function "expander",
     defined in "foo.ml", in "test.ml", unless you explicitely open the
     interface of "foo.ml", which would be unuseful since "foo.cmo" is
     not linkable: yes, in the toplevel...
   * The expander can generate any available input syntax. The example
     gives very simple syntax: "350", "8". But it can generate any
     expression or pattern code. This code is parsed by the compiler
     as expression or pattern, depending of its context.
   * All the source locations of the tree generated by the expander
     are "the quotation itself". When printing a typing error, for
     example, the whole quotation is showed. We are studying solutions
     to allow expanders to specify more precise locations.


Implementation

The implementation is quite simple, the new module "quotation.ml" in
the compiler (about 130 lines). Some modifications in the lexer and
the parser to accept quotations. And the function to load .cmo files
in the core of the compiler (got from the toplevel sources). The
examples given above, including the call to cslc, are real examples:
it works!

--------------------------------------------------------------------------
 Daniel de RAUGLAUDRE

 Projet Cristal - INRIA Rocquencourt
 Tel: +33 (1) 39 63 53 51
 Email: daniel.de_rauglaudre@inria.fr
 Web: http://pauillac.inria.fr:80/~ddr/
--------------------------------------------------------------------------