これすっげぇ難しい。。

自前の Scheme フロントエンドを実装する上ではsyntax-rulesの実装はどうしても避けて通れない。syntax-rulesは一応 Scheme 標準( R6RS とかR7RS)的には唯一のマクロ機構なので、これが無いとマクロが書けない。 今回は↓のような構成を考えてみた。 ... ちょっとクールな図が思いつかなかったので非常に解りづらいが、1つのsyntax-rulesルールから、

の組が1エントリとなる。更に、環境全体を1つのベクタで表現すれば、indexは先頭のオフセットだけ記録すれば良いことになる。 (実際にIRに記録されるものは、syntax-rulesの記述全体で有効な環境と、clause(パタン/テンプレートの組)のみで有効な環境の2つに分かれることになる。)

パタン / テンプレート要素表現

ここが未だできていない。

マクロを実際に使用した際に挿入されるコードの単位がパタン/テンプレート要素といえる。



(※ 図では#(5 2 1 ...) となっているが、↓のようにMLISTは 4 を当てているので、正しくは#(4 2 1 ...)。)

IRの図で赤い箱になっているところがパタン/テンプレートの要素クラスを表わしていて、この要素クラスが結構な種類に及ぶ。たとえば、ここではクラスMLISTのパタン要素を表わしている。

MLISTは ... を含むリストで、

... より前の要素 ... となっている要素(図では黄色) ... より後の要素

の3コンポーネントで表現される。このとき、vector3つでそれぞれの要素を表現するのではなく、要素の数を別途記録することでvectorを1つに抑えている。このようにしてvectorの数をケチることによって、ライブラリをロードした際のメモリ消費を抑えることができる。。。

実際には、今までの図に出てきていないようなクラスが当然存在する。

パタン要素クラス

1 : ( p0 . p1 ) => PAIR [ p0 : p1 ] 2 : ( p0 p1 p2 ) => LIST [ p0 p1 p2 ] 3 : ( p0 p1 p2 . p ) => DLIST [ p0 p1 p2 : p ] 4 : ( p0 p1 ... p2 p3 ) => MLIST [ p0 : p1 : p2 p3 ] 5 : ( p0 p1 ... p2 p3 . p ) => MDLIST [ p0 : p1 : p2 p3 : p ] 6 : #( p0 p1 p2 ) => VEC [ p0 p1 p2 ] 7 : #( p0 p1 ... p2 p3 ) => MVEC [ p0 : p1 : p2 p3 ]

MLISTはmeta listの略(適当に付けた)。DLISTはdotted listの略。DLISTの最終要素を空リストにすればLISTと等価になるけど、実際問題として殆どのsyntax-rulesマクロはDLISTを使わないのでクラスを分割してその分容量をケチっている。

テンプレート要素クラス

1 : ( a0 . a1 ) => PAIR [ a0 : a1 ] 2 : ( a0 a1 ) => LIST [ a0 a1 ] 3 : ( a0 a1 . a2 ) => DLIST [ a0 a1 : a2 ] 4 : a0 ... => SPLICE [ a0 ] 5 : SPPAIR 6 : SPLIST 7 : SPDLIST 8 : #( a0 a1 ) => VEC [ a0 a1 ] 9 : #( a0 a1 ... a2 a3 ) => MVEC [ a0 : a1 : a2 a3 ] 10 : SPVEC 11 : SPMVEC

... テンプレート要素クラスは考え中。SPLICEは現在展開中のリストまたはベクタにspliceする形でコードを挿入する。パタンは1エントリ内に複数のellipsisを含むことは無いが、テンプレートの方では普通に ... を複数含むケースが存在できるため、専用の配慮が必要になる。たぶんMVECも独立させる必要は無いな。。

例えば、テンプレート

( a b ... c ... )

は、

%root = LIST [ a %1 %2 ] %1 = SPLICE [ b ] %2 = SPLICE [ c ]

のようにエンコードされる。正直もうちょっと良いエンコードが有るんじゃないかという気がしてならない。