在Lisp解释器中,eval
中很容易存在一个可以扩展宏的分支,并且在扩展该宏的过程中,调用函数来构建扩展的表达式。在使用低级宏之前,我已经做到了这一点,这很容易理解。
但是,在编译器中,没有任何函数可以调用来构建扩展的代码:在以下示例中可以很简单地看出问题:
(defmacro cube (n)
(let ((x (gensym)))
`(let ((,x ,n))
(* ,x ,x ,x))))
当宏由解释器扩展时,它将调用
gensym
并执行您期望的操作。当由编译器扩展时,您将为let
生成代码,该代码将x
绑定(bind)到(gensym)
,但是gensymmed符号仅对于编译器执行正确的操作是必需的。而且由于在编译宏之前实际上并未调用gensym
,所以它不是很有用。当宏使用
map
或filter
建立一个用作扩展的列表时,这对我来说变得更加奇怪。那么这是如何工作的呢?当然,编译后的代码不会编译为
(eval *macro-code*)
,因为这效率极低。有没有写得很清楚的Lisp编译器? 最佳答案
在各种Lisp方言中,这是如何工作的非常不同。对于Common Lisp,它是在ANSI Common Lisp标准中进行标准化的,并且各种Common Lisp实现都主要不同,无论它们使用的是编译器,解释器还是两者。
以下假设Common Lisp。
EVAL
不是解释器。可以使用编译器来实现EVAL。一些Common Lisp实现甚至没有解释器。然后EVAL
是对编译器的调用,以编译代码,然后调用已编译的代码。这些实现使用增量编译器,该编译器还可以编译诸如2
,(+ 2 3)
,(gensym)
等的简单表达式。
宏扩展使用MACROEXPAND
和MACROEXPAND-1
函数完成。
Common Lisp中的宏是一个需要某些形式并返回另一种形式的函数。 DEFMACRO将此功能注册为宏。
你的宏
(defmacro cube (n)
(let ((x (gensym)))
`(let ((,x ,n))
(* ,x ,x ,x))))
只是Lisp函数,它注册为宏。
效果类似于:
(defun cube-internal (form environment)
(destructuring-bind (name n) form ; the name would be CUBE
(let ((x (gensym)))
`(let ((,x ,n))
(* ,x ,x ,x)))))
(setf (macro-function 'my-cube) #'cube-internal)
在实际的CL实现中,
DEFMACRO
的扩展方式不同,并且不使用类似于CUBE-INTERNAL
的名称。但是从概念上讲,它是在定义宏函数并进行注册。Lisp编译器看到宏定义时,通常会编译该宏函数并将其存储在当前所谓的环境中。如果环境是运行时环境,则会在运行时记住该环境。如果环境是编译文件时的编译器环境,则在编译文件后会忘记该宏。需要加载已编译的文件,以便Lisp知道宏。
因此,定义宏并进行编译存在副作用。编译器会记住已编译的宏并存储其代码。
现在,当编译器看到一些使用宏
(cube 10)
的代码时,编译器仅调用以CUBE
的名称存储在当前环境中的宏函数,然后调用以10
作为参数的宏函数,然后编译生成的表单。如上所述,它不是直接完成的,而是通过MACROEXPAND函数完成的。这是宏定义:
CL-USER 5 > (defmacro cube (n)
(let ((x (gensym)))
`(let ((,x ,n))
(* ,x ,x ,x))))
CUBE
我们编译宏:
CL-USER 6 > (compile 'cube)
CUBE
NIL
NIL
MACRO-FUNCTION
返回宏的函数。我们可以像其他函数一样使用FUNCALL
来调用它。它需要两个参数:像(cube 10)
这样的整体形式和一个环境(这里是NIL
)。CL-USER 7 > (funcall (macro-function 'cube) '(cube 10) nil)
(LET ((#:G2251 10)) (* #:G2251 #:G2251 #:G2251))
也可以采用一个函数(该函数接受两个参数:一个表单和一个环境)并使用SETF作为宏函数来存储它。
摘要
当Common Lisp编译器运行时,它只知道宏函数,并在需要时通过内置宏扩展器扩展代码以调用它们。宏函数本身就是Lisp代码。当Lisp编译器看到宏定义时,它将编译该宏函数,将其存储在当前环境中,并使用它来扩展该宏的后续用法。
注意:这使得在Common Lisp中必须定义一个宏,然后编译器才能使用它。
关于compiler-construction - 您如何在Lisp编译器中编译宏?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/7072980/