0.pre8.82:
[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* 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
23 ;; the default default for unsupplied &OPTIONAL and &KEY args
24 (defvar *default-default* nil)
25
26 ;;; temps that we introduce and might not reference
27 (defvar *ignorable-vars*)
28 (declaim (type list *ignorable-vars*))
29
30 ;;; Return, as multiple values, a body, possibly a declare form to put
31 ;;; where this code is inserted, the documentation for the parsed
32 ;;; body, and bounds on the number of arguments.
33 (defun parse-defmacro (lambda-list arg-list-name body name error-kind
34                                    &key
35                                    (anonymousp nil)
36                                    (doc-string-allowed t)
37                                    ((:environment env-arg-name))
38                                    ((:default-default *default-default*))
39                                    (error-fun 'error))
40   (multiple-value-bind (forms declarations documentation)
41       (parse-body body doc-string-allowed)
42     (let ((*arg-tests* ())
43           (*user-lets* ())
44           (*system-lets* ())
45           (*ignorable-vars* ()))
46       (multiple-value-bind (env-arg-used minimum maximum)
47           (parse-defmacro-lambda-list lambda-list arg-list-name name
48                                       error-kind error-fun (not anonymousp)
49                                       nil env-arg-name)
50         (values `(let* ,(nreverse *system-lets*)
51                    ,@(when *ignorable-vars*
52                        `((declare (ignorable ,@*ignorable-vars*))))
53                    ,@*arg-tests*
54                    (let* ,(nreverse *user-lets*)
55                      ,@declarations
56                      ,@forms))
57                 `(,@(when (and env-arg-name (not env-arg-used))
58                       `((declare (ignore ,env-arg-name)))))
59                 documentation
60                 minimum
61                 maximum)))))
62
63 ;;; partial reverse-engineered documentation:
64 ;;;   TOPLEVEL is true for calls through PARSE-DEFMACRO from DEFSETF and
65 ;;;     DESTRUCTURING-BIND, false otherwise.
66 ;;; -- WHN 19990620
67 (defun parse-defmacro-lambda-list (possibly-dotted-lambda-list
68                                    arg-list-name
69                                    name
70                                    error-kind
71                                    error-fun
72                                    &optional
73                                    toplevel
74                                    env-illegal
75                                    env-arg-name)
76   (let* (;; PATH is a sort of pointer into the part of the lambda list we're
77          ;; considering at this point in the code. PATH-0 is the root of the
78          ;; lambda list, which is the initial value of PATH.
79          (path-0 (if toplevel
80                      `(cdr ,arg-list-name)
81                      arg-list-name))
82          (path path-0) ; (will change below)
83          (now-processing :required)
84          (maximum 0)
85          (minimum 0)
86          (keys ())
87          ;; ANSI specifies that dotted lists are "treated exactly as if the
88          ;; parameter name that ends the list had appeared preceded by &rest."
89          ;; We force this behavior by transforming dotted lists into ordinary
90          ;; lists with explicit &REST elements.
91          (lambda-list (do ((in-pdll possibly-dotted-lambda-list (cdr in-pdll))
92                            (reversed-result nil))
93                           ((atom in-pdll)
94                            (nreverse (if in-pdll
95                                          (list* in-pdll '&rest reversed-result)
96                                          reversed-result)))
97                         (push (car in-pdll) reversed-result)))
98          rest-name restp allow-other-keys-p env-arg-used)
99     (when (member '&whole (rest lambda-list))
100       (error "&WHOLE may only appear first in ~S lambda-list." error-kind))
101     (do ((rest-of-args lambda-list (cdr rest-of-args)))
102         ((null rest-of-args))
103       (macrolet ((process-sublist (var sublist-name path)
104                    (once-only ((var var))
105                      `(if (consp ,var)
106                           (let ((sub-list-name (gensym ,sublist-name)))
107                             (push-sub-list-binding sub-list-name ,path ,var
108                                                    name error-kind error-fun)
109                             (parse-defmacro-lambda-list ,var sub-list-name name
110                                                         error-kind error-fun))
111                           (push-let-binding ,var ,path nil)))))
112         (let ((var (car rest-of-args)))
113           (typecase var
114             (list
115              (case now-processing
116                ((:required)
117                 (when restp
118                   (defmacro-error "required argument after &REST/&BODY"
119                       error-kind name))
120                 (process-sublist var "SUBLIST-" `(car ,path))
121                 (setq path `(cdr ,path)
122                       minimum (1+ minimum)
123                       maximum (1+ maximum)))
124                ((:optionals)
125                 (destructuring-bind (varname &optional initform supplied-p)
126                     var
127                   (push-optional-binding varname initform supplied-p
128                                          `(not (null ,path)) `(car ,path)
129                                          name error-kind error-fun))
130                 (setq path `(cdr ,path)
131                       maximum (1+ maximum)))
132                ((:keywords)
133                 (let* ((keyword-given (consp (car var)))
134                        (variable (if keyword-given
135                                      (cadar var)
136                                      (car var)))
137                        (keyword (if keyword-given
138                                     (caar var)
139                                     (keywordicate variable)))
140                        (supplied-p (caddr var)))
141                   (push-optional-binding variable (cadr var) supplied-p
142                                          `(keyword-supplied-p ',keyword
143                                                               ,rest-name)
144                                          `(lookup-keyword ',keyword
145                                                           ,rest-name)
146                                          name error-kind error-fun)
147                   (push keyword keys)))
148                ((:auxs)
149                 (push-let-binding (car var) (cadr var) nil))))
150             ((and symbol (not (eql nil)))
151              (case var
152                (&whole
153                 (cond ((cdr rest-of-args)
154                        (setq rest-of-args (cdr rest-of-args))
155                        (process-sublist (car rest-of-args)
156                                         "WHOLE-LIST-" arg-list-name))
157                       (t
158                        (defmacro-error "&WHOLE" error-kind name))))
159                (&environment
160                 (cond (env-illegal
161                        (error "&ENVIRONMENT is not valid with ~S." error-kind))
162                       ((not toplevel)
163                        (error "&ENVIRONMENT is only valid at top level of ~
164                              lambda-list.")))
165                 (cond ((and (cdr rest-of-args) (symbolp (cadr rest-of-args)))
166                        (setq rest-of-args (cdr rest-of-args))
167                        (push-let-binding (car rest-of-args) env-arg-name nil)
168                        (setq env-arg-used t))
169                       (t
170                        (defmacro-error "&ENVIRONMENT" error-kind name))))
171                ((&rest &body)
172                 (cond ((and (not restp) (cdr rest-of-args))
173                        (setq rest-of-args (cdr rest-of-args))
174                        (setq restp t)
175                        (process-sublist (car rest-of-args) "REST-LIST-" path))
176                       (t
177                        (defmacro-error (symbol-name var) error-kind name))))
178                (&optional
179                 (setq now-processing :optionals))
180                (&key
181                 (setq now-processing :keywords)
182                 (setq rest-name (gensym "KEYWORDS-"))
183                 (push rest-name *ignorable-vars*)
184                 (setq restp t)
185                 (push-let-binding rest-name path t))
186                (&allow-other-keys
187                 (setq allow-other-keys-p t))
188                (&aux
189                 (setq now-processing :auxs))
190                ;; FIXME: Other lambda list keywords.
191                (t
192                 (case now-processing
193                   ((:required)
194                    (when restp
195                      (defmacro-error "required argument after &REST/&BODY"
196                          error-kind name))
197                    (push-let-binding var `(car ,path) nil)
198                    (setq minimum (1+ minimum)
199                          maximum (1+ maximum)
200                          path `(cdr ,path)))
201                   ((:optionals)
202                    (push-let-binding var `(car ,path) nil `(not (null ,path)))
203                    (setq path `(cdr ,path)
204                          maximum (1+ maximum)))
205                   ((:keywords)
206                    (let ((key (keywordicate var)))
207                      (push-let-binding var
208                                        `(lookup-keyword ,key ,rest-name)
209                                        nil)
210                      (push key keys)))
211                   ((:auxs)
212                    (push-let-binding var nil nil))))))
213             (t
214              (error "non-symbol in lambda-list: ~S" var))))))
215     (let (;; common subexpression, suitable for passing to functions
216           ;; which expect a MAXIMUM argument regardless of whether
217           ;; there actually is a maximum number of arguments
218           ;; (expecting MAXIMUM=NIL when there is no maximum)
219           (explicit-maximum (and (not restp) maximum)))
220       (unless (and restp (zerop minimum))
221         (push `(unless ,(if restp
222                             ;; (If RESTP, then the argument list might be
223                             ;; dotted, in which case ordinary LENGTH won't
224                             ;; work.)
225                             `(list-of-length-at-least-p ,path-0 ,minimum)
226                             `(proper-list-of-length-p ,path-0 ,minimum ,maximum))
227                  ,(if (eq error-fun 'error)
228                       `(arg-count-error ',error-kind ',name ,path-0
229                                         ',lambda-list ,minimum
230                                         ,explicit-maximum)
231                       `(,error-fun 'arg-count-error
232                                    :kind ',error-kind
233                                    ,@(when name `(:name ',name))
234                                    :args ,path-0
235                                    :lambda-list ',lambda-list
236                                    :minimum ,minimum
237                                    :maximum ,explicit-maximum)))
238               *arg-tests*))
239       (when keys
240         (let ((problem (gensym "KEY-PROBLEM-"))
241               (info (gensym "INFO-")))
242           (push `(multiple-value-bind (,problem ,info)
243                      (verify-keywords ,rest-name
244                                       ',keys
245                                       ',allow-other-keys-p)
246                    (when ,problem
247                      (,error-fun
248                       'defmacro-lambda-list-broken-key-list-error
249                       :kind ',error-kind
250                       ,@(when name `(:name ',name))
251                       :problem ,problem
252                       :info ,info)))
253                 *arg-tests*)))
254       (values env-arg-used minimum explicit-maximum))))
255
256 ;;; We save space in macro definitions by calling this function.
257 (defun arg-count-error (error-kind name args lambda-list minimum maximum)
258   (let (#-sb-xc-host
259         (sb!debug:*stack-top-hint* (nth-value 1 (find-caller-name-and-frame))))
260     (error 'arg-count-error
261            :kind error-kind
262            :name name
263            :args args
264            :lambda-list lambda-list
265            :minimum minimum
266            :maximum maximum)))
267
268 (defun push-sub-list-binding (variable path object name error-kind error-fun)
269   (let ((var (gensym "TEMP-")))
270     (push `(,variable
271             (let ((,var ,path))
272               (if (listp ,var)
273                 ,var
274                 (,error-fun 'defmacro-bogus-sublist-error
275                             :kind ',error-kind
276                             ,@(when name `(:name ',name))
277                             :object ,var
278                             :lambda-list ',object))))
279           *system-lets*)))
280
281 (defun push-let-binding (variable path systemp &optional condition
282                                   (init-form *default-default*))
283   (let ((let-form (if condition
284                       `(,variable (if ,condition ,path ,init-form))
285                       `(,variable ,path))))
286     (if systemp
287       (push let-form *system-lets*)
288       (push let-form *user-lets*))))
289
290 (defun push-optional-binding (value-var init-form supplied-var condition path
291                                         name error-kind error-fun)
292   (unless supplied-var
293     (setq supplied-var (gensym "SUPPLIEDP-")))
294   (push-let-binding supplied-var condition t)
295   (cond ((consp value-var)
296          (let ((whole-thing (gensym "OPTIONAL-SUBLIST-")))
297            (push-sub-list-binding whole-thing
298                                   `(if ,supplied-var ,path ,init-form)
299                                   value-var name error-kind error-fun)
300            (parse-defmacro-lambda-list value-var whole-thing name
301                                        error-kind error-fun)))
302         ((symbolp value-var)
303          (push-let-binding value-var path nil supplied-var init-form))
304         (t
305          (error "illegal optional variable name: ~S" value-var))))
306
307 (defun defmacro-error (problem kind name)
308   (error "illegal or ill-formed ~A argument in ~A~@[ ~S~]"
309          problem kind name))
310
311 ;;; Determine whether KEY-LIST is a valid list of keyword/value pairs.
312 ;;; Do not signal the error directly, 'cause we don't know how it
313 ;;; should be signaled.
314 (defun verify-keywords (key-list valid-keys allow-other-keys)
315   (do ((already-processed nil)
316        (unknown-keyword nil)
317        (remaining key-list (cddr remaining)))
318       ((null remaining)
319        (if (and unknown-keyword
320                 (not allow-other-keys)
321                 (not (lookup-keyword :allow-other-keys key-list)))
322            (values :unknown-keyword (list unknown-keyword valid-keys))
323            (values nil nil)))
324     (cond ((not (and (consp remaining) (listp (cdr remaining))))
325            (return (values :dotted-list key-list)))
326           ((null (cdr remaining))
327            (return (values :odd-length key-list)))
328           ((or (eq (car remaining) :allow-other-keys)
329                (member (car remaining) valid-keys))
330            (push (car remaining) already-processed))
331           (t
332            (setq unknown-keyword (car remaining))))))
333
334 (defun lookup-keyword (keyword key-list)
335   (do ((remaining key-list (cddr remaining)))
336       ((endp remaining))
337     (when (eq keyword (car remaining))
338       (return (cadr remaining)))))
339
340 (defun keyword-supplied-p (keyword key-list)
341   (do ((remaining key-list (cddr remaining)))
342       ((endp remaining))
343     (when (eq keyword (car remaining))
344       (return t))))