aboutsummaryrefslogtreecommitdiffstats
path: root/csc/flag.csc
diff options
context:
space:
mode:
authorRose Hogenson <rhogenson@posteo.net>2022-07-28 21:16:42 -0700
committerRose Hogenson <rhogenson@posteo.net>2022-07-28 21:16:42 -0700
commit6521edcf224b7e044e9fa42a6f9c92fcf738cb34 (patch)
tree96d7c4822070e38339516deb43017093bbf2c930 /csc/flag.csc
parentImprove the strings API. (diff)
downloadchromatopelma-6521edcf224b7e044e9fa42a6f9c92fcf738cb34.tar.zst
Write the CLI frontend.
Diffstat (limited to 'csc/flag.csc')
-rw-r--r--csc/flag.csc101
1 files changed, 101 insertions, 0 deletions
diff --git a/csc/flag.csc b/csc/flag.csc
new file mode 100644
index 0000000..ea699db
--- /dev/null
+++ b/csc/flag.csc
@@ -0,0 +1,101 @@
+(define-library (csc flag)
+ (export
+ *args*
+ bool-flag
+ define-bool-flag
+ define-flag
+ parse-error-flag
+ parse-error-msg
+ parse-error?
+ parse-flags)
+ (import (scheme base)
+ (only (csc hash-map)
+ compare-strings
+ key-not-found-error?
+ lookup
+ make-map)
+ (only (csc loop)
+ loop)
+ (only (csc strings)
+ prefix?
+ contains?))
+ (begin
+
+
+ (define (bool-flag s)
+ (cond
+ ((member s '("1" "t" "T" "true" "TRUE" "True"))
+ #t)
+ ((member s '("0" "f" "F" "false" "FALSE" "False"))
+ #f)
+ (else (error "Argument could not be parsed as a boolean" s))))
+
+
+ (define *parsers* (make-map compare-strings))
+
+
+ (define-syntax define-flag
+ (syntax-rules ()
+ ((define-flag name flag type default)
+ (define name (begin
+ (set! *parsers* (insert *parsers* flag (lambda (x) (set! name (type x)))))
+ default)))))
+
+
+ (define *args* '())
+
+
+ (define-record-type <parse-error>
+ (make-parse-error msg flag)
+ parse-error?
+ (msg parse-error-msg)
+ (flag parse-error-flag))
+
+
+ (define (parse-flags)
+ (define args (cdr (command-line)))
+ ; Is this legal?
+ (define (parse-one)
+ (match args
+ ('() #f)
+ ((s . _) when (or (not (has-prefix? s "-"))
+ (string=? "-" s))
+ #f)
+ ((s . rest) when (string=? "--" s)
+ (set! args rest)
+ #f)
+ ((s . rest)
+ (define name (if (has-prefix? s "--")
+ (string-copy s 2)
+ (string-copy s 1)))
+ (when (or (string=? "" name)
+ (has-prefix? name "-")
+ (has-prefix? name "="))
+ (raise (make-parse-error "bad flag syntax" s)))
+ ; It's a flag. Does it have an argument?
+ (set! args rest)
+ (define value (match (split name "=" 2)
+ ((a b)
+ (set! name a)
+ b)
+ (_ #f)))
+ (define parser (guard (e ((key-not-found-error? e)
+ (raise (make-parse-error "flag provided but not defined" s))))
+ (lookup *parsers* name)))
+ (if (eq? parser bool-flag) ; Special case: doesn't need an arg.
+ (if value
+ (parser value)
+ (parser "true"))
+ (begin
+ ; It must have a value, which might be the next argument.
+ (when (and (not value)
+ (not (null? args)))
+ ; value is the next arg
+ (set! value (car args))
+ (set! args (cdr args)))
+ (unless value
+ (raise (make-parse-flag "flag needs an argument" s)))
+ (parser value)))
+ #t)))
+ (loop while (parse-one))
+ (set! *args* args))))