2012-10-06 49 views
2

通常Lisp宏通常使用包含前缀符号:(运算符填充...)Common Lisp:如何创建一个级联前缀宏,类似于'?

但是,特殊引用宏'使用连接前缀符号:运算符或操作符(或操作符)。

我想在SBCL Common Lisp中创建一个自定义宏,让我们称它为!,它使用串联前缀语法对下列列表(甚至原子)进行操作,类似于'。所以我可以在任何地方调用它,例如(setq foo!(bar)),而不会将其固定在括号内。

这怎么办? defmacro语法是什么样的?谢谢。

回答

1

我看到2种方式来做到这一点。

第一个简单的变体是适用的,如果您只需要它用于单个或非常有限的几种情况,并且只使用单个符号作为前缀(例如示例中的!),您就满意了。您可以创建一个读取宏,更专门设置一个macro-character来代替这种操作,比如说,除了:

(set-macro-character #\! (lambda (stream char) 
          (declare (ignore char)) 
          (cons '+ (let ((next (read stream t nil t))) 
           (if (consp next) next 
            (list next))))) 
CL-USER> !1 
1 
CL-USER> !(1 2) 
3 

其他的,更复杂,更灵活的方式是定义一个宏,在其内部应用自定义转换,将连接前缀代码转换为包含前缀。这个技巧使用的事实是,普通的Lisp阅读器将以同样的方式读取foo(bar)foo (bar),即它将它们分成2个元素。

简单的版本,例如宏可能看起来像这样:

(defmacro with-prefix-syntax (&body body) 
    `(progn ,@(loop :for tail :on body :while body 
        :collect (if (and (not (atom (second tail))) 
            (fboundp (first tail))) 
           (prog1 (cons (first tail) (second tail) 
           (setf tail (rest tail))) 
           (first tail))))) 

这将改变只有顶层形式:

CL-USER> (macroexpand-1 '(with-prefix-syntax 
          print(1))) 
(PROGN (PRINT 1)) 
CL-USER> (macroexpand-1 '(with-prefix-syntax 
          1)) 
(PROGN 1) 
CL-USER> (macroexpand-1 '(with-prefix-syntax 
          print(1) 
          2)) 
(PROGN (PRINT 1) 2) 

但不工作在较低的水平:

CL-USER> (macroexpand-1 '(with-prefix-syntax 
          print(1) 
          (+ print(2)))) 
(PROGN (PRINT 1) (+ PRINT (2))) 

虽然使它递归地变换所有图层是相当容易的(剩下的练习是读者:)

+0

我想你的意思是'(set-macro-character#\! ..' :) – Lex

+0

@lex是的,谢谢 –

+0

感谢您的有趣答案。请让这个人坐两个星期,看看我们还能得到什么... – DragonLord