fixed minor issues with advanced lambda lists, additional quoted-nil hack
[clinton/parenscript.git] / src / parse-lambda-list.lisp
CommitLineData
9da682ca
RD
1(in-package parenscript)
2;;;; This software was taken from the SBCL system. there are very few
3;;;; changes, and one SBCL-specific thing left (sb-c::collect
4
5;;; if you have found this on google, THIS IS NOT AN SBCL SOURCE FILE
6
7;;; Break something like a lambda list (but not necessarily actually a
8;;; lambda list, e.g. the representation of argument types which is
9;;; used within an FTYPE specification) into its component parts. We
10;;; return twelve values:
11;;; 1. a list of the required args;
12;;; 2. a list of the &OPTIONAL arg specs;
13;;; 3. true if a &REST arg was specified;
14;;; 4. the &REST arg;
15;;; 5. true if &KEY args are present;
16;;; 6. a list of the &KEY arg specs;
17;;; 7. true if &ALLOW-OTHER-KEYS was specified.;
18;;; 8. true if any &AUX is present (new in SBCL vs. CMU CL);
19;;; 9. a list of the &AUX specifiers;
20;;; 10. true if a &MORE arg was specified;
21;;; 11. the &MORE context var;
22;;; 12. the &MORE count var;
23;;; 13. true if any lambda list keyword is present (only for
24;;; PARSE-LAMBDA-LIST-LIKE-THING).
25;;;
26;;; The top level lambda list syntax is checked for validity, but the
27;;; arg specifiers are just passed through untouched. If something is
28;;; wrong, we use COMPILER-ERROR, aborting compilation to the last
29;;; recovery point.
46f794a4 30(eval-when (:compile-toplevel :load-toplevel :execute)
9da682ca
RD
31 (defun collect-list-expander (n-value n-tail forms)
32 (let ((n-res (gensym)))
33 `(progn
34 ,@(mapcar (lambda (form)
35 `(let ((,n-res (cons ,form nil)))
36 (cond (,n-tail
37 (setf (cdr ,n-tail) ,n-res)
38 (setq ,n-tail ,n-res))
39 (t
40 (setq ,n-tail ,n-res ,n-value ,n-res)))))
41 forms)
42 ,n-value))))
43
44(defmacro collect (collections &body body)
45 (let ((macros ())
46 (binds ()))
47 (dolist (spec collections)
48 ; (unless (proper-list-of-length-p spec 1 3)
49 ; (error "malformed collection specifier: ~S" spec))
50 (let* ((name (first spec))
51 (default (second spec))
52 (kind (or (third spec) 'collect))
53 (n-value (gensym (concatenate 'string
54 (symbol-name name)
55 "-N-VALUE-"))))
56 (push `(,n-value ,default) binds)
57 (if (eq kind 'collect)
58 (let ((n-tail (gensym (concatenate 'string
59 (symbol-name name)
60 "-N-TAIL-"))))
61 (if default
62 (push `(,n-tail (last ,n-value)) binds)
63 (push n-tail binds))
64 (push `(,name (&rest args)
65 (collect-list-expander ',n-value ',n-tail args))
66 macros))
67 (push `(,name (&rest args)
68 (collect-normal-expander ',n-value ',kind args))
69 macros))))
70 `(macrolet ,macros (let* ,(nreverse binds) ,@body))))
71
72(defparameter *lambda-list-keywords*
73 '(&allow-other-keys &aux &body &environment &key &optional &rest &whole))
74
75(defun style-warn (&rest args) (apply #'format t args))
76
9da682ca
RD
77(defun parse-lambda-list-like-thing (list)
78 (collect ((required)
79 (optional)
80 (keys)
81 (aux))
82 (let ((restp nil)
83 (rest nil)
84 (morep nil)
85 (more-context nil)
86 (more-count nil)
87 (keyp nil)
88 (auxp nil)
89 (allowp nil)
90 (state :required))
91 (declare (type (member :allow-other-keys :aux
92 :key
93 :more-context :more-count
94 :optional
95 :post-more :post-rest
96 :required :rest)
97 state))
98 (dolist (arg list)
99 (if (member arg *lambda-list-keywords*)
100 (case arg
101 (&optional
102 (unless (eq state :required)
103 (format t "misplaced &OPTIONAL in lambda list: ~S"
46f794a4 104 list))
9da682ca
RD
105 (setq state :optional))
106 (&rest
107 (unless (member state '(:required :optional))
108 (format t "misplaced &REST in lambda list: ~S" list))
109 (setq state :rest))
110 (&more
111 (unless (member state '(:required :optional))
112 (format t "misplaced &MORE in lambda list: ~S" list))
113 (setq morep t
114 state :more-context))
115 (&key
116 (unless (member state
117 '(:required :optional :post-rest :post-more))
118 (format t "misplaced &KEY in lambda list: ~S" list))
119 #-sb-xc-host
120 (when (optional)
121 (format t
122 "&OPTIONAL and &KEY found in the same lambda list: ~S" list))
123 (setq keyp t
124 state :key))
125 (&allow-other-keys
126 (unless (eq state ':key)
127 (format t "misplaced &ALLOW-OTHER-KEYS in ~
128 lambda list: ~S"
129 list))
130 (setq allowp t
131 state :allow-other-keys))
132 (&aux
133 (when (member state '(:rest :more-context :more-count))
134 (format t "misplaced &AUX in lambda list: ~S" list))
135 (when auxp
136 (format t "multiple &AUX in lambda list: ~S" list))
137 (setq auxp t
138 state :aux))
139 (t (format t "unknown LAMBDA-LIST-KEYWORD in lambda list: ~S." arg)))
140 (progn
141 (when (symbolp arg)
142 (let ((name (symbol-name arg)))
143 (when (and (plusp (length name))
144 (char= (char name 0) #\&))
145 (style-warn
146 "suspicious variable in lambda list: ~S." arg))))
147 (case state
148 (:required (required arg))
149 (:optional (optional arg))
150 (:rest
151 (setq restp t
152 rest arg
153 state :post-rest))
154 (:more-context
155 (setq more-context arg
156 state :more-count))
157 (:more-count
158 (setq more-count arg
159 state :post-more))
160 (:key (keys arg))
161 (:aux (aux arg))
162 (t
163 (format t "found garbage in lambda list when expecting ~
164 a keyword: ~S"
165 arg))))))
166 (when (eq state :rest)
167 (format t "&REST without rest variable"))
168
169 (values (required) (optional) restp rest keyp (keys) allowp auxp (aux)
170 morep more-context more-count
171 (not (eq state :required))))))
172
173;;; like PARSE-LAMBDA-LIST-LIKE-THING, except our LAMBDA-LIST argument
174;;; really *is* a lambda list, not just a "lambda-list-like thing", so
175;;; can barf on things which're illegal as arguments in lambda lists
176;;; even if they could conceivably be legal in not-quite-a-lambda-list
177;;; weirdosities
178(defun parse-lambda-list (lambda-list)
179
180 ;; Classify parameters without checking their validity individually.
181 (multiple-value-bind (required optional restp rest keyp keys allowp auxp aux
182 morep more-context more-count)
183 (parse-lambda-list-like-thing lambda-list)
184
185 ;; Check validity of parameters.
186 (flet ((need-symbol (x why)
187 (unless (or (symbolp x) t)
188 (format t "~A is not a symbol: ~S" why x))))
189 (dolist (i required)
190 (need-symbol i "Required argument"))
191 (dolist (i optional)
192 (typecase i
193 (symbol)
194 (cons
195 (destructuring-bind (var &optional init-form supplied-p) i
196 (declare (ignore init-form supplied-p))
197 (need-symbol var "&OPTIONAL parameter name")))
198 (t
199 (format t "&OPTIONAL parameter is not a symbol or cons: ~S"
200 i))))
201 (when restp
202 (need-symbol rest "&REST argument"))
203 (when keyp
204 (dolist (i keys)
205 (typecase i
206 (symbol)
207 (cons
208 (destructuring-bind (var-or-kv &optional init-form supplied-p) i
209 (declare (ignore init-form supplied-p))
210 (if (consp var-or-kv)
211 (destructuring-bind (keyword-name var) var-or-kv
212 (declare (ignore keyword-name))
213 (need-symbol var "&KEY parameter name"))
214 (need-symbol var-or-kv "&KEY parameter name"))))
215 (t
216 (format t "&KEY parameter is not a symbol or cons: ~S"
217 i))))))
218
219 ;; Voila.
220 (values required optional restp rest keyp keys allowp auxp aux
221 morep more-context more-count)))