bobuhiro11's diary

3imp 伝統的マクロとバイトコードの変換

18 Feb 2014

伝統的マクロ

VMの方は触らずに,コンパイラの方だけを修正した. マクロ展開時と定義時にマクロテーブルに追加したり,参照したりするようにしている. トップレベル変数とは名前空間が違うけど,これでいいよな… ということで以下のようにletとかbeginとかいろいろ自由に拡張できるようになった. また時間があれば他のマクロシステムも増やしたい.

(define-macro let
 (lambda (binds . bodies)
  (cons (append (list 'lambda (map (lambda (x) (car x)) binds))
         bodies)
   (map (lambda (x) (cadr x)) binds))))

(let ((a 10)) (+ a 1))

バイトコードをかっこよく

今までのバイトコードは下のような感じで,次の命令を繋げていく構造だった. schemeで扱うには適しているようなだけど,後々VMをCで書きたいのでもっと線形な感じにしたい.

(frame (halt) (constant 2 (argument (constant 1 (argument (close 0 (refer-local 1 (return 2)) (apply)))))))

こんな感じにした. これでCとのインターフェイスも簡単にかけそう.

0 frame 9             戻り番地を9にしてフレームを作る
1 constant 2          定数2をaccumulatorにロード
2 argument            スタックにつむ
3 constant 1          定数1をaccumulatorにロード
4 argument            スタックにつむ
5 close 0 7 8         7番地から8番地を本体にするclosureオブジェクトをaccumulatorにロード
6 apply               そのclosureオブジェクトを適用
7 refer-local 1       2つめのローカル変数をロード
8 return 2            レジスタからローカル変数2つ取り除いて復元
9 halt                おわり

この作業はなかなか大変だった. 問題点はclosureだった. closureをトップレベル変数に束縛しない限りは,そのclosureに対応するバイトコードが存在するから, closureオブジェクトには単純に,#(開始番地 終了番地)を入れておけばいい. しかし,トップレベル変数に束縛した場合は,呼び出し時には開始番地や終了番地が分かったところでそのバイトコードの自体がないので,呼び出せない. 色々考えてみる

(ただし,コンパイラが吐いた命令コードの領域をROM,VM自身が後から追加した命令コードの領域をRAMと 勝手に呼んでる)

まだ考えることはある

この変換プログラムとVMコードを書き直した. 無駄にRAMを使わないようになったので,よかったよかった. VMは方は前よりも簡単になった. こんな感じになった.

schemeコード -----> マクロ展開後コード -----> 3imp VMコード -----> 線形なコード -----> VM上で実行
           macro.scm               compile.scm           linear.scm             vm.scm
           マクロテーブル                                                       トップレベルテーブル

comments powered by Disqus < 3imp トップレベル変数とプリミティブな関数 scheme VMをCで書く >