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