feat: 9.5.9

This commit is contained in:
tmtt 2022-07-29 15:12:07 +02:00
parent cb1753732b
commit 35f43a7909
1084 changed files with 558985 additions and 0 deletions

54
stex/src/dsm.ss Normal file
View file

@ -0,0 +1,54 @@
;;; dsm.ss
;;;
;;; Copyright (c) 1998-2016 R. Kent Dybvig and Oscar Waddell
;;;
;;; Permission is hereby granted, free of charge, to any person obtaining a
;;; copy of this software and associated documentation files (the "Software"),
;;; to deal in the Software without restriction, including without limitation
;;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
;;; and/or sell copies of the Software, and to permit persons to whom the
;;; Software is furnished to do so, subject to the following conditions:
;;;
;;; The above copyright notice and this permission notice shall be included in
;;; all copies or substantial portions of the Software.
;;;
;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
;;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
;;; DEALINGS IN THE SOFTWARE.
;;; authors: R. Kent Dybvig and Oscar Waddell
(library (dsm) (export define-syntactic-monad) (import (chezscheme))
(define-syntax define-syntactic-monad
(syntax-rules ()
[(_ name formal ...)
(andmap identifier? #'(name formal ...))
(define-syntax name
(lambda (x)
(syntax-case x (lambda case-lambda)
[(key lambda more-formals . body)
(with-implicit (key formal ...)
#'(lambda (formal ... . more-formals) . body))]
[(key case-lambda (more-formals . body) (... ...))
(with-implicit (key formal ...)
#'(case-lambda ((formal ... . more-formals) . body) (... ...)))]
[(key proc ((x e) (... ...)) arg (... ...))
(andmap identifier? #'(x (... ...)))
(with-implicit (key formal ...)
(for-each
(lambda (x)
(unless (let mem ((ls #'(formal ...)))
(and (not (null? ls))
(or (free-identifier=? x (car ls))
(mem (cdr ls)))))
(syntax-error x (format "in syntactic monad ~s, unrecognized identifier" 'name))))
#'(x (... ...)))
(with-syntax ([(t (... ...)) (generate-temporaries #'(arg (... ...)))])
#'(let ((p proc) (x e) (... ...) (t arg) (... ...))
(p formal ... t (... ...)))))]
[(key proc) #'(key proc ())])))]))
)

34
stex/src/fixbibtex.ss Executable file
View file

@ -0,0 +1,34 @@
#! /usr/bin/scheme --program
;;; fixbibtex.ss
;;; fixbibtex removes the line breaks inserted by bibtex, sometimes
;;; in the middle of tex commands or urls.
(import (chezscheme))
(unless (= (length (command-line-arguments)) 1)
(printf "usage: fixbibtex <filename>\n")
(exit 1))
(define fn (car (command-line-arguments)))
(let ([s (call-with-port (open-input-file fn) get-string-all)])
(with-input-from-string s
(lambda ()
(with-output-to-file fn
(lambda ()
(define (s0 c)
(unless (eof-object? c)
(case c
[(#\\) (write-char c) (s1 (read-char))]
[(#\%) (s2 (read-char))]
[else (write-char c) (s0 (read-char))])))
(define (s1 c) ; seen \
(unless (eof-object? c)
(write-char c)
(s0 (read-char))))
(define (s2 c) ; seen %
(case c
[(#\newline) (s0 (read-char))]
[else (write-char #\%) (s0 c)]))
(s0 (read-char)))
'replace))))

1942
stex/src/html-prep.ss Executable file

File diff suppressed because it is too large Load diff

469
stex/src/preplib.ss Normal file
View file

@ -0,0 +1,469 @@
;;; preplib.ss
;;;
;;; Copyright (c) 1998-2016 R. Kent Dybvig and Oscar Waddell
;;;
;;; Permission is hereby granted, free of charge, to any person obtaining a
;;; copy of this software and associated documentation files (the "Software"),
;;; to deal in the Software without restriction, including without limitation
;;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
;;; and/or sell copies of the Software, and to permit persons to whom the
;;; Software is furnished to do so, subject to the following conditions:
;;;
;;; The above copyright notice and this permission notice shall be included in
;;; all copies or substantial portions of the Software.
;;;
;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
;;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
;;; DEALINGS IN THE SOFTWARE.
#!chezscheme
(library (preplib)
(export current-ifile genlab-prefix genlab-counters
state-case read-alpha-command read-command
command-symbol? read-back-slash read-open-brace read-close-brace
read-bracketed-text read-optional-arg push-ifile pop-ifile input-error
unexpected-eof unexpected-command genlab read-integer read-def-pattern
read-args expand-template suppress-white-space parse-index global-def
set-def! get-def conditional? populate-source-directories anchored-filename? find-filename
open-input-file)
(import (except (chezscheme) open-input-file))
(define current-ifile (make-parameter #f))
(define genlab-prefix (make-parameter #f))
(define genlab-counters (make-parameter #f))
(define-syntax state-case
(syntax-rules ()
[(_ (var exp) c1 c2 ...)
(identifier? (syntax var))
(let ([var exp]) (state-case-help var c1 c2 ...))]))
(define-syntax state-case-help
(syntax-rules (else)
[(_ var (else e1 e2 ...)) (begin e1 e2 ...)]
[(_ var ((k ...) e1 e2 ...) c ...)
(if (or (state-case-test var k) ...)
(begin e1 e2 ...)
(state-case-help var c ...))]))
(define-syntax state-case-test
(syntax-rules (eof -)
[(_ var eof)
(eof-object? var)]
[(_ var (char1 - char2))
(and (char? var) (char<=? char1 var char2))]
[(_ var char)
(and (char? var) (char=? var char))]))
; doesn't allow @ even in document class or style files. this won't
; work for us anyway because we use character-based rather than
; token-based substitution, so macros that insert @ symbols into
; their output won't work outside of the original context
(define read-alpha-command
; return symbol representing command; assume \ already seen and scan
; maximal string of alphabetic chars, e.g., \scheme => symbol scheme
; returns || when no command is recognized
(let ([buf (open-output-string)])
(lambda (ip)
(state-case (c (peek-char ip))
[((#\a - #\z) (#\A - #\Z))
(let loop ()
(write-char (read-char ip) buf)
(state-case (c (peek-char ip))
[((#\a - #\z) (#\A - #\Z)) (loop)]
[else (string->symbol (get-output-string buf))]))]
[else '||]))))
(define read-command
; like read-alpha-command, but allows single nonalphabetic char
; commands, e.g., \' => |'|
(let ([buf (open-output-string)])
(lambda (ip)
(state-case (c (peek-char ip))
[((#\a - #\z) (#\A - #\Z))
(let loop ()
(write-char (read-char ip) buf)
(state-case (c (peek-char ip))
[((#\a - #\z) (#\A - #\Z)) (loop)]
[else (string->symbol (get-output-string buf))]))]
[(eof) '||]
[else (read-char ip) (string->symbol (string c))]))))
(define command-symbol?
(lambda (cmd) ; true iff command is one character, nonalpabetic
(let ([s (symbol->string cmd)])
(and (fx= (string-length s) 1)
(state-case (c (string-ref s 0))
[((#\a - #\z) (#\A - #\Z)) #f]
[else #t])))))
(define read-back-slash
(lambda (ip)
(if (eqv? (peek-char ip) #\\)
(read-char ip)
(input-error "back slash expected"))))
(define read-open-brace
(lambda (ip)
(if (eqv? (peek-char ip) #\{)
(read-char ip)
(input-error "open brace expected"))))
(define read-close-brace
(lambda (ip)
(if (eqv? (peek-char ip) #\})
(read-char ip)
(input-error "close brace expected"))))
(define read-bracketed-text
(let ([buf (open-output-string)])
(case-lambda
[(ip) (read-open-brace ip) (read-bracketed-text ip 1)]
[(ip depth)
(state-case (c (read-char ip))
[(#\}) (if (= depth 1)
(get-output-string buf)
(begin (write-char #\} buf)
(read-bracketed-text ip (- depth 1))))]
[(#\{) (write-char #\{ buf) (read-bracketed-text ip (+ depth 1))]
[(eof) (input-error "file ended within bracketed text")]
[else (write-char c buf) (read-bracketed-text ip depth)])])))
(define read-optional-arg
(let ([buf (open-output-string)])
(lambda (ip)
(state-case (c (peek-char ip))
[(#\[)
(read-char ip)
(let loop ([depth 0])
(state-case (c (read-char ip))
[(#\]) (if (= depth 0)
(get-output-string buf)
(begin (write-char c buf) (loop depth)))]
[(#\{) (write-char c buf) (loop (+ depth 1))]
[(#\}) (write-char c buf) (loop (- depth 1))]
[(eof) (input-error "file ended within optional argument")]
[else (write-char c buf) (loop depth)]))]
[else #f]))))
(define push-ifile
(lambda (ip ifiles)
(current-ifile ip)
(cons ip ifiles)))
(define pop-ifile
(lambda (ifiles)
(let ([ifiles (cdr ifiles)])
(current-ifile (and (not (null? ifiles)) (car ifiles)))
ifiles)))
(define input-error
(lambda (msg . args)
(define file-coordinates
(lambda (ip)
(let ([n (file-position ip)])
(file-position ip 0)
(let f ([n n] [line 1] [char 1] [return? #f])
(if (= n 0)
(values line char)
(state-case (c (read-char ip))
[(#\newline) (f (- n 1) (if return? line (+ line 1)) 1 #f)]
[(#\return) (f (- n 1) (+ line 1) 1 #t)]
[(eof) (values line char)]
[else (f (- n 1) line (+ char 1) #f)]))))))
(let ([ip (current-ifile)])
(call-with-values (lambda () (file-coordinates ip))
(lambda (line char)
(errorf #f "~a on line ~d, character ~d of ~s"
(apply format msg args)
line char
(port-name ip)))))))
(define unexpected-eof
(lambda (where)
(input-error "unexpected end-of-input ~a" where)))
(define unexpected-command
(lambda (cmd)
(input-error "unexpected command '\\~a'" cmd)))
(define genlab
(lambda ()
(define next-count
(lambda (fn)
(cond
[(assoc fn (genlab-counters)) =>
(lambda (a)
(let ([n (+ (cdr a) 1)])
(set-cdr! a n)
n))]
[else
(genlab-counters (cons (cons fn 0) (genlab-counters)))
0])))
(let ([name (path-root (port-name (current-ifile)))])
(string->symbol
(format "~a:~a~d" name (genlab-prefix) (next-count name))))))
(define read-integer ; return integer or #f if none found
(lambda (ip)
(string->number
(list->string
(let loop ()
(state-case (c (peek-char ip))
[((#\0 - #\9)) (read-char ip) (cons c (loop))]
[else '()]))))))
(define read-def-pattern
(lambda (ip)
(let loop ([i 1])
(state-case (c (peek-char ip))
[(#\{) '()]
[(#\#)
(read-char ip)
(state-case (c1 (peek-char ip))
[(#\#) (read-char ip) (list* c1 c (loop i))]
[else
(let ([n (read-integer ip)])
(if (eq? n i)
(cons n (loop (+ i 1)))
(input-error "invalid \\def argument specifier")))])]
[(eof) (unexpected-eof "after \\def")]
[else (read-char ip) (cons c (loop i))]))))
(define read-args
(lambda (ip pattern cmd)
(define read-arg
(lambda (ip cmd)
(state-case (c (read-char ip))
[(#\\) (format "\\~a" (read-command ip))]
[(#\{) (read-bracketed-text ip 1)]
[(eof) (unexpected-eof (format "reading ~a arguments" cmd))]
[else (string c)])))
(let loop ([pattern pattern])
(if (null? pattern)
'()
(let ([x (car pattern)])
(cond
[(integer? x)
(let ([arg (read-arg ip cmd)])
(cons arg (loop (cdr pattern))))]
[(string? x)
(let ([arg (read-optional-arg ip)])
(cons (or arg x) (loop (cdr pattern))))]
[(eqv? x #\space)
(suppress-white-space ip)
(loop (cdr pattern))]
[(eqv? (read-char ip) x) (loop (cdr pattern))]
[else (input-error "~a use does not match pattern" cmd)]))))))
(define expand-template
(let ([buf (open-output-string)])
(lambda (template args cmd)
(let ([sip (open-input-string template)])
(let loop ()
(state-case (c (read-char sip))
[(#\\)
(write-char c buf)
(state-case (c (peek-char sip))
[(#\#) (read-char sip) (write-char c buf)]
[else (void)])
(loop)]
[(#\#)
(state-case (c (peek-char sip))
[(#\#) (read-char sip) (write-char #\# buf)]
[else (let ([n (read-integer sip)])
(let ([n (and n (- n 1))])
(unless (and n (< -1 n (length args)))
(input-error "invalid argument specifier in ~a template" cmd))
(display (list-ref args n) buf)))])
(loop)]
[(eof) (get-output-string buf)]
[else (write-char c buf) (loop)]))))))
(define (suppress-white-space ip)
(state-case (c (peek-char ip))
[(#\space #\tab #\newline) (read-char ip) (suppress-white-space ip)]
[(#\%)
(read-char ip)
(let loop ()
(state-case (c (read-char ip))
[(eof #\newline) (void)]
[else (loop)]))]
[else (void)]))
(define parse-index
(let ([buf (open-output-string)])
; if proper-nesting? is true, the characters ", @, !, and | lose their
; special meaning within nested groups.
(lambda (ip proper-nesting?)
(define nested-group
(lambda (depth)
(state-case (c (read-char ip))
[(#\{)
(write-char c buf)
(nested-group (+ depth 1))]
[(#\})
(write-char c buf)
(unless (= depth 0) (nested-group (- depth 1)))]
[(#\@ #\! #\|)
(if proper-nesting?
(write-char c buf)
(input-error "unquoted ~c within nested group in index entry" c))
(nested-group depth)]
[(#\")
(if proper-nesting?
(write-char c buf)
(state-case (c (read-char ip))
[(eof) (input-error "file ended within \\index{}")]
[else (write-char c buf)]))
(nested-group depth)]
[(#\")
(write-char c buf)
(unless proper-nesting?
(state-case (c (peek-char ip))
[(#\") (read-char ip) (write-char c buf)]
[else (void)]))
(nested-group depth)]
[else (write-char c buf) (nested-group depth)])))
(define before@
(lambda (ls)
; ls is list of levels seen so far
(state-case (c (read-char ip))
[(#\})
(let ([s (get-output-string buf)])
(values (reverse (cons (cons #f s) ls)) ""))]
[(#\{)
(write-char c buf)
(nested-group 0)
(before@ ls)]
[(#\|)
(let ([s (get-output-string buf)])
(values (reverse (cons (cons #f s) ls))
(read-bracketed-text ip 1)))]
[(#\@) (after@ ls (get-output-string buf))]
[(#\!)
(let ([s (get-output-string buf)])
(before@ (cons (cons #f s) ls)))]
[(#\")
(state-case (c (read-char ip))
[(eof) (input-error "file ended within \\index{}")]
[else
(write-char c buf)
(before@ ls)])]
[(#\\)
(write-char c buf)
(state-case (c (peek-char ip))
[(#\") (read-char ip) (write-char c buf)]
[else (void)])
(before@ ls)]
[(eof) (input-error "file ended within \\index{}")]
[else (write-char c buf) (before@ ls)])))
(define after@
(lambda (ls sort-key)
; ls is list of levels seen so far
; sort-key is sort key part of current level
(state-case (c (read-char ip))
[(#\})
(let ([s (get-output-string buf)])
(values (reverse (cons (cons sort-key s) ls)) ""))]
[(#\{)
(write-char c buf)
(nested-group 0)
(after@ ls sort-key)]
[(#\|)
(let ([s (get-output-string buf)])
(values (reverse (cons (cons sort-key s) ls))
(read-bracketed-text ip 1)))]
[(#\@) (input-error "at sign seen after at sign in \\index{}")]
[(#\!)
(let ([s (get-output-string buf)])
(before@ (cons (cons sort-key s) ls)))]
[(#\")
(state-case (c (read-char ip))
[(eof) (input-error "file ended within \\index{}")]
[else
; leave out quote; reinsert later
(write-char c buf)
(after@ ls sort-key)])]
[(#\\)
(write-char c buf)
(state-case (c (peek-char ip))
[(#\") (read-char ip) (write-char c buf)]
[else (void)])
(after@ ls sort-key)]
[(eof) (input-error "file ended within \\index{}")]
[else (write-char c buf) (after@ ls sort-key)])))
(before@ '()))))
;; support for definitions
(define-syntax global-def
(syntax-rules ()
[(_ name expr)
(set-def! 'name '() #f expr)]))
(define set-def!
(lambda (cmd env conditional? proc)
(if (null? env)
(putprop cmd 'def (cons conditional? proc))
(set-car! env (cons (list* cmd conditional? proc) (car env))))))
(module (get-def conditional?)
(define lookup-env
(lambda (cmd env)
(cond
[(null? env) (getprop cmd 'def '(#f . #f))]
[(assq cmd (car env)) => cdr]
[else (lookup-env cmd (cdr env))])))
(define get-def
(lambda (cmd env)
(cdr (lookup-env cmd env))))
(define conditional?
(lambda (cmd env)
(car (lookup-env cmd env)))))
(define (populate-source-directories)
(let ([inputs (or (getenv "TEXINPUTS") "")])
(unless (equal? inputs "")
(let ([ip (open-input-string inputs)] [op (open-output-string)])
(source-directories
(let loop ([ls '()])
(let ([c (read-char ip)])
(case c
[(#\:) (loop (cons (get-output-string op) ls))]
[(#!eof) (append (reverse ls) (source-directories))]
[else (write-char c op) (loop ls)]))))))))
(define anchored-filename?
(lambda (s)
(and (> (string-length s) 0)
(memv (string-ref s 0) '(#\/ #\.)))))
(define find-filename
(lambda (fn)
(if (anchored-filename? fn)
fn
(ormap
(lambda (p)
(let ([path (string-append p "/" fn)])
(and (file-exists? path) path)))
(source-directories)))))
(define open-input-file
(lambda (fn . flags)
(import scheme)
(let ([path (find-filename fn)])
(unless path
(errorf #f
(if (anchored-filename? fn)
"unable to find file ~a"
"unable to find file ~a in search path")
fn))
(apply open-input-file path flags))))
)

1166
stex/src/scheme-prep.ss Executable file

File diff suppressed because it is too large Load diff

710
stex/src/script.ss Normal file
View file

@ -0,0 +1,710 @@
;;; script.ss
;;;
;;; Copyright (c) 2005 R. Kent Dybvig
;;;
;;; Permission is hereby granted, free of charge, to any person obtaining a
;;; copy of this software and associated documentation files (the "Software"),
;;; to deal in the Software without restriction, including without limitation
;;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
;;; and/or sell copies of the Software, and to permit persons to whom the
;;; Software is furnished to do so, subject to the following conditions:
;;;
;;; The above copyright notice and this permission notice shall be included in
;;; all copies or substantial portions of the Software.
;;;
;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
;;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
;;; DEALINGS IN THE SOFTWARE.
#!chezscheme
(library (script)
(export command-line-case run-script)
(import (chezscheme))
#|
(command-line-case command-line [(<cmdspec>) <body>]*)
<cmdspec> -> (keyword <kwd> <required-arg>* <possible-action>]) <cmdspec>
| (flags [<kwd> <flag-arg>* <possible-action>]*) <cmdspec>
| <argspec>
<possible-action> -> <empty> | $ <action>
<flag-arg> -> <var> | (<type> <var>) | (<type> <var> <default>)
<argspec> -> <required-arg>* <optional-arg>*
| <required-arg>* <optional-arg>* <required-arg> ...
<required-arg> -> <var> | (<type> <var>)
<optional-arg> -> (optional <var>)
| (optional <type> <var>)
| (optional <type> <var> <default>)
<kwd> -> <id> | (<id>+)
<var> -> <id>
<type> -> <id>
<action> -> <expr>
<default> -> <expr>
Each <var> must be unique.
If any <type> is not specified, it defaults to string.
If any <default> is not specified, it defaults to #f. Each <default>
is scoped outside of the command-line-case form.
Command-line arguments are processed from left to right.
Command-line elements must appear in the order specified by <cmdspec>,
except that each the flags in a single flags section may appear in
any order and any flag may occur more than once.
For kewyword and flag clauses, the value of the <action>, if specified,
is evaluated for effect. Each <action> is scoped where each of the <var>s
is visible and each <var>'s current value is based on the specified or
implicit defaults and the arguments seen so far.
If a flag occurs more than once on a command line, the final value of
each corresponding <var> is its last specified value.
If <type> is not string, the procedure string-><type> is applied to the
string argument to cast the string argument the actual value; it should
return #f if the cast fails, in which case the clause doesn't match.
A string-><type> routine should not cause side effects since it may be
called even for clauses that don't match.
For flag clauses, variable ?id, where <kwd> = id or <kwd> = (id id1 ...),
is bound to #t if the argument is specified at least once, otherwise #f.
Within each <body>, the variable usage is bound to a thunk that prints
usage information. usage information is also printed if a command
line cannot be parsed to fit any of the clauses.
consider:
* instead of one keyword, have multiple keywords ALL of which must be
provided in some order. use syntax (all (keyword ...) ...), and use
(some (keyword ...) ...) instead of current "flags" syntax.
* add a prefix before <kwd> in flags section to allow multiple
occurrences of the given flag and listification of the flag
arguments
* option to print more verbose information when certain matches fail,
for example, when a keyword or flag argument requires more additional
arguments than are provided.
* allowing optional arguments after keywords and flags. leads to
ambiguity in some cases.
Testing:
scheme
(import (script))
(define (exit . args) (void))
> (command-line-case '("a" "-q" "-v" "-v" "-b" "c")
[((flags [-b c] [-q $ (write-char #\q)] [-v $ (write-char #\v)]))
(list ?-b c)])
qvv(#t "c")
> (let ()
(define (bar cl)
(command-line-case cl
[((keyword --foo (number n))
(flags [-b (number b)] [-q $ (write-char #\q)] [-v $ (write-char #\v)]))
(pretty-print (list ?-b b))]))
(bar '("a" "--foo" "32" "-q" "-v" "-v" "-b" "45"))
(bar '("a" "--foo" "foo" "-q" "-v" "-v" "-b" "45"))
(bar '("a" "--foo" "32" "-q" "-v" "-v" "-b" "b")))
qvv(#t 45)
usage: a --foo n [ -b b ] [ -q ] [ -v ]
usage: a --foo n [ -b b ] [ -q ] [ -v ]
(define (foo cl)
(define compact? #t)
(define (register-boot-file x) (printf "registering boot file ~s\n" x))
(define (register-heap-file x) (printf "registering boot file ~s\n" x))
(command-line-case cl
[((keyword --version))
(print-version)]
[((keyword --help (number helplevel)))
(printf "here's your help: ~s\n" helplevel)]
[((keyword --help))
(print-help)]
[((flags [(--boot -b) bootpath $ (register-boot-file bootpath)]
[(--compact -c) $ (set! compact? (not compact?))]
[(--heap -h) heappath $ (register-heap-file heappath)]
[(--quiet -q)]
[(--saveheap -s) (number level 0) savepath]
[--verbose])
(flags [--])
a (optional b) (optional number c 666) (number d) ...)
(let-syntax ([pr (syntax-rules ()
[(_ x ...) (begin (printf " ~s = ~s\n" 'x x) ...)])])
(pr ?--boot bootpath ?--compact ?--heap heappath ?--quiet
?--saveheap level savepath ?--verbose ?--
compact?
a b c d))]))
> (foo '("/usr/local/bin/foo" "--help" "3"))
here's your help: 3
> (foo '("/usr/local/bin/foo" "aaa"))
?--boot = #f
bootpath = #f
?--compact = #f
?--heap = #f
heappath = #f
?--quiet = #f
?--saveheap = #f
level = 0
savepath = #f
?--verbose = #f
?-- = #f
compact? = #t
a = "aaa"
b = #f
c = 666
d = ()
> (foo '("/usr/local/bin/foo" "aaa" "bbb"))
?--boot = #f
bootpath = #f
?--compact = #f
?--heap = #f
heappath = #f
?--quiet = #f
?--saveheap = #f
level = 0
savepath = #f
?--verbose = #f
?-- = #f
compact? = #t
a = "aaa"
b = "bbb"
c = 666
d = ()
> (foo '("/usr/local/bin/foo" "aaa" "bbb" "#xccc"))
?--boot = #f
bootpath = #f
?--compact = #f
?--heap = #f
heappath = #f
?--quiet = #f
?--saveheap = #f
level = 0
savepath = #f
?--verbose = #f
?-- = #f
compact? = #t
a = "aaa"
b = "bbb"
c = 3276
d = ()
> (foo '("/usr/local/bin/foo" "aaa" "bbb" "#xccc" "3" "4" "5"))
?--boot = #f
bootpath = #f
?--compact = #f
?--heap = #f
heappath = #f
?--quiet = #f
?--saveheap = #f
level = 0
savepath = #f
?--verbose = #f
?-- = #f
compact? = #t
a = "aaa"
b = "bbb"
c = 3276
d = (3 4 5)
> (foo '("/usr/local/bin/foo" "aaa" "bbb" "ccc"))
usage: foo --version
foo --help helplevel
foo --help
foo [ --boot|-b bootpath ] [ --compact|-c ] [ --heap|-h heappath ] [ --quiet|-q ] [ --saveheap|-s level savepath ] [ --verbose ] [ -- ] a [ b ] [ c ] d ...
> (foo '("/usr/local/bin/foo" "aaa" "bbb" "#xccc" "3" "4" "5" "ddd"))
usage: foo --version
foo --help helplevel
foo --help
foo [ --boot|-b bootpath ] [ --compact|-c ] [ --heap|-h heappath ] [ --quiet|-q ] [ --saveheap|-s level savepath ] [ --verbose ] [ -- ] a [ b ] [ c ] d ...
> (foo '("/usr/local/bin/foo" "-q" "--boot" "foo.boot" "aaa"))
registering boot file "foo.boot"
?--boot = #t
bootpath = "foo.boot"
?--compact = #f
?--heap = #f
heappath = #f
?--quiet = #t
?--saveheap = #f
level = 0
savepath = #f
?--verbose = #f
?-- = #f
compact? = #t
a = "aaa"
b = #f
c = 666
d = ()
> (foo '("/usr/local/bin/foo" "-q" "--boot" "foo.boot" "-b"
"--heap" "-h" "foo.heap" "-s" "7" "foo7.heap" "-c" "-c" "-c"
"--verbose" "aaa"))
registering boot file "foo.boot"
registering boot file "--heap"
registering boot file "foo.heap"
?--boot = #t
bootpath = "--heap"
?--compact = #t
?--heap = #t
heappath = "foo.heap"
?--quiet = #t
?--saveheap = #t
level = 7
savepath = "foo7.heap"
?--verbose = #t
?-- = #f
compact? = #f
a = "aaa"
b = #f
c = 666
d = ()
(command-line-case (cons "/usr/local/bin/foo" (command-line-arguments))
[((keyword --build ifn)
(flags [--verify] [(--output -o) (string ofn "a.out")])
lib* ...)
---]
[((keyword (--query -q) ifn))
---]
[((flags [(--verify -v)] [(--output -o) (string ofn "a.out")])
(string x*) ...)
---]
[((flags [(--verify -v)] [(--output -o) ofn])
(flags [--])
x* ...)
---]
[(x (optional integer y 0))
---]
[(x (optional integer y) x* ...)
---]
)
|#
(define-syntax command-line-case
(lambda (x)
;; Internal representation:
;; ({ keyword | flags }* reqarg* optarg* restarg?)
;; kwd's are strings
;; vars's are syntax objects (identifiers)
;; defaults are syntax objects
;; actions are syntax objects or #f
(define-record pkeyword ((immutable kwd*)
(immutable reqarg*)
(immutable action)))
(define-record pflags ((immutable flag*)))
(define-record pflag ((immutable kwd*)
(immutable ?var)
(immutable optarg*)
(immutable action)))
(define-record preqarg ((immutable type)
(immutable var)))
(define-record poptarg ((immutable type)
(immutable var)
(immutable default)))
(define-record prestarg ((immutable reqarg)))
(define-record pend ())
(module (parse-cmdspec)
(define (parse-cmdspec cmdspec)
(unless (syntax-case cmdspec () [(x ...) #t] [_ #f])
(syntax-error cmdspec "improper argument declaration list"))
(let parse-cmdspec ([cmdspec cmdspec])
(syntax-case cmdspec (keyword flags $)
[((keyword kwd reqarg ... $ actionexpr) . cmdspec)
(cons (make-pkeyword
(map id->string (parse-kwd #'kwd))
(map parse-reqarg
(syntax->list
#'(reqarg ...)))
#'actionexpr)
(parse-cmdspec #'cmdspec))]
[((keyword kwd reqarg ...) . cmdspec)
(cons (make-pkeyword
(map id->string (parse-kwd #'kwd))
(map parse-reqarg
(syntax->list
#'(reqarg ...)))
#f)
(parse-cmdspec #'cmdspec))]
[((keyword . ignore1) . ignore2)
(syntax-error (syntax-case cmdspec () [(x . r) #'x])
"invalid keyword declaration")]
[((flags flagdecl ...) . cmdspec)
(cons (make-pflags
(map (lambda (flagdecl)
(syntax-case flagdecl ($)
[(kwd flagarg ... $ actionexpr)
(let ([kwd* (parse-kwd #'kwd)])
(make-pflag
(map id->string kwd*)
(make-?var (car kwd*))
(map parse-flagarg
(syntax->list #'(flagarg ...)))
#'actionexpr))]
[(kwd flagarg ...)
(let ([kwd* (parse-kwd #'kwd)])
(make-pflag
(map id->string kwd*)
(make-?var (car kwd*))
(map parse-flagarg
(syntax->list #'(flagarg ...)))
#f))]
[_ (syntax-error flagdecl
"invalid flag declaration")]))
(syntax->list #'(flagdecl ...))))
(parse-cmdspec #'cmdspec))]
[((flags . ignore1) . ignore2)
(syntax-error (syntax-case cmdspec () [(x . r) #'x])
"invalid flags declaration")]
[argspec (parse-argspec #'argspec)])))
(define (parse-kwd kwd)
(syntax-case kwd ()
[id (identifier? #'id) (list #'id)]
[(id1 id2 ...) (syntax->list #'(id1 id2 ...))]
[_ (syntax-error kwd "invalid kwd specifier")]))
(define (parse-argspec argspec)
(define (dots? x)
(and (identifier? x)
(literal-identifier=? x #'(... ...))))
(syntax-case argspec ()
[() (list (make-pend))]
[(reqarg dots)
(dots? #'dots)
(list (make-prestarg (parse-reqarg #'reqarg)))]
[(arg . argspec)
(cons (parse-arg #'arg) (parse-argspec #'argspec))]
[(x . r) (syntax-error #'x "invalid argument declaration")]))
(define (parse-arg arg)
(syntax-case arg (optional)
[(optional . stuff) (parse-optarg arg)]
[_ (parse-reqarg arg)]))
(define (parse-reqarg reqarg)
(syntax-case reqarg ()
[var (identifier? #'var) (make-preqarg #'string #'var)]
[(type var)
(and (identifier? #'type) (identifier? #'var))
(make-preqarg #'type #'var)]
[x (syntax-error reqarg "invalid argument specifier")]))
(define (parse-optarg optarg)
(syntax-case optarg (optional)
[(optional var)
(identifier? #'var)
(make-poptarg #'string #'var #'#f)]
[(optional type var)
(and (identifier? #'type) (identifier? #'var))
(make-poptarg #'type #'var #'#f)]
[(optional type var default)
(and (identifier? #'type) (identifier? #'var))
(make-poptarg #'type #'var #'default)]
[x (syntax-error optarg "invalid optional argument specifier")]))
(define (parse-flagarg flagarg)
(syntax-case flagarg ()
[var (identifier? #'var) (make-poptarg #'string #'var #'#f)]
[(type var)
(and (identifier? #'type) (identifier? #'var))
(make-poptarg #'type #'var #'#f)]
[(type var default)
(and (identifier? #'type) (identifier? #'var))
(make-poptarg #'type #'var #'default)]
[x (syntax-error flagarg "invalid flag argument specifier")]))
(define (id->string x) (symbol->string (syntax-object->datum x)))
(define (make-?var x)
(datum->syntax-object x
(string->symbol (format "?~a" (id->string x))))))
(module (usage-printer)
(define (usage-printer cmdspec+)
#`(lambda (cl)
(let ([who (path-last (car cl))])
#,(usage-cmdspec "usage:" (car cmdspec+))
#,@(map (lambda (cmdspec) (usage-cmdspec " " cmdspec)) (cdr cmdspec+)))))
(define (usage-cmdspec leader cmdspec)
(define cmdspec-printer
(lambda (s*)
#`(printf #,(format "~a ~~a~~a\n" leader) who #,(apply string-append s*))))
(let-values ([(s* flag**) (usage-cmdspec-helper #t cmdspec)])
(assert (null? flag**))
(if (< (apply + (string-length leader) (map string-length s*)) 80)
(cmdspec-printer s*)
(let-values ([(s* flag**) (usage-cmdspec-helper #f cmdspec)])
(fold-left
(lambda (expr flag*)
(let ([flag-header (car flag*)] [flag* (cdr flag*)])
#`(begin
#,expr
(display-string #,(format " where each ~a is one of:\n~{ ~a\n~}"
flag-header
(map (lambda (kwd* optarg*)
(format " ~a~{~a~}"
(usage-kwd* kwd*)
(map usage-optarg optarg*)))
(map pflag-kwd* flag*)
(map pflag-optarg* flag*)))))))
(cmdspec-printer s*)
flag**)))))
(define (usage-cmdspec-helper inline-flags? cmdspec)
(let loop ([cmdspec cmdspec]
[rs* '()]
[rflag** '()]
[flagsno (and (not inline-flags?)
(cond [(memp pflags? cmdspec) => (lambda (cmdspec) (memp pflags? (cdr cmdspec)))] [else #f])
1)])
(if (null? cmdspec)
(values (reverse rs*) (reverse rflag**))
(let ([x (car cmdspec)])
(if (pflags? x)
(if inline-flags?
(loop (cdr cmdspec)
(cons (let ([flag* (pflags-flag* x)])
(format "~{~a~}"
(map (lambda (kwd* optarg*)
(format " [ ~a~{~a~} ]"
(usage-kwd* kwd*)
(map usage-optarg optarg*)))
(map pflag-kwd* flag*)
(map pflag-optarg* flag*))))
rs*)
rflag**
flagsno)
(let ([flag-header (if flagsno (format "flag~s" flagsno) "flag")])
(loop (cdr cmdspec)
(cons (format " ~a ..." flag-header) rs*)
(cons (cons flag-header (pflags-flag* x)) rflag**)
(and flagsno (+ flagsno 1)))))
(loop (cdr cmdspec)
(cons (cond
[(pkeyword? x)
(format " ~a~{~a~}"
(usage-kwd* (pkeyword-kwd* x))
(map usage-reqarg (pkeyword-reqarg* x)))]
[(preqarg? x) (usage-reqarg x)]
[(poptarg? x) (format " [~a ]" (usage-optarg x))]
[(prestarg? x) (format "~a ..." (usage-reqarg (prestarg-reqarg x)))]
[(pend? x) ""]
[else (errorf 'usage-cmdspec "unrecognized cmdspec ~s" x)])
rs*)
rflag**
flagsno))))))
(define (usage-kwd* kwd*) (format "~a~{|~a~}" (car kwd*) (cdr kwd*)))
(define (usage-reqarg x)
(format " ~a" (syntax-object->datum (preqarg-var x))))
(define (usage-optarg x)
(format " ~a" (syntax-object->datum (poptarg-var x)))))
(define (xmap p ls tail)
(if (null? ls)
tail
(p (car ls) (xmap p (cdr ls) tail))))
(module (findvars)
(define (findvars cmdspec)
(findvars-cmdspec cmdspec '()))
(define (findvars-cmdspec cmdspec tail)
(xmap
(lambda (x tail)
(cond
[(pkeyword? x) (xmap findvars-reqarg (pkeyword-reqarg* x) tail)]
[(pflags? x)
(xmap (lambda (flag tail)
(cons (pflag-?var flag)
(xmap findvars-optarg (pflag-optarg* flag) tail)))
(pflags-flag* x)
tail)]
[(preqarg? x) (findvars-reqarg x tail)]
[(poptarg? x) (findvars-optarg x tail)]
[(prestarg? x) (findvars-reqarg (prestarg-reqarg x) tail)]
[(pend? x) tail]
[else (errorf 'findvars-cmdspec "unrecognized cmdspec ~s" x)]))
cmdspec
tail))
(define (findvars-reqarg x tail)
(cons (preqarg-var x) tail))
(define (findvars-optarg x tail)
(cons (poptarg-var x) tail)))
(module (finddefaults)
(define (finddefaults cmdspec)
(finddefaults-cmdspec cmdspec '()))
(define (finddefaults-cmdspec cmdspec tail)
(xmap
(lambda (x tail)
(cond
[(pkeyword? x)
(xmap finddefaults-reqarg (pkeyword-reqarg* x) tail)]
[(pflags? x)
(xmap (lambda (flag tail)
(cons #'#f
(xmap finddefaults-optarg (pflag-optarg* flag) tail)))
(pflags-flag* x)
tail)]
[(preqarg? x) (finddefaults-reqarg x tail)]
[(poptarg? x) (finddefaults-optarg x tail)]
[(prestarg? x) (cons #''() tail)]
[(pend? x) tail]
[else (errorf 'finddefaults-cmdspec "unrecognized cmdspec ~s" x)]))
cmdspec
tail))
(define (finddefaults-reqarg x tail)
(cons #'(void) tail))
(define (finddefaults-optarg x tail)
(cons (poptarg-default x) tail)))
(module (build-clause)
(define (type->converter x)
(if (and x (not (literal-identifier=? x #'string)))
(datum->syntax-object x
(string->symbol
(format "string->~a" (syntax-object->datum x))))
#'values))
(define (build-clause-body var* cmdspec body)
(with-syntax ([(var ...) var*])
(let ([x (car cmdspec)])
(cond
[(pkeyword? x)
(let ([reqarg* (pkeyword-reqarg* x)])
(with-syntax ([(kwd ...) (pkeyword-kwd* x)]
[(reqvar ...) (map preqarg-var reqarg*)]
[(convert ...)
(map type->converter (map preqarg-type reqarg*))]
[action (or (pkeyword-action x) #'(#2%void))]
[finish (build-clause-body var* (cdr cmdspec) body)]
[n (length reqarg*)])
#'(cond
[(and (> (length cl) n) (member (car cl) '(kwd ...)))
(let ([cl (cdr cl)])
(apply (lambda (reqvar ... . ignore)
; performs unnecessary tests if convert
; is values; doesn't shortcut out as
; soon as one convert returns false
(let ([reqvar (convert reqvar)] ...)
(if (and reqvar ...)
(let ([cl (list-tail cl n)]
[act! (lambda () (act!) action)])
finish)
(next orig-cl))))
cl))]
[else (next orig-cl)])))]
[(pflags? x)
(let* ([flag* (pflags-flag* x)]
[optarg** (map pflag-optarg* flag*)])
(with-syntax ([((kwd ...) ...) (map pflag-kwd* flag*)]
[(?var ...) (map pflag-?var flag*)]
[((optvar ...) ...)
(map (lambda (optarg*)
(map poptarg-var optarg*))
optarg**)]
[((convert ...) ...)
(map (lambda (optarg*)
(map type->converter
(map poptarg-type optarg*)))
optarg**)]
[(action ...)
(map (lambda (flag)
(or (pflag-action flag) #'(#2%void)))
flag*)]
[finish (build-clause-body var* (cdr cmdspec) body)]
[(n ...) (map length optarg**)])
#'(let ([t (lambda (cl act! ?var ... optvar ... ...) finish)])
(let f ([cl cl]
[act! act!]
[?var ?var] ...
[optvar optvar] ... ...)
(cond
[(null? cl) (t cl act! ?var ... optvar ... ...)]
[(and (> (length cl) n) (member (car cl) '(kwd ...)))
(let ([cl (cdr cl)])
(apply (lambda (optvar ... . ignore)
; performs unnecessary tests if convert
; is values; doesn't shortcut out as
; soon as one convert returns false
(let ([optvar (convert optvar)] ...)
(if (and optvar ...)
(let ([?var #t])
(f (list-tail cl n)
(lambda () (act!) action)
?var ...
optvar ... ...))
(next orig-cl))))
cl))]
...
[else (t cl act! ?var ... optvar ... ...)])))))]
[(preqarg? x)
(with-syntax ([reqvar (preqarg-var x)]
[convert (type->converter (preqarg-type x))]
[finish (build-clause-body var* (cdr cmdspec) body)])
#'(cond
; performs unnecessary test if convert is values
[(and (not (null? cl)) (convert (car cl))) =>
(lambda (reqvar) (let ([cl (cdr cl)]) finish))]
[else (next orig-cl)]))]
[(poptarg? x)
(with-syntax ([optvar (poptarg-var x)]
[convert (type->converter (poptarg-type x))]
[finish (build-clause-body var* (cdr cmdspec) body)])
#'(let ([t (lambda (optvar cl) finish)])
(cond
[(null? cl) (t optvar cl)]
; performs unnecessary test if convert is values
[(convert (car cl)) => (lambda (optvar) (t optvar (cdr cl)))]
[else (next orig-cl)])))]
[(prestarg? x)
(let ([reqarg (prestarg-reqarg x)])
(with-syntax ([restvar (preqarg-var reqarg)]
[convert (type->converter (preqarg-type reqarg))]
[body body])
#'(let f ([cl cl] [rrestvar '()])
(cond
[(null? cl)
(let ([restvar (reverse rrestvar)])
(let () (act!) . body))]
; performs unnecessary test if convert is values
[(convert (car cl)) =>
(lambda (x) (f (cdr cl) (cons x rrestvar)))]
[else (next orig-cl)]))))]
[(pend? x)
(with-syntax ([body body])
#'(if (null? cl)
(let () (act!) . body)
(next orig-cl)))]
[else (errorf 'build-clause-body "unrecognized cmdspec ~s" x)]))))
(define (build-clause cmdspec body next-expr)
(let ([var* (findvars cmdspec)])
(with-syntax ([next-expr next-expr]
[(var ...) var*]
[(default ...) (finddefaults cmdspec)]
[clause-body (build-clause-body var* cmdspec body)])
#'(let ([next next-expr])
(lambda (orig-cl)
(let ([cl (cdr orig-cl)] [act! #2%void] [var default] ...)
clause-body)))))))
(syntax-case x ()
[(k clexpr [cmdspeca b1a b2a ...] [cmdspec b1 b2 ...] ...)
(let ([all-cmdspec* (map parse-cmdspec
(cons #'cmdspeca
(syntax->list #'(cmdspec ...))))]
[body* #'((b1a b2a ...) (b1 b2 ...) ...)])
(with-implicit (k usage)
#`(let ([usage-proc #,(usage-printer all-cmdspec*)] [cl clexpr])
(let ([usage (lambda () (usage-proc cl))])
#,(with-syntax ([p (let f ([cmdspec* all-cmdspec*] [body* body*])
(if (null? cmdspec*)
#'(lambda (cl) (usage-proc cl) (exit 1))
(build-clause (car cmdspec*) (car body*)
(f (cdr cmdspec*) (cdr body*)))))])
#'(p cl))))))])))
;;; (run-script script arg ...) runs named script in same process
(define (run-script script . arg*)
(command-line-arguments arg*)
(load script))
)