2014-02-18 19 views
6

我一直在试图编写一个宏,它可以生成相同名称的编译器宏。这是最少的代码我坚持:生成宏表单的宏包装在允许绑定变量中

(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绑定的内容是未加引号的。我明白如何手动执行此操作,但我不知道如何对任意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))) 

回答

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

我不认为使用backquotes来生成后一种形式是不可能的。问题是,由于该规范并没有定义反引号究竟是如何实现的,它不是像

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

调用生成的编译器宏函数,例如“(funcall(编译器宏函数'foobar)'(foobar 10 11)())”除了宏扩展1之外可以成为验证输出的有用工具。 –

+0

感谢您的优秀解释! – asm