"Martin C. Martin" <martin@...> writes:
> Is there a common idiom for macros that need two passes? For example,
> suppose I have a macro that takes a keyword, and when expanded, pushes
> the keyword onto a global list. Once all instances of the macro have
> been expanded, so we have the full set of keywords, we then figure out a
> perfect hash function for the keywords, and replace each instance of the
> keyword with its hash. Is there a standard way to do this in Common
> Lisp? Is it possible?
In general it's not possible portably without using a code-walker.
I think you could come up with a kludgy but portable solution which
would work as long as the whole frobbage happens in the same file, and
you always compile the file.
In case of SBCL that solution would could also work across multiple
files (compile-time effects affect the environment in which COMPILE-FILE
was run), though it would easily get very hairy.
Is the scope bound by a surrounding form? I.e. is it like
(with-registered-keywords (...)
... (register-keyword :foo) ...
... (register-keyword :bar) ... )
For something like that, a code walker is ideal. Take a look at cl-walker on
common-lisp.net. If you do not want drag in that dependency, take a look
at SB-WALK. In particular,
(let ((sb-walker:*walk-form-expand-macros-p* t))
(sb-walker:walk-form form))
is MACROEXPAND-ALL which you can call in WITH-REGISTERED-KEYWORDS to
expand all the REGISTER-KEYWORD which would push to a locally
established list.
If you don't want the surrounding form, but are content with file-only
scope, you could perhaps do something like
(declaim (special *perfect-hashtable*))
(eval-when (:compile-toplevel)
(defvar *compiling* t)
(defvar *keywords* nil))
(eval-when (:load-toplevel :execute)
(defvar *compiling* nil))
(defmacro register-keyword (keyword)
(when *compiling*
(push keyword *keywords*))
`(whatever ...))
....
(macrolet ((define-perfect-hashtable (...)
`(defvar *perfect-hashtable*
(create-perfect-hashtable
:keywords ,@*keywords*))))
(define-perfect-hashtable ...))
And don't forget to always compile that file. :-)
-T.
|