关于Common Lisp编译顺序的问题

Question about Common Lisp compilation order

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

(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))

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

The variable REAL-B is unbound.

然而,当我把defmacro放在defun之前时。一切都很好。

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

但是,如果宏定义和函数定义在编译时相同,那么顺序很重要,对吧?宏应该位于使用它们的位置之前(如果我创建两个函数,顺序无关紧要)。我可以详细了解 SBCL 的编译命令吗?它只适用于 SBCL 吗?还是 Common Lisp 的标准?

谢谢!

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

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

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

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

如果我们有一个表单 (foo bar baz) 那么 评估 基本上会查看 foo.

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

编译中看起来很相似:

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