R6RSという奴は…
R6RSではR4RSで登場したsyntax-caseが復活すると聞いていました。
確かに、R6RSの9.2節に
「syntax-caseでもdatum->syntaxを使わなければ衛生的なマクロが定義できる(12.6節参照)」
という説明がありましたが、syntax-caseという言葉が登場するのは恐らくそこだけで、
おまけに、R6RSは11章までしかありませんでした。
でも、よく見てみたら参照と書いてあるのは、標準ライブラリ(*)の12.6節でした。
R6RSの本体が90ページ(付録を除いても60ページ)もあるのに、
標準ライブラリの説明も70ページもあります。
英語がろくに読めない私にはあまりにも過酷です。
仕様書が薄いのがSchemeの特徴だったはずじゃ……
(*) Revised6 Report on the Algorithmic Language Scheme — Standard Libraries
—-
でも、syntax-caseが仕様に戻ったというのは嬉しいです。
syntax-ruleだけじゃ、私には使いこなせない気がするからです。
というのも、去年の夏休み、Common Lispでyaccもどきを作ったんです。
ただ、yaccのようにファイルに記述したBNFを読み取ってソースを吐き出すのではなく、
S式として記述してあるBNFを入力とし、Lispのプログラムを吐き出すマクロとして作りました。
例えば、次のような文法の構文解析器を生成したいとします。
E→E+T E→T T→T*F T→F F→<E> F→i
この場合は、次のように書きます。
(ここでは、構文解析と同時に計算もするものを作ってます)
(setf (symbol-function 'parser) (make-parser (E (E '+ T) (+ $1 $3) (T) $1) (T (T '* F) (* $1 $3) (F) $1) (F ('< E '>) $2 ('i) $1)))
そしたら、こんな関数が作られます。
#<CLOSURE :LAMBDA (#:G267) (LET ((#:G266 '(#:G253)) (#:G268 (POP #:G267))) (LABELS ((#:G265 (#:G269) (IF (EQ #:G268 '#:G252) (CAR #:G267) (CASE #:G269 ((#:G264) (CASE #:G268 ((#:G230) (LET (($1 NIL)) (SETF $1 (PROGN (POP #:G266) (POP #:G266) (POP #:G266))) (LET ((#:G292 (CAR #:G266))) (PUSH #:G268 #:G267) (SETF #:G268 'T) (PUSH $1 #:G267) (#:G265 #:G292)))) ((*) (LET (($1 NIL)) (SETF $1 (PROGN (POP #:G266) (POP #:G266) (POP #:G266))) (LET ((#:G291 (CAR #:G266))) (PUSH #:G268 #:G267) (SETF #:G268 'T) (PUSH $1 #:G267) (#:G265 #:G291)))) (以下略)
まあ、正直いいものとは言いがたいのですが、
とりあえず、動くものを作ることができました。
けど、実はCommon Lispのマクロをまともに使ったのはこれが初めてでした。
それでも問題なく書けたのは、マクロを使ったといっても、それは最初に入力を受け取る箇所だけで、
後は普通にLispの関数を書きなぐっただけだったからです。
だから、ボトムアップに開発を進めることができて、
関数単位でテストができたので、Cなんかで同じものを作るよりも、
圧倒的にデバッグが楽だったと思います。
しかし、これをR5RS通りのsyntax-caseがないSchemeで作ろうとすると、
syntax-ruleでマクロを書く必要があり、
syntax-ruleでは展開コードはテンプレートで記述する必要があり、
テンプレートからは普通のSchemeの関数を呼び出したりはできないので……
と、考えただけでも恐ろしいことになりそうです。
という訳で、Schemeを使ってマクロ展開コードを生成できるsyntax-caseが
復活したことに私は賛成です。(といっても、syntax-caseをいまいち分かってないんですが(笑))