关于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
.
- 如果
foo
是特殊运算符 -> 使用该特殊运算符
- 如果
foo
是宏运算符->宏展开代码重新开始
- 如果
foo
是一个函数 -> 使用评估的参数调用该函数
- 其他 -> 错误
在编译中看起来很相似:
- 如果
foo
是特殊运算符 -> 编译那个特殊形式
- 如果
foo
是一个宏运算符 -> 宏展开宏形式并编译该代码
- 如果
foo
是一个函数 -> 编译那个函数形式
- else -> 警告然后假设
foo
是一个函数并编译对该名称的未来函数的调用
我在一个文件中写了一个宏和一个函数,如下所示:
(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
.
- 如果
foo
是特殊运算符 -> 使用该特殊运算符 - 如果
foo
是宏运算符->宏展开代码重新开始 - 如果
foo
是一个函数 -> 使用评估的参数调用该函数 - 其他 -> 错误
在编译中看起来很相似:
- 如果
foo
是特殊运算符 -> 编译那个特殊形式 - 如果
foo
是一个宏运算符 -> 宏展开宏形式并编译该代码 - 如果
foo
是一个函数 -> 编译那个函数形式 - else -> 警告然后假设
foo
是一个函数并编译对该名称的未来函数的调用