Version française
Home     About     Download     Resources     Contact us    
Browse thread
Minim compiler as a camlp4 macro
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: -- (:)
From: Jon Harrop <jon@f...>
Subject: Minim compiler as a camlp4 macro

Mark Tarver posted an open challenge for people to write programs to evaluate 
Minim programs, a simple imperative language.

The following is my attempt at writing a Minim compiler in OCaml using the new 
Camlp4. The result is remarkably short, IMHO:

open Camlp4.PreCast;;
open Camlp4.PreCast.Syntax;;

let mmstatement = Gram.Entry.mk "mmstatement";;
let mmvalue = Gram.Entry.mk "mmvalue";;
let mmtest = Gram.Entry.mk "mmtest";;
let mmident = Gram.Entry.mk "mmident";;

let rec compile _loc tag e = function
  | [] -> <:binding< $lid:tag$ = fun () -> $e$ >>
  | `TL e'::`T tag'::t ->
      let bs = compile _loc tag' <:expr< () >> t in
      <:binding< $lid:tag$ = fun () -> $e$; $e'$ and $bs$ >>
  | (`TL e' | `E e')::t -> compile _loc tag <:expr< $e$; $e'$ >> t
  | `T tag'::t ->
      let bs = compile _loc tag' <:expr< () >> t in
      <:binding< $lid:tag$ = fun () -> $e$; $lid:tag'$() and $bs$ >>;;

let compile _loc ss = compile _loc "entry" <:expr< () >> ss;;

EXTEND Gram
  str_item: LEVEL "top"
  [ [ "MINIM"; "("; ss=LIST1 mmstatement; ")" ->
        <:str_item< let rec $compile _loc ss$ >>
    ] ];
  mmstatement:
  [ [ "("; x=mmident; "is"; y=mmvalue; ")" -> `E <:expr< $lid:x$ := $y$ >>
    | "("; "++"; x=mmident; ")" -> `E <:expr< incr $lid:x$ >>
    | "("; "--"; x=mmident; ")" -> `E <:expr< decr $lid:x$ >>
    | "("; "if"; p=mmtest; "then"; t=mmstatement; "else";
f=mmstatement; ")" ->
        (match t, f with
         | `TL t, `TL f -> `TL <:expr< if $p$ then $t$ else $f$ >>
         | (`TL t | `E t), (`TL f | `E f) ->
             `E <:expr< if $p$ then $t$ else $f$ >>
         | _ -> invalid_arg "Tag in 'if' expression")
    | "("; "goto"; t=mmident; ")" -> `TL <:expr< $lid:t$() >>
    | "("; "print"; s=STRING; ")" ->
        let s = String.escaped s in
        `E <:expr< print_string $str:s$ >>
    | "("; "print"; x=mmvalue; ")" -> `E <:expr< print_int $x$ >>
    | "nl" -> `E <:expr< print_newline() >>
    | "("; "input"; x=mmident; ")" ->
        `E <:expr< $lid:x$ := int_of_string(input_line stdin) >>
    | tag=mmident -> `T tag ] ];
  mmvalue:
  [ [ x=mmident -> <:expr< ! $lid:x$ >>
    | n=INT -> <:expr< $int:n$ >> ] ];
  mmtest:
  [ [ "("; f=mmvalue; "<"; g=mmvalue; ")" -> <:expr< $f$ < $g$ >>
    | "("; f=mmvalue; "="; g=mmvalue; ")" -> <:expr< $f$ = $g$ >>
    | "("; f=mmvalue; ">"; g=mmvalue; ")" -> <:expr< $f$ > $g$ >>
    | "("; f=mmtest; "and"; g=mmtest; ")" -> <:expr< $f$ && $g$ >>
    | "("; f=mmtest; "or"; g=mmtest; ")" -> <:expr< $f$ || $g$ >>
    | "("; "not"; f=mmtest; ")" -> <:expr< not $f$ >> ] ];
  mmident:
  [ [ x=LIDENT -> "_"^x
    | x=UIDENT -> "_"^x
    | "end" -> "_end" ] ];
END;;

Evaluating that in an OCaml top level lets you embed MINIM code in your OCaml:

let _x, _y = ref 0, ref 0;;

MINIM(
(print "Add x and y (what a feat!)")
 nl 
 (print "Input x: ") 
 (input x) 
 nl 
 (print "Input y: ") 
 (input y) 
main 
 (if (x = 0) then (goto end) else (goto sub1x)) 
 
sub1x 
 (-- x) 
 (++ y) 
 (goto main) 
 
end 
 nl 
 (print "The total of x and y is ") 
 (print y) 
 nl 
);;

The MINIM code is translated into OCaml ASTs on-the-fly and evaluated, giving 
a high-performance Minim environment.

-- 
Dr Jon D Harrop, Flying Frog Consultancy Ltd.
OCaml for Scientists
http://www.ffconsultancy.com/products/ocaml_for_scientists/?e