Gaucheではマクロ展開後のコードを見るためにmacroexpandという手続きが用意されているが、マクロの使用がcar部に来ていないと展開されない。
例えば、よく例として挙げられるorマクロは
(define-syntax or
(syntax-rules ()
((or) #f)
((or exp) exp)
((or exp1 exp2 ...)
(let ((tmp exp1))
(if tmp
tmp
(or exp2 ...))))))
3つ目のパターンでは再帰的にorが登場するが、そこまではmacroexpandで展開は出来ないので全体の様子を見る事ができない。
なので、マクロの使用が完全に無くなるまで展開を続ける手続きを書いた。
(use srfi-1)
(define evalMacroDef ;macro定義を見つけたらそれをevalする。
(let ((macroName '())) ;定義があればその名前をここに格納していく。
(lambda (schemeCode)
(if (pair? schemeCode)
(if (eq? (car schemeCode) 'define-syntax)
(begin
(eval schemeCode (interaction-environment))
(set! macroName (alist-cons (cadr schemeCode) schemeCode macroName)))
(if (assoc (car schemeCode) macroName) ;macroNameの中に入っているマクロだったとき展開する
(begin
(set! schemeCode (apply macroexpand (list schemeCode)))
(if (pair? schemeCode)
(cons
(evalMacroDef (car schemeCode))
(evalMacroDef (cdr schemeCode)))
schemeCode))
(cons
(evalMacroDef (car schemeCode))
(evalMacroDef (cdr schemeCode))))))
schemeCode)))
うーん,もっと綺麗に書きたい.
これを次のように使う
(define code
'(begin
(define-syntax or
(syntax-rules ()
((or) #f)
((or exp) exp)
((or exp1 exp2 ...)
(let ((tmp exp1))
(if tmp
tmp
(or exp2 ...))))))
(or 1 2 3 4 5 6)))
(evalMacroDef code)
出力結果
gosh> evaluated (define-syntax or (syntax-rules () ((or) #f) ((or exp) exp) ((or exp1 exp2 ...) (let ((tmp exp1)) (if tmp tmp (or exp2 ...))))))
(begin #<undef> (#<identifier user#let> ((#0=#<identifier user#tmp> 1))
(#<identifier user#if> #0# #0# (#<identifier user#let> ((#1=#<identifier user#tmp> 2))
(#<identifier user#if> #1# #1# (#<identifier user#let> ((#2=#<identifier user#tmp> 3))
(#<identifier user#if> #2# #2# (#<identifier user#let> ((#3=#<identifier user#tmp> 4))
(#<identifier user#if> #3# #3# (#<identifier user#let> ((#4=#<identifier user#tmp> 5))
(#<identifier user#if> #4# #4# 6)))))))))))