Version française
Home     About     Download     Resources     Contact us    
Browse thread
hacks using camlp4
[ 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 <daniel.de_rauglaudre@i...>
Subject: Re: hacks using camlp4
> I lately checked the camlp4 preprocessor. I think this tool may have lots
> of useful applications, since it allows especially custom syntaxes for the
> input of certain kind of objects, in a more programmer-friendly fashion
> than just inputting raw data structures into the source code.
>
> ...
>
> I therefore have three problems:
> 
> 1. Precompiled expressions.
>    More generally, what would be needed would be some construction to
>    evaluate an expression as soon as it is possible.
> 
> 2. It would be nice if regexp precompilation could be done at compile or
>    preprocessing time (I was thinking of marshalling the precompiled
>    regexp, but I fear some C-library private data structure inside the
>    regexp type).

A solution is partial evaluation. I have implemented a syntax solution
below (working with ocaml syntax, not righteous one). The idea is to
automatically generate global declarations.

The important file is "partial.ml" which gives a general mechanism of
partial evaluations, in Ocaml (not righteous) syntax. The function
"Partial.eval" generates a global variable and returns a (syntax)
access to it.

Then, an example: partially evaluate strings concatenation (^) when
both parameters are pure strings. The file "concat.ml" changes the
predefined string concatenation to generate a global variable in the
interesting case.

Once these files compiled (see their sources further), here is an
example:

   $ cat foo.ml
   let foo x =
     function
       0 -> x
     | 1 -> x ^ "aa"
     | 2 -> "aa" ^ x
     | _ -> "aa" ^ "bb"
   ;;

No partial evaluation:

   $ camlp4o pr_o.cmo foo.ml
   let foo x =
     function
       0 -> x
     | 1 -> x ^ "aa"
     | 2 -> "aa" ^ x
     | _ -> "aa" ^ "bb"
   ;;

Partial evaluation, automatically generated by partial.cmo + concat.cmo

   $ camlp4o pr_o.cmo ./partial.cmo ./concat.cmo foo.ml
   let v_1 = "aa" ^ "bb";;
   let foo x =
     function
       0 -> x
     | 1 -> x ^ "aa"
     | 2 -> "aa" ^ x
     | _ -> v_1
   ;;

Remark: the last case is not pretty printed like this by Camlp4
version 1.06+1; this is just a pretty printing bug fixed in the
version 1.06+2 (patches soon available in the ftp distribution).
Anyway, it works.

Now, the files "partial.ml" and "concat.ml":

========================= partial.ml
open Pcaml;;

Grammar.warning_verbose := false;;
let o2b = function Some _ -> True | None -> False;;

(* Global declarations generated *)

let globals = ref [];;
let add_globals loc si =
  match !globals with
    [] -> si
  | g -> globals := []; <:str_item< declare $list:g @ [si]$ end >>
;;

(* Changes the declarations which holds expressions (cf etc/pa_o.ml) in
   order to declare before the possible global declarations *)

EXTEND
  str_item:
    [ [ "let"; r = OPT "rec"; "_"; "="; e = expr ->
          add_globals loc <:str_item< $exp:e$ >>
      | "let"; r = OPT "rec"; l = LIST1 let_binding SEP "and"; "in";
        x = expr ->
          let e = <:expr< let $rec:o2b r$ $list:l$ in $x$ >> in
          add_globals loc <:str_item< $exp:e$ >>
      | "let"; r = OPT "rec"; l = LIST1 let_binding SEP "and" ->
          add_globals loc <:str_item< value $rec:o2b r$ $list:l$ >>
      | e = expr ->
          add_globals loc <:str_item< $exp:e$ >> ] ]
  ;
END;;

(* Generates a global variable name. If conflict with program variables,
   the user must change the "v_" into something else. *)

let genvar =
  let cnt = ref 0 in
  fun () -> incr cnt; "v_" ^ string_of_int !cnt
;;

(* [Partial.eval loc e] generates a global variable equal to [e] and returns
   an access to it; [loc] is the location of [e] for possible semantic
   errors *)

let eval loc e =
  let v = genvar () in
  globals := !globals @ [ <:str_item< value $lid:v$ = $e$ >> ];
  <:expr< $lid:v$ >>
;;
========================= concat.ml
open Pcaml;;

(* Changes the syntax of "^" (cf etc/pa_o.ml) in order to generate
   a global variable, using [Partial.eval] when both parameters are
   pure strings *)

EXTEND
  expr: LEVEL "^"
    [ [ e1 = expr;
        f = [ op = "^" -> op
            | op = "@" -> op ];
        e2 = expr ->
          let e = <:expr< $lid:f$ $e1$ $e2$ >> in
          match f, e1, e2 with
            "^", <:expr< $str:_$ >>, <:expr< $str:_$ >> -> Partial.eval loc e
          | _ -> e ] ]
  ;
END;;
=========================

Compilations:
ocamlc -pp "camlp4o pa_extend.cmo q_MLast.cmo" -I `camlp4 -where` -c partial.ml
ocamlc -pp "camlp4o pa_extend.cmo q_MLast.cmo" -I `camlp4 -where` -c concat.ml

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

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