Fix make-array transforms.
[sbcl.git] / src / compiler / parse-lambda-list.lisp
1 ;;;; This software is part of the SBCL system. See the README file for
2 ;;;; more information.
3 ;;;;
4 ;;;; This software is derived from the CMU CL system, which was
5 ;;;; written at Carnegie Mellon University and released into the
6 ;;;; public domain. The software is in the public domain and is
7 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
8 ;;;; files for more information.
9
10 (in-package "SB!C")
11
12 (/show0 "parse-lambda-list.lisp 12")
13
14 ;;; Break something like a lambda list (but not necessarily actually a
15 ;;; lambda list, e.g. the representation of argument types which is
16 ;;; used within an FTYPE specification) into its component parts. We
17 ;;; return twelve values:
18 ;;;  1. a list of the required args;
19 ;;;  2. a list of the &OPTIONAL arg specs;
20 ;;;  3. true if a &REST arg was specified;
21 ;;;  4. the &REST arg;
22 ;;;  5. true if &KEY args are present;
23 ;;;  6. a list of the &KEY arg specs;
24 ;;;  7. true if &ALLOW-OTHER-KEYS was specified.;
25 ;;;  8. true if any &AUX is present (new in SBCL vs. CMU CL);
26 ;;;  9. a list of the &AUX specifiers;
27 ;;; 10. true if a &MORE arg was specified;
28 ;;; 11. the &MORE context var;
29 ;;; 12. the &MORE count var;
30 ;;; 13. true if any lambda list keyword is present (only for
31 ;;;     PARSE-LAMBDA-LIST-LIKE-THING).
32 ;;;
33 ;;; The top level lambda list syntax is checked for validity, but the
34 ;;; arg specifiers are just passed through untouched. If something is
35 ;;; wrong, we use COMPILER-ERROR, aborting compilation to the last
36 ;;; recovery point.
37 (declaim (ftype (sfunction (list &key (:silent boolean))
38                            (values list list boolean t boolean list boolean
39                                    boolean list boolean t t boolean))
40                 parse-lambda-list-like-thing))
41 (declaim (ftype (sfunction (list &key (:silent boolean))
42                            (values list list boolean t boolean list boolean
43                                    boolean list boolean t t))
44                 parse-lambda-list))
45 (defun parse-lambda-list-like-thing (list &key silent)
46   (collect ((required)
47             (optional)
48             (keys)
49             (aux))
50     (let ((restp nil)
51           (rest nil)
52           (morep nil)
53           (more-context nil)
54           (more-count nil)
55           (keyp nil)
56           (auxp nil)
57           (allowp nil)
58           (state :required))
59       (declare (type (member :allow-other-keys :aux
60                              :key
61                              :more-context :more-count
62                              :optional
63                              :post-more :post-rest
64                              :required :rest)
65                      state))
66       (dolist (arg list)
67         (if (member arg sb!xc:lambda-list-keywords)
68             (case arg
69               (&optional
70                (unless (eq state :required)
71                  (compiler-error "misplaced &OPTIONAL in lambda list: ~S"
72                                  list))
73                (setq state :optional))
74               (&rest
75                (unless (member state '(:required :optional))
76                  (compiler-error "misplaced &REST in lambda list: ~S" list))
77                (setq state :rest))
78               (&more
79                (unless (member state '(:required :optional))
80                  (compiler-error "misplaced &MORE in lambda list: ~S" list))
81                (setq morep t
82                      state :more-context))
83               (&key
84                (unless (member state
85                                '(:required :optional :post-rest :post-more))
86                  (compiler-error "misplaced &KEY in lambda list: ~S" list))
87                #-sb-xc-host
88                (when (optional)
89                  (unless silent
90                    (compiler-style-warn
91                     "&OPTIONAL and &KEY found in the same lambda list: ~S" list)))
92                (setq keyp t
93                      state :key))
94               (&allow-other-keys
95                (unless (eq state ':key)
96                  (compiler-error "misplaced &ALLOW-OTHER-KEYS in ~
97                                   lambda list: ~S"
98                                  list))
99                (setq allowp t
100                      state :allow-other-keys))
101               (&aux
102                (when (member state '(:rest :more-context :more-count))
103                  (compiler-error "misplaced &AUX in lambda list: ~S" list))
104                (when auxp
105                  (compiler-error "multiple &AUX in lambda list: ~S" list))
106                (setq auxp t
107                      state :aux))
108               (t
109                ;; It could be argued that &WHOLE and friends would be
110                ;; just ordinary variables in an ordinary lambda-list,
111                ;; but since (1) that seem exceedingly to have been the
112                ;; programmers intent and (2) the spec can be
113                ;; interpreted as giving as licence to signal an
114                ;; error[*] that is what we do.
115                ;;
116                ;; [* All lambda list keywords used in the
117                ;; implementation appear in LAMBDA-LIST-KEYWORDS. Each
118                ;; member of a lambda list is either a parameter
119                ;; specifier ot a lambda list keyword. Ergo, symbols
120                ;; appearing in LAMBDA-LIST-KEYWORDS cannot be
121                ;; parameter specifiers.]
122                (compiler-error 'simple-program-error
123                                :format-control "Bad lambda list keyword ~S in: ~S"
124                                :format-arguments (list arg list))))
125             (progn
126               (when (symbolp arg)
127                 (let ((name (symbol-name arg)))
128                   (when (and (plusp (length name))
129                              (char= (char name 0) #\&))
130                     ;; Should this be COMPILER-STYLE-WARN?
131                     (unless silent
132                       (style-warn
133                        "suspicious variable in lambda list: ~S." arg)))))
134               (case state
135                 (:required (required arg))
136                 (:optional (optional arg))
137                 (:rest
138                  (setq restp t
139                        rest arg
140                        state :post-rest))
141                 (:more-context
142                  (setq more-context arg
143                        state :more-count))
144                 (:more-count
145                  (setq more-count arg
146                        state :post-more))
147                 (:key (keys arg))
148                 (:aux (aux arg))
149                 (t
150                  (compiler-error "found garbage in lambda list when expecting ~
151                                   a keyword: ~S"
152                                  arg))))))
153       (when (eq state :rest)
154         (compiler-error "&REST without rest variable"))
155
156       (values (required) (optional) restp rest keyp (keys) allowp auxp (aux)
157               morep more-context more-count
158               (neq state :required)))))
159
160 ;;; like PARSE-LAMBDA-LIST-LIKE-THING, except our LAMBDA-LIST argument
161 ;;; really *is* a lambda list, not just a "lambda-list-like thing", so
162 ;;; can barf on things which're illegal as arguments in lambda lists
163 ;;; even if they could conceivably be legal in not-quite-a-lambda-list
164 ;;; weirdosities
165 (defun parse-lambda-list (lambda-list &key silent)
166   ;; Classify parameters without checking their validity individually.
167   (multiple-value-bind (required optional restp rest keyp keys allowp auxp aux
168                         morep more-context more-count)
169       (parse-lambda-list-like-thing lambda-list :silent silent)
170
171     ;; Check validity of parameters.
172     (flet ((need-symbol (x why)
173              (unless (symbolp x)
174                (compiler-error "~A is not a symbol: ~S" why x))))
175       (dolist (i required)
176         (need-symbol i "Required argument"))
177       (dolist (i optional)
178         (typecase i
179           (symbol)
180           (cons
181            (destructuring-bind (var &optional init-form supplied-p) i
182              (declare (ignore init-form supplied-p))
183              (need-symbol var "&OPTIONAL parameter name")))
184           (t
185            (compiler-error "&OPTIONAL parameter is not a symbol or cons: ~S"
186                            i))))
187       (when restp
188         (need-symbol rest "&REST argument"))
189       (when keyp
190         (dolist (i keys)
191           (typecase i
192             (symbol)
193             (cons
194              (destructuring-bind (var-or-kv &optional init-form supplied-p) i
195                (declare (ignore init-form supplied-p))
196                (if (consp var-or-kv)
197                    (destructuring-bind (keyword-name var) var-or-kv
198                      (declare (ignore keyword-name))
199                      (need-symbol var "&KEY parameter name"))
200                    (need-symbol var-or-kv "&KEY parameter name"))))
201             (t
202              (compiler-error "&KEY parameter is not a symbol or cons: ~S"
203                              i))))))
204
205     ;; Voila.
206     (values required optional restp rest keyp keys allowp auxp aux
207             morep more-context more-count)))
208
209 (/show0 "parse-lambda-list.lisp end of file")