diff options
-rw-r--r-- | eval.c | 14 | ||||
-rw-r--r-- | tests/011/macros-3.expected | 0 | ||||
-rw-r--r-- | tests/011/macros-3.tl | 12 | ||||
-rw-r--r-- | txr.1 | 38 |
4 files changed, 63 insertions, 1 deletions
@@ -1911,6 +1911,20 @@ static val expand_macro(val form, val mac_binding, val menv) { val expander = cdr(mac_binding); val expanded = funcall2(expander, form, menv); + if (form == expanded) { + val sym = car(form); + val up_binding = mac_binding; + + while (menv && up_binding == mac_binding) { + menv = menv->e.up_env; + up_binding = lookup_mac(menv, sym); + } + + if (up_binding && up_binding != mac_binding) + return expand_macro(form, up_binding, menv); + + return form; + } set_origin(expanded, form); return expanded; } diff --git a/tests/011/macros-3.expected b/tests/011/macros-3.expected new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/011/macros-3.expected diff --git a/tests/011/macros-3.tl b/tests/011/macros-3.tl new file mode 100644 index 00000000..bf7cf9a6 --- /dev/null +++ b/tests/011/macros-3.tl @@ -0,0 +1,12 @@ +(load "../common") + +(defmacro m () 42) + +(test + (macrolet ((m (:form f) f)) + (let ((n 3)) + (macrolet ((m (:form f) f)) + (let ((n 3)) + (macrolet ((m (:form f) f)) + (m)))))) + 42) @@ -30165,13 +30165,34 @@ The return value of the macro is the macro expansion. It is substituted in place of the entire macro call form. That form is then expanded again; it may itself be another macro call, or contain more macro calls. -.TP* "Dialect Note:" +A global macro defined using +.code defmacro +may decline to expand a macro form. Declining to expand is achieved by +returning the original unexpanded form, which may be captured using the +.code :form +parameter. When a global macro declines to expand a form, the form is +taken as-is. At evaluation time, it will be treated as a function call. +Note: when a local macro defined by +.code macrolet +declines, more complicated requirements apply; see the description of +.codn macrolet . + +.TP* "Dialect Notes:" A macro in the global namespace introduced by .code defmacro may co-exist with a function of the same name introduced by .codn defun . This is not permitted in ANSI Common Lisp. +ANSI Common Lisp doesn't describe the concept of declining to expand, except in +the area of compiler macros. Since TXR Lisp allows global macros and functions +of the same name to co-exist, ordinary macros can be used to optimize functions +in a manner similar to Common Lisp compiler macros. A macro can be written +of the same name as a function, and can optimize certain cases of the function +call by expanding them to some alternative syntax. Cases which it doesn't +optimize are handled by declining to expand, in which case the form remains +as the original function call. + .TP* Example: .cblk @@ -30248,6 +30269,21 @@ macros to have any visibility to any surrounding lexical variable bindings, which are only instantiated in the evaluation phase, after expansion is done and macros no longer exist. +A local macro defined using +.code defmacro +may decline to expand a macro form. Declining to expand is achieved by returning the original +unexpanded form, which may be captured using the +.code :form +parameter. When a local macro declines to expand a form, the macro definition +is temporarily hidden, as if it didn't exist in the lexical scope. If another +macro of the same name is thereby revealed (a global macro or another local macro +at a shallower nesting level), then an expansion is tried with that macro. If +no such macro is revealed, or if a lexical function binding of that name is +revealed, then no expansion takes place; the original form is taken as-is. +When another macro is tried, the process repeats, resulting in a search which +proceeds as far as possible through outer lexical scopes and finally the +global scope. + .coNP Function @ macro-form-p .synb .mets (macro-form-p < obj <> [ env ]) |