(in-package :cl-irc) (defmacro destructuring-irc-message-arguments (lambda-list message &body body) "Destructures the arguments slot in MESSAGE according to LAMBDA-LIST and binds them in BODY. destructuring-irc-message-arguments's lambda list syntax is as follows: reqvars::= var* optvars::= [&optional {var | (var [init-form [supplied-p-parameter]])}*] restvar::= [&rest var] wholevar::= [&whole var] lastvar::= [&last var] lambda-list::= (wholevar reqvars optvars restvar lastvar) With the exception of &last, all lambda list keywords are analogous to a destructuring lambda list's (see clhs 3.4.5). If &last is given, the specified variable is bound to the last argument in the message. Specifying &last implies that all arguments past the last of the required variables will be ignored, even if there is no &rest lambda list keyword present." (let ((valid-bare-ll-keywords '(&optional &rest &whole)) (nothing (gensym)) (%message (gensym))) (labels ((keyword-ll-entry-p (entry) (eql (schar (symbol-name entry) 0) #\&)) (valid-bare-ll-entry-p (entry) (or (not (keyword-ll-entry-p entry)) (member entry valid-bare-ll-keywords :test 'string=))) (append-&rest-p (last-entries destructuring-ll) (not (or (null last-entries) (member '&rest destructuring-ll :test 'string=))))) (let* ((last-entries (member '&last lambda-list :test 'string=)) (last-variable (second last-entries)) (destructuring-ll (butlast lambda-list (length last-entries))) (invalid-ll-entries (remove-if #'valid-bare-ll-entry-p destructuring-ll))) (unless (or (null last-entries) (= 2 (length last-entries))) (error "Invalid number of &last arguments in ~S" lambda-list)) (when (and last-variable (member last-variable destructuring-ll)) (error "Duplicate entry ~S in lambda list ~S" last-variable lambda-list)) (when invalid-ll-entries (error "Invalid lambda list entries ~S found in ~S" invalid-ll-entries lambda-list)) `(let ((,%message ,message)) (let (,@(when last-entries `((,last-variable (car (last (arguments ,%message))))))) (destructuring-bind ,(if (append-&rest-p last-entries destructuring-ll) (append destructuring-ll `(&rest ,nothing)) destructuring-ll) (arguments ,%message) ,@(when (append-&rest-p last-entries destructuring-ll) `((declare (ignore ,nothing)))) ,@body))))))) ;;; test with: #|(destructuring-irc-message-arguments (victim &last meat) (create-irc-message (format nil ":kire!~~eenge@216.248.178.227 PRIVMSG cl-irc heyhey!~A" #\Return)) (format t "~A was sent ~A" victim meat)) |#