+;;;
+;;; FIXME: in here we have not one but two mini-copies of a weird
+;;; hybrid of PARSE-LAMBDA-LIST and PARSE-DEFMACRO-LAMBDA-LIST.
+(defun deal-with-args-option (wrapped-body args-lambda-list)
+ (let ((intercept-rebindings
+ (let (rebindings)
+ (dolist (arg args-lambda-list (nreverse rebindings))
+ (unless (member arg lambda-list-keywords :test #'eq)
+ (typecase arg
+ (symbol (push `(,arg ',arg) rebindings))
+ (cons
+ (unless (symbolp (car arg))
+ (error "invalid lambda-list specifier: ~S." arg))
+ (push `(,(car arg) ',(car arg)) rebindings))
+ (t (error "invalid lambda-list-specifier: ~S." arg)))))))
+ (nreq 0)
+ (nopt 0)
+ (whole nil))
+ ;; Count the number of required and optional parameters in
+ ;; ARGS-LAMBDA-LIST into NREQ and NOPT, and set WHOLE to the
+ ;; name of a &WHOLE parameter, if any.
+ (when (member '&whole (rest args-lambda-list))
+ (error 'simple-program-error
+ :format-control "~@<The value of the :ARGUMENTS option of ~
+ DEFINE-METHOD-COMBINATION is~2I~_~S,~I~_but &WHOLE may ~
+ only appear first in the lambda list.~:>"
+ :format-arguments (list args-lambda-list)))
+ (loop with state = 'required
+ for arg in args-lambda-list do
+ (if (memq arg lambda-list-keywords)
+ (setq state arg)
+ (case state
+ (required (incf nreq))
+ (&optional (incf nopt))
+ (&whole (setq whole arg state 'required)))))
+ ;; This assumes that the head of WRAPPED-BODY is a let, and it
+ ;; injects let-bindings of the form (ARG 'SYM) for all variables
+ ;; of the argument-lambda-list; SYM is a gensym.
+ (aver (memq (first wrapped-body) '(let let*)))
+ (setf (second wrapped-body)
+ (append intercept-rebindings (second wrapped-body)))
+ ;; Be sure to fill out the args lambda list so that it can be too
+ ;; short if it wants to.
+ (unless (or (memq '&rest args-lambda-list)
+ (memq '&allow-other-keys args-lambda-list))
+ (let ((aux (memq '&aux args-lambda-list)))
+ (setq args-lambda-list
+ (append (ldiff args-lambda-list aux)
+ (if (memq '&key args-lambda-list)
+ '(&allow-other-keys)
+ '(&rest .ignore.))
+ aux))))
+ ;; .GENERIC-FUNCTION. is bound to the generic function in the
+ ;; method combination function, and .GF-ARGS* is bound to the
+ ;; generic function arguments in effective method functions
+ ;; created for generic functions having a method combination that
+ ;; uses :ARGUMENTS.
+ ;;
+ ;; The DESTRUCTURING-BIND binds the parameters of the
+ ;; ARGS-LAMBDA-LIST to actual generic function arguments. Because
+ ;; ARGS-LAMBDA-LIST may be shorter or longer than the generic
+ ;; function's lambda list, which is only known at run time, this
+ ;; destructuring has to be done on a slighly modified list of
+ ;; actual arguments, from which values might be stripped or added.
+ ;;
+ ;; Using one of the variable names in the body inserts a symbol
+ ;; into the effective method, and running the effective method
+ ;; produces the value of actual argument that is bound to the
+ ;; symbol.
+ `(let ((inner-result. ,wrapped-body)
+ (gf-lambda-list (generic-function-lambda-list .generic-function.)))
+ `(destructuring-bind ,',args-lambda-list
+ (frob-combined-method-args
+ .gf-args. ',gf-lambda-list
+ ,',nreq ,',nopt)
+ ,,(when (memq '.ignore. args-lambda-list)
+ ''(declare (ignore .ignore.)))
+ ;; If there is a &WHOLE in the args-lambda-list, let
+ ;; it result in the actual arguments of the generic-function
+ ;; not the frobbed list.
+ ,,(when whole
+ ``(setq ,',whole .gf-args.))
+ ,inner-result.))))