(* 式の型 *)
type exp =
...
| Var of string
| Let of string * exp * exp
変数 x は、Var "x" という形のデータとして表現する。
また、let x = e1 in e2 という式は、Let("x",e1,e2)という形のデータとして表現する。
なお、今回は、値(計算結果となる式)の種類は増えていないので、value型には変更はない。
次に、インタープリタに環境を導入する。 改訂版を eval3と呼ぶと、eval3は exp型の式だけでなく、 環境も引数として受けとり、value型の値を返す関数である。つまり、eval3 の型は、
eval3 : exp -> (string * value) list -> valueとなる。ここで、環境の型が (string * value) list である。
さて、eval3 を書いてみよう。 eval3 の引数に環境を表す env が加わるので, eval3 の中でeval3自身を再帰的に呼び出すところもすべて env を引数に追加する.
(* eval3 : exp -> (string * value) list -> value *)
(* let と変数、環境の導入 *)
let rec eval3 e env = (* env を引数に追加 *)
let binop f e1 e2 env = (* binop の中でも eval3 を呼ぶので env を追加 *)
match (eval3 e1 env, eval3 e2 env) with
| (IntVal(n1),IntVal(n2)) -> IntVal(f n1 n2)
| _ -> failwith "integer value expected"
in
match e with
| Var(x) -> 変数の処理は後で追加する
| IntLit(n) -> IntVal(n)
| Plus(e1,e2) -> binop (+) e1 e2 env (* env を追加 *)
| Times(e1,e2) -> binop ( * ) e1 e2 env (* env を追加 *)
| Eq(e1,e2) -> ...
| If(e1,e2,e3) ->
begin
match (eval3 e1 env) with (* env を追加 *)
| BoolVal(true) -> eval3 e2 env (* env を追加 *)
| BoolVal(false) -> eval3 e3 env (* env を追加 *)
| _ -> failwith "wrong value"
end
| Let(x,e1,e2) -> Let式の処理はこれから追加する
| _ -> failwith "unknown expression"
変更は、eval3 を呼びだす部分に、env という引数を追加しただけである。
さて,いよいよ Var と Letの処理を記述する.
let rec eval3 e env =
...
match e with
| Var(x) -> lookup x env
| ...
| Let(x,e1,e2) ->
let env1 = ext env x (eval3 e1 env)
in eval3 e2 env1
| _ -> failwith "unknown expression"
まず,変数 x の処理だが,環境 env の中の x に対応する値を取ってくればよい.
このために lookup関数を使う.env の中に x が含まれないときは,
x に値が束縛されていないという意味であるのでエラーとなる。
Let式の処理は少し複雑だが、以下の手順で行われる。
亀山幸義