-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy patharguments.scm
153 lines (140 loc) · 4.64 KB
/
arguments.scm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
;;; Most of this code was written by Per Eckerdal for the Blackhole module system
;;; Copyright (c) 2013-2014, Alvaro Castro-Castilla. All rights reserved.
;;; Argument processing for command-line programs
(cond-expand
(optimize
(declare (standard-bindings) (extended-bindings) (not safe) (block)))
(debug
(declare (safe) (debug) (debug-location) (debug-source) (debug-environments)))
(else (void)))
(define (die/error . args)
(let ((err (current-error-port)))
(display "Error: " err)
(for-each
(lambda (arg)
(display arg err)
(display " " err))
args)
(display "\n") err)
(exit 1))
;;!! Parse arguments
;; .parameter args List of arguments to process
;; .parameter options List of options following this structure:
;; '((#\g 0 "global")
;; (#\f 1 "file"))
;; 1 means that takes an argument, 0 it doesn't
(define (parse-arguments args kont options)
(define (string-contains haystack chr)
(call/cc
(lambda (ret)
(let ((strlen (string-length haystack)))
(let loop ((i 0))
(if (>= i strlen)
(ret #f)
(let ((c (string-ref haystack i)))
(if (eq? c chr)
(ret i)
(loop (+ i 1))))))))))
(define (opt? str)
(and (> (string-length str) 1)
(char=? #\- (string-ref str 0))))
(define (long-opt? str)
(and (> (string-length str) 2)
(char=? #\- (string-ref str 0))
(char=? #\- (string-ref str 1))))
(define (short-opt? str)
(and (opt? str)
(not (long-opt? str))))
(let loop ((args args)
(args-sans-opts '())
(opts '()))
(define (consume-next-argument!)
(if (or (null? (cdr args))
(equal? "--" (cadr args)))
(die/error "Expected an argument to" (car args)))
(let ((val (cadr args)))
(set-cdr! args (cddr args))
val))
(cond
((null? args)
(kont (reverse args-sans-opts)
(reverse opts)))
((equal? "--" (car args))
(kont (append (reverse args-sans-opts)
(cdr args))
(reverse opts)))
((long-opt? (car args))
(let* ((=-pos (string-contains (car args) #\=))
(opt-name
(substring (car args)
2
(or =-pos
(string-length (car args)))))
(opt-val
(and =-pos
(substring (car args)
(+ 1 =-pos)
(string-length (car args))))))
(loop (cdr args)
args-sans-opts
(cons (list opt-name
(string-append "--" opt-name)
opt-val)
opts))))
((short-opt? (car args))
(let* ((str (car args))
(len (string-length str)))
(let inner-loop ((idx 1) (opts opts))
(cond
((= len idx)
(loop (cdr args)
args-sans-opts
opts))
(else
(let* ((opt-chr (string-ref str idx))
(opt (assq opt-chr options)))
(if (not opt)
(die/error "Unrecognized option" (car args)))
(let ((val
(cond
((zero? (cadr opt))
#f)
((not (= 2 len))
(die/error "Option that takes an argument must not be grouped"
(car args)))
(else
(consume-next-argument!)))))
(inner-loop (+ 1 idx)
(cons (list (caddr opt)
(string #\- opt-chr)
val)
opts)))))))))
(else
(loop (cdr args)
(cons (car args) args-sans-opts)
opts)))))
(define (handle-opts! opts handlers)
(for-each
(lambda (opt)
(let ((handler (assoc (car opt) handlers)))
(if handler
((cdr handler) (caddr opt))
(die/error "Option is not valid in this context:"
(cadr opt)))))
opts))
(define (ensure-no-args! args)
(if (not (null? args))
(apply
die/error
(cons "Did not expect arguments:" args))))
(define (ensure-args! args)
(if (null? args)
(apply
die/error
(list "At least one argument is required."))))
(define (ensure-one-arg! args)
(if (not (and (list? args)
(= 1 (length args))))
(apply
die/error
(cons "Expected exactly one argument:" args))))