我一直在尝试编写一个宏,它可以生成同名的编译器宏。这是我坚持的最小代码:
(defmacro definline (name lambda-list &body body)
`(define-compiler-macro ,name ,lambda-list
`(let ,,(mapcar (lambda (v) ``(,',v ,,v)) lambda-list)
,,@body)))
我想要的是这样的:
(definline foobar (a b) (print "foobar") (+ a b))
;; Expands to
(define-compiler-macro foobar (a b)
`(let ((a ,a) (b ,b))
(print "foobar") (+ a b)))
但我无法弄清楚如何生成let绑定((a ,a) (b ,b))
。我无法解决的问题是如何生成编译器宏表单,使得lambda绑定的内容在扩展中 unquoted 。我知道如何手动执行此操作,但我不确定如何为任意lambda列表执行此操作。
修改
经过一番调整,我想出了这个。哪个有效。但是,这很可怕。
(defmacro definline (name lambda-list &body body)
(read-from-string
(format nil "(define-compiler-macro ~S (~{~S~^ ~})
`(let (~{(~S ,~S)~})
~{~S~}))"
name lambda-list (loop for l in lambda-list nconc (list l l)) body)))
答案 0 :(得分:3)
如果修改了一些与I / O相关的变量,那么使用format
的方法根本不起作用。使用format
生成符号名称通常非常脆弱且不可移植。这个 是一个难以解决的问题,并且可能更容易使用列表构造而不是单独的反引号来处理它。例如,在这种情况下,我们可以:
(defmacro definline (name variables &body body)
(list 'define-compiler-macro name variables
`(list* 'let (list ,@(mapcar (lambda (variable)
`(list (quote ,variable) ,variable))
variables))
',body)))
这样做:
CL-USER> (pprint (macroexpand-1 '(definline foobar (a b)
(print "foobar")
(+ a b))))
(DEFINE-COMPILER-MACRO FOOBAR
(A B)
(LIST* 'LET (LIST (LIST 'A A) (LIST 'B B)) '((PRINT "foobar") (+ A B))))
除非我误读了某些内容,否则应该具有与以下相同的结果:
(define-compiler-macro foobar (a b)
`(let ((a ,a) (b ,b))
(print "foobar")
(+ a b)))
我不认为单独使用反引号生成后一种形式是不可能的。问题在于,由于规范没有准确定义反引用的实现方式,因此它并不像
那样简单。(define-compiler-macro foobar (a b)
(backquote (let ((a (unquote a))
(b (unquote b)))
(print "foobar")
(+ a b)))
如果您的实现 以这种方式实现它,那么您可以编写一个生成该类型输出的扩展。除非你从实现中获得这样的保证,否则我认为没有办法获得你需要注入更高层的“逗号变量”。很难清楚地说明这一点,但你可能会看一下这种尝试:
(defmacro definline (name variables &body body)
`(define-compiler-macro ,name ,variables
`(let ,',(mapcar (lambda (variable)
`(,variable (unquote ,variable)))
variables)
,@',body)))
这将产生如下结果:
(DEFINE-COMPILER-MACRO FOOBAR
(A B)
'(LET ((A (UNQUOTE A)) (B (UNQUOTE B)))
(PRINT "foobar")
(+ A B)))
请注意,SBCL已经足够聪明,可以用正常引号替换反引号,因为内部没有任何内容需要取消引用。生成拼接形式的mapcar
无法生成包含逗号的代码,因为未指定这些逗号的实现方式,并且根据2.4.7 Comma,“如果使用逗号则无效除了在反引号表达体内“。我认为这意味着你最好的选择是:
(defmacro definline (name variables &body body)
`(define-compiler-macro ,name ,variables
`(let ,(mapcar 'list
',variables
(list ,@variables))
,@',body)))
在不同的实现中,这种扩展会有所不同,但在SBCL中它是:
(DEFINE-COMPILER-MACRO FOOBAR (A B)
`(LET (SB-IMPL::BACKQ-COMMA (MAPCAR 'LIST '(A B) (LIST A B)))
(PRINT "foobar")
(+ A B)))
在CCL中你会得到:
(DEFINE-COMPILER-MACRO FOOBAR (A B)
(LIST* 'LET
(LIST* (MAPCAR 'LIST '(A B) (LIST A B))
'((PRINT "foobar") (+ A B)))))
在CLISP中:
(DEFINE-COMPILER-MACRO FOOBAR (A B)
(CONS 'LET
(CONS (MAPCAR 'LIST '(A B) (LIST A B)) '((PRINT "foobar") (+ A B)))))