"format.ss"
;;; format.ss
;;; Copyright 1984-2016 Cisco Systems, Inc.
;;; 
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at
;;; 
;;; http://www.apache.org/licenses/LICENSE-2.0
;;; 
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.

#| TODO:
 * more tests
    - tests of format with #f, #t, or port as first argument; test of printf
      and fprintf, tests that exercise all paths of cp1in format handler
    - verify complete coverage of code paths
    - extract all tests from cltl2
    - more # and v parameter tests
    - ~^ tests:
      - need tests for outside abort, abort in indirect, nested {super-,}abort
        in conditionals, nested {super-,}abort in case-conversion, etc.
      - need tests with one parameter and two parameters
    - ~* and ~:p tests for moving around loop args
    - test float printing with Bob's set of floats
 * use something better than string-append for constructing ~f and ~e output
 * use more efficient dispatch, e.g., have case use binary search for fixnum
   keys; or modify compiler to use jump tables for well-behaved case's
 * look into not hardcoding float-base = 10
 * vparams adds substantial allocation overhead, probably because of the
   compiler's handling of mvlet producers containing if expressions; fix
   the compiler
 * abstract out Chez Scheme specifics, like display-string, $list-length,
   string ports, use of generic port
|#

;;; missing directives
;;;  pretty-printer controls (^_, ~:>, ~i, ~:t ~/name/)

;;; known incompatibilities with Common Lisp
;;;  : [print nil as ()] modifier ignored for ~a
;;;  : [print nil as ()] modifier treated as "print-gensym #f" for ~s
;;;  common lisp doesn't complain when there are unused arguments,
;;;    may not complain when there are too few arguments.  we always
;;;    complain when there are too few and complain when we can determine
;;;    statically that there are too many
;;;  we insist on real argument for ~f, ~e, and ~g; common lisp is
;;;    lax and sends off anything else to ~D.

;;; other notees
;;;  we always assume that format starts at the beginning of a line
;;;    in support of ~&, ~t, and ~<...>

(let ()
  ;;; configuration

  ;; check for too many args at parse time
  (define static-too-many-args-check #t)
  ;; check for too many args at parse time for indirects and loop bodies
  (define indirect-too-many-args-check #f)
  ;; check for too many args at run time.  the check is always suppressed
  ;; when we terminate a format or indirect format as the result of ~^
  (define dynamic-too-many-args-check #f)

  ;;; predicates used to check format parameters
  (define nnfixnum? (lambda (x) (and (fixnum? x) (fx>= x 0))))
  (define true? (lambda (x) #t))
  (define pfixnum? (lambda (x) (and (fixnum? x) (fx> x 0))))
  (define radix? (lambda (x) (and (fixnum? x) (fx<= 2 x 36))))

 ; we require nongenerative records because the compiler embeds parsed
 ; format strings in object files.  force cp1in-parse-format to return #f
 ; to bootstrap after making changes to any of these records
  (define-datatype (#{fmt cgos0c9ufi1rq-fd} (immutable directive))
    (#{newline cgos0c9ufi1rq-ez} n)
    (#{fresh-line cgos0c9ufi1rq-fc} n)
    (#{dup-char cgos0c9ufi1rq-fh} n c)
    (#{display cgos0c9ufi1rq-fi} mincol colinc minpad pad-char left?)
    (#{simple-display cgos0c9ufi1rq-et})
    (#{simple-write cgos0c9ufi1rq-es})
    (#{write cgos0c9ufi1rq-ei} mincol colinc minpad pad-char nogensym? left?)
    (#{cwrite cgos0c9ufi1rq-fk} colon? at?)
    (#{fwrite cgos0c9ufi1rq-fb} w d k oc pc sign?)
    (#{ewrite cgos0c9ufi1rq-ff} w d ew k oc pc ec sign?)
    (#{gwrite cgos0c9ufi1rq-e9} w d ew k oc pc ec sign?)
    (#{$write cgos0c9ufi1rq-eg} d n w pc sign-before-pad? sign?)
    (#{write-radix cgos0c9ufi1rq-eh} base w pc cc ci sign? commas?)
    (#{plural cgos0c9ufi1rq-ey} back-up? y/ies?)
    (#{fancy-radix cgos0c9ufi1rq-fe} colon? at?)
    (#{indirect cgos0c9ufi1rq-e6} splice?)
    (#{goto cgos0c9ufi1rq-fa} n reverse? absolute?)
    (#{tabulate cgos0c9ufi1rq-ek} colnum colinc relative?)
    (#{convert-case cgos0c9ufi1rq-fl} nested-cmd* colon? at?)
    (#{conditional cgos0c9ufi1rq-fo} n cases default)
    (#{conditional/at cgos0c9ufi1rq-fn} consequent)
    (#{conditional/colon cgos0c9ufi1rq-fm} alternative consequent)
    (#{justify cgos0c9ufi1rq-e1} mincol colinc minpad pad-char before? after? initial margin columns segments)
    (#{abort cgos0c9ufi1rq-ft} n m super?)
    (#{iteration cgos0c9ufi1rq-e2} body n sublists? use-remaining? at-least-once?)
    (#{columntrack cgos0c9ufi1rq-fq} body)
  )

  ;;; parse string to list of strings, chars, and fmt records
  (define parse
    (lambda (who cntl)
      (define column? #f)
      (define-syntactic-monad state nargs cmd* stack)
      (define-record-type frame
        (fields (immutable directive) (immutable cmd*))
        (nongenerative))
      (define-record-type cvtcase-frame
        (parent frame)
        (fields (immutable colon?) (immutable at?))
        (nongenerative)
        (sealed #t))
      (define-record-type conditional/at-frame
        (parent frame)
        (nongenerative)
        (sealed #t))
      (define-record-type conditional/colon-frame
        (parent frame)
        (fields (mutable altern))
        (nongenerative)
        (sealed #t)
        (protocol
          (lambda (make-new)
            (lambda (directive cmd*)
              ((make-new directive cmd*) #f)))))
      (define-record-type conditional-frame
        (parent frame)
        (fields (immutable n) (mutable cases) (mutable default?))
        (nongenerative)
        (sealed #t)
        (protocol
          (lambda (make-new)
            (lambda (directive cmd* n)
              ((make-new directive cmd*) n '() #f)))))
      (define-record-type justify-frame
        (parent frame)
        (fields
          (immutable mincol)
          (immutable colinc)
          (immutable minpad)
          (immutable pc)
          (immutable before?)
          (immutable after?)
          (mutable segments)
          (mutable initial)
          (mutable margin)
          (mutable columns))
        (nongenerative)
        (sealed #t)
        (protocol
          (lambda (make-new)
            (lambda (directive cmd* mincol colinc minpad pc before? after?)
              ((make-new directive cmd*) mincol colinc minpad pc before? after? '() #f #f #f)))))
      (define-record-type iteration-frame
        (parent frame)
        (fields (immutable n) (immutable sublists?) (immutable use-remaining?))
        (nongenerative)
        (sealed #t))
      (define incomplete-format-directive
        (lambda (b i)
          ($oops who "incomplete format directive ~s"
            (substring cntl b i))))
      (define (bump x n) (and x n (fx+ x n)))
      (unless (string? cntl)
        ($oops who "~s is not a string" cntl))
      (let ([nmax (fx- (string-length cntl) 1)])
        (define char
          (lambda (i)
            (if (fx> i nmax)
                #!eof
                (string-ref cntl i))))
        (define sfinal
          (state lambda ()
            (unless (null? stack)
              ($oops who "unclosed directive ~a" (frame-directive (car stack))))
            (let ([cmd* (reverse cmd*)])
              (values (if column? (list (fmt-columntrack "" cmd*)) cmd*) nargs))))
        (define s0
          (state lambda (i)
            (let ([c (char i)])
              (state-case c
                [eof (state sfinal ())]
                [(#\~) (state s3 () (fx+ i 1) i)]
                [else (state s1 () (fx+ i 1) i c)]))))
        (define s1
          (state lambda (i b c0)
            (let ([c (char i)])
              (state-case c
                [eof (state sfinal ([cmd* (cons c0 cmd*)]))]
                [(#\~) (state s3 ([cmd* (cons c0 cmd*)]) (fx+ i 1) i)]
                [else (state s2 () (fx+ i 1) b)]))))
        (define s2
          (state lambda (i b)
            (let ([c (char i)])
              (state-case c
                [eof (state sfinal ([cmd* (cons (substring cntl b i) cmd*)]))]
                [(#\~) (state s3 ([cmd* (cons (substring cntl b i) cmd*)]) (fx+ i 1) i)]
                [else (state s2 () (fx+ i 1) b)]))))
        (define s3
          (state lambda (i b)
            (let ([c (char i)])
              (state-case c
                [eof (incomplete-format-directive b i)]
                [(#\~) (state s1 () (fx+ i 1) i #\~)]
                [(#\- #\+) (state s4-sign () (fx+ i 1) b '() i)]
                [((#\0 - #\9)) (state s4-digit () (fx+ i 1) b '() i)]
                [(#\,) (state s4-comma () (fx+ i 1) b '(#f))]
                [(#\') (state s4-quote () (fx+ i 1) b '())]
                [(#\#) (state s4-after-param () (fx+ i 1) b '(hash))]
                [(#\v) (state s4-after-param ([nargs (bump nargs 1)]) (fx+ i 1) b '(v))]
                [else (state s5 () i b '())]))))
        (define s4-sign
          (state lambda (i b p* bp)
            (let ([c (char i)])
              (state-case c
                [eof (incomplete-format-directive b i)]
                [((#\0 - #\9)) (state s4-digit () (fx+ i 1) b p* bp)]
                [else (incomplete-format-directive b i)]))))
        (define s4-quote
          (state lambda (i b p*)
            (let ([c (char i)])
              (state-case c
                [eof (incomplete-format-directive b i)]
                [else (state s4-after-param () (fx+ i 1) b (cons c p*))]))))
        (define s4-after-param
          (state lambda (i b p*)
            (let ([c (char i)])
              (state-case c
                [eof (incomplete-format-directive b i)]
                [(#\,) (state s4-comma () (fx+ i 1) b p*)]
                [else (state s5 () i b (reverse p*))]))))
        (define s4-digit
          (state lambda (i b p* bp)
            (let ([c (char i)])
              (state-case c
                [eof (incomplete-format-directive b i)]
                [((#\0 - #\9)) (state s4-digit () (fx+ i 1) b p* bp)]
                [(#\,) (state s4-comma () (fx+ i 1) b (cons (string->number (substring cntl bp i)) p*))]
                [else (state s5 () i b (reverse (cons (string->number (substring cntl bp i)) p*)))]))))
        (define s4-comma
          (state lambda (i b p*)
            (let ([c (char i)])
              (state-case c
                [eof (incomplete-format-directive b i)]
                [(#\- #\+) (state s4-sign () (fx+ i 1) b p* i)]
                [((#\0 - #\9)) (state s4-digit () (fx+ i 1) b p* i)]
                [(#\,) (state s4-comma () (fx+ i 1) b (cons #f p*))]
                [(#\') (state s4-quote () (fx+ i 1) b p*)]
                [(#\#) (state s4-after-param () (fx+ i 1) b (cons 'hash p*))]
                [(#\v) (state s4-after-param ([nargs (bump nargs 1)]) (fx+ i 1) b (cons 'v p*))]
                [else (state s5 () i b (reverse (cons #f p*)))]))))
        (define s5
          (state lambda (i b p*)
            (let ([c (char i)])
              (state-case c
                [eof (incomplete-format-directive b i)]
                [(#\:) (state s5-colon () (fx+ i 1) b p*)]
                [(#\@) (state s5-at () (fx+ i 1) b p*)]
                [else (state s6 () i b p* #f #f)]))))
        (define s5-colon
          (state lambda (i b p*)
            (let ([c (char i)])
              (state-case c
                [eof (incomplete-format-directive b i)]
                [(#\@) (state s6 () (fx+ i 1) b p* #t #t)]
                [else (state s6 () i b p* #t #f)]))))
        (define s5-at
          (state lambda (i b p*)
            (let ([c (char i)])
              (state-case c
                [eof (incomplete-format-directive b i)]
                [(#\:) (state s6 () (fx+ i 1) b p* #t #t)]
                [else (state s6 () i b p* #f #t)]))))
        (define s6
          (state lambda (i b p* colon? at?)
            (define skip-non-newline-white
              (lambda (i)
                (let ([c (char i)])
                  (state-case c
                    [eof i]
                    [(#\space #\tab #\page #\return)
                     (skip-non-newline-white (fx+ i 1))]
                    [else i]))))
            (let ([c (char i)])
              (define no-colon
                (lambda ()
                  (when colon?
                    ($oops who "~~~c directive has no : flag" c))))
              (define no-at
                (lambda ()
                  (when at?
                    ($oops who "~~~c directive has no @ flag" c))))
              (define too-many-parameters
                (lambda ()
                  ($oops who
                    "too many parameters in ~~~c directive ~s"
                    c (substring cntl b (fx+ i 1)))))
              (define missing-parameter
                (lambda (what)
                  ($oops who
                    "missing required ~s parameter in ~~~c directive ~s"
                    what c (substring cntl b (fx+ i 1)))))
              (define invalid-parameter
                (lambda (what arg)
                  ($oops who
                    "invalid ~s parameter ~a in ~~~c directive ~s"
                    what arg c (substring cntl b (fx+ i 1)))))
              (define misplaced-directive
                (lambda ()
                  ($oops who "misplaced directive ~s"
                    (substring cntl b (fx+ i 1)))))
              (define-syntax parameters
                (lambda (x)
                  (define process-param
                    (lambda (t* param* body)
                      (if (null? param*)
                          body
                          (with-syntax ([body (process-param (cdr t*) (cdr param*) body)]
                                        [t (car t*)])
                            (syntax-case (car param*) (implicit)
                              [(implicit e) #'(let ([t e]) body)]
                              [(type? p)
                               #'(begin
                                   (when (null? p*) (missing-parameter 'p))
                                   (let ([t (car p*)] [p* (cdr p*)])
                                     (when (not t) (missing-parameter 'p))
                                     (unless (or (type? t) (memq t '(hash v))) (invalid-parameter 'p t))
                                     body))]
                              [(type? p default)
                               #'(let ([proc (lambda (t p*) body)])
                                   (if (null? p*)
                                       (proc 'default p*)
                                       (let ([t (car p*)] [p* (cdr p*)])
                                         (if (not t)
                                             (proc default p*)
                                             (begin
                                               (unless (or (type? t) (memq t '(hash v))) (invalid-parameter 'p t))
                                               (proc t p*))))))])))))
                  (syntax-case x ()
                    [(_ ([t param] ...) e1 e2 ...)
                     (process-param
                       #'(t ...)
                       #'(param ...)
                       #'(begin
                           (unless (null? p*) (too-many-parameters))
                           (let () e1 e2 ...)))])))
              (define-syntax directive
                (lambda (x)
                  (define construct-name
                    (lambda (template-identifier . args)
                      (datum->syntax
                        template-identifier
                        (string->symbol
                          (apply string-append
                                 (map (lambda (x)
                                        (if (string? x)
                                            x
                                            (symbol->string (syntax->datum x))))
                                      args))))))
                  (syntax-case x ()
                    [(k (d param ...) n)
                     (with-syntax ([(t ...) (generate-temporaries #'(param ...))]
                                   [fmt-d (construct-name #'d "fmt-" #'d)])
                       (with-implicit (k state cmd* nargs)
                         #'(parameters ([t param] ...)
                             (state s0
                               ([cmd* (cons (fmt-d (substring cntl b (fx+ i 1)) t ...) cmd*)]
                                [nargs (bump nargs n)])
                               (fx+ i 1)))))])))
              (define-syntax parse-radix
                (syntax-rules ()
                  [(_ base)
                   (directive
                     (write-radix [implicit base]
                                  [nnfixnum? w #f]
                                  [char? pad-char #\space]
                                  [char? comma-char #\,]
                                  [pfixnum? comma-interval 3]
                                  [implicit at?]
                                  [implicit colon?])
                     1)]))
              (state-case c
                [eof (incomplete-format-directive b i)]
                [(#\% #\n #\N)
                 (no-at)
                 (no-colon)
                 (if (or (null? p*) (equal? p* '(1)))
                     (state s0 ([cmd* (cons #\newline cmd*)]) (fx+ i 1))
                     (directive (dup-char [nnfixnum? n 1] [implicit #\newline]) 0))]
                [(#\&)
                 (no-at)
                 (no-colon)
                 (directive (fresh-line [nnfixnum? n 1]) 0)]
                [(#\a #\A)
                 (no-colon)
                 (if (null? p*)
                     (directive
                       (simple-display)
                       1)
                     (directive
                       (display [nnfixnum? mincol 0]
                                [pfixnum? colinc 1]
                                [nnfixnum? minpad 0]
                                [char? pad-char #\space]
                                [implicit at?])
                       1))]
                [(#\s #\S #\w #\W)
                 (if (and (null? p*) (not colon?))
                     (directive
                       (simple-write)
                       1)
                     (directive
                       (write [nnfixnum? mincol 0]
                              [pfixnum? colinc 1]
                              [nnfixnum? minpad 0]
                              [char? pad-char #\space]
                              [implicit colon?]
                              [implicit at?])
                       1))]
                [(#\f #\F)
                 (no-colon)
                 (directive
                   (fwrite [nnfixnum? w #f]
                           [nnfixnum? d #f]
                           [fixnum? k 0]
                           [char? overflow-char #f]
                           [char? pad-char #\space]
                           [implicit at?])
                   1)]
                [(#\e #\E)
                 (no-colon)
                 (directive
                   (ewrite [nnfixnum? w #f]
                           [nnfixnum? d #f]
                           [pfixnum? e #f]
                           [fixnum? k 1]
                           [char? overflow-char #f]
                           [char? pad-char #\space]
                           [char? exponent-char #\e]
                           [implicit at?])
                   1)]
                [(#\g #\G)
                 (no-colon)
                 (directive
                   (gwrite [nnfixnum? w #f]
                           [nnfixnum? d #f]
                           [pfixnum? e #f]
                           [fixnum? k 1]            ; assumption
                           [char? overflow-char #f]
                           [char? pad-char #\space]
                           [char? exponent-char #\e]
                           [implicit at?])
                   1)]
                [(#\$)
                 (directive
                   ($write [nnfixnum? d 2]
                           [nnfixnum? n 1]
                           [nnfixnum? w 0]
                           [char? pad-char #\space]
                           [implicit colon?]
                           [implicit at?])
                   1)]
                [(#\c #\C)
                 (directive
                   (cwrite [implicit colon?] [implicit at?])
                   1)]
                [(#\b #\B) (parse-radix 2)]
                [(#\o #\O) (parse-radix 8)]
                [(#\d #\D) (parse-radix 10)]
                [(#\x #\X) (parse-radix 16)]
                [(#\r #\R)
                 (if (null? p*)
                     (directive
                       (fancy-radix [implicit colon?] [implicit at?])
                       1)
                     (directive
                       (write-radix [radix? n 10]
                                    [nnfixnum? w #f]
                                    [char? pad-char #\space]
                                    [char? comma-char #\,]
                                    [pfixnum? comma-interval 3]
                                    [implicit at?]
                                    [implicit colon?])
                       1))]
                [(#\p #\P)
                 (directive
                   (plural [implicit colon?] [implicit at?])
                   (if colon? 0 1))]
                [(#\t #\T)
                 (no-colon)
                 (set! column? #t)
                 (directive
                   (tabulate [nnfixnum? colnum 1]
                             [nnfixnum? colinc 1]
                             [implicit at?])
                   0)]
                [(#\?)
                 (no-colon)
                 (set! column? #t)
                 (directive
                   (indirect [implicit at?])
                   (if at? #f 2))]
                [(#\*)
                 (when (and colon? at?)
                   ($oops who
                     "@ and : modifiers are mutually exclusive for format directive ~~~c"
                     c))
                 (directive
                   (goto [nnfixnum? n #f] [implicit colon?] [implicit at?])
                   #f)]
                [(#\( #|)|#)
                 (parameters ()
                   (state s0
                     ([stack (cons (make-cvtcase-frame (substring cntl b (fx+ i 1)) cmd* colon? at?) stack)]
                      [cmd* '()])
                     (fx+ i 1)))]
                [(#|(|# #\))
                 (no-at)
                 (no-colon)
                 (let ([x (and (not (null? stack)) (car stack))])
                   (unless (cvtcase-frame? x) (misplaced-directive))
                   (let ([nested-cmd* (reverse cmd*)])
                     (let ([stack (cdr stack)] [cmd* (frame-cmd* x)])
                       (directive
                         (convert-case [implicit nested-cmd*]
                                       [implicit (cvtcase-frame-colon? x)]
                                       [implicit (cvtcase-frame-at? x)])
                         0))))]
                [(#\;)
                 (no-at)
                 (let ([x (and (not (null? stack)) (car stack))])
                   (cond
                     [(and (conditional/colon-frame? x)
                           (not colon?)
                           (not (conditional/colon-frame-altern x)))
                      (parameters ()
                        (conditional/colon-frame-altern-set! x (reverse cmd*)))
                      (state s0 ([cmd* '()]) (fx+ i 1))]
                     [(and (conditional-frame? x) (not (conditional-frame-default? x)))
                      (parameters ()
                        (when colon? (conditional-frame-default?-set! x #t))
                        (conditional-frame-cases-set! x
                          (cons (reverse cmd*) (conditional-frame-cases x))))
                      (state s0 ([cmd* '()]) (fx+ i 1))]
                     [(and (justify-frame? x)
                           (or (not colon?)
                               (and (not (justify-frame-initial x))
                                    (null? (justify-frame-segments x)))))
                      (if colon?
                          (parameters ([margin (nnfixnum? n 0)]
                                       [cols (nnfixnum? lw 72)])
                            (set! column? #t)
                            (justify-frame-initial-set! x (reverse cmd*))
                            (justify-frame-margin-set! x margin)
                            (justify-frame-columns-set! x cols))
                          (parameters ()
                            (justify-frame-segments-set! x
                              (cons (reverse cmd*) (justify-frame-segments x)))))
                      (state s0 ([cmd* '()]) (fx+ i 1))]
                     [else (misplaced-directive)]))]
                [(#\^)
                 (no-at)
                 (directive
                   (abort [true? n #f] [true? m #f] [implicit colon?])
                   #f)]
                [(#\{ #|}|#)
                 (when (null? cmd*) (set! column? #t))
                 (parameters ([n (nnfixnum? n #f)])
                   (state s0
                     ([stack (cons (make-iteration-frame (substring cntl b (fx+ i 1)) cmd* n colon? at?) stack)]
                      [cmd* '()])
                     (fx+ i 1)))]
                [(#|{|# #\})
                 (no-at)
                 (let ([x (and (not (null? stack)) (car stack))])
                   (unless (iteration-frame? x) (misplaced-directive))
                   (let ([nested-cmd* (reverse cmd*)])
                     (let ([stack (cdr stack)] [cmd* (frame-cmd* x)])
                       (directive
                         (iteration [implicit nested-cmd*]
                                    [implicit (iteration-frame-n x)]
                                    [implicit (iteration-frame-sublists? x)]
                                    [implicit (iteration-frame-use-remaining? x)]
                                    [implicit colon?])
                         #f))))]
                [(#\[ #|]|#)
                 (if at?
                     (if colon?
                         ($oops who "@ and : modifiers are mutually exclusive for format directive ~~~c" c)
                         (parameters ()
                           (state s0
                             ([stack (cons (make-conditional/at-frame (substring cntl b (fx+ i 1)) cmd*) stack)]
                              [cmd* '()])
                             (fx+ i 1))))
                     (if colon?
                         (parameters ()
                           (state s0
                             ([stack (cons (make-conditional/colon-frame (substring cntl b (fx+ i 1)) cmd*) stack)]
                              [cmd* '()])
                             (fx+ i 1)))
                         (parameters ([n (nnfixnum? n #f)])
                           (state s0
                             ([stack (cons (make-conditional-frame (substring cntl b (fx+ i 1)) cmd* n) stack)]
                              [cmd* '()])
                             (fx+ i 1)))))]
                [(#|[|# #\])
                 (no-at)
                 (no-colon)
                 (let ([x (and (not (null? stack)) (car stack))])
                   (let ([nested-cmd* (reverse cmd*)])
                     (cond
                       [(conditional/at-frame? x)
                        (let ([stack (cdr stack)] [cmd* (frame-cmd* x)])
                          (directive
                            (conditional/at [implicit nested-cmd*])
                            #f))]
                       [(conditional/colon-frame? x)
                        (let ([altern (conditional/colon-frame-altern x)])
                          (unless altern
                            ($oops who "no ~~; found within ~a...~~]" (frame-directive (car stack))))
                          (let ([stack (cdr stack)] [cmd* (frame-cmd* x)])
                            (directive
                              (conditional/colon [implicit altern]
                                                 [implicit nested-cmd*])
                              #f)))]
                       [(conditional-frame? x)
                        (let ([stack (cdr stack)] [cmd* (frame-cmd* x)])
                          (let ([n (conditional-frame-n x)])
                            (if (conditional-frame-default? x)
                                (directive
                                  (conditional [implicit n]
                                               [implicit (list->vector (reverse (conditional-frame-cases x)))]
                                               [implicit nested-cmd*])
                                  #f)
                                (directive
                                  (conditional [implicit n]
                                               [implicit (list->vector (reverse (cons nested-cmd* (conditional-frame-cases x))))]
                                               [implicit '()])
                                  #f))))]
                       [else (misplaced-directive)])))]
                [(#\<)
                 (parameters ([mincol (nnfixnum? mincol 0)]
                              [colinc (nnfixnum? colinc 1)]
                              [minpad (nnfixnum? minpad 0)]
                              [pc (char? pad-char #\space)])
                   (state s0
                     ([stack (cons (make-justify-frame (substring cntl b (fx+ i 1)) cmd* mincol colinc minpad pc colon? at?) stack)]
                      [cmd* '()])
                     (fx+ i 1)))]
                [(#\>)
                 (no-at)
                 (let ([x (and (not (null? stack)) (car stack))])
                   (unless (justify-frame? x) (misplaced-directive))
                   (let ([nested-cmd* (reverse cmd*)])
                     (let ([stack (cdr stack)] [cmd* (frame-cmd* x)])
                       (directive
                         (justify [implicit (justify-frame-mincol x)]
                                  [implicit (justify-frame-colinc x)]
                                  [implicit (justify-frame-minpad x)]
                                  [implicit (justify-frame-pc x)]
                                  [implicit (justify-frame-before? x)]
                                  [implicit (justify-frame-after? x)]
                                  [implicit (justify-frame-initial x)]
                                  [implicit (justify-frame-margin x)]
                                  [implicit (justify-frame-columns x)]
                                  [implicit (reverse (cons nested-cmd* (justify-frame-segments x)))])
                         0))))]
                [(#\~)
                 (no-at)
                 (no-colon)
                 (if (or (null? p*) (equal? p* '(1)))
                     (state s0 ([cmd* (cons #\~ cmd*)]) (fx+ i 1))
                     (directive (dup-char [nnfixnum? n 1] [implicit #\~]) 0))]
                [(#\|)
                 (no-at)
                 (no-colon)
                 (if (or (null? p*) (equal? p* '(1)))
                     (state s0 ([cmd* (cons #\page cmd*)]) (fx+ i 1))
                     (directive (dup-char [nnfixnum? n 1] [implicit #\page]) 0))]
                [(#\return) ; ~\r\n is treated like ~\n
                 (if (eq? (char (fx+ i 1)) #\newline)
                     (state s6 () (fx+ i 1) b p* colon? at?)
                     ($oops who "unrecognized directive ~~~:c" c))]
                [(#\newline)
                 (parameters ()
                   (when (and colon? at?)
                     ($oops who
                       "@ and : modifiers are mutually exclusive for format directive ~~~c"
                       c))
                   (cond
                     [colon? (state s0 () (fx+ i 1))]
                     [at? (state s0 ([cmd* (cons c cmd*)]) (skip-non-newline-white (fx+ i 1)))]
                     [else (state s0 () (skip-non-newline-white (fx+ i 1)))]))]
                [else ($oops who "unrecognized directive ~~~:c" c)]))))
        (state s0 ([nargs 0] [cmd* '()] [stack '()]) 0))))

  ;;; squash together adjacent strings and characters
  (define squash
    (lambda (ls)
      (define insert-string!
        (lambda (s1 i1 s2)
          (let ([n2 (string-length s2)])
            (do ([i1 i1 (fx+ i1 1)] [i2 0 (fx+ i2 1)])
                ((fx= i2 n2))
              (string-set! s1 i1 (string-ref s2 i2))))))
      (define squash0
        (lambda (ls)
          (let ([a (car ls)] [d (cdr ls)])
            (if (null? d)
                ls
                (if (string? a)
                    (let-values ([(s d) (squash1 d (string-length a))])
                      (if (string? s)
                          (begin (insert-string! s 0 a) (cons s d))
                          (cons a d)))
                    (if (char? a)
                        (let-values ([(s d) (squash1 d 1)])
                          (if (string? s)
                              (begin (string-set! s 0 a) (cons s d))
                              (cons a d)))
                        (cons a (squash0 d))))))))
      (define squash1
        (lambda (ls n)
          (if (null? ls)
              (values n ls)
              (let ([a (car ls)] [d (cdr ls)])
                (if (string? a)
                    (let-values ([(s d) (squash1 d (fx+ n (string-length a)))])
                      (let ([s (if (string? s) s (make-string s))])
                        (insert-string! s n a)
                        (values s d)))
                    (if (char? a)
                        (let-values ([(s d) (squash1 d (fx+ n 1))])
                          (let ([s (if (string? s) s (make-string s))])
                            (string-set! s n a)
                            (values s d)))
                        (values n (if (null? d) ls (cons a (squash0 d))))))))))
      (if (null? ls) '() (squash0 ls))))

  ;;; convert simple formats to expressions.  returns #f for other inputs.
  (define (make-fmt->expr build-quote build-seq build-primcall)
    (lambda (src sexpr cmd* op arg*)
      (define-syntax make-seq
        (syntax-rules ()
          [(_ ?a ?d)
           (let ([d ?d])
             (and d
                  (let ([a ?a])
                    (if (null? d) a (build-seq a d)))))]))
      (define-syntax make-call
        (syntax-rules ()
          [(_ src proc arg ...)
           (build-primcall src sexpr 'proc (list arg ...))]))
      (if (null? cmd*)
          (build-quote (void))
          (let f ([cmd* cmd*] [arg* arg*] [src src])
            (if (null? cmd*)
                '()
                (let ([cmd (car cmd*)] [cmd* (cdr cmd*)])
                  (cond
                    [(string? cmd)
                     (make-seq (make-call src display-string (build-quote cmd)  op)
                       (f cmd* arg* #f))]
                    [(char? cmd)
                     (make-seq (make-call src write-char (build-quote cmd)  op)
                       (f cmd* arg* #f))]
                    [(fmt? cmd)
                     (fmt-case cmd
                       [simple-display ()
                         (make-seq (make-call src display (car arg*) op)
                           (f cmd* (cdr arg*) #f))]
                       [simple-write ()
                         (make-seq (make-call src write (car arg*) op)
                           (f cmd* (cdr arg*) #f))]
                       [cwrite (colon? at?)
                         (and (not colon?)
                              (not at?)
                              (make-seq (make-call src write-char (car arg*) op)
                                (f cmd* (cdr arg*) #f)))]
                       [else #f])]
                    [else ($oops 'fmt->expr "internal error: ~s" cmd)])))))))

  ;;; perform formatting operation from parsed string (cmd*)
  (define dofmt
    (lambda (who fmt-op cntl cmd* arg*)
      (define flonum->digits #%$flonum->digits)
      (define flonum-sign #%$flonum-sign)
      (define (exact-integer? x) (or (fixnum? x) (bignum? x)))
      (define float-base 10) ; hardcoding base 10 for now
      (define fd->string
        (lambda (ls d n sign?)
          (define flonum-digit->char
            (lambda (n)
              (string-ref
                "#00123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                (fx+ n 2))))
          (let ([s (car ls)] [e (cadr ls)] [ls (cddr ls)])
            (let ([op (open-output-string)])
              (if (eqv? s -1)
                  (write-char #\- op)
                  (when sign? (write-char #\+ op)))
              (cond
                [(fx< e 0)
                 (when (fx> n 0) (display (make-string n #\0) op))
                 (write-char #\. op)
                 (if (and (not d) (fx= (car ls) -1)) ; some flavor of 0.0
                     (write-char #\0 op)
                     (do ([e e (fx+ e 1)] [d d (and d (fx- d 1))])
                         ((or (fx>= e -1) (and d (fx= d 0)))
                          (do ([ls ls (cdr ls)] [d d (and d (fx- d 1))])
                              ((if d (fx= d 0) (fx< (car ls) 0)))
                            (write-char (flonum-digit->char (car ls)) op)))
                       (write-char #\0 op)))]
                [(fx= (car ls) -1) ; some flavor of 0.0
                 (display (make-string (if (and (fx= n 0) (eqv? d 0)) 1 n) #\0) op)
                 (write-char #\. op)
                 (display (make-string (or d 1) #\0) op)]
                [else
                 (let ([n (fx- n e 1)])
                   (when (fx> n 0) (display (make-string n #\0) op)))
                 (write-char (flonum-digit->char (car ls)) op)
                 (do ([ls (cdr ls) (cdr ls)] [e e (fx- e 1)])
                     ((fx= e 0)
                      (write-char #\. op)
                      (if (and (not d) (fx< (car ls) 0))
                          (write-char (flonum-digit->char (car ls)) op)
                          (do ([ls ls (cdr ls)] [d d (and d (fx- d 1))])
                              ((if d (fx= d 0) (fx< (car ls) 0)))
                            (write-char (flonum-digit->char (car ls)) op))))
                   (write-char (flonum-digit->char (car ls)) op))])
              (get-output-string op)))))
      (define string-upcase!
        (lambda (s)
          (let ([n (string-length s)])
            (do ([i 0 (fx+ i 1)])
                ((fx= i n))
              (string-set! s i (char-upcase (string-ref s i)))))))
      (define string-downcase!
        (lambda (s)
          (let ([n (string-length s)])
            (do ([i 0 (fx+ i 1)])
                ((fx= i n))
              (string-set! s i (char-downcase (string-ref s i)))))))
      (define string-capitalize!
        (lambda (s)
          (let ([n (string-length s)])
            (define interword
              (lambda (i)
                (unless (fx= i n)
                  (let ([c (string-ref s i)])
                    (if (or (char-alphabetic? c) (char-numeric? c))
                        (begin
                          (string-set! s i (char-upcase c))
                          (intraword (fx+ i 1)))
                        (interword (fx+ i 1)))))))
            (define intraword
              (lambda (i)
                (unless (fx= i n)
                  (let ([c (string-ref s i)])
                    (if (or (char-alphabetic? c) (char-numeric? c))
                        (begin
                          (string-set! s i (char-downcase c))
                          (intraword (fx+ i 1)))
                        (interword (fx+ i 1)))))))
            (interword 0))))
      (define string-capitalize-first!
        (lambda (s)
          (let ([n (string-length s)])
            (unless (fx= (string-length s) 0)
              (string-set! s 0 (char-upcase (string-ref s 0)))
              (do ([i 1 (fx+ i 1)])
                  ((fx= i n))
                (string-set! s i (char-downcase (string-ref s i))))))))
      (define-syntax pad
        (syntax-rules ()
          [(_ mincol colinc minpad pad-char left? op expr)
           (if (and (fx= mincol 0) (fx= minpad 0))
               expr
               (let ([s (let ([op (open-output-string)])
                          expr
                          (get-output-string op))])
                 (unless left? (display s op))
                 (let ([n (let ([n (fxmax 0 (fx- mincol minpad (string-length s)))])
                            (fx+ minpad
                                 (fx* (fxquotient
                                        (fx+ n (fx- colinc 1))
                                        colinc)
                                      colinc)))])
                     (unless (fx= n 0)
                       (display (make-string n pad-char) op)))
                 (when left? (display s op))))]))
      (define (padnum w oc pc op s)
        (if (not w)
            (display s op)
            (let ([n (string-length s)])
              (cond
                [(fx> n w)
                 (if oc
                     (display (make-string w oc) op)
                     (display s op))]
                [else
                 (when (fx< n w) (display (make-string (fx- w n) pc) op))
                 (display s op)]))))
      (define (write-old-roman x op)
        (if (<= 1 x 4999)
            (let f ([x x] [a '(1000 . #\M)] [ls '((500 . #\D) (100 . #\C) (50 . #\L) (10 . #\X) (5 . #\V) (1 . #\I))])
              (if (>= x (car a))
                  (begin (write-char (cdr a) op) (f (- x (car a)) a ls))
                  (unless (null? ls) (f x (car ls) (cdr ls)))))
            (fprintf op "~d" x)))
      (define (write-roman x op)
        (if (<= 1 x 3999)
            (let f ([x x] [a '(1000 . "M")] [ls '((900 . "CM") (500 . "D") (400 . "CD") (100 . "C") (90 . "XC") (50 . "L") (40 . "XL") (10 . "X") (9 . "IX") (5 . "V") (4 . "IV") (1 . "I"))])
              (if (>= x (car a))
                  (begin (display (cdr a) op) (f (- x (car a)) a ls))
                  (unless (null? ls) (f x (car ls) (cdr ls)))))
            (fprintf op "~d" x)))
      (module (write-ordinal write-cardinal)
        (define (f100 x op)
          (cond
            [(>= x 100)
             (f10 (quotient x 100) op)
             (display " hundred" op)
             (let ([x (remainder x 100)])
               (unless (= x 0)
                 (display " " op)
                 (f10 x op)))]
            [else (f10 x op)]))
        (define (f10 x op)
          (cond
            [(>= x 20)
             (display (vector-ref v20 (quotient x 10)) op)
             (let ([x (remainder x 10)])
               (unless (= x 0)
                 (display "-" op)
                 (f10 x op)))]
            [else (display (vector-ref v0 x) op)]))
        (define (f1000000 x op)
          (cond
            [(>= x 1000000)
             (f100 (quotient x 1000000) op)
             (display " million" op)
             (let ([x (remainder x 1000000)])
               (unless (= x 0)
                 (display " " op)
                 (f1000 x op)))]
            [else (f1000 x op)]))
        (define (f1000 x op)
          (cond
            [(<= 1100 x 1999) (f100 x op)]
            [(>= x 1000)
             (f100 (quotient x 1000) op)
             (display " thousand" op)
             (let ([x (remainder x 1000)])
               (unless (= x 0)
                 (display " " op)
                 (f100 x op)))]
            [else (f100 x op)]))
        (define (*f1000000 x op)
          (cond
            [(>= x 1000000)
             (f100 (quotient x 1000000) op)
             (let ([x (remainder x 1000000)])
               (if (= x 0)
                   (display " millionth" op)
                   (begin
                     (display " million " op)
                     (*f1000 x op))))]
            [else (*f1000 x op)]))
        (define (*f1000 x op)
          (cond
            [(<= 1100 x 1999) (*f100 x op)]
            [(>= x 1000)
             (f100 (quotient x 1000) op)
             (let ([x (remainder x 1000)])
               (if (= x 0)
                   (display " thousandth" op)
                   (begin
                     (display " thousand " op)
                     (*f100 x op))))]
            [else (*f100 x op)]))
        (define (*f100 x op)
          (cond
            [(>= x 100)
             (f10 (quotient x 100) op)
             (let ([x (remainder x 100)])
               (if (= x 0)
                   (display " hundredth" op)
                   (begin
                     (display " hundred " op)
                     (*f10 x op))))]
            [else (*f10 x op)]))
        (define (*f10 x op)
          (cond
            [(>= x 20)
             (let ([q (quotient x 10)] [x (remainder x 10)])
               (if (= x 0)
                   (display (vector-ref *v20 q) op)
                   (begin
                     (display (vector-ref v20 q) op)
                     (display "-" op)
                     (*f10 x op))))]
            [else (display (vector-ref *v0 x) op)]))
        (define v20 '#(#f #f twenty thirty forty fifty sixty seventy eighty ninety))
        (define v0 '#(zero one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen))
        (define *v20 '#(#f #f twentieth thirtieth fortieth fiftieth sixtieth seventieth eightieth ninetieth))
        (define *v0 '#(zeroth first second third fourth fifth sixth seventh eighth ninth tenth eleventh twelveth thirteenth fourteenth fifteenth sixteenth seventeenth eighteenth nineteenth))
        (define (write-ordinal x op)
          (if (<= -999999999 x +999999999)
              (if (< x 0)
                  (begin (display "minus " op) (*f1000000 (- x) op))
                  (*f1000000 x op))
              (fprintf op "~:d~a" x
                (let ([n (remainder (abs x) 100)])
                  (if (<= 11 n 19)
                      "th"
                      (case (remainder n 10)
                        [(1) "st"]
                        [(2) "nd"]
                        [(3) "rd"]
                        [else "th"]))))))
        (define (write-cardinal x op)
          (if (<= -999999999 x +999999999)
              (if (< x 0)
                  (begin (display "minus " op) (f1000000 (- x) op))
                  (f1000000 x op))
              (fprintf op "~:d" x))))
      (define cheap-scale
        (lambda (ls k)
          `(,(car ls) ,(fx+ (cadr ls) k) ,@(cddr ls))))
      (define (do-fwrite-d op x w d k oc pc sign? ls)
        (let ([ls (cheap-scale ls k)])
          (padnum w oc pc op
            (fd->string ls d
              (if (and w (fx< (cadr ls) 0) (fx> (fx+ (if (or sign? (fx= (car ls) -1)) 3 2) d) w)) 0 1)
              sign?))))
      (define (do-fwrite op x w d k oc pc sign?)
        (cond
          [d (do-fwrite-d op x w d k oc pc sign?
               (flonum->digits x float-base 'absolute (fx- (fx+ k d))))]
          [w (padnum w oc pc op
               (let ([ls (cheap-scale (flonum->digits x float-base 'normal 0) k)])
                 (let ([s (car ls)] [e (cadr ls)])
                   (if (fx< e 0)
                       (let ([n (fx+ w e (if (or sign? (fx< s 0)) -1 0))])
                         (let f ([ds (cddr ls)] [i n])
                           (if (fx<= i 0)
                               (let ([ls (cheap-scale (flonum->digits x float-base 'absolute (fx- (fx+ k (fxmax (fx- n e 1) 1)))) k)])
                                 (if (fx= (caddr ls) -1) ; rounded to zero?
                                     (if (fx< s 0)
                                         (if (fx< w 4) "-.0" "-0.0")
                                         (if sign?
                                             (if (fx< w 4) "+.0" "+0.0")
                                             (if (fx< w 3) ".0" "0.0")))
                                     (fd->string ls #f 0 sign?)))
                               (if (fx= (cadr ds) -1) ; can't be -2 w/normal
                                   (fd->string ls #f (if (fx= i 1) 0 1) sign?)
                                   (f (cdr ds) (fx- i 1))))))
                       (let ([n (fx+ w (if (or sign? (fx< s 0)) -2 -1))])
                         (let g ([e e] [ds (cddr ls)] [i n])
                           (if (fx< i 0)
                               (if (fx< e -1)
                                   (fd->string (cheap-scale (flonum->digits x float-base 'absolute (fx- (fx+ e 2) k)) k) (and (fx= e -2) 0) 1 sign?)
                                   (fd->string (cheap-scale (flonum->digits x float-base 'absolute (fx- k)) k) 0 1 sign?))
                               (if (fx= (car ds) -1) ; can't be -2 w/normal
                                   (if (fx< e 0)
                                       (fd->string ls (and (fx= e -1) (fx= i 0) 0) 1 sign?)
                                       (if (fx< e (fx- i 1))
                                           (fd->string (cheap-scale (flonum->digits x float-base 'absolute (fx- k (fx- i e))) k) #f 1 sign?)
                                           (fd->string (cheap-scale (flonum->digits x float-base 'absolute (fx- k)) k) 0 1 sign?)))
                                   (g (fx- e 1) (cdr ds) (fx- i 1))))))))))]
          [else (padnum w oc pc op
                  (fd->string
                    (let ([ls (cheap-scale (flonum->digits x float-base 'normal 0) k)])
                      (let f ([e (cadr ls)] [ds (cddr ls)])
                        (if (fx= (car ds) -1) ; w/normal, can't be -2
                            (cheap-scale (flonum->digits x float-base 'absolute (fx- -1 k)) k)
                            (if (fx< e 0)
                                ls
                                (f (fx- e 1) (cdr ds))))))
                    d 1 sign?))]))
      (define (do-ewrite op x w d ew k oc pc ec sign?)
        (cond
          [(fl= x 0.0)
           (padnum w oc pc op
             (let ([ss (if (fx= (flonum-sign x) 1) "-" (if sign? "+" ""))]
                   [es (if ew (make-string ew #\0) "0")])
               (let ([d (and d (if (fx<= k 0) d (fx+ (fx- d k) 1)))])
                 (if (and w (fx> (fx+ (string-length ss) 4 (or d 1) (string-length es)) w))
                     (if (if d (fx= d 0) (fx> k 0))
                         (string-append ss "0." (string ec) "+" es)
                         (string-append ss "." (if d (make-string d #\0) "0") (string ec) "+" es))
                     (string-append ss "0." (if d (make-string d #\0) "0") (string ec) "+" es)))))]
          [d (let ([ls (flonum->digits x float-base 'relative (if (fx<= k 0) (fx- (fx+ d k)) (fx- -1 d)))])
               (let* ([e (fx- (cadr ls) (fx- k 1))]
                      [es (number->string (fxabs e))]
                      [esl (string-length es)])
                 (if (and w oc ew (fx> esl ew))
                     (display (make-string w oc) op)
                     (let ([ew (if ew (fxmax ew esl) esl)])
                       (padnum w oc pc op
                         (string-append
                           (fd->string
                             `(,(car ls) ,(fx- k 1) ,@(cddr ls))
                             (if (fx<= k 0) d (fx+ (fx- d k) 1))
                             (if (and w (fx> (fx+ (if (or sign? (fx= (car ls) -1)) 5 4) ew d) w)) 0 1)
                             sign?)
                           (if ec (string ec) "e")
                           (if (fx< e 0) "-" "+")
                           (make-string (fx- ew esl) #\0)
                           es))))))]
          [w (let ([sign? (or sign? (fx= (flonum-sign x) 1))])
               (let loop ([ew-guess (or ew 1)])
                 (let d ([d (fxmax (fx- w (if sign? 5 4) ew-guess)
                                 (if (fx= k 0) 0 (if (fx< k 0) (fx- 1 k) (fx- k 1))))])
                   (let ([ls (flonum->digits x float-base 'relative (if (fx<= k 0) (fx- (fx+ d k)) (fx- -1 d)))])
                     (let* ([e (fx- (cadr ls) (fx- k 1))]
                            [es (number->string (fxabs e))]
                            [esl (string-length es)])
                       (if (fx> esl ew-guess)
                           (if (and oc ew)
                               (display (make-string w oc) op)
                               (loop esl))
                           (let ([ew (if ew (fxmax ew esl) esl)])
                             (padnum w oc pc op
                               (string-append
                                 (fd->string
                                   `(,(car ls) ,(fx- k 1) ,@(cddr ls))
                                   (and (fx= (fx- k d) 1) (fx>= (fx+ (if sign? 5 4) ew d) w) 0)
                                   (if (fx> (fx+ (if sign? 5 4) ew d) w) 0 1)
                                   sign?)
                                 (if ec (string ec) "e")
                                 (if (fx< e 0) "-" "+")
                                 (make-string (fx- ew esl) #\0)
                                 es)))))))))]
          [else (display
                  (let ([ls (flonum->digits x float-base 'normal 0)])
                    (let ([e (fx- (cadr ls) (fx- k 1))])
                      (string-append
                        (fd->string `(,(car ls) ,(fx- k 1) ,@(cddr ls)) #f 1 sign?)
                        (if ec (string ec) "e")
                        (if (fx< e 0) "-" "+")
                        (let ([op (open-output-string)])
                          (padnum ew #f #\0 op (number->string (fxabs e)))
                          (get-output-string op)))))
                  op)]))
      (define invalid-parameter
        (lambda (who cmd what p)
          ($oops who
            "invalid ~s parameter ~a in directive ~s"
            what p (fmt-directive cmd))))
      (define (outer-loop cmd* arg* op cntl all-arg* super-arg* ct? succ fail)
        (define tostr
          (lambda (cmd* arg* super-arg* succ fail)
            (let ([op (open-output-string)])
              (let ([xop (if ct? (make-format-port op) op)])
                (outer-loop cmd* arg* xop cntl all-arg* super-arg* ct?
                  (lambda (arg*)
                    (when ct? (close-output-port xop))
                    (succ (get-output-string op) arg*))
                  (lambda (arg* super?)
                    (when ct? (close-output-port xop))
                    (fail (get-output-string op) arg* super?)))))))
        (define next
          (lambda (arg*)
            (when (null? arg*)
              ($oops who "too few arguments for control string ~s" cntl))
            (car arg*)))
        (let loop ([cmd* cmd*] [arg* arg*])
          (if (null? cmd*)
              (succ arg*)
              (let ([cmd (car cmd*)])
                (define-syntax vparams
                  (lambda (x)
                    (define process-param
                      (lambda (arg* t* param* body)
                        (if (null? param*)
                            body
                            (with-syntax ([body (process-param arg* (cdr t*) (cdr param*) body)] [arg* arg*] [t (car t*)])
                              (syntax-case (car param*) ()
                                [(type? p)
                                 #'(let-values ([(t arg*)
                                                 (cond
                                                   [(eq? t 'v) (let ([t (next arg*)])
                                                                 (unless (type? t) (invalid-parameter who cmd 'p t))
                                                                 (values t (cdr arg*)))]
                                                   [(eq? t 'hash) (let ([t (length arg*)])
                                                                    (unless (type? t) (invalid-parameter who cmd 'p t))
                                                                    (values t arg*))]
                                                   [else (values t arg*)])])
                                     body)])))))
                    (syntax-case x ()
                      [(_ arg* ([t param] ...) e1 e2 ...)
                       (process-param
                         #'arg*
                         #'(t ...)
                         #'(param ...)
                         #'(let () e1 e2 ...))])))
                (cond
                  [(string? cmd) (display-string cmd op) (loop (cdr cmd*) arg*)]
                  [(char? cmd) (write-char cmd op) (loop (cdr cmd*) arg*)]
                  [(fmt? cmd)
                   (fmt-case cmd
                     [simple-display ()
                      (display (next arg*) op)
                      (loop (cdr cmd*) (cdr arg*))]
                     [simple-write ()
                      (write (next arg*) op)
                      (loop (cdr cmd*) (cdr arg*))]
                     [fresh-line (n)
                      (vparams arg* ([n (nnfixnum? n)])
                        (when (fx> n 0)
                          (fresh-line op)
                          (when (fx> n 1)
                            (display (make-string (fx- n 1) #\newline) op)))
                        (loop (cdr cmd*) arg*))]
                     [display (mincol colinc minpad pad-char left?)
                      (vparams arg* ([mincol (nnfixnum? mincol)]
                                     [colinc (pfixnum? colinc)]
                                     [minpad (nnfixnum? minpad)]
                                     [pad-char (char? pad-char)])
                        (pad mincol colinc minpad pad-char left? op
                          (display (next arg*) op))
                        (loop (cdr cmd*) (cdr arg*)))]
                     [write (mincol colinc minpad pad-char nogensym? left?)
                      (vparams arg* ([mincol (nnfixnum? mincol)]
                                     [colinc (pfixnum? colinc)]
                                     [minpad (nnfixnum? minpad)]
                                     [pad-char (char? pad-char)])
                        (pad mincol colinc minpad pad-char left? op
                          (if nogensym?
                              (parameterize ([print-gensym #f])
                                (write (next arg*) op))
                              (write (next arg*) op)))
                        (loop (cdr cmd*) (cdr arg*)))]
                     [cwrite (colon? at?)
                      (let ([c (next arg*)])
                        (unless (char? c)
                          ($oops who "expected character for ~~c, received ~s" c))
                        (if colon?
                            (let ([x (char-name c)])
                              (if x
                                  (begin
                                    (write-char #\< op)
                                    (display x op)
                                    (write-char #\> op))
                                  (let ([n (char->integer c)])
                                    (if (fx< n #x20)
                                        (begin
                                          (write-char #\^ op)
                                          (write-char (integer->char (fx+ n #x40)) op))
                                        (write-char c op)))))
                            (if at?
                                (write c op)
                                (write-char c op))))
                      (loop (cdr cmd*) (cdr arg*))]
                     [fwrite (w d k oc pc sign?)
                      (vparams arg* ([w (nnfixnum? w)]
                                     [d (nnfixnum? d)]
                                     [k (fixnum? k)]
                                     [oc (char? overflow-char)]
                                     [pc (char? pad-char)])
                        (let ([x (next arg*)])
                          (unless (real? x)
                            ($oops who "expected real number for ~~f, received ~s" x))
                          (let ([x (inexact x)])
                            (if (exceptional-flonum? x)
                                (padnum w oc pc op (number->string x))
                                (do-fwrite op x w d k oc pc sign?))))
                        (loop (cdr cmd*) (cdr arg*)))]
                     [ewrite (w d ew k oc pc ec sign?)
                      (vparams arg* ([w (nnfixnum? w)]
                                     [d (nnfixnum? d)]
                                     [ew (nnfixnum? e)]
                                     [k (fixnum? k)]
                                     [oc (char? overflow-char)]
                                     [pc (char? pad-char)]
                                     [ec (char? exponent-char)])
                        (let ([x (next arg*)])
                          (unless (real? x)
                            ($oops who "expected real number for ~~e, received ~s" x))
                          (let ([x (inexact x)])
                            (if (exceptional-flonum? x)
                                (padnum w oc pc op (number->string x))
                                (if (or (not d) (fx< (fx- d) k (fx+ d 2)))
                                    (do-ewrite op x w d ew k oc pc ec sign?)
                                   ; signaling an error might be kind, but cltl2 says otherwise
                                    (if (and w oc)
                                        (display (make-string w oc) op)
                                        (let ([d (if (fx> k 0) (fx- k 1) (fx- 1 k))])
                                          (do-ewrite op x w d ew k oc pc ec sign?)))))))
                        (loop (cdr cmd*) (cdr arg*)))]
                     [gwrite (w d ew k oc pc ec sign?)
                      (vparams arg* ([w (nnfixnum? w)]
                                     [d (nnfixnum? d)]
                                     [ew (nnfixnum? e)]
                                     [k (fixnum? k)]
                                     [oc (char? overflow-char)]
                                     [pc (char? pad-char)]
                                     [ec (char? exponent-char)])
                        (let ([x (next arg*)])
                          #;(define (ilog x) (fx+ (cadr (flonum->digits x float-base 'normal 0)) 1))
                          (define (ilog x) ; 4x faster and good enough
                            (if (fl= x 0.0)
                                0
                                (fx+ (flonum->fixnum (floor (fl- (fl* (log (flabs x)) (fl/ (log 10))) 1e-10))) 1)))
                          (define significant-digits
                            (lambda (ls)
                              (if (fx< (car ls) 0)
                                  0
                                  (fx+ 1 (significant-digits (cdr ls))))))
                          (unless (real? x)
                            ($oops who "expected real number for ~~g, received ~s" x))
                          (let ([x (inexact x)])
                            (if (exceptional-flonum? x)
                                (padnum w oc pc op (number->string x))
                                (if d
                                    (let f ([n (ilog x)])  ; can x be negative here?
                                      (let ([dd (fx- d n)])
                                        (if (not (fx<= 0 dd d))
                                            (do-ewrite op x w d ew k oc pc ec sign?)
                                            (let ([ls (flonum->digits x float-base 'absolute (fx- dd))])
                                              (let ([actual-n (fx+ (cadr ls) 1)])
                                                (if (fx> actual-n n) ; e.g., .9999 came back as 1.000
                                                    (f actual-n)
                                                    (let* ([ee (if ew (fx+ ew 2) 4)]
                                                           [ww (and w (fx- w ee))])
                                                     ; scale k not used when treated as ~f
                                                      (do-fwrite-d op x ww dd 0 oc pc sign? ls)
                                                      (when w (display (make-string ee #\space) op)))))))))
                                    (let* ([ls (flonum->digits x float-base 'normal 0)]
                                           [n (fx+ (cadr ls) 1)]
                                           [est-d (max (significant-digits (cddr ls)) (min n 7))]
                                           [dd (fx- est-d n)])
                                      (if (fx<= 0 dd est-d)
                                          (let* ([ee (if ew (fx+ ew 2) 4)]
                                                 [ww (and w (fx- w ee))])
                                           ; scale k not used when treated as ~f
                                            (do-fwrite op x ww dd 0 oc pc sign?)
                                           ; suppressing trailing whitespace when (not w)
                                            (when w (display (make-string ee #\space) op)))
                                         ; cltl seems to want our estimated d here (est-d)
                                         ; but original d (#f) makes more sense
                                          (do-ewrite op x w d ew k oc pc ec sign?)))))))
                        (loop (cdr cmd*) (cdr arg*)))]
                     [$write (d n w pc sign-before-pad? sign?)
                      (vparams arg* ([d (nnfixnum? d)]
                                     [n (nnfixnum? n)]
                                     [w (nnfixnum? w)]
                                     [pc (char? pad-char)])
                        (let ([x (next arg*)])
                          (unless (real? x)
                            ($oops who "expected real number for ~~$, received ~s" x))
                          (let ([x (inexact x)])
                            (if (exceptional-flonum? x)
                                (padnum w #f pc op (number->string x))
                                (let ([ls (flonum->digits x float-base 'absolute (fx- d))])
                                  (if (and sign-before-pad? (or sign? (fx= (car ls) -1)))
                                      (begin
                                        (write-char (if (fx= (car ls) -1) #\- #\+) op)
                                        (padnum (fx- w 1) #f pc op
                                          (fd->string (cons 1 (cdr ls)) d n #f)))
                                      (padnum w #f pc op
                                        (fd->string ls d n sign?)))))))
                        (loop (cdr cmd*) (cdr arg*)))]
                     [write-radix (base w pc cc ci sign? commas?)
                      (vparams arg* ([base (radix? n)]
                                     [w (nnfixnum? w)]
                                     [pc (char? pad-char)]
                                     [cc (char? comma-char)]
                                     [ci (pfixnum? comma-interval)])
                        (let ([x (next arg*)])
                          (padnum w #f pc op
                            (cond
                              [(exact-integer? x)
                               (let* ([s (number->string x base)]
                                      [s (if (and sign? (>= x 0)) (string-append "+" s) s)])
                                 (if commas?
                                     (let* ([n (string-length s)]
                                            [sign (let ([c (string-ref s 0)])
                                                    (and (memv c '(#\+ #\-)) c))]
                                            [m (if sign (fx- n 1) n)]
                                            [nc (fxquotient (fx- m 1) ci)]
                                            [s2 (make-string (fx+ n nc))]
                                            [k (fxremainder m ci)]
                                            [k (if (fx= k 0) ci k)])
                                       (define (loop i j k)
                                         (cond
                                           [(fx= i n) s2]
                                           [(fx= k 0)
                                            (string-set! s2 j cc)
                                            (loop i (fx+ j 1) ci)]
                                           [else
                                            (string-set! s2 j (string-ref s i))
                                            (loop (fx+ i 1) (fx+ j 1) (fx- k 1))]))
                                       (cond
                                         [sign
                                          (string-set! s2 0 sign)
                                          (loop 1 1 k)]
                                         [else (loop 0 0 k)]))
                                     s))]
                              [else
                               (let ([op (open-output-string)])
                                 (parameterize ([print-radix base])
                                   (display x op))
                                 (get-output-string op))])))
                        (loop (cdr cmd*) (cdr arg*)))]
                     [plural (back-up? y/ies?)
                      (let ([arg* (if back-up?
                                      (let f ([prev #f] [ls all-arg*])
                                        (if (eq? ls arg*)
                                            (if prev
                                                prev
                                                ($oops who "no previous argument for ~a" (fmt-directive (car cmd*))))
                                            (f ls (cdr ls))))
                                      arg*)])
                        (if (eqv? (next arg*) 1)
                            (when y/ies? (write-char #\y op))
                            (if y/ies?
                                (display "ies" op)
                                (write-char #\s op)))
                        (loop (cdr cmd*) (cdr arg*)))]
                     [fancy-radix (colon? at?)
                      (let ([x (next arg*)])
                        (unless (exact-integer? x)
                          ($oops who "expected exact integer for ~~r, received ~s" x))
                        (if colon?
                            (if at?
                                (write-old-roman x op)
                                (write-ordinal x op))
                            (if at?
                                (write-roman x op)
                                (write-cardinal x op))))
                      (loop (cdr cmd*) (cdr arg*))]
                     [dup-char (n c)
                      (vparams arg* ([n (nnfixnum? n)])
                        (display (make-string n c) op)
                        (loop (cdr cmd*) arg*))]
                     [tabulate (colnum colinc relative?)
                      (vparams arg* ([colnum (nnfixnum? colnum)]
                                     [colinc (nnfixnum? colinc)])
                        (cond
                          [relative?
                           (display (make-string colnum #\space) op)
                           (unless (= colinc 0)
                             (let ([col (output-column op)])
                               (when col
                                 (let ([n (modulo col colinc)])
                                   (unless (= n 0)
                                     (display (make-string (- colinc n) #\space) op))))))]
                          [else
                           (let ([col (output-column op)])
                             (if col
                                 (if (>= col colnum)
                                     (unless (= colinc 0)
                                       (display (make-string (- colinc (modulo (- col colnum) colinc)) #\space) op))
                                     (display (make-string (- colnum col) #\space) op))
                                 (display "  " op)))])
                        (loop (cdr cmd*) arg*))]
                     [indirect (splice?)
                      (let ([xcntl (next arg*)])
                        (unless (string? xcntl)
                          ($oops who "first ~a argument ~s is not a string" (fmt-directive (car cmd*)) xcntl))
                        (let-values ([(xcmd* expected) (parse who xcntl)])
                          (if splice?
                              (outer-loop xcmd* (cdr arg*) op cntl all-arg* #f ct?
                                (lambda (arg*) (loop (cdr cmd*) arg*))
                                (lambda (arg* super?) (loop (cdr cmd*) arg*)))
                              (let* ([arg* (cdr arg*)]
                                     [xarg* (next arg*)])
                                (let ([len ($list-length xarg* who)])
                                  (when (and indirect-too-many-args-check expected)
                                    (check-nargs who expected len xcntl)))
                                (outer-loop xcmd* xarg* op xcntl xarg* #f ct?
                                  (lambda (xarg*)
                                    (when (and dynamic-too-many-args-check (not (null? xarg*)))
                                      ($oops who "too many arguments for control string ~s" xcntl))
                                    (loop (cdr cmd*) (cdr arg*)))
                                  (lambda (xarg* super?)
                                    (loop (cdr cmd*) (cdr arg*))))))))]
                     [conditional (n cases default)
                      (vparams arg* ([n (nnfixnum? n)])
                        (let-values ([(n arg*) (if n (values n arg*) (let ([n (next arg*)]) (values n (cdr arg*))))])
                          (loop
                            (append (if (and (fixnum? n) (fx<= 0 n) (fx< n (vector-length cases)))
                                        (vector-ref cases n)
                                        default)
                                    (cdr cmd*))
                            arg*)))]
                     [conditional/colon (alternative consequent)
                      (let ([arg (next arg*)])
                        (loop (append (if arg consequent alternative) (cdr cmd*))
                              (cdr arg*)))]
                     [conditional/at (consequent)
                      (if (next arg*)
                          (loop (append consequent (cdr cmd*)) arg*)
                          (loop (cdr cmd*) (cdr arg*)))]
                     [justify (mincol colinc minpad pc before? after? initial margin columns segments)
                      (vparams arg* ([mincol (nnfixnum? mincol)]
                                     [colinc (nnfixnum? colinc)]
                                     [minpad (nnfixnum? minpad)]
                                     [pc (char? pad-char)])
                        (let ()
                          (define (process-segments initial complete segments arg*)
                            (if (null? segments)
                                (finalize initial (reverse complete) arg*)
                                (tostr (car segments) arg* #f
                                  (lambda (s arg*) (process-segments initial (cons s complete) (cdr segments) arg*))
                                  (lambda (s arg* super?) (finalize initial (reverse complete) arg*)))))
                          (define (finalize initial segments arg*)
                            (let* ([chars (apply fx+ (map string-length segments))]
                                   [segments (if before?
                                                 (if after?
                                                     `("" ,@segments "")
                                                     `("" ,@segments))
                                                 (if after?
                                                     `(,@segments "")
                                                     (if (null? segments)
                                                         '("")
                                                         segments)))]
                                   [npads (fx- (length segments) 1)]
                                   [size (fx+ chars (fx* minpad npads))]
                                   [size (if (fx<= size mincol)
                                             mincol
                                             (fx+ size (fxmodulo (fx- mincol size) colinc)))])
                              (when initial 
                                (let ([oc (output-column op)])
                                  (when (and oc (fx> (fx+ oc size margin) columns))
                                    (display initial op))))
                              (cond
                                [(fx= npads 0) ; right justify single item
                                 (display (make-string (fx- size chars) pc) op)
                                 (display (car segments) op)]
                                [else
                                 (let* ([pad-amt (fx- size chars)]
                                        [pad-q (fxquotient pad-amt npads)]
                                        [pad-r (fxremainder pad-amt npads)]
                                        [pad-i (if (fx= pad-r 0) 0 (fxquotient npads pad-r))])
                                   (let f ([s (car segments)] [s* (cdr segments)] [i 1] [pad-r pad-r])
                                     (display s op)
                                     (unless (null? s*)
                                       (cond
                                         [(and (fx> pad-r 0) (fx= i 1))
                                          (display (make-string (fx+ pad-q 1) pc) op)
                                          (f (car s*) (cdr s*) pad-i (fx- pad-r 1))]
                                         [else
                                          (display (make-string pad-q pc) op)
                                          (f (car s*) (cdr s*) (fx- i 1) pad-r)]))))]))
                            (loop (cdr cmd*) arg*))
                          (if initial
                              (tostr initial arg* #f
                                (lambda (initial arg*) (process-segments initial '() segments arg*))
                                (lambda (s arg* super?) (finalize #f '() arg*)))
                              (process-segments #f '() segments arg*))))]
                     [goto (n reverse? absolute?)
                      (vparams arg* ([n (nnfixnum? n)])
                        (loop (cdr cmd*)
                              (cond
                                [absolute?
                                 (let ([n (or n 0)])
                                   (unless (fx<= n (length all-arg*))
                                     ($oops who "~a would move beyond argument list" (fmt-directive (car cmd*))))
                                   (list-tail all-arg* n))]
                                [reverse?
                                 (let ([n (or n 1)])
                                   (let ([n (fx- (length all-arg*) (length arg*) n)])
                                     (unless (fx>= n 0)
                                       ($oops who "~a would move before first argument" (fmt-directive (car cmd*))))
                                     (list-tail all-arg* n)))]
                                [else
                                 (let ([n (or n 1)])
                                   (unless (fx<= n (length arg*))
                                     ($oops who "~a would move beyond argument list" (fmt-directive (car cmd*))))
                                   (list-tail arg* n))])))]
                     [convert-case (nested-cmd* colon? at?)
                      (let ()
                        (define convert-display
                          (lambda (s)
                            (if colon?
                                (if at?
                                    (string-upcase! s)
                                    (string-capitalize! s))
                                (if at?
                                    (string-capitalize-first! s)
                                    (string-downcase! s)))
                            (display s op)))
                        (tostr nested-cmd* arg* super-arg*
                          (lambda (s arg*) (convert-display s) (loop (cdr cmd*) arg*))
                          (lambda (s arg* super?) (convert-display s) (fail arg* super?))))]
                     [iteration (body n sublists? use-remaining? at-least-once?)
                      (vparams arg* ([n (nnfixnum? n)])
                        (let-values ([(body body-cntl body-expected arg*)
                                      (if (null? body)
                                          (let ([arg (next arg*)])
                                             (let-values ([(cmd* expected) (parse who arg)])
                                               (values cmd* arg expected (cdr arg*))))
                                          (values body cntl #f arg*))])
                          (if use-remaining?
                              (if sublists?
                                  (let f ([n n] [arg* arg*] [at-least-once? at-least-once?])
                                    (if (or (and n (fx= n 0)) (and (not at-least-once?) (null? arg*)))
                                        (loop (cdr cmd*) arg*)
                                        (let-values ([(xarg* arg*) (if (null? arg*) (values '() '()) (values (car arg*) (cdr arg*)))])
                                          (let ([len ($list-length xarg* who)])
                                            (when (and indirect-too-many-args-check body-expected)
                                              (check-nargs who body-expected len body-cntl)))
                                          (outer-loop body xarg* op body-cntl xarg* arg* ct?
                                            (lambda (xarg*)
                                              (when (and dynamic-too-many-args-check (not (null? xarg*)))
                                                ($oops who "too many arguments for control string ~s" body-cntl))
                                              (f (and n (fx- n 1)) arg* #f))
                                            (lambda (xarg* super?)
                                              (if super?
                                                  (loop (cdr cmd*) arg*)
                                                  (f (and n (fx- n 1)) arg* #f)))))))
                                  (let f ([n n] [arg* arg*] [at-least-once? at-least-once?])
                                    (if (or (and n (fx= n 0)) (and (not at-least-once?) (null? arg*)))
                                        (loop (cdr cmd*) arg*)
                                        (outer-loop body arg* op body-cntl all-arg* #f ct?
                                          (lambda (arg*) (f (and n (fx- n 1)) arg* #f))
                                          (lambda (arg* super?) (f (and n (fx- n 1)) arg* #f))))))
                              (let ([all-larg* (next arg*)])
                                (unless (list? all-larg*)
                                  ($oops who "~s is not a proper list" all-larg*))
                                (if sublists?
                                    (let f ([n n] [larg* all-larg*] [at-least-once? at-least-once?])
                                      (if (or (and n (fx= n 0)) (and (not at-least-once?) (null? larg*)))
                                          (loop (cdr cmd*) (cdr arg*))
                                          (let-values ([(xarg* larg*) (if (null? larg*) (values '() '()) (values (car larg*) (cdr larg*)))])
                                            (let ([len ($list-length xarg* who)])
                                              (when (and indirect-too-many-args-check body-expected)
                                                (check-nargs who body-expected len body-cntl)))
                                            (outer-loop body xarg* op body-cntl xarg* larg* ct?
                                              (lambda (xarg*)
                                                (when (and dynamic-too-many-args-check (not (null? xarg*)))
                                                  ($oops who "too many arguments for control string ~s" body-cntl))
                                                (f (and n (fx- n 1)) larg* #f))
                                              (lambda (xarg* super?)
                                                (if super?
                                                    (loop (cdr cmd*) (cdr arg*))
                                                    (f (and n (fx- n 1)) larg* #f)))))))
                                    (let f ([n n] [larg* all-larg*] [at-least-once? at-least-once?])
                                      (if (or (and n (fx= n 0)) (and (not at-least-once?) (null? larg*)))
                                          (loop (cdr cmd*) (cdr arg*))
                                          (outer-loop body larg* op body-cntl all-larg* #f ct?
                                            (lambda (larg*) (f (and n (fx- n 1)) larg* #f))
                                            (lambda (larg* super?) (f (and n (fx- n 1)) larg* #f))))))))))]
                     [abort (n m super?)
                      (vparams arg* ([n (true? n)] [m (true? m)])
                        (if (if n
                                (if m (eqv? n m) (eqv? n 0))
                                (null? (if super? super-arg*  arg*)))
                            (fail arg* super?)
                            (loop (cdr cmd*) arg*)))]
                     [columntrack (body)
                      (let ([xop (make-format-port op)])
                        (outer-loop body arg* xop cntl arg* super-arg* #t
                          (lambda (arg*)
                            (close-output-port xop)
                            (outer-loop (cdr cmd*) arg* op cntl arg* super-arg* ct? succ fail))
                          (lambda (arg* super?)
                            (close-output-port xop)
                            (fail arg* super?))))]
                     [else ($oops who "internal error: ~s" cmd)])]
                  [else ($oops who "internal error: ~s" cmd)])))))
      (let ([op (or fmt-op (open-output-string))])
        (outer-loop cmd* arg* op cntl arg* #f #f
          (lambda (arg*)
            (when (and dynamic-too-many-args-check (not (null? arg*)))
              ($oops who "too many arguments for control string ~s" cntl))
            (void))
          (lambda (arg* super?) (void)))
        (unless fmt-op (get-output-string op)))))

  (define check-nargs
    (lambda (who expected received cntl)
      (when (and expected received)
        (unless (fx= expected received)
          (if (fx< received expected)
              ($oops who "too few arguments for control string ~s" cntl)
              ($oops who "too many arguments for control string ~s" cntl))))))

  (define format-port-name "format port")
  (define (output-column p)
    (unless (eq? (port-name p) format-port-name)
      ($oops 'format "internal error: port is not a format port"))
    ((port-handler p) 'column p))
 
  (define make-format-port
    (lambda (subop)
      (define column 0)
      (define update-column!
        (lambda (p s n)
          (let f ([i 0] [col 0] [newline? #f])
            (if (fx= i n)
                (begin
                  (set! column (if newline? col (+ column col)))
                  (set-port-bol! p newline?))
                (if (char=? (string-ref s i) #\newline)
                    (f (fx+ i 1) 0 #t)
                    (f (fx+ i 1) (fx+ col 1) newline?))))))
      (define handler
        (message-lambda
          (lambda (msg . args) ($oops 'format-port "operation ~s not handled" msg))
          [(block-write p s n)
           (flush-output-port p)
           (update-column! p s n)
           (block-write subop s n)]
          [(clear-output-port p) (set-textual-port-output-index! p 0)]
          [(close-port p)
           (flush-output-port p)
           (set-textual-port-output-size! p 0)
           (mark-port-closed! p)]
;         [(file-length p) #f]
          [(file-position p) (most-negative-fixnum)]
          [(file-position p pos) ($oops 'format-port "cannot reposition")]
          [(flush-output-port p)
           (let ([b (textual-port-output-buffer p)]
                 [i (textual-port-output-index p)])
             (unless (fx= i 0)
               (update-column! p b i)
               (block-write subop b i)))
           (set-textual-port-output-index! p 0)]
          [(port-name p) format-port-name]
          [(write-char c p)
           (let ([b (textual-port-output-buffer p)]
                 [i (textual-port-output-index p)])
             (string-set! b i c)
             (block-write subop b (fx+ i 1)))
           (set-textual-port-output-index! p 0)]
          [(column p) (flush-output-port p) column]))
      (let ([len 1024])
        (let ([p (make-output-port handler (make-string len))])
          (set-textual-port-output-size! p (fx- len 1))
          (set-port-bol! p #t)
          p))))

  (define go
    (lambda (who op cntl args)
      (let-values ([(cmd* expected) (parse who cntl)])
        (when static-too-many-args-check
          (check-nargs who expected (length args) cntl))
        (dofmt who op cntl cmd* args))))

  (set! format
    (case-lambda
      [(port/cntl cntl/arg . args)
       (cond
         [(port? port/cntl)
          (unless (and (output-port? port/cntl) (textual-port? port/cntl))
            ($oops 'format "~s is not a textual output port" port/cntl))
          (go 'format port/cntl cntl/arg args)]
         [(eq? port/cntl #t) (go 'format (current-output-port) cntl/arg args)]
         [(eq? port/cntl #f) (go 'format #f cntl/arg args)]
         [else (go 'format #f port/cntl (cons cntl/arg args))])]
      [(cntl . args) (go 'format #f cntl args)]))

  (set! $dofmt dofmt)

  (set! $make-fmt->expr make-fmt->expr)

  (set! $parse-format-string
    (lambda (who cntl received)
      (let-values ([(cmd* expected) (parse who cntl)])
        (when static-too-many-args-check
          (check-nargs who expected received cntl))
        (squash cmd*))))

  (set! printf
    (lambda (cntl . args)
      (go 'printf (current-output-port) cntl args)))

  (set! fprintf
    (lambda (op cntl . args)
      (unless (and (output-port? op) (textual-port? op))
        ($oops 'fprintf "~s is not a textual output port" op))
      (go 'fprintf op cntl args))))
