1 ;;;; the PARSE-DEFMACRO function and related code
3 ;;;; This software is part of the SBCL system. See the README file for
6 ;;;; This software is derived from the CMU CL system, which was
7 ;;;; written at Carnegie Mellon University and released into the
8 ;;;; public domain. The software is in the public domain and is
9 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
10 ;;;; files for more information.
12 (in-package "SB!KERNEL")
14 ;;; variables for accumulating the results of parsing a DEFMACRO. (Declarations
15 ;;; in DEFMACRO are the reason this isn't as easy as it sounds.)
16 (defvar *arg-tests* nil) ; tests that do argument counting at expansion time
17 (declaim (type list *arg-tests*))
18 (defvar *system-lets* nil) ; LET bindings done to allow lambda-list parsing
19 (declaim (type list *system-lets*))
20 (defvar *user-lets* nil) ; LET bindings that the user has explicitly supplied
21 (declaim (type list *user-lets*))
22 (defvar *env-var* nil) ; &ENVIRONMENT variable name
24 ;; the default default for unsupplied &OPTIONAL and &KEY args
25 (defvar *default-default* nil)
27 ;;; temps that we introduce and might not reference
28 (defvar *ignorable-vars*)
29 (declaim (type list *ignorable-vars*))
31 ;;; Return, as multiple values, a body, possibly a declare form to put
32 ;;; where this code is inserted, the documentation for the parsed
33 ;;; body, and bounds on the number of arguments.
34 (defun parse-defmacro (lambda-list arg-list-name body name error-kind
37 (doc-string-allowed t)
38 ((:environment env-arg-name))
39 ((:default-default *default-default*))
42 (multiple-value-bind (forms declarations documentation)
43 (parse-body body doc-string-allowed)
44 (let ((*arg-tests* ())
49 (multiple-value-bind (env-arg-used minimum maximum)
50 (parse-defmacro-lambda-list lambda-list arg-list-name name
51 error-kind error-fun (not anonymousp)
53 (values `(let* (,@(when env-arg-used
54 `((,*env-var* ,env-arg-name)))
55 ,@(nreverse *system-lets*))
56 ,@(when *ignorable-vars*
57 `((declare (ignorable ,@*ignorable-vars*))))
59 (let* ,(nreverse *user-lets*)
62 `((block ,(fun-name-block-name name)
65 `(,@(when (and env-arg-name (not env-arg-used))
66 `((declare (ignore ,env-arg-name)))))
71 ;;; partial reverse-engineered documentation:
72 ;;; TOPLEVEL is true for calls through PARSE-DEFMACRO from DEFSETF and
73 ;;; DESTRUCTURING-BIND, false otherwise.
75 (defun parse-defmacro-lambda-list (possibly-dotted-lambda-list
83 (let* (;; PATH is a sort of pointer into the part of the lambda list we're
84 ;; considering at this point in the code. PATH-0 is the root of the
85 ;; lambda list, which is the initial value of PATH.
89 (path path-0) ; (will change below)
90 (now-processing :required)
95 ;; ANSI specifies that dotted lists are "treated exactly as if the
96 ;; parameter name that ends the list had appeared preceded by &rest."
97 ;; We force this behavior by transforming dotted lists into ordinary
98 ;; lists with explicit &REST elements.
99 (lambda-list (do ((in-pdll possibly-dotted-lambda-list (cdr in-pdll))
100 (reversed-result nil))
102 (nreverse (if in-pdll
103 (list* in-pdll '&rest reversed-result)
105 (push (car in-pdll) reversed-result)))
106 rest-name restp allow-other-keys-p env-arg-used)
107 (when (member '&whole (rest lambda-list))
108 (error "&WHOLE may only appear first in ~S lambda-list." error-kind))
109 (do ((rest-of-args lambda-list (cdr rest-of-args)))
110 ((null rest-of-args))
111 (macrolet ((process-sublist (var sublist-name path)
112 (once-only ((var var))
114 (let ((sub-list-name (gensym ,sublist-name)))
115 (push-sub-list-binding sub-list-name ,path ,var
116 name error-kind error-fun)
117 (parse-defmacro-lambda-list ,var sub-list-name name
118 error-kind error-fun))
119 (push-let-binding ,var ,path nil)))))
120 (let ((var (car rest-of-args)))
126 (defmacro-error "required argument after &REST/&BODY"
128 (process-sublist var "SUBLIST-" `(car ,path))
129 (setq path `(cdr ,path)
131 maximum (1+ maximum)))
133 (destructuring-bind (varname &optional initform supplied-p)
135 (push-optional-binding varname initform supplied-p
136 `(not (null ,path)) `(car ,path)
137 name error-kind error-fun))
138 (setq path `(cdr ,path)
139 maximum (1+ maximum)))
141 (let* ((keyword-given (consp (car var)))
142 (variable (if keyword-given
145 (keyword (if keyword-given
147 (keywordicate variable)))
148 (supplied-p (caddr var)))
149 (push-optional-binding variable (cadr var) supplied-p
150 `(keyword-supplied-p ',keyword
152 `(lookup-keyword ',keyword
154 name error-kind error-fun)
155 (push keyword keys)))
157 (push-let-binding (car var) (cadr var) nil))))
158 ((and symbol (not (eql nil)))
161 (cond ((cdr rest-of-args)
162 (setq rest-of-args (cdr rest-of-args))
163 (process-sublist (car rest-of-args)
164 "WHOLE-LIST-" arg-list-name))
166 (defmacro-error "&WHOLE" error-kind name))))
169 (error "&ENVIRONMENT is not valid with ~S." error-kind))
171 (error "&ENVIRONMENT is only valid at top level of ~
174 (error "Repeated &ENVIRONMENT.")))
175 (cond ((and (cdr rest-of-args) (symbolp (cadr rest-of-args)))
176 (setq rest-of-args (cdr rest-of-args))
177 (check-defmacro-arg (car rest-of-args))
178 (setq *env-var* (car rest-of-args))
179 (setq env-arg-used t))
181 (defmacro-error "&ENVIRONMENT" error-kind name))))
183 (cond ((and (not restp) (cdr rest-of-args))
184 (setq rest-of-args (cdr rest-of-args))
186 (process-sublist (car rest-of-args) "REST-LIST-" path))
188 (defmacro-error (symbol-name var) error-kind name))))
190 (setq now-processing :optionals))
192 (setq now-processing :keywords)
193 (setq rest-name (gensym "KEYWORDS-"))
194 (push rest-name *ignorable-vars*)
197 (push-let-binding rest-name path t))
199 (setq allow-other-keys-p t))
201 (setq now-processing :auxs))
202 ;; FIXME: Other lambda list keywords.
207 (defmacro-error "required argument after &REST/&BODY"
209 (push-let-binding var `(car ,path) nil)
210 (setq minimum (1+ minimum)
214 (push-let-binding var `(car ,path) nil `(not (null ,path)))
215 (setq path `(cdr ,path)
216 maximum (1+ maximum)))
218 (let ((key (keywordicate var)))
219 (push-let-binding var
220 `(lookup-keyword ,key ,rest-name)
224 (push-let-binding var nil nil))))))
226 (error "non-symbol in lambda-list: ~S" var))))))
227 (let (;; common subexpression, suitable for passing to functions
228 ;; which expect a MAXIMUM argument regardless of whether
229 ;; there actually is a maximum number of arguments
230 ;; (expecting MAXIMUM=NIL when there is no maximum)
231 (explicit-maximum (and (not restp) maximum)))
232 (unless (and restp (zerop minimum))
233 (push `(unless ,(if restp
234 ;; (If RESTP, then the argument list might be
235 ;; dotted, in which case ordinary LENGTH won't
237 `(list-of-length-at-least-p ,path-0 ,minimum)
238 `(proper-list-of-length-p ,path-0 ,minimum ,maximum))
239 ,(if (eq error-fun 'error)
240 `(arg-count-error ',error-kind ',name ,path-0
241 ',lambda-list ,minimum
243 `(,error-fun 'arg-count-error
245 ,@(when name `(:name ',name))
247 :lambda-list ',lambda-list
249 :maximum ,explicit-maximum)))
252 (let ((problem (gensym "KEY-PROBLEM-"))
253 (info (gensym "INFO-")))
254 (push `(multiple-value-bind (,problem ,info)
255 (verify-keywords ,rest-name
257 ',allow-other-keys-p)
260 'defmacro-lambda-list-broken-key-list-error
262 ,@(when name `(:name ',name))
266 (values env-arg-used minimum explicit-maximum))))
268 ;;; We save space in macro definitions by calling this function.
269 (defun arg-count-error (error-kind name args lambda-list minimum maximum)
271 (sb!debug:*stack-top-hint* (nth-value 1 (find-caller-name-and-frame))))
272 (error 'arg-count-error
276 :lambda-list lambda-list
280 (defun push-sub-list-binding (variable path object name error-kind error-fun)
281 (check-defmacro-arg variable)
282 (let ((var (gensym "TEMP-")))
287 (,error-fun 'defmacro-bogus-sublist-error
289 ,@(when name `(:name ',name))
291 :lambda-list ',object))))
294 (defun push-let-binding (variable path systemp &optional condition
295 (init-form *default-default*))
296 (check-defmacro-arg variable)
297 (let ((let-form (if condition
298 `(,variable (if ,condition ,path ,init-form))
299 `(,variable ,path))))
301 (push let-form *system-lets*)
302 (push let-form *user-lets*))))
304 (defun push-optional-binding (value-var init-form supplied-var condition path
305 name error-kind error-fun)
307 (setq supplied-var (gensym "SUPPLIEDP-")))
308 (push-let-binding supplied-var condition t)
309 (cond ((consp value-var)
310 (let ((whole-thing (gensym "OPTIONAL-SUBLIST-")))
311 (push-sub-list-binding whole-thing
312 `(if ,supplied-var ,path ,init-form)
313 value-var name error-kind error-fun)
314 (parse-defmacro-lambda-list value-var whole-thing name
315 error-kind error-fun)))
317 (push-let-binding value-var path nil supplied-var init-form))
319 (error "illegal optional variable name: ~S" value-var))))
321 (defun defmacro-error (problem kind name)
322 (error "illegal or ill-formed ~A argument in ~A~@[ ~S~]"
325 (defun check-defmacro-arg (arg)
326 (when (or (and *env-var* (eq arg *env-var*))
327 (member arg *system-lets* :key #'car)
328 (member arg *user-lets* :key #'car))
329 (error "variable ~S occurs more than once" arg)))
331 ;;; Determine whether KEY-LIST is a valid list of keyword/value pairs.
332 ;;; Do not signal the error directly, 'cause we don't know how it
333 ;;; should be signaled.
334 (defun verify-keywords (key-list valid-keys allow-other-keys)
335 (do ((already-processed nil)
336 (unknown-keyword nil)
337 (remaining key-list (cddr remaining)))
339 (if (and unknown-keyword
340 (not allow-other-keys)
341 (not (lookup-keyword :allow-other-keys key-list)))
342 (values :unknown-keyword (list unknown-keyword valid-keys))
344 (cond ((not (and (consp remaining) (listp (cdr remaining))))
345 (return (values :dotted-list key-list)))
346 ((null (cdr remaining))
347 (return (values :odd-length key-list)))
348 ((or (eq (car remaining) :allow-other-keys)
349 (member (car remaining) valid-keys))
350 (push (car remaining) already-processed))
352 (setq unknown-keyword (car remaining))))))
354 (defun lookup-keyword (keyword key-list)
355 (do ((remaining key-list (cddr remaining)))
357 (when (eq keyword (car remaining))
358 (return (cadr remaining)))))
360 (defun keyword-supplied-p (keyword key-list)
361 (do ((remaining key-list (cddr remaining)))
363 (when (eq keyword (car remaining))