I am working my way through Practical Common Lisp. I got to the example where you define a deftest
macro that works like defun
with some added functionality. This got me thinking that it would be nice to be able to add a docstring. I have found that both of the following work, but is one of them more correct? Is there a "right" way to achieve the optional-docstring-like behaviour of defun
?
(defmacro deftest (name parameters &body body)
(let ((docstring ""))
(when (stringp (car body)) (setf docstring (car body) body (cdr body)))
`(defun ,name ,parameters
,docstring
(let ((*test-name* (append *test-name* (list ',name))))
,@body))))
(defmacro deftest (name parameters &optional docstring &body body)
(when (not (stringp docstring)) (setf docstring ""))
`(defun ,name ,parameters
,docstring
(let ((*test-name* (append *test-name* (list ',name))))
,@body)))
In general you probably want to parse out both a possible docstring and any declarations from the body of the function, so you can put the declarations where they belong. I use something like this:
(defun parse-body (body)
(multiple-value-bind (docstring decls/forms)
(if (stringp (first body))
(values (first body) (rest body))
(values nil body))
(loop for remainder on decls/forms
while (and (not (null remainder))
(consp (first remainder))
(eql (car (first remainder)) 'declare))
collect (first remainder) into decls
finally (return (values docstring decls remainder)))))
And then your deftest
would be
(defmacro deftest (name parameters &body body)
(multiple-value-bind (docstring decls forms) (parse-body body)
`(defun ,name ,parameters
,@(if docstring (list docstring) '())
,@decls
(let ((*test-name* (append *test-name* (list ',name))))
,@forms))))
I wish I could say that I have a standard parse-body
function, but I don't: I just write a new one each time.