我正在编写一个宏来生成 Common Lisp 中另一个宏使用的代码。但是我对此很陌生,并且很难构建一个接收列表(bar1 bar2 ... barn)
并通过循环生成以下代码的宏。
`(foo
,@bar1
,@bar2
...
,@barn)
我想知道这是否可以在不涉及依赖SB-IMPL::UNQUOTE-SPLICE
于实现的单词(例如 sbcl)的情况下实现。
也许我没有对我的问题给出明确的描述。事实上,我想写一个gen-case
这样的宏
(gen-case
(simple-array simple-vector)
('(dotimes ($1 $5)
(when (and (= (aref $4 $2 $1) 1) (zerop (aref $3 $1)))
$0))
'(dolist ($1 (aref $4 $2))
(when (zerop (aref $3 $1))
$0)))
objname body)
产生类似的东西
`(case (car (type-of ,objname))
(simple-array
,@(progn
(setf temp
'(dotimes ($1 $5)
(when (and (= (aref $4 $2 $1) 1) (zerop (aref $3 $1)))
$0)))
(code-gen body)))
(simple-vector
,@(progn
(setf temp
'(dolist ($1 (aref $4 $2))
(when (zerop (aref $3 $1))
$0)))
(code-gen body))))
在一般情况下,被接受的列表gen-case
可能包含两个以上的项目。我努力了
``(case (car (type-of ,,objname))
,',@(#|Some codes that produce target codes|#))
但是目标代码被插入到quote
块中,因此在调用宏的宏中抛出异常gen-case
。此外,我无法插入,@
目标代码,因为直接插入会导致“逗号不在反引号内”异常。
生成的代码是另一个宏的一部分
(defmacro DSI-Layer ((obj-name tag-name) &body body)
"Data Structure Independent Layer."
(let ((temp))
(defun code-gen (c)
(if (atom c) c
(if (eq (car c) tag-name)
(let ((args (cadr c)) (codes (code-gen (cddr c))) (flag nil))
(defun gen-code (c)
(if (atom c) c
(if (eq (car c) *arg*)
(let ((n (cadr c)))
(if (zerop n) (progn (setf flag t) codes)
(nth (1- n) args)))
(let ((h (gen-code (car c))))
(if flag
(progn
(setf flag nil)
(append h (gen-code (cdr c))))
(cons h (gen-code (cdr c))))))))
(gen-code temp))
(cons (code-gen (car c)) (code-gen (cdr c))))))
`(case (car (type-of ,obj-name))
(simple-array
,@(progn
(setf temp
'(dotimes ($1 $5)
(when (and (= (aref $4 $2 $1) 1) (zerop (aref $3 $1)))
$0)))
(code-gen body)))
(simple-vector
,@(progn
(setf temp
'(dolist ($1 (aref $4 $2))
(when (zerop (aref $3 $1))
$0)))
(code-gen body))))))
我已经设置了一个读取宏
(defvar *arg* (make-symbol "ARG"))
(set-macro-character #\$
#'(lambda (stream char)
(declare (ignore char))
(list *arg* (read stream t nil t))))
的本意DSI-Layer
是添加一段代码来判断输入参数的类型。例如,代码
(defun BFS (G v)
(let* ((n (car (array-dimensions G)))
(visited (make-array n :initial-element 0))
(queue (list v))
(vl nil))
(incf (aref visited v))
(DSI-Layer (G next-vertex)
(do nil ((null queue) nil)
(setf v (pop queue)) (push v vl)
(next-vertex (i v visited G n)
(setf queue (nconc queue (list i)))
(incf (aref visited i)))))
vl))
将转换为
(defun BFS (G v)
(let* ((n (car (array-dimensions G)))
(visited (make-array n :initial-element 0))
(queue (list v))
(vl nil))
(incf (aref visited v))
(case (car (type-of G))
(simple-array
(do nil ((null queue) nil)
(setf v (pop queue))
(push v vl)
(dotimes (i n)
(when (and (= (aref G v i) 1) (zerop (aref visited i)))
(setf queue (nconc queue (list i)))
(incf (aref visited i))))))
(simple-vector
(do nil ((null queue) nil)
(setf v (pop queue))
(push v vl)
(dolist (i (aref G v))
(when (zerop (aref visited i))
(setf queue (nconc queue (list i)))
(incf (aref visited i)))))))))
现在我只是想知道是否DSI-Layer
可以gen-case
通过将类型名称和相应的代码模板传递给它来从另一个宏生成。
顺便说一句,我认为生成代码的具体含义在我的问题中并不重要。它们只是被视为数据。