Function MACROEXPAND, MACROEXPAND-1
macroexpand form &optional env => expansion, expanded-p
macroexpand-1 form &optional env => expansion, expanded-p
form - フォーム
env - 環境オブジェクト。デフォルトはnil。
expansion - フォーム
expanded-p - generalized-boolean
macroexpandとmacroexpand-1は、マクロを展開します。
もしformがマクロフォームであれば、 macroexpand-1は一度だけマクロフォームを呼び出して展開します。
macroexpandは、マクロフォームではなくなるまでformを繰り返し展開します。 実際にmacroexpandは、 macroexpand-1を第二返却値がnilになるまで 繰り返し呼び出します。
もしformがマクロフォームならば、 expansionはマクロ展開の結果であり、 expanded-pはtrueです。 それ以外のときは、expansionは与えられたformそのものであり、 expanded-pはfalseです。
マクロ展開は次のようにして行われます。
macroexpand-1は、formがマクロフォームであると決定すると、 適用可能なマクロかシンボルマクロの展開関数を取得します。 *macroexpand-hook*の値は関数であることを強制し、 3つの引数を持つ関数として呼び出しを行います。 引数は展開関数とformとenvです。 この呼び出しによって得られた値は、formの展開として受け取ります。
グローバル環境下のマクロ定義に加えて、 env内に定義されている macroletとsymbol-macroletによって確立された ローカルマクロも考慮されます。 もしformのみが引数に指定されたときは、 環境はnullとして効果があり、 ただdefmacroによって確立された グローバルマクロの定義のみが考慮されます。 マクロ定義はローカル関数の定義によってシャドウされます。
(defmacro alpha (x y) `(beta ,x ,y)) => ALPHA
(defmacro beta (x y) `(gamma ,x ,y)) => BETA
(defmacro delta (x y) `(gamma ,x ,y)) => EPSILON
(defmacro expand (form &environment env)
(multiple-value-bind (expansion expanded-p)
(macroexpand form env)
`(values ',expansion ',expanded-p))) => EXPAND
(defmacro expand-1 (form &environment env)
(multiple-value-bind (expansion expanded-p)
(macroexpand-1 form env)
`(values ',expansion ',expanded-p))) => EXPAND-1
;; 単純なグローバル環境の実行例
(macroexpand-1 '(alpha a b)) => (BETA A B), true
(expand-1 (alpha a b)) => (BETA A B), true
(macroexpand '(alpha a b)) => (GAMMA A B), true
(expand (alpha a b)) => (GAMMA A B), true
(macroexpand-1 'not-a-macro) => NOT-A-MACRO, false
(expand-1 not-a-macro) => NOT-A-MACRO, false
(macroexpand '(not-a-macro a b)) => (NOT-A-MACRO A B), false
(expand (not-a-macro a b)) => (NOT-A-MACRO A B), false
;; レキシカル環境の実行例
(macrolet ((alpha (x y) `(delta ,x ,y)))
(macroexpand-1 '(alpha a b))) => (BETA A B), true
(macrolet ((alpha (x y) `(delta ,x ,y)))
(expand-1 (alpha a b))) => (DELTA A B), true
(macrolet ((alpha (x y) `(delta ,x ,y)))
(macroexpand '(alpha a b))) => (GAMMA A B), true
(macrolet ((alpha (x y) `(delta ,x ,y)))
(expand (alpha a b))) => (GAMMA A B), true
(macrolet ((beta (x y) `(epsilon ,x ,y)))
(expand (alpha a b))) => (EPSILON A B), true
(let ((x (list 1 2 3)))
(symbol-macrolet ((a (first x)))
(expand a))) => (FIRST X), true
(let ((x (list 1 2 3)))
(symbol-macrolet ((a (first x)))
(macroexpand 'a))) => A, false
(symbol-macrolet ((b (alpha x y)))
(expand-1 b)) => (ALPHA X Y), true
(symbol-macrolet ((b (alpha x y)))
(expand b)) => (GAMMA X Y), true
(symbol-macrolet ((b (alpha x y))
(a b))
(expand-1 a)) => B, true
(symbol-macrolet ((b (alpha x y))
(a b))
(expand a)) => (GAMMA X Y), true
;; シャドウの例
(flet ((beta (x y) (+ x y)))
(expand (alpha a b))) => (BETA A B), true
(macrolet ((alpha (x y) `(delta ,x ,y)))
(flet ((alpha (x y) (+ x y)))
(expand (alpha a b)))) => (ALPHA A B), false
(let ((x (list 1 2 3)))
(symbol-macrolet ((a (first x)))
(let ((a x))
(expand a)))) => A, falsedefmacro, (setf macro-function), macrolet, symbol-macrolet
なし。
*macroexpand-hook*, defmacro, (setf macro-function), macrolet, symbol-macrolet, 3.1. 評価
macroexpandとmacroexpand-1のどちらも、 マクロフォーム内にあるサブフォームのformやマクロを 明に展開するようなことはしません。 しかしそのような展開は 意味解析や実装のマクロ展開によって 暗黙的に行われることがあります。