Ypsilonにあるguardの実装を試してみようとおもったら、パターンマッチでエラーがでた。
具体的にはこんなパターン。
;; guardの実装で使われていたものの一部 (define-syntax hoge (syntax-rules (else) ((_ (var clause ... (else clause2 ...)) b1 b2 ...) (do-something))))エラーとしてはellipsisが足りないらしい。
元々はMIT Scheme由来のものだったので、とりあえず本家でも試してみたが、こけた。一応moshでも試してみたがOKだったので、R6RS的にはOKなんだろうと推測。
ということは、足りない部分を補うか、新たに何とかするしかないということだ。ここは一発気合をいれて実装してみようと思い、まじめに仕様を読むことにした。
要求されているのは以下のとおり。
- P が下線(_)である場合
- P がバターン変数である場合
- P がリテラル識別子であり、マクロの出力に挿入される識別子以外で P と F が両方ともマクロ出力に現れたとき、 F が P と同一の束縛を参照している場合(ふたつの名前風の識別子がどちらも何の束縛も参照しないない場合、すなわち、どちらも未定義である場合も、両方とも同じ束縛を参照しているものと考える)。※1
- P が (P1 ... Pn) の形式でF が n 要素のリストで P1 から Pn に一致する場合
- P が (P1 ... Pn . Px) の形式で F が n 要素以上のリストないしは非真正リストで、最初の n 要素が P1 から Pn に一致し、 n 番目の cdr が Px に一致する場合。
- P が (P1 ... Pk Pe <ellipsis> Pm+1 ... Pn) の形式で、 <ellipsis> が識別子 ... で、かつ F が n 要素のリストで、最初の k 要素が P1 から Pk に一致し、 次の m - k 要素が Pe に一致し、残りの n - m 要素が Pm+1 から Pn に一致する場合。
- P が (P1 ... Pk Pe <ellipsis> Pm+1 ... Pn . Px) の形式で、 <ellipsis> が識別子 ... で、かつ F が n 要素のリストないしは非真正リストで、最初の k 要素が P1 から Pk に一致し、 次の m - k 要素が Pe に一致し、残りの n - m 要素が Pm+1 から Pn に一致し、最後の n 番目の cdr が Px に一致する場合
- P が #(P1 ... Pn) の形式で F が P1 から Pn に一致する n 個の要素のベクタである場合
- P が #(P1 ... Pk Pe <ellipsis> Pm+1 ... Pn) の形式で、 <ellipsis> が識別子 ... で、かつ F が n要素以上のベクタで、その最初の k 要素が P1 から Pk に一致し、次の m - k 要素がそれぞれ Pe に一致し、残りの n - m 要素が Pm+1 から Pn に一致する場合
- P がパターンデータ(リスト、ベクタ、シンボル以外のデータ)であり、 F が equal? 手続きの意味で等しい場合
基本的なパターンマッチの部分は同じで細かい違いは別にすればなんとかなりそうだろうか。
moshはよく知らないが、YpsilonとGaucheはsyntax-rulesで現れたパターンを一度コンパイル(後でパターンマッチがしやすいように情報を集めておくという意味)して、マクロが使用された際にその情報を元に展開していくという方法を取っている。
それとは別にMIT SchemeやChibi Schemeではsyntax-rulesが現れたらそれ自体をS式を返すS式に変換し、展開時には元のS式をマクロ展開用のS式(要するに展開器)に食わせている。
前者はマクロを展開するコンパイラが展開方法を知っていないといけないが、後者はマクロを展開するタイミングだけ知っていれば後はマクロが勝手に展開してくれる。現状Sagittariusは後者の方法を取っている。(なので、Sagittariusには組み込みのマクロというのはない。マクロはS式とコンパイル時の環境をペアで取るλ式に過ぎなかったりする)
とりあえず、パターンの部分の解析からはじめてみよう。