de-cpsed command-line pattern-matching code.
This commit is contained in:
parent
75aaa0b235
commit
120a6dab52
|
@ -3,7 +3,7 @@
|
||||||
;;; WORK IN PROGRESS, NOT FOR CONSUMPTION
|
;;; WORK IN PROGRESS, NOT FOR CONSUMPTION
|
||||||
;;; TODO: long options
|
;;; TODO: long options
|
||||||
;;; multiple options in one go, e.g., -XYZ
|
;;; multiple options in one go, e.g., -XYZ
|
||||||
;;; concat value with option, e.g., -Xxvalue
|
;;; concat value with option, e.g., -Xxvalue
|
||||||
;;; usage error message [ok?]
|
;;; usage error message [ok?]
|
||||||
;;; -h --help should not be user-defined
|
;;; -h --help should not be user-defined
|
||||||
;;; check duplicate options
|
;;; check duplicate options
|
||||||
|
@ -11,7 +11,17 @@
|
||||||
(import (ikarus))
|
(import (ikarus))
|
||||||
|
|
||||||
(define (dispatch-opts arguments data* proc*)
|
(define (dispatch-opts arguments data* proc*)
|
||||||
(define (print-usage)
|
(define-condition-type &help &condition
|
||||||
|
make-help-condition help-condition?
|
||||||
|
(extended? help-extended?))
|
||||||
|
(define-condition-type &unmatched &condition
|
||||||
|
make-unmatched-condition unmatched-condition?)
|
||||||
|
(define (help x)
|
||||||
|
(raise (make-help-condition x)))
|
||||||
|
(define (unmatched)
|
||||||
|
(raise (make-unmatched-condition)))
|
||||||
|
|
||||||
|
(define (print-usage detailed?)
|
||||||
(define-record-type f (fields id char type def))
|
(define-record-type f (fields id char type def))
|
||||||
(define (mkf x id)
|
(define (mkf x id)
|
||||||
(make-f id (car x) (cadr x) (cddr x)))
|
(make-f id (car x) (cadr x) (cddr x)))
|
||||||
|
@ -27,44 +37,45 @@
|
||||||
(format " [-~a <~a>]~a" (f-char x) (f-id x) c)))
|
(format " [-~a <~a>]~a" (f-char x) (f-id x) c)))
|
||||||
(define (fmt-<> x)
|
(define (fmt-<> x)
|
||||||
(format " <~a>" x))
|
(format " <~a>" x))
|
||||||
|
(define (synopsis f* args args-rest)
|
||||||
|
(let ([opt* (get 'optional f*)]
|
||||||
|
[flag* (get 'flag f*)]
|
||||||
|
[req0* (get 'required0 f*)]
|
||||||
|
[req1* (get 'required1 f*)]
|
||||||
|
[z0* (get 'zero-plus f*)]
|
||||||
|
[z1* (get 'one-plus f*)])
|
||||||
|
(let-values ([(p e) (open-string-output-port)])
|
||||||
|
(display (car arguments) p)
|
||||||
|
(display (apply string-append (map fmt-req-no-value req0*)) p)
|
||||||
|
(unless (null? flag*)
|
||||||
|
(fprintf p " [-~a]"
|
||||||
|
(list->string (map f-char flag*))))
|
||||||
|
(display (apply string-append (map (fmt-z "") opt*)) p)
|
||||||
|
(display (apply string-append (map (fmt-z "*") z0*)) p)
|
||||||
|
(display (apply string-append (map (fmt-z "+") z1*)) p)
|
||||||
|
(display (apply string-append (map fmt-req req1*)) p)
|
||||||
|
(display (apply string-append (map fmt-<> args)) p)
|
||||||
|
(when args-rest
|
||||||
|
(display (string-append (fmt-<> args-rest) " ...") p))
|
||||||
|
(e))))
|
||||||
(define (print-usage-line help fields field-ids args args-rest dash-rest)
|
(define (print-usage-line help fields field-ids args args-rest dash-rest)
|
||||||
(let ([f* (map mkf fields field-ids)])
|
(let ([f* (map mkf fields field-ids)])
|
||||||
(let ([opt* (get 'optional f*)]
|
(display " ")
|
||||||
[flag* (get 'flag f*)]
|
(display (synopsis f* args args-rest))
|
||||||
[req0* (get 'required0 f*)]
|
(newline)
|
||||||
[req1* (get 'required1 f*)]
|
(unless (string=? help "")
|
||||||
[z0* (get 'zero-plus f*)]
|
(display " ")
|
||||||
[z1* (get 'one-plus f*)])
|
(display help)
|
||||||
(display
|
(newline))
|
||||||
(string-append
|
(when detailed?
|
||||||
"\n "
|
(let ([def* (filter f-def (get 'optional f*))])
|
||||||
(car arguments)
|
|
||||||
(apply string-append (map fmt-req-no-value req0*))
|
|
||||||
(if (null? flag*)
|
|
||||||
""
|
|
||||||
(format " [-~a]"
|
|
||||||
(list->string (map f-char flag*))))
|
|
||||||
(apply string-append (map (fmt-z "") opt*))
|
|
||||||
(apply string-append (map (fmt-z "*") z0*))
|
|
||||||
(apply string-append (map (fmt-z "+") z1*))
|
|
||||||
(apply string-append (map fmt-req req1*))
|
|
||||||
(apply string-append (map fmt-<> args))
|
|
||||||
(if args-rest
|
|
||||||
(string-append (fmt-<> args-rest) " ...")
|
|
||||||
"")
|
|
||||||
"\n"))
|
|
||||||
(unless (string=? help "")
|
|
||||||
(printf "\n ~a\n" help))
|
|
||||||
(let ([def* (filter f-def opt*)])
|
|
||||||
(unless (null? def*)
|
(unless (null? def*)
|
||||||
(printf "\n")
|
|
||||||
(for-each
|
(for-each
|
||||||
(lambda (x)
|
(lambda (x)
|
||||||
(printf " -~a defaults to ~a\n" (f-char x)
|
(printf " -~a defaults to ~a\n" (f-char x)
|
||||||
(f-def x)))
|
(f-def x)))
|
||||||
def*)
|
def*))))))
|
||||||
)))))
|
(printf "\nUsage:\n")
|
||||||
(printf "\nUsage:")
|
|
||||||
(for-each (lambda (x) (apply print-usage-line x)) data*)
|
(for-each (lambda (x) (apply print-usage-line x)) data*)
|
||||||
(print-usage-line "Display this help message"
|
(print-usage-line "Display this help message"
|
||||||
'([#\h required0 . #f])
|
'([#\h required0 . #f])
|
||||||
|
@ -80,127 +91,114 @@
|
||||||
(char=? (string-ref x 0) #\-)
|
(char=? (string-ref x 0) #\-)
|
||||||
(not (char=? (string-ref x 1) #\-))))
|
(not (char=? (string-ref x 1) #\-))))
|
||||||
|
|
||||||
(define (fill-char-opt c ls fields k)
|
(define (fill-char-opt c ls fields)
|
||||||
;;; field = [c required ]
|
;;; field = [c required ]
|
||||||
;;; | [c flag . default]
|
;;; | [c flag . default]
|
||||||
;;; | [c zero-plus . list]
|
;;; | [c zero-plus . list]
|
||||||
;;; | [c one-plus . list]
|
;;; | [c one-plus . list]
|
||||||
;;; | [c optional . str]
|
;;; | [c optional . str]
|
||||||
(let f ([fields fields] [k k])
|
(let f ([fields fields])
|
||||||
(and (pair? fields)
|
(when (null? fields) (unmatched))
|
||||||
(let ([field (car fields)])
|
(let ([field (car fields)])
|
||||||
(if (char=? c (car field))
|
(if (char=? c (car field))
|
||||||
(let ([t (cadr field)])
|
(let ([t (cadr field)])
|
||||||
(case t
|
(case t
|
||||||
[(required1 optional)
|
[(required1 optional)
|
||||||
(and (not (null? ls))
|
(when (null? ls) (unmatched))
|
||||||
(let ([val (car ls)] [ls (cdr ls)])
|
(let ([val (car ls)] [ls (cdr ls)])
|
||||||
(k (cons
|
(values (cons (cons* c 'ok val) (cdr fields)) ls))]
|
||||||
(cons* c 'ok val)
|
[(flag)
|
||||||
(cdr fields))
|
(values (cons (cons* c 'ok #t) (cdr fields)) ls)]
|
||||||
ls)))]
|
[(zero-plus one-plus)
|
||||||
[(flag)
|
(when (null? ls) (unmatched))
|
||||||
(k (cons (cons* c 'ok #t) (cdr fields)) ls)]
|
(let ([val (car ls)])
|
||||||
[(zero-plus one-plus)
|
(values
|
||||||
(and (not (null? ls))
|
(cons (cons* c 'zero-plus (cons val (cddr field)))
|
||||||
(let ([val (car ls)])
|
(cdr fields))
|
||||||
(k (cons (cons* c 'zero-plus
|
(cdr ls)))]
|
||||||
(cons val (cddr field)))
|
[else (unmatched)]))
|
||||||
(cdr fields))
|
(let-values ([(fields ls) (f (cdr fields))])
|
||||||
(cdr ls))))]
|
(values (cons field fields) ls))))))
|
||||||
[else #f]))
|
|
||||||
(f (cdr fields)
|
|
||||||
(lambda (fields ls)
|
|
||||||
(k (cons field fields) ls))))))))
|
|
||||||
|
|
||||||
(define (fill-option a ls fields k)
|
(define (fill-option a ls fields)
|
||||||
(if (= (string-length a) 2)
|
(if (= (string-length a) 2)
|
||||||
(let ([char (string-ref a 1)])
|
(let ([char (string-ref a 1)])
|
||||||
(fill-char-opt char ls fields
|
(when (char=? char #\h) (help #f))
|
||||||
(lambda (fields ls)
|
(fill-char-opt char ls fields))
|
||||||
(match-fields fields ls k))))
|
|
||||||
(error 'fill-option "not yet")))
|
(error 'fill-option "not yet")))
|
||||||
|
|
||||||
(define (match-fields fields ls k)
|
(define (match-fields fields ls)
|
||||||
(if (null? ls)
|
(if (null? ls)
|
||||||
(k fields ls)
|
(values fields ls)
|
||||||
(let ([a (car ls)])
|
(let ([a (car ls)])
|
||||||
(if (option? a)
|
(if (option? a)
|
||||||
(fill-option a (cdr ls) fields k)
|
(let-values ([(fields ls) (fill-option a (cdr ls) fields)])
|
||||||
(k fields ls)))))
|
(match-fields fields ls))
|
||||||
|
(values fields ls)))))
|
||||||
|
|
||||||
(define (match-args args ls k)
|
(define (match-args args ls)
|
||||||
(if (null? args)
|
(cond
|
||||||
(k '() ls)
|
[(null? args) (values '() ls)]
|
||||||
(and (not (null? ls))
|
[(null? ls) (unmatched)]
|
||||||
(let ([a (car ls)])
|
[else
|
||||||
(and (not (option? a))
|
(let ([a (car ls)])
|
||||||
(match-args (cdr args) (cdr ls)
|
(when (option? a) (unmatched))
|
||||||
(lambda (a* ls)
|
(let-values ([(a* ls) (match-args (cdr args) (cdr ls))])
|
||||||
(k (cons a a*) ls))))))))
|
(values (cons a a*) ls)))]))
|
||||||
|
|
||||||
(define (match-args-rest a/f ls k)
|
(define (match-args-rest a/f ls)
|
||||||
(if a/f
|
(if a/f
|
||||||
(let f ([ls ls] [k (lambda (a ls) (k (list a) ls))])
|
(let-values ([(x ls)
|
||||||
(if (null? ls)
|
(let f ([ls ls])
|
||||||
(k '() ls)
|
(if (null? ls)
|
||||||
(let ([a (car ls)])
|
(values '() ls)
|
||||||
(if (string=? a "--")
|
(let ([a (car ls)])
|
||||||
(k '() ls)
|
(if (string=? a "--")
|
||||||
(and (not (option? a))
|
(values '() ls)
|
||||||
(f (cdr ls)
|
(if (option? a)
|
||||||
(lambda (a* ls)
|
(unmatched)
|
||||||
(k (cons a a*) ls))))))))
|
(let-values ([(a* ls) (f (cdr ls))])
|
||||||
(and (or (null? ls) (string=? (car ls) "--"))
|
(values (cons a a*) ls)))))))])
|
||||||
(k '() ls))))
|
(values (list x) ls))
|
||||||
|
(if (or (null? ls) (string=? (car ls) "--"))
|
||||||
|
(values '() ls)
|
||||||
|
(unmatched))))
|
||||||
|
|
||||||
(define (match-dash-rest a/f ls k)
|
(define (match-dash-rest a/f ls)
|
||||||
(if a/f
|
(if a/f
|
||||||
(if (null? ls)
|
(if (null? ls)
|
||||||
(k '(()))
|
'(())
|
||||||
(and (string=? (car ls) "--")
|
(if (string=? (car ls) "--")
|
||||||
(k (list (cdr ls)))))
|
(list (cdr ls))
|
||||||
(and (null? ls) (k '()))))
|
(unmatched)))
|
||||||
|
(if (null? ls) '() (unmatched))))
|
||||||
|
|
||||||
(define (fix-fields ls k)
|
(define (fix-field x)
|
||||||
(cond
|
(let ([type (cadr x)] [value (cddr x)])
|
||||||
[(null? ls) (k '())]
|
(case type
|
||||||
[else
|
[(ok flag optional) value]
|
||||||
(let ([a (car ls)]
|
[(zero-plus) (reverse value)]
|
||||||
[k (lambda (a)
|
[else (unmatched)])))
|
||||||
(fix-fields (cdr ls)
|
|
||||||
(lambda (ls)
|
|
||||||
(k (cons a ls)))))])
|
|
||||||
(let ([type (cadr a)] [value (cddr a)])
|
|
||||||
(case type
|
|
||||||
[(ok flag optional) (k value)]
|
|
||||||
[(zero-plus) (k (reverse value))]
|
|
||||||
[else #f])))]))
|
|
||||||
|
|
||||||
(define (match _help fields _field-ids args args-rest dash-rest)
|
(define (match _help fields _field-ids args args-rest dash-rest)
|
||||||
(let ([prog (car arguments)])
|
(cons (car arguments)
|
||||||
(match-fields fields (cdr arguments)
|
(let*-values ([(fields ls) (match-fields fields (cdr arguments))]
|
||||||
(lambda (fields ls)
|
[(fields) (map fix-field fields)]
|
||||||
(fix-fields fields
|
[(args ls) (match-args args ls)]
|
||||||
(lambda (fields)
|
[(args-rest ls) (match-args-rest args-rest ls)]
|
||||||
(match-args args ls
|
[(dash-rest) (match-dash-rest dash-rest ls)])
|
||||||
(lambda (args ls)
|
(append fields args args-rest dash-rest))))
|
||||||
(match-args-rest args-rest ls
|
|
||||||
(lambda (args-rest ls)
|
(guard (con
|
||||||
(match-dash-rest dash-rest ls
|
[(help-condition? con)
|
||||||
(lambda (dash-rest)
|
(print-usage (help-extended? con))])
|
||||||
(cons prog
|
(let f ([data* data*] [proc* proc*])
|
||||||
(append fields
|
(if (null? data*)
|
||||||
args args-rest
|
(help #f)
|
||||||
dash-rest))))))))))))))
|
(guard (con
|
||||||
|
[(unmatched-condition? con)
|
||||||
(let f ([data* data*] [proc* proc*])
|
(f (cdr data*) (cdr proc*))])
|
||||||
(if (null? data*)
|
(apply (car proc*) (apply match (car data*))))))))
|
||||||
(print-usage)
|
|
||||||
(let ([opts (apply match (car data*))])
|
|
||||||
(if opts
|
|
||||||
(apply (car proc*) opts)
|
|
||||||
(f (cdr data*) (cdr proc*)))))))
|
|
||||||
|
|
||||||
|
|
||||||
(define-syntax parse-command-line
|
(define-syntax parse-command-line
|
||||||
|
@ -413,6 +411,19 @@
|
||||||
(test command8 ("./prog" "-h") #f)
|
(test command8 ("./prog" "-h") #f)
|
||||||
|
|
||||||
|
|
||||||
|
(define (ls-command ls)
|
||||||
|
(parse-command-line ls
|
||||||
|
[(ls "-A?" A "-B?" B "-C?" C "-F?" F "-G?" G "-H?" H "-L?" L
|
||||||
|
"-P?" P "-R?" R "-S?" S "-T?" T "-W?" W "-Z?" Z "-a?" a
|
||||||
|
"-b?" b "-c?" c "-d?" d "-e?" e "-f?" f "-g?" g "-i?" i
|
||||||
|
"-k?" k "-l?" l "-m?" m "-n?" n "-o?" o "-p?" p "-q?" q
|
||||||
|
"-r?" r "-s?" s "-t?" t "-u?" u "-w?" w "-x?" x "-1?" o1
|
||||||
|
files ...)
|
||||||
|
#t]))
|
||||||
|
|
||||||
|
(test ls-command ("ls" "-h") #f)
|
||||||
|
|
||||||
|
|
||||||
#!eof
|
#!eof
|
||||||
(define (real-test ls)
|
(define (real-test ls)
|
||||||
(command-line-interface ls
|
(command-line-interface ls
|
||||||
|
@ -438,14 +449,14 @@
|
||||||
(list 2 program libdirs script-file)]
|
(list 2 program libdirs script-file)]
|
||||||
[(program "-O0" "-L" libdirs ... "-l" library-files ...
|
[(program "-O0" "-L" libdirs ... "-l" library-files ...
|
||||||
init-files ... "--script" script-file args ...)
|
init-files ... "--script" script-file args ...)
|
||||||
"Run <script-file> in R5RS-script mode
|
"Run <script-file> in R5RS-script mode"
|
||||||
Each of <library-files> must contain a library and are
|
"Each of <library-files> must contain a library and are"
|
||||||
installed before <init-files> are loaded and
|
"installed before <init-files> are loaded and"
|
||||||
<script-file> is run."
|
"<script-file> is run."
|
||||||
(list 3 program libdirs library-files init-files script-file args)]
|
(list 3 program libdirs library-files init-files script-file args)]
|
||||||
[(program "-O0" "-L" libdirs ... "-l" library-files init-files ...
|
[(program "-O0" "-L" libdirs ... "-l" library-files init-files ...
|
||||||
"--" args ...)
|
"--" args ...)
|
||||||
"Run Ikarus in interactive mode. Each of <library-files>
|
"Run Ikarus in interactive mode."
|
||||||
must contain a library and are installed before the
|
"Each of <library-files> must contain a library and are"
|
||||||
<init-files> are loaded"
|
"installed before the <init-files> are loaded"
|
||||||
(list 4 program libdirs library-files init-files args)]))
|
(list 4 program libdirs library-files init-files args)]))
|
||||||
|
|
Loading…
Reference in New Issue