Browse thread
Minim compiler as a camlp4 macro
- Jon Harrop
[
Home
]
[ Index:
by date
|
by threads
]
[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: | 2007-08-17 (09:47) |
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