組込みの手続きをサポートする - Scheme VM を書く

cons, +, number?, pair? などの手続きをサポートしたい。
やることは大まかに2つに分けられる。

  1. 各手続きが他の手続きから見えるようにする
  2. 名前にマッピングされる手続き本体を記述する

ある手続き proc から number? が見えるというのは

;; 外側に free variable number.

(lambda ()
  (proc)
)

と呼び出したときに free variable として number? が存在すれば実現できるのではないかな。
これは display closure に number? の参照を放りこんでおけば良さそう。


具体的にはコンパイル時に

(compile '(number? 3) '(() number?) '() '(halt))

e に number? を入れておくと、カレントフレームからの index を参照する形でコードがコンパイルされる。
compile の第2引数は quote されているので number? というシンボルを登録しているだけなことに注意。


実際の number? 本体は display closre の free variable として VM に渡される。
スタックに number? の本体を push し、その後 closure 命令で closure を作る。

(define (evalute-with-numberp x . args)
  (let* ((code (compile x '(() number?) '() '(halt)))
        (dummy (push number? 0))
        (clo (closure code 1 1)))
        (VM clo code 0 clo 0)
))

ここまでやれば、VM 実行時に本来 closure が置かれる場所に number? が直接置かれるようになる。

VM も変更が必要で (apply) 時に accumulator に置かれた closure を参照している部分で分岐。

     [apply ()
       (if (procedure? a)
           (VM (apply a (list (index s 0)))
               '(return 1)
               f
               c
               s)
           (VM a (closure-body a) s a s))
           ]

accumulator から number? を取り出し、スタックから引数を取り出す。
その後 native の apply で実際に手続きを呼んで accumulator 置いて (return) で処理を継続する。

細かい話をいくつか

  • 任意の数の組込み手続きを登録できるようにする必要がある
  • apply 時に引数の数が必要になるのでコンパイラを変更する
  • 引数の数に応じて (return) の引数を変更する
  • C++ で書くことを考えると
    • 処理系ネイティブの手続きを表す型が必要
    • accumulator に結果を返すときも適切な型に変換する必要がある

というのを実装しよう。