interpret :: Lang -> Lang -> Computer Lang interpret m l = Run $ \p -> let (p', l') = runner (run l) p in (I m p' l', m)
実行結果。
*Main> showComp (P S) $ return S >>= interpret T >>= interpret M "(I M (P S) S,M)" *Main> showComp (P S) $ return S >>= interpret T >>= interpret M >>= run "(P S,S)"
何のことはなく、run を interpret の中に一個入れただけ。
コンパイラもさっきのだとまずかった。コンパイラをコンパイルする、みたいなことをやると
*Main> showComp (P A) $ compile C B A >>= compile E D "(I E (I D (I C (I B (P A) A) B) C) D,E)"
となって、run は4個必要だし、言語の適用の仕方もおかしくなる。
書き直してみると、こんな感じ。更に書き直しました。
compile :: Lang -> Lang -> Lang -> Computer Lang compile m t s = Run $ \p -> let (p', s') = runner (run s) p in (I m (I t p' s') t, m)
で、実行結果。
*Main> showComp (P A) $ compile C B A >>= compile E D "(I E (I D (I B (P A) A) B) D,E)" *Main> showComp (P A) $ compile C B A >>= compile E D >>= run "(I D (I B (P A) A) B,D)" *Main> showComp (P A) $ compile C B A >>= compile E D >>= run >>= run "(I B (P A) A,B)" *Main> showComp (P A) $ compile C B A >>= compile E D >>= run >>= run >>= run "(P A,A)"
Cで書かれた、AからBへのコンパイラを、Eで書かれた、CからDへのコンパイラでコンパイルする。
これを実行すると(run一回適用)、Dで書かれた、AからBへのコンパイラができあがる。言語D上で2回目のrunを適用して、出てきたプログラム(Bで書かれている)を、B上でもう一度runすると、所望の動作が得られる。(更に続く)
p.s. コンパイラの教科書に良く出てくる、T字型の図式(クロスコンパイルとか考えるときに使うやつ)って、なんていうんでしたっけ?(現在帰省中にて、手元に資料なし)あれ書きながら考えてたんですが、名前を失念しちゃいました。