Skip to content

Commit 5dea652

Browse files
committed
WIP Add argument destructuring
1 parent f9211ed commit 5dea652

File tree

2 files changed

+72
-33
lines changed

2 files changed

+72
-33
lines changed

args.lisp

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77

88
;;; TODO Use types from declarations to parse.
99

10+
;;; TODO Properly handle suppliedp vars.
11+
1012
(defclass param ()
1113
((var :type symbol :initarg :var :reader param-var)))
1214

1315
;;; Abstract.
1416
(defclass default-param (param)
1517
((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)))
1719

1820
(defclass required-param (param)
1921
())
@@ -106,7 +108,8 @@
106108
(mapcar #'param-var
107109
params)))
108110
(multiple-value-bind (required-args rest)
109-
(halves args len)
111+
(values (take len args)
112+
(drop len args))
110113
(values
111114
(mapcar (op (cons (param-var _) _))
112115
params
@@ -148,17 +151,18 @@
148151
((string^= "-" (car args))
149152
(error "Unknown short keyword argument: ~a"
150153
(car args)))))
151-
(if (string^= "-" (cadr args))
154+
(if (param-supplied-p param)
152155
(progn
153156
(push (cons (param-var param)
154-
nil)
157+
t)
155158
alist)
156159
(parse (cdr args)))
157160
(progn
158161
(push (cons (param-var param)
159162
(cadr args))
160163
alist)
161-
(parse (cddr args))))))))))))
164+
(parse (cddr args))))))
165+
(values alist args)))))))
162166

163167
(defun parse-rest-argument (args rules)
164168
(if-let (param (rest-param-p rules))
@@ -200,6 +204,14 @@
200204
(list (lambda-list-rules x))
201205
(rules x)))
202206

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+
203215
(defun generate-binding-lookups (lambda-list dict-var)
204216
(multiple-value-bind
205217
(required-params
@@ -215,21 +227,49 @@
215227
(mapcar (op `(,_1 (@ ,dict-var ',_1)))
216228
(append
217229
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)))
221239
aux-params)))
222240

223241
(defmacro with-argument-destructuring ((&rest bindings)
224242
(&key
225243
(argv (uiop:command-line-arguments)))
226244
&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+
"
227260
(with-unique-names (dict)
228261
`(let ((,dict (parse-args ,argv ',bindings)))
229-
(let ,(generate-binding-lookups bindings dict)
262+
(let* ,(generate-binding-lookups bindings dict)
230263
,@body))))
231264

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))

scripts/echo.lisp

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
(defpackage :kiln/scripts/echo
22
(:use :cl :alexandria :serapeum :kiln/utils)
3-
(:local-nicknames (:cli :clingon))
3+
(:local-nicknames
4+
(:args :kiln/args)
5+
(:cli :clingon))
46
(:export :main)
57
(:documentation "Echo arguments to standard output"))
68
(in-package :kiln/scripts/echo)
@@ -27,22 +29,19 @@ Accepts -- to signal the end of the options."
2729
:options options))
2830

2931
(defun main (args)
30-
(let* ((pure-args (rest (member "--" args :test #'equal)))
31-
(opt-args (ldiff args pure-args))
32-
(opts (cli:parse-command-line command opt-args))
33-
(strings
34-
(append
35-
(cli:command-arguments opts)
36-
pure-args))
37-
(no-newline (cli:getopt opts :n))
38-
(interpolate (cli:getopt opts :e))
39-
(stdout *standard-output*))
40-
(with-boolean (interpolate)
41-
(loop for (string . more?) on strings
42-
do (:if interpolate
43-
(write-string (interpolate-escapes string) stdout)
44-
(write-string string stdout))
45-
(when more?
46-
(write-char #\Space stdout))))
47-
(unless no-newline
48-
(terpri))))
32+
(args:with-argument-destructuring
33+
(&rest strings
34+
&key (n nil no-newline) (e nil interpolate))
35+
(:argv args)
36+
(declare (ignore n e))
37+
(let* ((stdout *standard-output*))
38+
(list interpolate no-newline)
39+
(with-boolean (interpolate)
40+
(loop for (string . more?) on strings
41+
do (:if interpolate
42+
(write-string (interpolate-escapes string) stdout)
43+
(write-string string stdout))
44+
(when more?
45+
(write-char #\Space stdout))))
46+
(unless no-newline
47+
(terpri)))))

0 commit comments

Comments
 (0)