Open1

手抜きの限りを尽くしてScheme処理系を作りたい(再)

okuokuokuoku

ちょっと前回( https://zenn.dev/okuoku/scraps/98d91b70370703 )スケジュールの都合で何もせずに終わってしまったので再度。。

Schemeサブセットの設計

作業を始める前にSchemeサブセットの仕様を考えておく。

GenericSchemeとFakeScheme

実装を適当に済ませるために、サブセットをGenericSchemeとFakeSchemeの2段階に分ける。GenericSchemeは define-macro がある処理系、FakeSchemeは無い処理系と要約できる。

実際にSchemeプログラムを実行するためには define-macro で定義されるマクロを展開(expand)する必要があり、これの実装が地味に面倒なのでC/C++ではFakeSchemeの方だけを実装することにしたい。

で、今回は主に GenericScheme仕様のプログラムをFakeScheme仕様のプログラムに変換するための expander をSchemeで実装する。GenericSchemeは既存のs7とかGaucheのようなScheme実装の厳密なサブセット、FakeSchemeはGenericSchemeの厳密なサブセットになるため、既存のScheme実装をFakeScheme実装と見做してexpanderをテストできる。

GenericSchemeにおける文脈

Schemeプログラム内部には3つの文脈 1) トップレベル 2) lambda 3) シーケンス が存在する。トップレベルはプログラムの開始時点のものであり、 lambda は lambda とか let のような予約語で遷移する。シーケンスはただの式。

FIXME: 後でR7RSを確認してScheme標準と用語を揃える

トップレベル には define define-macro および通常の式を置くことができる。トップレベルに define または define-macro したものはライブラリとしてエクスポートできるという特徴があるため特別扱いする。

lambda には define および通常の式を置くことができる。以前BiwaSchemeがこの位置に define-macro を置けなかったためGenericSchemeでは置けないルールにしているが他の大半のScheme実装では問題なく置けるのでこの制約は外してしまっても良いかもしれない。また、 lambda内での define はlambdaを抜けた際に無効化する必要がある ため、特別扱いしている。更に、 define 以外の式が現われたら以降は通常の式のみ という制約がSchemeの規格として有り、これによりコンパイルのコストを下げられるようになっている。

シーケンス は通常の式のみを置くことができる。