Neutral way to find DECLARE from body and body
I am writing a macro that generates a call DEFUN
- accordingly, I want to make sure that anything DECLARE
in the body for the macro is placed immediately after DEFUN
. Here's what I have:
(defmacro defsynced (name (&rest args) &body body)
(let* ((decl (if (eql (caar body) 'cl:declare)
(list (car body))))
(body (if decl
(cdr body)
body)))
`(defun ,name ,args
,@decl
(bordeaux-threads:with-lock-held (*request-lock*)
,@body))))
Unfortunately this is pretty ugly and isn't necessarily obvious what's going on here. Is there a better way you can think of?
source to share
Since parsing declarations can be tricky, there is a library called parse-declarations that helps with it. It is available from Quicklisp .
(ql:quickload "parse-declarations-1.0")
The function is parse-body
especially important for your question.
source to share
Your solution is not complete because there may be one or more ads.
While you can use some of the shelf's features to do this, it does provide a good learning curve that can be useful in situations like this.
If you have a list of forms
(alpha beta x epsilon ... omega)
where x is the object of interest by which you want to split the list, you can use a function member
to find the sublist starting with x
and then a function ldiff
to get the prefix of that list (alpha beta)
that excludes (x epsilon omega)
. First step:
(member-if-not (lambda (x) (eq x 'declare)) '(declare declare 3 4 5))
-> (3 4 5)
Of course we're looking for (declare ...)
not declare
. We cannot use :key #'car
for this, because the forms may not match, so:
(member-if-not (lambda (x) (and (consp x) (eq (car x) 'declare)))
'((declare foo) (declare bar) 3 4 5))
-> (3 4 5)
Now, how to get declarations and other forms yourself:
(defun separate-decls-and-body (body)
(let* ((just-the-code (member-if-not (lambda (x)
(and (consp x) (eq (car x) 'declare)))
body))
(just-the-decls (ldiff body just-the-code)))
(values just-the-decls just-the-code)))
Tests:
> (separate-decls-and-body '((declare (optimize (speed 3))) (declare (type)) 1 2 3))
((DECLARE (OPTIMIZE (SPEED 3))) (DECLARE (TYPE))) ;
(1 2 3)
> (separate-decls-and-body '((declare (optimize (speed 3)))))
((DECLARE (OPTIMIZE (SPEED 3)))) ;
NIL
> (separate-decls-and-body '())
NIL ;
NIL
> (separate-decls-and-body '(1 2 3))
NIL ;
(1 2 3)
Family member
and ldiff
- your friends. ldiff
based on what member
returns a substructure of the original list, not a copy; it simply steps down the list looking for that pointer and returns all previous items as a new list.
source to share
There is no answer without a ritual sacrifice to the deity loop
:
(defun separate-decls-and-body (body)
(loop for sub-body on body
for form = (first sub-body)
until (or (atom form) (not (eq (first form) 'declare)))
collecting form into decls
finally (return (values decls sub-body))))
source to share