在代码编译/评估期间扩展嵌套的宏调用

0 common-lisp

我有下一段代码:

(in-package :cl-user)

(defmacro test0 (form)
  (format t "test0: Expander phase: ~s" form)
  `(format t "test0: Expansion phase: ~s" ,form))

(defmacro test1 (form)
  (format t "test1: Expander phase: ~s" form)
  (test0 form)
  `(format t "test1: Expansion phase: ~s" ,form))
Run Code Online (Sandbox Code Playgroud)

Common Lisp实现:"SBCL 1.3.16".

  1. 结果(compile-file "source.lisp"): ; compiling (IN-PACKAGE :CL-USER) ; compiling (DEFMACRO TEST0 ...) ; compiling (DEFMACRO TEST1 ...)test0: Expander phase: FORMtest0: Expander phase: FORM

  2. 结果(load "source.lisp"): ; #<PACKAGE "COMMON-LISP-USER"> ; TEST0 test0: Expander phase: FORM ; TEST1

而我无法理解下一件事:

  1. 为什么(test0 form)test1宏的定义中扩展和处理嵌套子表单?为什么不在宏调用中处理它呢?

  2. Common Lisp标准在哪里指定这样的行为?eval-when?文件编译?表格评估?

  3. 最后,为什么"test0: Expander phase: FORM"在编译期间打印两次,compile-file在评估期间只打印一次(load)?

我认为答案非常明显,但我找不到它们.

sds*_*sds 5

是上操作的功能.由于在Lisp 代码中只是a list, 宏函数看起来像普通函数.

defmacro test1被视为函数的定义,因此所有代码都经过适当处理,并(test0 form)在SBCL编译时进行宏扩展defmacro test1.这回答了q1.

load"按顺序执行它遇到的每个表单",所以要回答你的q2,你需要阅读3.1评估,特别是 3.1.2.1.2.2宏表格.

至于扩展宏的次数,标准没有指定(实现可以在每次调用用户函数时扩展它!),这就是为什么宏不是一个好主意的原因.副作用(例如,做输出).在您的情况下,加载需要在test1定义时进行扩展.编译在定义时会扩展test1,然后在编译时再次展开.请记住,defmacro安排宏可以在以下代码中使用(包括本身递归),因此必须立即定义.