arnesi
(defun check-required (name vars required)
(dolist (var required)
(assert (member var vars)
(var)
"Unrecognized symbol ~S in ~S." var name)))
(defmacro def-special-enviroment (name (&key accessor binder binder*) &rest vars)
"Define two macros for dealing with groups or related special variables.
[...]
ACCESSOR is defined as a macro: (defmacro ACCESSOR (VARS &rest
BODY)). Each element of VARS will be bound to the
current (dynamic) value of the special variable.
BINDER is defined as a macro for introducing (and binding new)
special variables. It is basically a readable LET form with the
prorpe declarations appended to the body. The first argument to
BINDER must be a form suitable as the first argument to LET.
ACCESSOR defaults to a new symbol in the same package as NAME
which is the concatenation of \"WITH-\" NAME. BINDER is built as
\"BIND-\" and BINDER* is BINDER \"*\"."
(unless accessor
(setf accessor (intern-concat (list '#:with- name) (symbol-package name))))
(unless binder
(setf binder (intern-concat (list '#:bind- name) (symbol-package name))))
(unless binder*
(setf binder* (intern-concat (list binder '#:*) (symbol-package binder))))
`(eval-when (:compile-toplevel :load-toplevel :execute)
(flet ()
(defmacro ,binder (requested-vars &body body)
(check-required ',name ',vars (mapcar #'car requested-vars))
`(let ,requested-vars
(declare (special ,@(mapcar #'car requested-vars)))
,@body))
(defmacro ,binder* (requested-vars &body body)
(check-required ',name ',vars (mapcar #'car requested-vars))
`(let* ,requested-vars
(declare (special ,@(mapcar #'car requested-vars)))
,@body))
(defmacro ,accessor (requested-vars &body body)
(check-required ',name ',vars requested-vars)
`(locally (declare (special ,@requested-vars))
,@body))
',name)))