|
7 | 7 |
|
8 | 8 | ;;; TODO Use types from declarations to parse. |
9 | 9 |
|
| 10 | +;;; TODO Properly handle suppliedp vars. |
| 11 | + |
10 | 12 | (defclass param () |
11 | 13 | ((var :type symbol :initarg :var :reader param-var))) |
12 | 14 |
|
13 | 15 | ;;; Abstract. |
14 | 16 | (defclass default-param (param) |
15 | 17 | ((default :initarg :default :reader param-default) |
16 | | - (suppliedp :type symbol :initarg :suppliedp :reader param-suppliedp))) |
| 18 | + (suppliedp :type symbol :initarg :suppliedp :reader param-supplied-p))) |
17 | 19 |
|
18 | 20 | (defclass required-param (param) |
19 | 21 | ()) |
|
106 | 108 | (mapcar #'param-var |
107 | 109 | params))) |
108 | 110 | (multiple-value-bind (required-args rest) |
109 | | - (halves args len) |
| 111 | + (values (take len args) |
| 112 | + (drop len args)) |
110 | 113 | (values |
111 | 114 | (mapcar (op (cons (param-var _) _)) |
112 | 115 | params |
|
148 | 151 | ((string^= "-" (car args)) |
149 | 152 | (error "Unknown short keyword argument: ~a" |
150 | 153 | (car args))))) |
151 | | - (if (string^= "-" (cadr args)) |
| 154 | + (if (param-supplied-p param) |
152 | 155 | (progn |
153 | 156 | (push (cons (param-var param) |
154 | | - nil) |
| 157 | + t) |
155 | 158 | alist) |
156 | 159 | (parse (cdr args))) |
157 | 160 | (progn |
158 | 161 | (push (cons (param-var param) |
159 | 162 | (cadr args)) |
160 | 163 | alist) |
161 | | - (parse (cddr args)))))))))))) |
| 164 | + (parse (cddr args)))))) |
| 165 | + (values alist args))))))) |
162 | 166 |
|
163 | 167 | (defun parse-rest-argument (args rules) |
164 | 168 | (if-let (param (rest-param-p rules)) |
|
200 | 204 | (list (lambda-list-rules x)) |
201 | 205 | (rules x))) |
202 | 206 |
|
| 207 | +(defmacro lookup-or-eval (dict key default) |
| 208 | + (with-unique-names (v vp) |
| 209 | + `(multiple-value-bind (,v ,vp) |
| 210 | + (@ ,dict ,key) |
| 211 | + (if ,vp |
| 212 | + ,v |
| 213 | + ,default)))) |
| 214 | + |
203 | 215 | (defun generate-binding-lookups (lambda-list dict-var) |
204 | 216 | (multiple-value-bind |
205 | 217 | (required-params |
|
215 | 227 | (mapcar (op `(,_1 (@ ,dict-var ',_1))) |
216 | 228 | (append |
217 | 229 | required-params |
218 | | - (mapcar #'car optional-params) |
219 | | - (ensure-list rest-param-p) |
220 | | - (mapcar #'cadar keyword-params))) |
| 230 | + (ensure-list rest-param-p))) |
| 231 | + (mapply (op `(,_1 (lookup-or-eval ,dict-var ',_1 ,_2))) |
| 232 | + (append |
| 233 | + (mapcar (op (take 2 _)) optional-params) |
| 234 | + (mapcar (op (list (cadar _1) (second _1))) |
| 235 | + keyword-params))) |
| 236 | + (mapcar (op `(,_1 (nth-value 1 (@ ,dict-var ',_1)))) |
| 237 | + (mapcar #'third |
| 238 | + (filter #'third keyword-params))) |
221 | 239 | aux-params))) |
222 | 240 |
|
223 | 241 | (defmacro with-argument-destructuring ((&rest bindings) |
224 | 242 | (&key |
225 | 243 | (argv (uiop:command-line-arguments))) |
226 | 244 | &body body) |
| 245 | + "Do simple argument destructuring. |
| 246 | +Multiple bindings can refer to the same var. The leftmost binding |
| 247 | +wins (in terms of defaults). This can be used to add |
| 248 | +short (single-char) and long keywords for the same variable. |
| 249 | +
|
| 250 | + # Accepts --long-name or -l. |
| 251 | + (&key long-name ((:l long-name) long-name)) |
| 252 | +
|
| 253 | +Keywords arguments intended to use as flags should define a supplied-p |
| 254 | +variable that is the same as the variable. In this case they do not |
| 255 | +consume an argument. |
| 256 | +
|
| 257 | + (&key (do-thing nil do-thing) (no-do-thing nil no-do-thing)) |
| 258 | +
|
| 259 | +" |
227 | 260 | (with-unique-names (dict) |
228 | 261 | `(let ((,dict (parse-args ,argv ',bindings))) |
229 | | - (let ,(generate-binding-lookups bindings dict) |
| 262 | + (let* ,(generate-binding-lookups bindings dict) |
230 | 263 | ,@body)))) |
231 | 264 |
|
232 | | -;; (assert (equal '("x" "foo") |
233 | | -;; (with-argument-destructuring (x &key y) |
234 | | -;; (:argv '("x" "-y" "foo") ) |
235 | | -;; (list x y)))) |
| 265 | +(assert (equal '("x" "foo") |
| 266 | + (with-argument-destructuring (x &key y) |
| 267 | + (:argv '("x" "-y" "foo") ) |
| 268 | + (list x y)))) |
| 269 | +(assert (null |
| 270 | + (with-argument-destructuring (&key (flag nil flag)) |
| 271 | + (:argv '()) |
| 272 | + flag))) |
| 273 | +(assert (with-argument-destructuring (&key (flag nil flag)) |
| 274 | + (:argv '("--flag")) |
| 275 | + flag)) |
0 commit comments