マクロのマッチングを実装しよう - 3.acond2マクロ

match の実装では acond2 が要求されている。
もちろん acond2 がなくても match を書くことは出来るが、実装がとても汚くなるので用意する。
On Lispの acond2 は Common Lisp で書かれていて、多値の束縛に multiple-value-bind が使われる。
Scheme では receive を使うのが良さそう。(multiple-value-bind の方が寛容な仕様のようだ)。


acond2 は cond の条件部分で多値(2値)を受け取り、それを元に分岐する。
条件に一致する場合、2値の2つ目の値が it に束縛される。

;; acond2
(define-macro acond2
  (lambda clauses
    (if (null? clauses)
        '()
        (let ((cl1 (car clauses))
              (val (gensym))
              (win (gensym)))
          `(receive (,val ,win) ,(car cl1)
                    (if (or ,val ,win)
                        (let  ((it ,val)) ,@(cdr cl1))
                        (acond2 ,@(cdr clauses))))))))

(assert-check-true
 "acond2"
 (= (acond2
     ((values #f #f) it)
     ((values 1 #t) it)) 1)
 (= (acond2
     ((values 2 #f) it)
     ((values 1 #t) it)) 2))

gensym は intern されていないシンボルを返し、マクロ内で使用する一時変数が呼出元で衝突することを避ける役割で使われる。
この機能は R5RS では要求されておらず、Gaucheが独自に提供してくれている。
Traditional Macro をサポートするならば必須な機能かな。

<前:マクロのマッチングを実装しよう2 | 次:マクロのマッチングを実装しよう4>