TinySCHEME のソースを読む -4 トップレベルからの処理の流れ

TinySCHEME のソースを読む -1 セルの構造
TinySCHEME のソースを読む -2 シンボル、環境
TinySCHEME のソースを読む -3 TinySCHEME の処理エンジン
TinySCHEME のソースを読む -4 トップレベルからの処理の流れ
TinySCHEME のソースを読む -5 define の例
tsdbg TinyScheme 用デバッグ extension

処理の流れ

ここからは、トップレベルからの処理の流れを、オペレーション毎におおまかにメモ。

OP_T0LVL

このオペレーションが最初に呼ばれる。
s_save() で、OP_T0LVL( 自分自身 ), OP_VECTOR, OP_T1LVL を dump に積んでおき、OP_READ_INTERNAL に s_goto() する。

OP_READ_INTERNAL

トークンを得る処理。
sc->tok に token() の戻り値を代入する。
EOF なら終了処理へ。
そうでなければ、OP_RDSEXPR へ s_goto()

OP_RDSEXPR

入力された sc->tok の評価を行い、まだ読み続けるか、それとも戻るか判断する。
OP_RDSEXPR からさらに処理が分かれる場合があるが、最終的に戻るオペレーションは( OP_RDSEXPR が最初に呼ばれた時点で一番最後に dump に積まれている)OP_T1LVL である。

OP_RDLIST

リストを作る処理である。作られたリストは sc->args へ代入される。

OP_T1LVL

eval するための準備をする。
OP_T1LVL が呼ばれた時点で、sc->value には入力されたプログラムが入っている。
sc->value を sc->code に代入し、OP_EVAL へ s_goto()

OP_EVAL, OP_REAL_EVAL

eval が評価するのは、sc_code である。
1. code がシンボルなら、envir からシンボルの値をひっぱってきて返す
2. code が cons セルなら、code の car 部に注目する
2.1 code の car 部が syntax の場合、syntax のオペレーションへ s_goto() する
( if、lambda, define 等が syntax である)
2.2 syntax で無ければ、s_save() でOP_E0ARGS へ処理を引き継ぐ。code を code の car 部に更新し、OP_EVAL を続ける
3. code がシンボルでも cons セルでもなければ、そのまま返す

OP_E0ARGS, OP_E1ARGS

引数を一つ一つ評価していく。評価し終えた引数のリストは sc->args に格納され、OP_APPLY へ。

OP_APPLY

sc->code が何であるかにより処理が分かれるが、ここではユーザが定義した関数など、T_CLOSURE のセルの場合のみ説明する。
OP_APPLY は新しい環境を作る。
そして、code (プログラム)で定義された引数と、実際にユーザが与えた引数から slot を作り、環境へ追加する処理を行う。

たとえば、(my-sum 1 2) を呼び出した場合の envir は以下のようになる:

処理は OP_BEGIN に引き継ぎ、OP_BEGIN、OP_EVAL で評価された値が戻される、というのを繰り返す。
次に OP_EVAL に処理が移ったとき、x と y はそれぞれ新しい環境において、上図のように実際の値を含んだ状態で定義されている。
よって eval は、x と y の値を envir からひっぱってきて返すことが出来るのである。