1.0.19.30: muffle code deletion note from destructuring-bind
[sbcl/pkhuong.git] / src / code / parse-defmacro.lisp
blobbc233baaaa42ba81cfa4198eb3cfe1f934ffc864
1 ;;;; the PARSE-DEFMACRO function and related code
3 ;;;; This software is part of the SBCL system. See the README file for
4 ;;;; more information.
5 ;;;;
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*) ; tests that do argument counting at expansion time
17 (declaim (type list *arg-tests*))
18 (defvar *system-lets*) ; LET bindings done to allow lambda-list parsing
19 (declaim (type list *system-lets*))
20 (defvar *user-lets*) ; LET bindings that the user has explicitly supplied
21 (declaim (type list *user-lets*))
22 (defvar *env-var*) ; &ENVIRONMENT variable name
24 ;; the default default for unsupplied &OPTIONAL and &KEY args
25 (defvar *default-default*)
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 whole-var body name context
35 &key
36 (anonymousp nil)
37 (doc-string-allowed t)
38 ((:environment env-arg-name))
39 ((:default-default *default-default*))
40 (error-fun 'error)
41 (wrap-block t))
42 (multiple-value-bind (forms declarations documentation)
43 (parse-body body :doc-string-allowed doc-string-allowed)
44 (let ((*arg-tests* ())
45 (*user-lets* ())
46 (*system-lets* ())
47 (*ignorable-vars* ())
48 (*env-var* nil))
49 (multiple-value-bind (env-arg-used minimum maximum)
50 (parse-defmacro-lambda-list lambda-list whole-var name context
51 :error-fun error-fun
52 :anonymousp anonymousp)
53 (values `(let* (,@(nreverse *system-lets*))
54 #-sb-xc-host
55 (declare (muffle-conditions sb!ext:code-deletion-note))
56 ,@(when *ignorable-vars*
57 `((declare (ignorable ,@*ignorable-vars*))))
58 ,@*arg-tests*
59 (let* (,@(when env-arg-used
60 `((,*env-var* ,env-arg-name)))
61 ,@(nreverse *user-lets*))
62 ,@declarations
63 ,@(if wrap-block
64 `((block ,(fun-name-block-name name)
65 ,@forms))
66 forms)))
67 `(,@(when (and env-arg-name (not env-arg-used))
68 `((declare (ignore ,env-arg-name)))))
69 documentation
70 minimum
71 maximum)))))
73 (defun parse-defmacro-lambda-list (possibly-dotted-lambda-list
74 whole-var
75 name
76 context
77 &key
78 error-fun
79 anonymousp
80 env-illegal
81 sublist)
82 (let* (;; PATH is a sort of pointer into the part of the lambda list we're
83 ;; considering at this point in the code. PATH-0 is the root of the
84 ;; lambda list, which is the initial value of PATH.
85 (path-0 (if (or anonymousp sublist) whole-var `(cdr ,whole-var)))
86 (path path-0) ; will change below
87 (compiler-macro-whole (gensym "CMACRO-&WHOLE"))
88 (now-processing :required)
89 (maximum 0)
90 (minimum 0)
91 (keys ())
92 (key-seen nil)
93 (aux-seen nil)
94 (optional-seen nil)
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))
101 ((atom in-pdll)
102 (nreverse (if in-pdll
103 (list* in-pdll '&rest reversed-result)
104 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." context))
109 ;; Special case compiler-macros: if car of the form is FUNCALL,
110 ;; skip over it for destructuring, pretending cdr of the form is
111 ;; the actual form. Save original for &WHOLE.
112 (when (eq context 'define-compiler-macro)
113 (push-let-binding compiler-macro-whole whole-var :system t)
114 (push compiler-macro-whole *ignorable-vars*)
115 (push-let-binding whole-var whole-var
116 :system t
117 :when `(not (eq 'funcall (car ,whole-var)))
118 ;; Do we need to SETF too?
119 :else `(setf ,whole-var (cdr ,whole-var))))
120 (do ((rest-of-lambda-list lambda-list (cdr rest-of-lambda-list)))
121 ((null rest-of-lambda-list))
122 (macrolet ((process-sublist (var kind path)
123 (once-only ((var var))
124 `(if (listp ,var)
125 (let ((sublist-name (gensym ,kind)))
126 (push-sublist-binding sublist-name ,path ,var
127 name context error-fun)
128 (parse-defmacro-lambda-list ,var sublist-name name
129 context
130 :error-fun error-fun
131 :sublist t))
132 (push-let-binding ,var ,path))))
133 (normalize-singleton (var)
134 `(when (null (cdr ,var))
135 (setf (cdr ,var) (list *default-default*)))))
136 (let ((var (car rest-of-lambda-list)))
137 (typecase var
138 (list
139 (case now-processing
140 ((:required)
141 (when restp
142 (defmacro-error (format nil "required argument after ~A"
143 restp)
144 context name))
145 (when (process-sublist var "REQUIRED-" `(car ,path))
146 ;; Note &ENVIRONMENT from DEFSETF sublist
147 (aver (eq context 'defsetf))
148 (setf env-arg-used t))
149 (setq path `(cdr ,path)
150 minimum (1+ minimum)
151 maximum (1+ maximum)))
152 ((:optionals)
153 (normalize-singleton var)
154 (destructuring-bind
155 (varname &optional default-form suppliedp-name)
157 (push-optional-binding varname default-form suppliedp-name
158 :is-supplied-p `(not (null ,path))
159 :path `(car ,path)
160 :name name
161 :context context
162 :error-fun error-fun))
163 (setq path `(cdr ,path)
164 maximum (1+ maximum)))
165 ((:keywords)
166 (normalize-singleton var)
167 (let* ((keyword-given (consp (car var)))
168 (variable (if keyword-given
169 (cadar var)
170 (car var)))
171 (keyword (if keyword-given
172 (caar var)
173 (keywordicate variable)))
174 (default-form (cadr var))
175 (suppliedp-name (caddr var)))
176 (push-optional-binding variable default-form suppliedp-name
177 :is-supplied-p
178 `(keyword-supplied-p ',keyword
179 ,rest-name)
180 :path
181 `(lookup-keyword ',keyword ,rest-name)
182 :name name
183 :context context
184 :error-fun error-fun)
185 (push keyword keys)))
186 ((:auxs)
187 (push-let-binding (car var) (cadr var)))))
188 ((and symbol (not (eql nil)))
189 (case var
190 (&whole
191 (cond ((cdr rest-of-lambda-list)
192 (pop rest-of-lambda-list)
193 (process-sublist (car rest-of-lambda-list)
194 "WHOLE-LIST-"
195 (if (eq 'define-compiler-macro context)
196 compiler-macro-whole
197 whole-var)))
199 (defmacro-error "&WHOLE" context name))))
200 (&environment
201 (cond (env-illegal
202 (error "&ENVIRONMENT is not valid with ~S." context))
203 ;; DEFSETF explicitly allows &ENVIRONMENT, and we get
204 ;; it here in a sublist.
205 ((and sublist (neq context 'defsetf))
206 (error "&ENVIRONMENT is only valid at top level of ~
207 lambda-list."))
208 (env-arg-used
209 (error "Repeated &ENVIRONMENT.")))
210 (cond ((and (cdr rest-of-lambda-list)
211 (symbolp (cadr rest-of-lambda-list)))
212 (setq rest-of-lambda-list (cdr rest-of-lambda-list))
213 (check-defmacro-arg (car rest-of-lambda-list))
214 (setq *env-var* (car rest-of-lambda-list)
215 env-arg-used t))
217 (defmacro-error "&ENVIRONMENT" context name))))
218 ((&rest &body)
219 (cond ((or key-seen aux-seen)
220 (error "~A after ~A in ~A"
221 var (or key-seen aux-seen) context))
222 ((and (not restp) (cdr rest-of-lambda-list))
223 (setq rest-of-lambda-list (cdr rest-of-lambda-list)
224 restp var)
225 (process-sublist (car rest-of-lambda-list)
226 "REST-LIST-" path))
228 (defmacro-error (symbol-name var) context name))))
229 (&optional
230 (when (or key-seen aux-seen restp)
231 (error "~A after ~A in ~A lambda-list."
232 var (or key-seen aux-seen restp) context))
233 (when optional-seen
234 (error "Multiple ~A in ~A lambda list." var context))
235 (setq now-processing :optionals
236 optional-seen var))
237 (&key
238 (when aux-seen
239 (error "~A after ~A in ~A lambda-list." '&key '&aux context))
240 (when key-seen
241 (error "Multiple ~A in ~A lambda-list." '&key context))
242 (setf now-processing :keywords
243 rest-name (gensym "KEYWORDS-")
244 restp var
245 key-seen var)
246 (push rest-name *ignorable-vars*)
247 (push-let-binding rest-name path :system t))
248 (&allow-other-keys
249 (unless (eq now-processing :keywords)
250 (error "~A outside ~A section of lambda-list in ~A."
251 var '&key context))
252 (when allow-other-keys-p
253 (error "Multiple ~A in ~A lambda-list." var context))
254 (setq allow-other-keys-p t))
255 (&aux
256 (when (eq context 'defsetf)
257 (error "~A not allowed in a ~A lambda-list." var context))
258 (when aux-seen
259 (error "Multiple ~A in ~A lambda-list." '&aux context))
260 (setq now-processing :auxs
261 aux-seen var))
262 ;; FIXME: Other lambda list keywords.
264 (case now-processing
265 ((:required)
266 (when restp
267 (defmacro-error (format nil "required argument after ~A"
268 restp)
269 context name))
270 (push-let-binding var `(car ,path))
271 (setq minimum (1+ minimum)
272 maximum (1+ maximum)
273 path `(cdr ,path)))
274 ((:optionals)
275 (push-let-binding var `(car ,path)
276 :when `(not (null ,path)))
277 (setq path `(cdr ,path)
278 maximum (1+ maximum)))
279 ((:keywords)
280 (let ((key (keywordicate var)))
281 (push-let-binding
283 `(lookup-keyword ,key ,rest-name)
284 :when `(keyword-supplied-p ,key ,rest-name))
285 (push key keys)))
286 ((:auxs)
287 (push-let-binding var nil))))))
289 (error "non-symbol in lambda-list: ~S" var))))))
290 (let (;; common subexpression, suitable for passing to functions
291 ;; which expect a MAXIMUM argument regardless of whether
292 ;; there actually is a maximum number of arguments
293 ;; (expecting MAXIMUM=NIL when there is no maximum)
294 (explicit-maximum (and (not restp) maximum)))
295 (unless (and restp (zerop minimum))
296 (push (let ((args-form (if (eq 'define-compiler-macro context)
297 `(if (eq 'funcall (car ,whole-var))
298 (cdr ,path-0)
299 ,path-0)
300 path-0)))
301 (with-unique-names (args)
302 `(let ((,args ,args-form))
303 (unless ,(if restp
304 ;; (If RESTP, then the argument list
305 ;; might be dotted, in which case
306 ;; ordinary LENGTH won't work.)
307 `(list-of-length-at-least-p ,args ,minimum)
308 `(proper-list-of-length-p ,args
309 ,minimum
310 ,maximum))
311 ,(if (eq error-fun 'error)
312 `(arg-count-error ',context ',name ,args
313 ',lambda-list ,minimum
314 ,explicit-maximum)
315 `(,error-fun 'arg-count-error
316 :kind ',context
317 ,@(when name `(:name ',name))
318 :args ,args
319 :lambda-list ',lambda-list
320 :minimum ,minimum
321 :maximum ,explicit-maximum))))))
322 *arg-tests*))
323 (when key-seen
324 (let ((problem (gensym "KEY-PROBLEM-"))
325 (info (gensym "INFO-")))
326 (push `(multiple-value-bind (,problem ,info)
327 (verify-keywords ,rest-name
328 ',keys
329 ',allow-other-keys-p)
330 (when ,problem
331 (,error-fun
332 'defmacro-lambda-list-broken-key-list-error
333 :kind ',context
334 ,@(when name `(:name ',name))
335 :problem ,problem
336 :info ,info)))
337 *arg-tests*)))
338 (values env-arg-used minimum explicit-maximum))))
340 ;;; We save space in macro definitions by calling this function.
341 (defun arg-count-error (context name args lambda-list minimum maximum)
342 (let (#-sb-xc-host
343 (sb!debug:*stack-top-hint* (nth-value 1 (find-caller-name-and-frame))))
344 (error 'arg-count-error
345 :kind context
346 :name name
347 :args args
348 :lambda-list lambda-list
349 :minimum minimum
350 :maximum maximum)))
352 (defun push-sublist-binding (variable path object name context error-fun)
353 (check-defmacro-arg variable)
354 (let ((var (gensym "TEMP-")))
355 (push `(,variable
356 (let ((,var ,path))
357 (if (listp ,var)
358 ,var
359 (,error-fun 'defmacro-bogus-sublist-error
360 :kind ',context
361 ,@(when name `(:name ',name))
362 :object ,var
363 :lambda-list ',object))))
364 *system-lets*)))
366 (defun push-let-binding (variable form
367 &key system when (else *default-default*))
368 (check-defmacro-arg variable)
369 (let ((let-form (if when
370 `(,variable (if ,when ,form ,else))
371 `(,variable ,form))))
372 (if system
373 (push let-form *system-lets*)
374 (push let-form *user-lets*))))
376 (defun push-optional-binding (value-var init-form suppliedp-name
377 &key is-supplied-p path name context error-fun)
378 (unless suppliedp-name
379 (setq suppliedp-name (gensym "SUPPLIEDP-")))
380 (push-let-binding suppliedp-name is-supplied-p :system t)
381 (cond ((consp value-var)
382 (let ((whole-thing (gensym "OPTIONAL-SUBLIST-")))
383 (push-sublist-binding whole-thing
384 `(if ,suppliedp-name ,path ,init-form)
385 value-var name context error-fun)
386 (parse-defmacro-lambda-list value-var whole-thing name
387 context
388 :error-fun error-fun
389 :sublist t)))
390 ((symbolp value-var)
391 (push-let-binding value-var path :when suppliedp-name :else init-form))
393 (error "illegal optional variable name: ~S" value-var))))
395 (defun defmacro-error (problem context name)
396 (error "illegal or ill-formed ~A argument in ~A~@[ ~S~]"
397 problem context name))
399 (defun check-defmacro-arg (arg)
400 (when (or (and *env-var* (eq arg *env-var*))
401 (member arg *system-lets* :key #'car)
402 (member arg *user-lets* :key #'car))
403 (error "variable ~S occurs more than once" arg)))
405 ;;; Determine whether KEY-LIST is a valid list of keyword/value pairs.
406 ;;; Do not signal the error directly, 'cause we don't know how it
407 ;;; should be signaled.
408 (defun verify-keywords (key-list valid-keys allow-other-keys)
409 (do ((already-processed nil)
410 (unknown-keyword nil)
411 (remaining key-list (cddr remaining)))
412 ((null remaining)
413 (if (and unknown-keyword
414 (not allow-other-keys)
415 (not (lookup-keyword :allow-other-keys key-list)))
416 (values :unknown-keyword (list unknown-keyword valid-keys))
417 (values nil nil)))
418 (cond ((not (and (consp remaining) (listp (cdr remaining))))
419 (return (values :dotted-list key-list)))
420 ((null (cdr remaining))
421 (return (values :odd-length key-list)))
422 ((or (eq (car remaining) :allow-other-keys)
423 (member (car remaining) valid-keys))
424 (push (car remaining) already-processed))
426 (setq unknown-keyword (car remaining))))))
428 (defun lookup-keyword (keyword key-list)
429 (do ((remaining key-list (cddr remaining)))
430 ((endp remaining))
431 (when (eq keyword (car remaining))
432 (return (cadr remaining)))))
434 (defun keyword-supplied-p (keyword key-list)
435 (do ((remaining key-list (cddr remaining)))
436 ((endp remaining))
437 (when (eq keyword (car remaining))
438 (return t))))