Common Lisp:控制宏扩展时间

时间:2012-12-26 23:34:04

标签: macros common-lisp clos

我正在使用常见的lisp,发现自己输入了以下表单的插槽定义:

(name :initarg :name :accessor name)

所以我想编造一个宏来加速这个。我想出了以下内容:

(defmacro quickslot (name)
`(,name :initarg ,(intern (string-upcase name) "KEYWORD") :accessor ,name))

肮脏的黑客,毫无疑问,但功能性。或者我想。当我试图运行我的代码时,我遇到了一个障碍:因为defclass是一个宏,所以参数传递给它没有评估。这意味着,而不是看到

(x :initarg :x :accessor x)

它看到了

(quickslot x)

当然,这表示错误。

在我看来,答案是以某种方式控制宏扩展的顺序,以确保在defclass之前扩展quickslot。这让我想到了一个问题:如何实现这个目标?或者,如果你对我最初的难题有不同的解决方案,那也不会不受重视。

3 个答案:

答案 0 :(得分:4)

这不值得一个宏。宏通常将文字Lisp源代码作为输入。

相反,你可以使用一个函数。来自Practical Common Lisp, Ch.24

(defun as-keyword (sym) (intern (string sym) :keyword))

(defun slot->defclass-slot (spec)
  (let ((name (first spec)))
    `(,name :initarg ,(as-keyword name) :accessor ,name)))

然后你可以做类似的事情(再次改编自PCL):

(defmacro my-defclass (name slots)
  `(defclass ,name ()
     ,(mapcar #'slot->defclass-slot slots)))

答案 1 :(得分:1)

不,你不能这样做。你可以在defclass周围编写一个宏(尽管你的快速时隙有一些特殊的语法)。

答案 2 :(得分:1)

您可以完全不同地处理问题,并提出一个读取器宏,指示读者在其后面的代码上调用macroexpand,这将更通用,然后只是插槽声明的一个目的班级。但是完整的解决方案会有所涉及,因为你必须考虑到读者的许多特点和要求,然而,即使是像这样丑陋的事情也可以做到这一点:

(defmacro quickslot (name)
`(,name :initarg ,(intern (string-upcase name) "KEYWORD") :accessor ,name))

(macroexpand '(defclass test-class ()
               (#.(macroexpand '(quickslot some-slot)))))

所以,你需要做的就是#.(macroexpand ...)

的别名

而且......你走了:

(set-macro-character
 #\{
 #'(lambda (str char)
     (declare (ignore char))
     (let ((*readtable* (copy-readtable *readtable* nil))
           (reading-p t))
       (set-macro-character
        #\}
        #'(lambda (stream char)
            (declare (ignore char stream))
            (setf reading-p nil)))
       (loop for exp = (read str nil nil t)
          while reading-p
          collect (macroexpand exp)))))

(read-from-string "'(defclass test-class ()
               {(quickslot some-slot)
               (quickslot some-other-slot)})")
'(DEFCLASS TEST-CLASS NIL
           ((SOME-SLOT :INITARG :SOME-SLOT :ACCESSOR SOME-SLOT)
            (SOME-OTHER-SLOT :INITARG :SOME-OTHER-SLOT :ACCESSOR
             SOME-OTHER-SLOT)))

:)