SICP 5.2.1 The Machine Model
ここからはシミュレータの解説。モデルとしては完全にオブジェクト指向的。
ノート
make-machineではメッセージパッシングに応答する手続きを作る。つまりメソッドを持ったオブジェクト。実際にはmake-new-machineがオブジェクトを作り、make-machineではそれに対して'allocate-register、'install-operations、'install-instruction-sequenceと言うメッセージを送ってマシンを作っている。それぞれのメッセージは手続きを返す様に実装されている。
Registers
make-registerもオブジェクトとしての手続きを返す。'getと'setのメッセージを受ける。メッセージに対応するメソッドは用意されずディスパッチャに直に書いてある。ローカルな変数contentsにその値を保存する。Racketでもローカルな変数に対してはset!が使える。
The stack
スタックもオブジェクトとしての手続きを返す。'push、'pop、'initializeのメッセージを受ける。メッセージに対応するそれぞれのメソッドが用意されている。データは当然リストで保存する。
The basic machine
これもオブジェクトとしての手続きを返す。
ユーザーが定義するレジスタとは別ににpc(プログラムカウンタ)とflag(testの結果を保存して条件分岐に使用)を用意。レジスタのリストにも予め登録しておく。命令リストにはスタックを初期化する命令を用意する。
'startの時点でpcに命令リストを保存してexecuteを呼び出す。executeではpcのcarを取り出して実行。各命令はinstruction-execution-procが返す手続きとなり、それを実行すると結果としてpcが更新されるので、もう一度executeを呼び出す。命令リスト自体が変更されることは無いので、pcはそのなかのある部分から以降のリストとなる。pcはmake-new-machineのローカル変数だがオブジェクトそのものはレジスタリストから探せるので外部から変更可能。
5.2.2 The Assembler
4.1.7で学んだ様にプログラムの解釈と実行は分離することが出来る。ここでもプログラムを解釈して命令の列に変換してから実行する。
assembleからextract-labelsを呼び出してプログラムを解釈する。assembleがextract-labelsに渡す第二引数が最終的に呼ばれ、その引数instsとlabelsに命令トラベルのリストが渡される。
extract-labelsはプログラムのcarを見て、シンボル(すなわちラベル)だったらラベルのリストに、そうでなければ命令リストにそれぞれ追加する手続きを第二引数としてextract-labelsを再帰呼び出しする。再帰の戻りでリストを構築して、最後にassembleから渡した手続きが呼ばれる。
update-insts!は命令リストの一つ一つに対してmake-execution-procedureを呼び出して、set-instruction-execution-proc!でinstのcdrに手続きを保存する。make-execution-procedureは次節に登場。
Exercise 5.8
(define machine (make-machine '(a) '() '(controller start (goto (label here)) here (assign a (const 3)) (goto (label there)) here (assign a (const 4)) (goto (label there)) there)))
で動かしてみると
> (start machine) 'done > (get-register-contents machine 'a) 3 >
最初に見つけたhereに飛ぶ。
extract-labelsでラベルを登録する際に既に存在しているかを確認する。
(define (extract-labels text receive) (if (null? text) (receive '() '()) (extract-labels (cdr text) (lambda (insts labels) (let ((next-inst (car text))) (if (symbol? next-inst) (if (assq next-inst labels) (error "Multiply defined label: " next-inst) (receive insts (cons (make-label-entry next-inst insts) labels))) (receive (cons (make-instruction next-inst) insts) labels)))))))
マシンを定義しようとすると
> (define machine (make-machine '(a) '() '(controller start (goto (label here)) here (assign a (const 3)) (goto (label there)) here (assign a (const 4)) (goto (label there)) there))) . . Multiply defined label: here >
エラーで止まる。