R5RSマクロその2

R5RSマクロは、

  • 式の構造のパターンマッチ
  • マッチした時に展開されるテンプレート

という形で定義されるってのはこの前やった。
しかし、式の構造だけではなく、同じ字句かどうかで判定しなければならないことがある。それがcondで、なぜならelseという字句を見つける必要があるから。
特定の字句か否かでパターンマッチングする場合に使うのが、syntax-rulesの第1引数。今まではずっと空リストを与えてきた。


さて、condの定義を考える前に、以下のような簡単なマクロfooを考えてみる。

gosh> (foo hoge "hello")
"hello"
gosh> (foo hoge 1)
1
gosh> (foo hoge 'bar)
bar

2つの引数をとる。一つ目は必ずhogeという字句、第2引数は任意の式。特に何もせずに第2引数へと展開される。
このマクロの定義は次のようになる。

(define-syntax foo
  (syntax-rules (hoge)
    ((foo hoge e) e)))

第1引数のリストに含まれる字句は、字面が同じかどうかチェックされる。

gosh> (foo bar 42)
*** ERROR: Compile Error: malformed foo: (foo bar 42)
"(stdin)":8:(foo bar 42)

Stack Trace:
_______________________________________

となる。


では以上のことを応用して、condを定義することを考えてみる。
condは以下のような形をしている。ただし、elseのリストは無くても良いし、値1から値n+1も無くても良い。

(cond
  (条件1 値1)
  (条件2 値2)
  ...
  (条件n 値n)
  (else 値n+1))

あと、「=>」が出てくる形は、話しが複雑になるので省略。
このように、全ての条件で真とならない場合にelseで捕まえられるけど、elseという字句にマッチさせるためにsyntax-rulesの第1引数を使う。

(define-syntax cond
  (syntax-rules (else)
    ((cond (else expr ...))
     (begin expr ...))
    ((cond (test) clause ...)
     (or test (cond clause ...)))
    ((cond (test expr ...) clause ...)
     (if test (begin expr ...) (cond clause ...)))))

3行目のelseその次のexprとは違ってelseという字句にしかマッチしない、ちょっと特別な存在。