(declaim (type list *system-lets*))
(defvar *user-lets* nil) ; LET bindings that the user has explicitly supplied
(declaim (type list *user-lets*))
+(defvar *env-var* nil) ; &ENVIRONMENT variable name
;; the default default for unsupplied &OPTIONAL and &KEY args
(defvar *default-default* nil)
(doc-string-allowed t)
((:environment env-arg-name))
((:default-default *default-default*))
- (error-fun 'error))
+ (error-fun 'error)
+ (wrap-block t))
(multiple-value-bind (forms declarations documentation)
(parse-body body doc-string-allowed)
(let ((*arg-tests* ())
(*user-lets* ())
(*system-lets* ())
- (*ignorable-vars* ()))
+ (*ignorable-vars* ())
+ (*env-var* nil))
(multiple-value-bind (env-arg-used minimum maximum)
(parse-defmacro-lambda-list lambda-list arg-list-name name
error-kind error-fun (not anonymousp)
- nil env-arg-name)
- (values `(let* ,(nreverse *system-lets*)
+ nil)
+ (values `(let* (,@(when env-arg-used
+ `((,*env-var* ,env-arg-name)))
+ ,@(nreverse *system-lets*))
,@(when *ignorable-vars*
`((declare (ignorable ,@*ignorable-vars*))))
,@*arg-tests*
(let* ,(nreverse *user-lets*)
,@declarations
- ,@forms))
+ ,@(if wrap-block
+ `((block ,(fun-name-block-name name)
+ ,@forms))
+ forms)))
`(,@(when (and env-arg-name (not env-arg-used))
- `((declare (ignore ,env-arg-name)))))
+ `((declare (ignore ,env-arg-name)))))
documentation
minimum
maximum)))))
error-fun
&optional
toplevel
- env-illegal
- env-arg-name)
+ env-illegal)
(let* (;; PATH is a sort of pointer into the part of the lambda list we're
;; considering at this point in the code. PATH-0 is the root of the
;; lambda list, which is the initial value of PATH.
(maximum 0)
(minimum 0)
(keys ())
+ (key-seen nil)
;; ANSI specifies that dotted lists are "treated exactly as if the
;; parameter name that ends the list had appeared preceded by &rest."
;; We force this behavior by transforming dotted lists into ordinary
((null rest-of-args))
(macrolet ((process-sublist (var sublist-name path)
(once-only ((var var))
- `(if (consp ,var)
+ `(if (listp ,var)
(let ((sub-list-name (gensym ,sublist-name)))
(push-sub-list-binding sub-list-name ,path ,var
name error-kind error-fun)
(error "&ENVIRONMENT is not valid with ~S." error-kind))
((not toplevel)
(error "&ENVIRONMENT is only valid at top level of ~
- lambda-list.")))
+ lambda-list."))
+ (env-arg-used
+ (error "Repeated &ENVIRONMENT.")))
(cond ((and (cdr rest-of-args) (symbolp (cadr rest-of-args)))
(setq rest-of-args (cdr rest-of-args))
- (push-let-binding (car rest-of-args) env-arg-name nil)
+ (check-defmacro-arg (car rest-of-args))
+ (setq *env-var* (car rest-of-args))
(setq env-arg-used t))
(t
(defmacro-error "&ENVIRONMENT" error-kind name))))
(setq rest-name (gensym "KEYWORDS-"))
(push rest-name *ignorable-vars*)
(setq restp t)
+ (setq key-seen t)
(push-let-binding rest-name path t))
(&allow-other-keys
(setq allow-other-keys-p t))
:minimum ,minimum
:maximum ,explicit-maximum)))
*arg-tests*))
- (when keys
+ (when key-seen
(let ((problem (gensym "KEY-PROBLEM-"))
(info (gensym "INFO-")))
(push `(multiple-value-bind (,problem ,info)
:maximum maximum)))
(defun push-sub-list-binding (variable path object name error-kind error-fun)
+ (check-defmacro-arg variable)
(let ((var (gensym "TEMP-")))
(push `(,variable
(let ((,var ,path))
(defun push-let-binding (variable path systemp &optional condition
(init-form *default-default*))
+ (check-defmacro-arg variable)
(let ((let-form (if condition
`(,variable (if ,condition ,path ,init-form))
`(,variable ,path))))
(error "illegal or ill-formed ~A argument in ~A~@[ ~S~]"
problem kind name))
+(defun check-defmacro-arg (arg)
+ (when (or (and *env-var* (eq arg *env-var*))
+ (member arg *system-lets* :key #'car)
+ (member arg *user-lets* :key #'car))
+ (error "variable ~S occurs more than once" arg)))
+
;;; Determine whether KEY-LIST is a valid list of keyword/value pairs.
;;; Do not signal the error directly, 'cause we don't know how it
;;; should be signaled.