インタプリタとコンパイラの続き
「最初からごまかしていた点」を直した実装(のつもり):
let run lang (`Prog(lang', f)) x = (* `Prog(lang', f)は言語lang'で書かれたプログラムf。xはfへの入力。*) if lang = lang' then f x else failwith "invalid program" let interpreter mlang slang = (* 言語mlangで書かれた、slangのためのインタプリタ *) `Prog(mlang, fun sprog -> run slang sprog) let compiler mlang slang tlang = (* 言語mlangで書かれた、slangからtlangへのコンパイラ *) `Prog(mlang, fun sprog -> `Prog(tlang, run slang sprog)) (* 簡単な例: # run `M (interpreter `M `S) (`Prog(`S, fun x -> x + 1)) 2 ;; - : int = 3 # run `T (run `M (compiler `M `S `T) (`Prog(`S, fun x -> x + 1))) 2 ;; - : int = 3 *) (* bonotake氏の例: # let cprog = compiler `C `A `B ;; (* Cで書かれた、AからBへのコンパイラ *) val cprog : [> `Prog of [> `C ] * (_[< `Prog of _[> `A ] * ('_a -> '_b) ] -> [> `Prog of [> `B ] * ('_a -> '_b) ]) ] = `Prog (`C, <fun>) # let eprog = compiler `E `C `D ;; (* Eで書かれた、CからDへのコンパイラ *) val eprog : [> `Prog of [> `E ] * (_[< `Prog of _[> `C ] * ('_a -> '_b) ] -> [> `Prog of [> `D ] * ('_a -> '_b) ]) ] = `Prog (`E, <fun>) # let dprog = run `E eprog cprog ;; (* cprogをDへコンパイル *) val dprog : [> `Prog of [> `D ] * (_[< `Prog of _[> `A ] * ('_a -> '_b) ] -> _[> `Prog of _[> `B ] * ('_a -> '_b) ]) ] = `Prog (`D, <fun>) # let aprog = `Prog(`A, fun x -> x + 1) ;; (* λx.x+1を表すAのプログラム *) val aprog : [> `Prog of [> `A ] * (int -> int) ] = `Prog (`A, <fun>) # let bprog = run `D dprog aprog ;; (* aprogをBへコンパイル *) val bprog : [ `Prog of _[> `B ] * (int -> int) ] = `Prog (`B, <fun>) # run `B bprog 2 ;; (* bprogを実行 *) - : int = 3 *)
追加。bonotake氏のもう一つの例。Mの上でTのインタプリタを動かし、その上でSのインタプリタを動かす。
# run `M (interpreter `M `T) (interpreter `T `S) (`Prog(`S, fun x -> x + 1)) 2 ;; - : int = 3
余談。この手の話はマシン語がボトムに来て終わってしまいがちですが、実際はCPUの内部でも解釈実行やJITコンパイルをしているし、マイクロコードを解釈実行するハードウェアも「物理」によって解釈実行されているのではないかと。