Toy VM(8)
ビッグピクチャーを書こう。今やっていることを見失わないように。
( SECD マシン+コンパイラ) --> (命令列だけベクターにした SECD マシン+コンパイラ)--> (スタックもベクターにした SECD マシン+コンパイラ) --> (gauche VM の一部だけ動くようなマシン+コンパイラはgauche) -->
上と下は、SECD マシンを改造していく道と gauche vm に似せたものを作っていく道だ。どこかで一致すると素敵だ。もし何か作れれば、だけど。
リストというデータ構造はとてもすばらしい発明だ 。そしてリストによる SECD マシンはとても簡潔だ。
リストは何もかもがポインタだ。1次元の配列と添字は、なんて扱いが面倒なんだろう。そして、SECD マシンをベクトルで書くことも、ひたすらに面倒だ。SECD マシンのよさをひたすら無くしているだけ、という気すらする。だけど、それができるとずいぶんと理解が進むはずだ。リストはあまりに便利すぎるんだ。
LDF を何とか実装。入れ子が可能なリストならなんでもないけどベクトルだと二つの位置 -- プログラム本体の開始位置と、次の処理の開始位置をコンパイル時に決めておく必要がある、たぶん。
たぶん、なのは、セットで実装しないと関数実行までいかないからだ。いまのわたしのプログラムでは、命令を一つ加える際に、3カ所に手を入れなくちゃならない。リストにコンパイルする部分と、リストからベクトルにコンパイルする部分と、VM側だ。いかにもどんくさい作りだが、当面仕方が無い。
あとは AP と引数を実現するための CONS 。
;;
さすがにユニットテストが無いと、明日の自分が不安すぎる。とりあえず Practical Common Lisp のテストケースフレームワークをコピーしてテストを足していく。このフレームワークはいろいろ不満もあるけど、短いのがいいところだ。
コンパイラはまだだけど関数適用が動くようになった。手動コンパイルで。うん、大したことは無いな。ちょっとだけ理解が深まった気はする。
全く無駄なことをやったかもしれない、という気もする。でもいい。次だ。
VSECD> (run #(:NIL :LDC 3 :CONS :LDC 2 :CONS :LDF 10 18 :LD 1 2 :LD 1 1 :+ :RTN :AP :STOP)) #<SECD VM S: (5) E: ((2 3 . NIL)) C: #(NIL LDC 3 CONS LDC 2 CONS LDF 10 18 LD 1 2 LD 1 1 + RTN AP STOP) D:(NIL) >
大急ぎで後回しにしていたコンパイラと、ユニットテストを書く。本来は逆だ。LDF, RTN, AP が終わった。ああ、よく見ると scheme で書いたやつもなんてユニットテストが不足しているんだろう。今回はちゃんと書いておこう。あとは DUM と RAP が書ければ、プログラムをベクトルで持つ版は完了だ。fib は RAP が書けるまでは実行できないのが残念だ。
ダンプとスタック、環境はリストを使っている。これを全部一次元の配列にくっつけてやれば、ずいぶんと目指すあれに近づくはずだ。
ついでに、プロファイラを仕込んでおこう。とりあえず命令とその実行回数をハッシュテーブルに持つだけ。インストラクション定義を抽象化していたので、こういう追加が簡単にできる。まあインストラクション自体をオブジェクトにしてしまうのだろうけど、いまはまだ。それからおまけに近いけど、describe を実行したときに有用な情報を吐くように(もちろん、わたし自身のために!)describe-object も定義しておこう。print-object よりも詳しい情報を表示するためのものだ。
続く