Common Lisp 编译顺序问题

ccQ*_*ein 2 lisp macros compilation sbcl common-lisp

我在一个文件中写了一个宏和一个函数,如下所示:

(defun test ()
  (let ((x '(1 2 3)))
    (macro-test (x real-b)
      (print (+ 1 (car real-b))))))

(defmacro macro-test ((a b) &body body)
  `(do ((,b ,a (cdr ,b)))
       ((not ,b))
     ,@body))
Run Code Online (Sandbox Code Playgroud)

然后我在 repl 中加载这个文件并运行(test). 我收到此错误:

The variable REAL-B is unbound.
Run Code Online (Sandbox Code Playgroud)

然而,当我defmacro 之前defun. 一切安好。

我对常见的 lisp 编译顺序感到困惑。我知道如果defmacro在里面使用一些函数,那些函数应该(eval-when (:compile-toplevel :load-toplevel :execute)),否则编译会失败。

但是,如果编译时宏定义和函数定义相同,那么顺序就是问题,对吗?宏应位于使用它们之前的位置(如果我创建两个函数,则顺序无关紧要)。我可以得到更多关于 SBCL 编译顺序的细节吗?它仅适用于 SBCL 吗?还是按照 Common Lisp 的标准?

谢谢!

Rai*_*wig 6

顺序总是很重要:当你想使用宏时,它必须是已知的。宏执行源转换。您将如何使用未知宏进行源转换?

Common Lisp 标准不需要多遍编译,即首先读取所有源代码并收集所有宏,然后从文件顶部开始编译。Common Lisp 中的文件编译只是从头到尾遍历源代码。稍后可能会有多个编译阶段,但这留给实现......

test当宏macro-test未知时,Lisp 应该如何编译函数?Lisp 编译器需要 a) 知道它是一个宏 b) 它需要有它的定义来扩展宏形式。

对于 Common Lisp,这是一个基本规则:

如果我们有一个表格,(foo bar baz)那么评估基本上是看foo

  1. 如果foo是特殊运算符 -> 使用该特殊运算符
  2. 如果foo是宏运算符 -> 宏展开代码并重新开始
  3. iffoo是一个函数 -> 使用评估参数调用该函数
  4. 否则 -> 错误

编译时它看起来很相似:

  1. iffoo是一个特殊的运算符 -> 编译那个特殊的形式
  2. 如果foo是宏运算符 -> 宏展开宏形式并编译该代码
  3. iffoo是一个函数 -> 编译该函数形式
  4. else -> 警告然后假设foo是一个函数并编译对该名称的未来函数的调用