2

Common Lisp 宏通常使用包含前缀表示法:(运算符的东西......)

但是,特殊的引号宏 ' 使用连接前缀表示法: operator stuff 或 operator(stuff)。

我想在 SBCL Common Lisp 中创建一个自定义宏,我们称之为 !,它使用连接前缀语法对以下列表(甚至原子)进行操作,其方式类似于 ' 。因此,我可以在任何地方调用它,例如 (setq foo !(bar) ),而无需将其插入括号本身。

如何才能做到这一点?defmacro 语法是什么样的?谢谢。

4

1 回答 1

1

我看到了两种方法。

第一个简单的变体是合适的,如果您只需要它用于单个或非常有限数量的案例,并且您对仅使用单个符号作为前缀感到满意(如!示例案例中)。您可以创建一个读取宏,更具体地说,设置 amacro-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)))

虽然让它递归地转换所有层相当容易(这留给读者作为练习:)

于 2012-10-06T14:48:21.223 回答