http://d.hatena.ne.jp/komamitsu/20091012/1255307045

以前、ocamlyacc辺りを弄っていて萌えてこなくて挫折、というケースがあったのだけど、構文解析が云々という以前にまともに構文設計らしきことをしていないのが、そもそもの原因ではないかと。

その辺を少し練習してみることに。


最初は四則演算に変数を加えた簡単なものをやってみる。

open Printf

type symbol = string

type stmt =
  | Expr of expr
  | Bind of symbol * expr
  | Print of expr
and expr =
  | Add of expr * expr
  | Sub of expr * expr
  | Mul of expr * expr
  | Div of expr * expr
  | Atom of atom
  | Appl of symbol
and atom =
  Int of int

let rec eval_expr env = function
  | Add (a, b) -> (eval_expr env a) + (eval_expr env b)
  | Sub (a, b) -> (eval_expr env a) - (eval_expr env b)
  | Mul (a, b) -> (eval_expr env a) * (eval_expr env b)
  | Div (a, b) -> (eval_expr env a) / (eval_expr env b)
  | Atom a -> (match a with Int a -> a)
  | Appl k -> Hashtbl.find env k

let rec eval_stmt env stmt =
  ignore (
    match stmt with
    | Expr e -> ignore (eval_expr env e)
    | Bind (s, e) -> Hashtbl.replace env s (eval_expr env e)
    | Print e -> printf "=> %d\n" (eval_expr env e)
  );
  env

let sample_stmt_list =
(*
 * x = 17 - 13;
 * y = 3 + 7;
 * 31 + 11;
 * print (5 * x / y);
 *)
  [
    Bind ("x", Sub (Atom (Int 17), Atom (Int 13)));
    Bind ("y", Add (Atom (Int 3), Atom (Int 7)));
    Expr (Add (Atom (Int 31), Atom (Int 11)));
    Print (Div (Mul (Atom (Int 5), Appl "x"), Appl "y"))
  ]

let _ =
  let empty = Hashtbl.create 10 in
  List.fold_left
    (fun env stmt -> eval_stmt env stmt)
    empty sample_stmt_list  (* stdout : "=> 2" *)

これだけでも結構悩めてとても楽しい。とりあえずprintが文になっているのが寂しいけれど。

あと、世の中のプログラミング言語の構文仕様が気になってきたのは、なかなか良い兆候だと思う。