123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572 |
- ;;;; "format.scm" Common LISP text output formatter for SLIB
- ;;; Copyright (C) 2010-2013,2019 Free Software Foundation, Inc.
- ;;;
- ;;; This library is free software; you can redistribute it and/or
- ;;; modify it under the terms of the GNU Lesser General Public
- ;;; License as published by the Free Software Foundation; either
- ;;; version 3 of the License, or (at your option) any later version.
- ;;;
- ;;; This library is distributed in the hope that it will be useful,
- ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- ;;; Lesser General Public License for more details.
- ;;;
- ;;; You should have received a copy of the GNU Lesser General Public
- ;;; License along with this library; if not, write to the Free Software
- ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- ;;;
- ;;; This code was orignally in the public domain.
- ;;;
- ;;; Written 1992-1994 by Dirk Lutzebaeck (lutzeb@cs.tu-berlin.de).
- ;;;
- ;;; Authors of the version from SLIB (< 1.4) were Ken Dickey and Aubrey
- ;;; Jaffer.
- ;;;
- ;;; Assimilated into Guile May 1999.
- ;;;
- ;;; Please don't bother the original authors with bug reports, though;
- ;;; send them to bug-guile@gnu.org.
- ;;;
- (define-module (ice-9 format)
- #:autoload (ice-9 pretty-print) (pretty-print truncated-print)
- #:autoload (ice-9 i18n) (%global-locale number->locale-string)
- ;; Actually replaces the global format as soon as loaded; see the end
- ;; of this file.
- #:replace (format))
- (define format:version "3.0")
- (define (format destination format-string . format-args)
- (define port
- (begin
- (unless (string? format-string)
- (error "format: expected a string for format string" format-string))
- (cond
- ((not destination) (open-output-string))
- ((boolean? destination) (current-output-port)) ; boolean but not false
- ((output-port? destination) destination)
- (else
- (error "format: bad destination `~a'" destination)))))
- (define %output-col (or (port-column port) 0))
- (define %flush-output? #f)
- (define %case-conversion #f)
- (define %pos 0) ; curr. format string parsing position
- (define %arg-pos 0) ; curr. format argument position
- ;; format string and char output routines on port
- (define (put-string str)
- (if %case-conversion
- (display (%case-conversion str) port)
- (display str port))
- (set! %output-col
- (+ %output-col (string-length str))))
- (define (put-char ch)
- (if %case-conversion
- (display (%case-conversion (string ch))
- port)
- (write-char ch port))
- (set! %output-col
- (if (char=? ch #\newline)
- 0
- (+ %output-col 1))))
- (define (put-substring str i n)
- (put-string (substring str i n)))
- (define (put-fill-chars n ch)
- (put-string (make-string n ch)))
- ;; format's user error handler
- (define (format-error . args) ; never returns!
- (with-throw-handler #t
- (lambda ()
- (let ((port (current-error-port)))
- (unless (zero? %arg-pos)
- (set! %arg-pos (- %arg-pos 1)))
- (format port
- "~%FORMAT: error with call: (format ~a \"~a<===~a\" ~
- ~{~a ~}===>~{~a ~})~% "
- destination
- (substring format-string 0 %pos)
- (substring format-string %pos
- (string-length format-string))
- (list-head format-args %arg-pos)
- (list-tail format-args %arg-pos))
- (apply format port args)
- (newline port)
- (error "error in format")))
- (lambda (key . args)
- (display "FORMAT: INTERNAL ERROR IN FORMAT-ERROR!") (newline)
- (display " destination: ") (write destination) (newline)
- (display " format string: ") (write format-string) (newline)
- (display " format args: ") (write format-args) (newline)
- (display " error args: ") (write args) (newline))))
- (define format:parameter-characters
- '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\- #\+ #\v #\# #\'))
- (define (format:format-work format-string arglist) ; does the formatting work
- (define format-string-len (string-length format-string))
- (define arg-pos 0) ; argument position in arglist
- (define arg-len (length arglist)) ; number of arguments
- (define modifier #f) ; 'colon | 'at | 'colon-at | #f
- (define params '()) ; directive parameter list
- (define param-value-found #f) ; a directive parameter value found
- (define conditional-nest 0) ; conditional nesting level
- (define clause-pos 0) ; last cond. clause beginning char pos
- (define clause-default #f) ; conditional default clause string
- (define clauses '()) ; conditional clause string list
- (define conditional-type #f) ; reflects the conditional modifiers
- (define conditional-arg #f) ; argument to apply the conditional
- (define iteration-nest 0) ; iteration nesting level
- (define iteration-pos 0) ; iteration string beginning char pos
- (define iteration-type #f) ; reflects the iteration modifiers
- (define max-iterations #f) ; maximum number of iterations
- (define recursive-pos-save %pos)
- (define (next-char) ; gets the next char from format-string
- (let ((ch (peek-next-char)))
- (set! %pos (+ 1 %pos))
- ch))
- (define (peek-next-char)
- (when (>= %pos format-string-len)
- (format-error "illegal format string"))
- (string-ref format-string %pos))
- (define (one-positive-integer? params )
- (cond
- ((null? params) #f)
- ((and (integer? (car params))
- (>= (car params) 0)
- (= (length params) 1)) #t)
- (else
- (format-error
- "one positive integer parameter expected"))))
- (define (next-arg)
- (when (>= arg-pos arg-len)
- (set! %arg-pos (+ arg-len 1))
- (format-error "missing argument(s)"))
- (add-arg-pos 1)
- (list-ref arglist (- arg-pos 1)))
- (define (prev-arg)
- (add-arg-pos -1)
- (when (negative? arg-pos)
- (format-error "missing backward argument(s)"))
- (list-ref arglist arg-pos))
- (define (rest-args)
- (let loop ((l arglist) (k arg-pos)) ; list-tail definition
- (if (= k 0) l (loop (cdr l) (- k 1)))))
- (define (add-arg-pos n)
- (set! arg-pos (+ n arg-pos))
- (set! %arg-pos arg-pos))
- (define (anychar-dispatch) ; dispatches the format-string
- (if (>= %pos format-string-len)
- arg-pos ; used for ~? continuance
- (let ((char (next-char)))
- (cond
- ((char=? char #\~)
- (set! modifier #f)
- (set! params '())
- (set! param-value-found #f)
- (tilde-dispatch))
- (else
- (when (and (zero? conditional-nest)
- (zero? iteration-nest))
- (put-char char))
- (anychar-dispatch))))))
- (define (tilde-dispatch)
- (cond
- ((>= %pos format-string-len)
- (put-string "~") ; tilde at end of
- ; string is just
- ; output
- arg-pos) ; used for ~?
- ; continuance
- ((and (or (zero? conditional-nest)
- (memv (peek-next-char) ; find conditional
- ; directives
- (append '(#\[ #\] #\; #\: #\@ #\^)
- format:parameter-characters)))
- (or (zero? iteration-nest)
- (memv (peek-next-char) ; find iteration
- ; directives
- (append '(#\{ #\} #\: #\@ #\^)
- format:parameter-characters))))
- (case (char-upcase (next-char))
- ;; format directives
- ((#\A) ; Any -- for humans
- (set! format:read-proof
- (memq modifier '(colon colon-at)))
- (format:out-obj-padded (memq modifier '(at colon-at))
- (next-arg) #f params)
- (anychar-dispatch))
- ((#\S) ; Slashified -- for parsers
- (set! format:read-proof
- (memq modifier '(colon colon-at)))
- (format:out-obj-padded (memq modifier '(at colon-at))
- (next-arg) #t params)
- (anychar-dispatch))
- ((#\D) ; Decimal
- (format:out-num-padded modifier (next-arg) params 10)
- (anychar-dispatch))
- ((#\H) ; Localized number
- (let* ((num (next-arg))
- (locale (case modifier
- ((colon) (next-arg))
- (else %global-locale)))
- (argc (length params))
- (width (format:par params argc 0 #f "width"))
- (decimals (format:par params argc 1 #t "decimals"))
- (padchar (integer->char
- (format:par params argc 2 format:space-ch
- "padchar")))
- (str (number->locale-string num decimals
- locale)))
- (put-string (if (and width
- (< (string-length str) width))
- (string-pad str width padchar)
- str)))
- (anychar-dispatch))
- ((#\X) ; Hexadecimal
- (format:out-num-padded modifier (next-arg) params 16)
- (anychar-dispatch))
- ((#\O) ; Octal
- (format:out-num-padded modifier (next-arg) params 8)
- (anychar-dispatch))
- ((#\B) ; Binary
- (format:out-num-padded modifier (next-arg) params 2)
- (anychar-dispatch))
- ((#\R)
- (if (null? params)
- (format:out-obj-padded ; Roman, cardinal,
- ; ordinal numerals
- #f
- ((case modifier
- ((at) format:num->roman)
- ((colon-at) format:num->old-roman)
- ((colon) format:num->ordinal)
- (else format:num->cardinal))
- (next-arg))
- #f params)
- (format:out-num-padded ; any Radix
- modifier (next-arg) (cdr params) (car params)))
- (anychar-dispatch))
- ((#\F) ; Fixed-format floating-point
- (format:out-fixed modifier (next-arg) params)
- (anychar-dispatch))
- ((#\E) ; Exponential floating-point
- (format:out-expon modifier (next-arg) params)
- (anychar-dispatch))
- ((#\G) ; General floating-point
- (format:out-general modifier (next-arg) params)
- (anychar-dispatch))
- ((#\$) ; Dollars floating-point
- (format:out-dollar modifier (next-arg) params)
- (anychar-dispatch))
- ((#\I) ; Complex numbers
- (let ((z (next-arg)))
- (unless (complex? z)
- (format-error "argument not a complex number"))
- (format:out-fixed modifier (real-part z) params)
- (format:out-fixed 'at (imag-part z) params)
- (put-char #\i))
- (anychar-dispatch))
- ((#\C) ; Character
- (let ((ch (if (one-positive-integer? params)
- (integer->char (car params))
- (next-arg))))
- (unless (char? ch)
- (format-error "~~c expects a character"))
- (case modifier
- ((at)
- (put-string (object->string ch)))
- ((colon)
- (let ((c (char->integer ch)))
- (when (< c 0)
- (set! c (+ c 256))) ; compensate
- ; complement
- ; impl.
- (cond
- ((< c #x20) ; assumes that control
- ; chars are < #x20
- (put-char #\^)
- (put-char
- (integer->char (+ c #x40))))
- ((>= c #x7f)
- (put-string "#\\")
- (put-string
- (number->string c 8)))
- (else
- (put-char ch)))))
- (else (put-char ch))))
- (anychar-dispatch))
- ((#\P) ; Plural
- (when (memq modifier '(colon colon-at))
- (prev-arg))
- (let ((arg (next-arg)))
- (unless (number? arg)
- (format-error "~~p expects a number argument"))
- (if (= arg 1)
- (when (memq modifier '(at colon-at))
- (put-char #\y))
- (if (memq modifier '(at colon-at))
- (put-string "ies")
- (put-char #\s))))
- (anychar-dispatch))
- ((#\~) ; Tilde
- (if (one-positive-integer? params)
- (put-fill-chars (car params) #\~)
- (put-char #\~))
- (anychar-dispatch))
- ((#\%) ; Newline
- (if (one-positive-integer? params)
- (put-fill-chars (car params) #\newline)
- (put-char #\newline))
- (set! %output-col 0)
- (anychar-dispatch))
- ((#\&) ; Fresh line
- (if (one-positive-integer? params)
- (begin
- (when (> (car params) 0)
- (put-fill-chars (- (car params)
- (if (> %output-col 0) 0 1))
- #\newline))
- (set! %output-col 0))
- (when (> %output-col 0)
- (put-char #\newline)))
- (anychar-dispatch))
- ((#\_) ; Space character
- (if (one-positive-integer? params)
- (put-fill-chars (car params) #\space)
- (put-char #\space))
- (anychar-dispatch))
- ((#\/) ; Tabulator character
- (if (one-positive-integer? params)
- (put-fill-chars (car params) #\tab)
- (put-char #\tab))
- (anychar-dispatch))
- ((#\|) ; Page seperator
- (if (one-positive-integer? params)
- (put-fill-chars (car params) #\page)
- (put-char #\page))
- (set! %output-col 0)
- (anychar-dispatch))
- ((#\T) ; Tabulate
- (format:tabulate modifier params)
- (anychar-dispatch))
- ((#\Y) ; Structured print
- (let ((width (if (one-positive-integer? params)
- (car params)
- 79)))
- (case modifier
- ((at)
- (put-string
- (call-with-output-string
- (lambda (p)
- (truncated-print (next-arg) p
- #:width width)))))
- ((colon-at)
- (put-string
- (call-with-output-string
- (lambda (p)
- (truncated-print (next-arg) p
- #:width
- (max (- width
- %output-col)
- 1))))))
- ((colon)
- (format-error "illegal modifier in ~~?"))
- (else
- (pretty-print (next-arg) port
- #:width width)
- (set! %output-col 0))))
- (anychar-dispatch))
- ((#\? #\K) ; Indirection (is "~K" in T-Scheme)
- (cond
- ((memq modifier '(colon colon-at))
- (format-error "illegal modifier in ~~?"))
- ((eq? modifier 'at)
- (let* ((frmt (next-arg))
- (args (rest-args)))
- (add-arg-pos (format:format-work frmt args))))
- (else
- (let* ((frmt (next-arg))
- (args (next-arg)))
- (format:format-work frmt args))))
- (anychar-dispatch))
- ((#\!) ; Flush output
- (set! %flush-output? #t)
- (anychar-dispatch))
- ((#\newline) ; Continuation lines
- (when (eq? modifier 'at)
- (put-char #\newline))
- (if (< %pos format-string-len)
- (do ((ch (peek-next-char) (peek-next-char)))
- ((or (not (char-whitespace? ch))
- (= %pos (- format-string-len 1))))
- (if (eq? modifier 'colon)
- (put-char (next-char))
- (next-char))))
- (anychar-dispatch))
- ((#\*) ; Argument jumping
- (case modifier
- ((colon) ; jump backwards
- (if (one-positive-integer? params)
- (do ((i 0 (+ i 1)))
- ((= i (car params)))
- (prev-arg))
- (prev-arg)))
- ((at) ; jump absolute
- (set! arg-pos
- (if (one-positive-integer? params) (car params) 0)))
- ((colon-at)
- (format-error "illegal modifier `:@' in ~~* directive"))
- (else ; jump forward
- (if (one-positive-integer? params)
- (do ((i 0 (+ i 1)))
- ((= i (car params)))
- (next-arg))
- (next-arg))))
- (anychar-dispatch))
- ((#\() ; Case conversion begin
- (set! %case-conversion
- (case modifier
- ((at) string-capitalize-first)
- ((colon) string-capitalize)
- ((colon-at) string-upcase)
- (else string-downcase)))
- (anychar-dispatch))
- ((#\)) ; Case conversion end
- (unless %case-conversion
- (format-error "missing ~~("))
- (set! %case-conversion #f)
- (anychar-dispatch))
- ((#\[) ; Conditional begin
- (set! conditional-nest (+ conditional-nest 1))
- (cond
- ((= conditional-nest 1)
- (set! clause-pos %pos)
- (set! clause-default #f)
- (set! clauses '())
- (set! conditional-type
- (case modifier
- ((at) 'if-then)
- ((colon) 'if-else-then)
- ((colon-at) (format-error "illegal modifier in ~~["))
- (else 'num-case)))
- (set! conditional-arg
- (if (one-positive-integer? params)
- (car params)
- (next-arg)))))
- (anychar-dispatch))
- ((#\;) ; Conditional separator
- (when (zero? conditional-nest)
- (format-error "~~; not in ~~[~~] conditional"))
- (unless (null? params)
- (format-error "no parameter allowed in ~~;"))
- (when (= conditional-nest 1)
- (let ((clause-str
- (cond
- ((eq? modifier 'colon)
- (set! clause-default #t)
- (substring format-string clause-pos
- (- %pos 3)))
- ((memq modifier '(at colon-at))
- (format-error "illegal modifier in ~~;"))
- (else
- (substring format-string clause-pos
- (- %pos 2))))))
- (set! clauses (append clauses (list clause-str)))
- (set! clause-pos %pos)))
- (anychar-dispatch))
- ((#\]) ; Conditional end
- (when (zero? conditional-nest)
- (format-error "missing ~~["))
- (set! conditional-nest (- conditional-nest 1))
- (when modifier
- (format-error "no modifier allowed in ~~]"))
- (unless (null? params)
- (format-error "no parameter allowed in ~~]"))
- (cond
- ((zero? conditional-nest)
- (let ((clause-str (substring format-string clause-pos
- (- %pos 2))))
- (if clause-default
- (set! clause-default clause-str)
- (set! clauses (append clauses (list clause-str)))))
- (case conditional-type
- ((if-then)
- (when conditional-arg
- (format:format-work (car clauses)
- (list conditional-arg))))
- ((if-else-then)
- (add-arg-pos
- (format:format-work (if conditional-arg
- (cadr clauses)
- (car clauses))
- (rest-args))))
- ((num-case)
- (when (or (not (integer? conditional-arg))
- (< conditional-arg 0))
- (format-error "argument not a positive integer"))
- (unless (and (>= conditional-arg (length clauses))
- (not clause-default))
- (add-arg-pos
- (format:format-work
- (if (>= conditional-arg (length clauses))
- clause-default
- (list-ref clauses conditional-arg))
- (rest-args))))))))
- (anychar-dispatch))
- ((#\{) ; Iteration begin
- (set! iteration-nest (+ iteration-nest 1))
- (cond
- ((= iteration-nest 1)
- (set! iteration-pos %pos)
- (set! iteration-type
- (case modifier
- ((at) 'rest-args)
- ((colon) 'sublists)
- ((colon-at) 'rest-sublists)
- (else 'list)))
- (set! max-iterations
- (if (one-positive-integer? params)
- (car params)
- #f))))
- (anychar-dispatch))
- ((#\}) ; Iteration end
- (when (zero? iteration-nest) (format-error "missing ~~{"))
- (set! iteration-nest (- iteration-nest 1))
- (case modifier
- ((colon)
- (unless max-iterations (set! max-iterations 1)))
- ((colon-at at) (format-error "illegal modifier")))
- (unless (null? params)
- (format-error "no parameters allowed in ~~}"))
- (if (zero? iteration-nest)
- (let ((iteration-str
- (substring format-string iteration-pos
- (- %pos (if modifier 3 2)))))
- (when (string=? iteration-str "")
- (set! iteration-str (next-arg)))
- (case iteration-type
- ((list)
- (let ((args (next-arg))
- (args-len 0))
- (unless (list? args)
- (format-error "expected a list argument"))
- (set! args-len (length args))
- (do ((arg-pos 0 (+ arg-pos
- (format:format-work
- iteration-str
- (list-tail args arg-pos))))
- (i 0 (+ i 1)))
- ((or (>= arg-pos args-len)
- (and max-iterations
- (>= i max-iterations)))))))
- ((sublists)
- (let ((args (next-arg))
- (args-len 0))
- (unless (list? args)
- (format-error "expected a list argument"))
- (set! args-len (length args))
- (do ((arg-pos 0 (+ arg-pos 1)))
- ((or (>= arg-pos args-len)
- (and max-iterations
- (>= arg-pos max-iterations))))
- (let ((sublist (list-ref args arg-pos)))
- (unless (list? sublist)
- (format-error "expected a list of lists argument"))
- (format:format-work iteration-str sublist)))))
- ((rest-args)
- (let* ((args (rest-args))
- (args-len (length args))
- (usedup-args
- (do ((arg-pos 0 (+ arg-pos
- (format:format-work
- iteration-str
- (list-tail
- args arg-pos))))
- (i 0 (+ i 1)))
- ((or (>= arg-pos args-len)
- (and max-iterations
- (>= i max-iterations)))
- arg-pos))))
- (add-arg-pos usedup-args)))
- ((rest-sublists)
- (let* ((args (rest-args))
- (args-len (length args))
- (usedup-args
- (do ((arg-pos 0 (+ arg-pos 1)))
- ((or (>= arg-pos args-len)
- (and max-iterations
- (>= arg-pos max-iterations)))
- arg-pos)
- (let ((sublist (list-ref args arg-pos)))
- (unless (list? sublist)
- (format-error "expected list arguments"))
- (format:format-work iteration-str sublist)))))
- (add-arg-pos usedup-args)))
- (else (format-error "internal error in ~~}")))))
- (anychar-dispatch))
- ((#\^) ; Up and out
- (let* ((continue
- (cond
- ((not (null? params))
- (not
- (case (length params)
- ((1) (zero? (car params)))
- ((2) (= (list-ref params 0) (list-ref params 1)))
- ((3) (<= (list-ref params 0)
- (list-ref params 1)
- (list-ref params 2)))
- (else (format-error "too much parameters")))))
- (%case-conversion ; if conversion stop conversion
- (set! %case-conversion string-copy) #t)
- ((= iteration-nest 1) #t)
- ((= conditional-nest 1) #t)
- ((>= arg-pos arg-len)
- (set! %pos format-string-len) #f)
- (else #t))))
- (when continue
- (anychar-dispatch))))
- ;; format directive modifiers and parameters
- ((#\@) ; `@' modifier
- (when (memq modifier '(at colon-at))
- (format-error "double `@' modifier"))
- (set! modifier (if (eq? modifier 'colon) 'colon-at 'at))
- (tilde-dispatch))
- ((#\:) ; `:' modifier
- (when (memq modifier '(colon colon-at))
- (format-error "double `:' modifier"))
- (set! modifier (if (eq? modifier 'at) 'colon-at 'colon))
- (tilde-dispatch))
- ((#\') ; Character parameter
- (when modifier
- (format-error "misplaced modifier"))
- (set! params (append params (list (char->integer (next-char)))))
- (set! param-value-found #t)
- (tilde-dispatch))
- ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\- #\+) ; num. paramtr
- (when modifier
- (format-error "misplaced modifier"))
- (let ((num-str-beg (- %pos 1))
- (num-str-end %pos))
- (do ((ch (peek-next-char) (peek-next-char)))
- ((not (char-numeric? ch)))
- (next-char)
- (set! num-str-end (+ 1 num-str-end)))
- (set! params
- (append params
- (list (string->number
- (substring format-string
- num-str-beg
- num-str-end))))))
- (set! param-value-found #t)
- (tilde-dispatch))
- ((#\V) ; Variable parameter from next argum.
- (when modifier
- (format-error "misplaced modifier"))
- (set! params (append params (list (next-arg))))
- (set! param-value-found #t)
- (tilde-dispatch))
- ((#\#) ; Parameter is number of remaining args
- (when param-value-found
- (format-error "misplaced '#'"))
- (when modifier
- (format-error "misplaced modifier"))
- (set! params (append params (list (length (rest-args)))))
- (set! param-value-found #t)
- (tilde-dispatch))
- ((#\,) ; Parameter separators
- (when modifier
- (format-error "misplaced modifier"))
- (unless param-value-found
- (set! params (append params '(#f)))) ; append empty paramtr
- (set! param-value-found #f)
- (tilde-dispatch))
- ((#\Q) ; Inquiry messages
- (if (eq? modifier 'colon)
- (put-string format:version)
- (let ((nl (string #\newline)))
- (put-string
- (string-append
- "SLIB Common LISP format version " format:version nl
- " (C) copyright 1992-1994 by Dirk Lutzebaeck" nl
- " please send bug reports to `lutzeb@cs.tu-berlin.de'"
- nl))))
- (anychar-dispatch))
- (else ; Unknown tilde directive
- (format-error "unknown control character `~c'"
- (string-ref format-string (- %pos 1))))))
- (else (anychar-dispatch)))) ; in case of conditional
- (set! %pos 0)
- (set! %arg-pos 0)
- (anychar-dispatch) ; start the formatting
- (set! %pos recursive-pos-save)
- arg-pos) ; return the position in the arg. list
- ;; when format:read-proof is true, format:obj->str will wrap
- ;; result strings starting with "#<" in an extra pair of double
- ;; quotes.
- (define format:read-proof #f)
- ;; format:obj->str returns a R4RS representation as a string of
- ;; an arbitrary scheme object.
- (define (format:obj->str obj slashify)
- (let ((res (if slashify
- (object->string obj)
- (call-with-output-string (lambda (p) (display obj p))))))
- (if (and format:read-proof (string-prefix? "#<" res))
- (object->string res)
- res)))
- (define format:space-ch (char->integer #\space))
- (define format:zero-ch (char->integer #\0))
- (define (format:par pars length index default name)
- (if (> length index)
- (let ((par (list-ref pars index)))
- (if par
- (if name
- (if (< par 0)
- (format-error
- "~s parameter must be a positive integer" name)
- par)
- par)
- default))
- default))
- (define (format:out-obj-padded pad-left obj slashify pars)
- (if (null? pars)
- (put-string (format:obj->str obj slashify))
- (let ((l (length pars)))
- (let ((mincol (format:par pars l 0 0 "mincol"))
- (colinc (format:par pars l 1 1 "colinc"))
- (minpad (format:par pars l 2 0 "minpad"))
- (padchar (integer->char
- (format:par pars l 3 format:space-ch #f)))
- (objstr (format:obj->str obj slashify)))
- (unless pad-left
- (put-string objstr))
- (do ((objstr-len (string-length objstr))
- (i minpad (+ i colinc)))
- ((>= (+ objstr-len i) mincol)
- (put-fill-chars i padchar)))
- (when pad-left
- (put-string objstr))))))
- (define (format:out-num-padded modifier number pars radix)
- (unless (integer? number)
- (format-error "argument not an integer"))
- (let ((numstr (number->string number radix)))
- (if (and (null? pars) (not modifier))
- (put-string numstr)
- (let ((l (length pars))
- (numstr-len (string-length numstr)))
- (let ((mincol (format:par pars l 0 #f "mincol"))
- (padchar (integer->char
- (format:par pars l 1 format:space-ch #f)))
- (commachar (integer->char
- (format:par pars l 2 (char->integer #\,) #f)))
- (commawidth (format:par pars l 3 3 "commawidth")))
- (when mincol
- (let ((numlen numstr-len)) ; calc. the output len of number
- (when (and (memq modifier '(at colon-at)) (>= number 0))
- (set! numlen (+ numlen 1)))
- (when (memq modifier '(colon colon-at))
- (set! numlen (+ (quotient (- numstr-len
- (if (< number 0) 2 1))
- commawidth)
- numlen)))
- (when (> mincol numlen)
- (put-fill-chars (- mincol numlen) padchar))))
- (when (and (memq modifier '(at colon-at))
- (>= number 0))
- (put-char #\+))
- (if (memq modifier '(colon colon-at)) ; insert comma character
- (let ((start (remainder numstr-len commawidth))
- (ns (if (< number 0) 1 0)))
- (put-substring numstr 0 start)
- (do ((i start (+ i commawidth)))
- ((>= i numstr-len))
- (when (> i ns)
- (put-char commachar))
- (put-substring numstr i (+ i commawidth))))
- (put-string numstr)))))))
- (define (format:tabulate modifier pars)
- (let ((l (length pars)))
- (let ((colnum (format:par pars l 0 1 "colnum"))
- (colinc (format:par pars l 1 1 "colinc"))
- (padch (integer->char (format:par pars l 2 format:space-ch #f))))
- (case modifier
- ((colon colon-at)
- (format-error "unsupported modifier for ~~t"))
- ((at) ; relative tabulation
- (put-fill-chars
- (if (= colinc 0)
- colnum ; colnum = colrel
- (do ((c 0 (+ c colinc))
- (col (+ %output-col colnum)))
- ((>= c col)
- (- c %output-col))))
- padch))
- (else ; absolute tabulation
- (put-fill-chars
- (cond
- ((< %output-col colnum)
- (- colnum %output-col))
- ((= colinc 0)
- 0)
- (else
- (do ((c colnum (+ c colinc)))
- ((>= c %output-col)
- (- c %output-col)))))
- padch))))))
- ;; roman numerals (from dorai@cs.rice.edu).
- (define format:roman-alist
- '((1000 #\M) (500 #\D) (100 #\C) (50 #\L)
- (10 #\X) (5 #\V) (1 #\I)))
- (define format:roman-boundary-values
- '(100 100 10 10 1 1 #f))
- (define (format:num->old-roman n)
- (if (and (integer? n) (>= n 1))
- (let loop ((n n)
- (romans format:roman-alist)
- (s '()))
- (if (null? romans)
- (list->string (reverse s))
- (let ((roman-val (caar romans))
- (roman-dgt (cadar romans)))
- (do ((q (quotient n roman-val) (- q 1))
- (s s (cons roman-dgt s)))
- ((= q 0)
- (loop (remainder n roman-val)
- (cdr romans) s))))))
- (format-error "only positive integers can be romanized")))
- (define (format:num->roman n)
- (unless (and (integer? n) (> n 0))
- (format-error "only positive integers can be romanized"))
- (let loop ((n n)
- (romans format:roman-alist)
- (boundaries format:roman-boundary-values)
- (s '()))
- (if (null? romans)
- (list->string (reverse s))
- (let ((roman-val (caar romans))
- (roman-dgt (cadar romans))
- (bdry (car boundaries)))
- (let loop2 ((q (quotient n roman-val))
- (r (remainder n roman-val))
- (s s))
- (if (= q 0)
- (if (and bdry (>= r (- roman-val bdry)))
- (loop (remainder r bdry) (cdr romans)
- (cdr boundaries)
- (cons roman-dgt
- (append
- (cdr (assv bdry romans))
- s)))
- (loop r (cdr romans) (cdr boundaries) s))
- (loop2 (- q 1) r (cons roman-dgt s))))))))
- ;; cardinals & ordinals (from dorai@cs.rice.edu)
- (define format:cardinal-ones-list
- '(#f "one" "two" "three" "four" "five"
- "six" "seven" "eight" "nine" "ten" "eleven" "twelve" "thirteen"
- "fourteen" "fifteen" "sixteen" "seventeen" "eighteen"
- "nineteen"))
- (define format:cardinal-tens-list
- '(#f #f "twenty" "thirty" "forty" "fifty" "sixty" "seventy" "eighty"
- "ninety"))
- (define (format:num->cardinal999 n)
- ;; this procedure is inspired by the Bruno Haible's CLisp
- ;; function format-small-cardinal, which converts numbers
- ;; in the range 1 to 999, and is used for converting each
- ;; thousand-block in a larger number
- (let* ((hundreds (quotient n 100))
- (tens+ones (remainder n 100))
- (tens (quotient tens+ones 10))
- (ones (remainder tens+ones 10)))
- (append
- (if (> hundreds 0)
- (append
- (string->list
- (list-ref format:cardinal-ones-list hundreds))
- (string->list" hundred")
- (if (> tens+ones 0) '(#\space) '()))
- '())
- (if (< tens+ones 20)
- (if (> tens+ones 0)
- (string->list
- (list-ref format:cardinal-ones-list tens+ones))
- '())
- (append
- (string->list
- (list-ref format:cardinal-tens-list tens))
- (if (> ones 0)
- (cons #\-
- (string->list
- (list-ref format:cardinal-ones-list ones)))
- '()))))))
- (define format:cardinal-thousand-block-list
- '("" " thousand" " million" " billion" " trillion" " quadrillion"
- " quintillion" " sextillion" " septillion" " octillion" " nonillion"
- " decillion" " undecillion" " duodecillion" " tredecillion"
- " quattuordecillion" " quindecillion" " sexdecillion" " septendecillion"
- " octodecillion" " novemdecillion" " vigintillion"))
- (define (format:num->cardinal n)
- (cond ((not (integer? n))
- (format-error
- "only integers can be converted to English cardinals"))
- ((= n 0) "zero")
- ((< n 0) (string-append "minus " (format:num->cardinal (- n))))
- (else
- (let ((power3-word-limit
- (length format:cardinal-thousand-block-list)))
- (let loop ((n n)
- (power3 0)
- (s '()))
- (if (= n 0)
- (list->string s)
- (let ((n-before-block (quotient n 1000))
- (n-after-block (remainder n 1000)))
- (loop n-before-block
- (+ power3 1)
- (if (> n-after-block 0)
- (append
- (if (> n-before-block 0)
- (string->list ", ")
- '())
- (format:num->cardinal999 n-after-block)
- (if (< power3 power3-word-limit)
- (string->list
- (list-ref
- format:cardinal-thousand-block-list
- power3))
- (append
- (string->list " times ten to the ")
- (string->list
- (format:num->ordinal
- (* power3 3)))
- (string->list " power")))
- s)
- s)))))))))
- (define format:ordinal-ones-list
- '(#f "first" "second" "third" "fourth" "fifth"
- "sixth" "seventh" "eighth" "ninth" "tenth" "eleventh" "twelfth"
- "thirteenth" "fourteenth" "fifteenth" "sixteenth" "seventeenth"
- "eighteenth" "nineteenth"))
- (define format:ordinal-tens-list
- '(#f #f "twentieth" "thirtieth" "fortieth" "fiftieth" "sixtieth"
- "seventieth" "eightieth" "ninetieth"))
- (define (format:num->ordinal n)
- (cond ((not (integer? n))
- (format-error
- "only integers can be converted to English ordinals"))
- ((= n 0) "zeroth")
- ((< n 0) (string-append "minus " (format:num->ordinal (- n))))
- (else
- (let ((hundreds (quotient n 100))
- (tens+ones (remainder n 100)))
- (string-append
- (if (> hundreds 0)
- (string-append
- (format:num->cardinal (* hundreds 100))
- (if (= tens+ones 0) "th" " "))
- "")
- (if (= tens+ones 0)
- ""
- (if (< tens+ones 20)
- (list-ref format:ordinal-ones-list tens+ones)
- (let ((tens (quotient tens+ones 10))
- (ones (remainder tens+ones 10)))
- (if (= ones 0)
- (list-ref format:ordinal-tens-list tens)
- (string-append
- (list-ref format:cardinal-tens-list tens)
- "-"
- (list-ref format:ordinal-ones-list ones))))
- )))))))
- ;; format inf and nan.
- (define (format:out-inf-nan number width digits edigits overch padch)
- ;; inf and nan are always printed exactly as "+inf.0", "-inf.0" or
- ;; "+nan.0", suitably justified in their field. We insist on
- ;; printing this exact form so that the numbers can be read back in.
- (let* ((str (number->string number))
- (len (string-length str))
- (dot (string-index str #\.))
- (digits (+ (or digits 0)
- (if edigits (+ edigits 2) 0))))
- (if (and width overch (< width len))
- (put-fill-chars width (integer->char overch))
- (let* ((leftpad (if width
- (max (- width (max len (+ dot 1 digits))) 0)
- 0))
- (rightpad (if width
- (max (- width leftpad len) 0)
- 0))
- (padch (integer->char (or padch format:space-ch))))
- (put-fill-chars leftpad padch)
- (put-string str)
- (put-fill-chars rightpad padch)))))
- ;; format fixed flonums (~F)
- (define (format:out-fixed modifier number pars)
- (unless (or (number? number) (string? number))
- (format-error "argument is not a number or a number string"))
- (let ((l (length pars)))
- (let ((width (format:par pars l 0 #f "width"))
- (digits (format:par pars l 1 #f "digits"))
- (scale (format:par pars l 2 0 #f))
- (overch (format:par pars l 3 #f #f))
- (padch (format:par pars l 4 format:space-ch #f)))
- (cond
- ((and (number? number)
- (or (inf? number) (nan? number)))
- (format:out-inf-nan number width digits #f overch padch))
- (digits
- (format:parse-float number #t scale)
- (if (<= (- format:fn-len format:fn-dot) digits)
- (format:fn-zfill #f (- digits (- format:fn-len format:fn-dot)))
- (format:fn-round digits))
- (if width
- (let ((numlen (+ format:fn-len 1)))
- (when (or (not format:fn-pos?) (eq? modifier 'at))
- (set! numlen (+ numlen 1)))
- (when (and (= format:fn-dot 0) (> width (+ digits 1)))
- (set! numlen (+ numlen 1)))
- (when (< numlen width)
- (put-fill-chars (- width numlen) (integer->char padch)))
- (if (and overch (> numlen width))
- (put-fill-chars width (integer->char overch))
- (format:fn-out modifier (> width (+ digits 1)))))
- (format:fn-out modifier #t)))
- (else
- (format:parse-float number #t scale)
- (format:fn-strip)
- (if width
- (let ((numlen (+ format:fn-len 1)))
- (when (or (not format:fn-pos?) (eq? modifier 'at))
- (set! numlen (+ numlen 1)))
- (when (= format:fn-dot 0)
- (set! numlen (+ numlen 1)))
- (when (< numlen width)
- (put-fill-chars (- width numlen) (integer->char padch)))
- (if (> numlen width) ; adjust precision if possible
- (let ((dot-index (- numlen
- (- format:fn-len format:fn-dot))))
- (if (> dot-index width)
- (if overch ; numstr too big for required width
- (put-fill-chars width (integer->char overch))
- (format:fn-out modifier #t))
- (begin
- (format:fn-round (- width dot-index))
- (format:fn-out modifier #t))))
- (format:fn-out modifier #t)))
- (format:fn-out modifier #t)))))))
- ;; format exponential flonums (~E)
- (define (format:out-expon modifier number pars)
- (unless (or (number? number) (string? number))
- (format-error "argument is not a number"))
- (let ((l (length pars)))
- (let ((width (format:par pars l 0 #f "width"))
- (digits (format:par pars l 1 #f "digits"))
- (edigits (format:par pars l 2 #f "exponent digits"))
- (scale (format:par pars l 3 1 #f))
- (overch (format:par pars l 4 #f #f))
- (padch (format:par pars l 5 format:space-ch #f))
- (expch (format:par pars l 6 #f #f)))
- (cond
- ((and (number? number)
- (or (inf? number) (nan? number)))
- (format:out-inf-nan number width digits edigits overch padch))
- (digits ; fixed precision
- (let ((digits (if (> scale 0)
- (if (< scale (+ digits 2))
- (+ (- digits scale) 1)
- 0)
- digits)))
- (format:parse-float number #f scale)
- (if (<= (- format:fn-len format:fn-dot) digits)
- (format:fn-zfill #f (- digits (- format:fn-len format:fn-dot)))
- (format:fn-round digits))
- (if width
- (if (and edigits overch (> format:en-len edigits))
- (put-fill-chars width (integer->char overch))
- (let ((numlen (+ format:fn-len 3))) ; .E+
- (when (or (not format:fn-pos?) (eq? modifier 'at))
- (set! numlen (+ numlen 1)))
- (when (and (= format:fn-dot 0) (> width (+ digits 1)))
- (set! numlen (+ numlen 1)))
- (set! numlen
- (+ numlen
- (if (and edigits (>= edigits format:en-len))
- edigits
- format:en-len)))
- (when (< numlen width)
- (put-fill-chars (- width numlen)
- (integer->char padch)))
- (if (and overch (> numlen width))
- (put-fill-chars width (integer->char overch))
- (begin
- (format:fn-out modifier (> width (- numlen 1)))
- (format:en-out edigits expch)))))
- (begin
- (format:fn-out modifier #t)
- (format:en-out edigits expch)))))
- (else
- (format:parse-float number #f scale)
- (format:fn-strip)
- (if width
- (if (and edigits overch (> format:en-len edigits))
- (put-fill-chars width (integer->char overch))
- (let ((numlen (+ format:fn-len 3))) ; .E+
- (when (or (not format:fn-pos?) (eq? modifier 'at))
- (set! numlen (+ numlen 1)))
- (when (= format:fn-dot 0)
- (set! numlen (+ numlen 1)))
- (set! numlen
- (+ numlen
- (if (and edigits (>= edigits format:en-len))
- edigits
- format:en-len)))
- (when (< numlen width)
- (put-fill-chars (- width numlen)
- (integer->char padch)))
- (if (> numlen width) ; adjust precision if possible
- (let ((f (- format:fn-len format:fn-dot))) ; fract len
- (if (> (- numlen f) width)
- (if overch ; numstr too big for required width
- (put-fill-chars width
- (integer->char overch))
- (begin
- (format:fn-out modifier #t)
- (format:en-out edigits expch)))
- (begin
- (format:fn-round (+ (- f numlen) width))
- (format:fn-out modifier #t)
- (format:en-out edigits expch))))
- (begin
- (format:fn-out modifier #t)
- (format:en-out edigits expch)))))
- (begin
- (format:fn-out modifier #t)
- (format:en-out edigits expch))))))))
- ;; format general flonums (~G)
- (define (format:out-general modifier number pars)
- (unless (or (number? number) (string? number))
- (format-error "argument is not a number or a number string"))
- (let ((l (length pars)))
- (let ((width (if (> l 0) (list-ref pars 0) #f))
- (digits (if (> l 1) (list-ref pars 1) #f))
- (edigits (if (> l 2) (list-ref pars 2) #f))
- (overch (if (> l 4) (list-ref pars 4) #f))
- (padch (if (> l 5) (list-ref pars 5) #f)))
- (cond
- ((and (number? number)
- (or (inf? number) (nan? number)))
- ;; FIXME: this isn't right.
- (format:out-inf-nan number width digits edigits overch padch))
- (else
- (format:parse-float number #t 0)
- (format:fn-strip)
- (let* ((ee (if edigits (+ edigits 2) 4)) ; for the following algorithm
- (ww (if width (- width ee) #f)) ; see Steele's CL book p.395
- (n (if (= format:fn-dot 0) ; number less than (abs 1.0) ?
- (- (format:fn-zlead))
- format:fn-dot))
- (d (if digits
- digits
- (max format:fn-len (min n 7)))) ; q = format:fn-len
- (dd (- d n)))
- (if (<= 0 dd d)
- (begin
- (format:out-fixed modifier number (list ww dd #f overch padch))
- (put-fill-chars ee #\space)) ;~@T not implemented yet
- (format:out-expon modifier number pars))))))))
- ;; format dollar flonums (~$)
- (define (format:out-dollar modifier number pars)
- (unless (or (number? number) (string? number))
- (format-error "argument is not a number or a number string"))
- (let ((l (length pars)))
- (let ((digits (format:par pars l 0 2 "digits"))
- (mindig (format:par pars l 1 1 "mindig"))
- (width (format:par pars l 2 0 "width"))
- (padch (format:par pars l 3 format:space-ch #f)))
- (cond
- ((and (number? number)
- (or (inf? number) (nan? number)))
- (format:out-inf-nan number width digits #f #f padch))
- (else
- (format:parse-float number #t 0)
- (if (<= (- format:fn-len format:fn-dot) digits)
- (format:fn-zfill #f (- digits (- format:fn-len format:fn-dot)))
- (format:fn-round digits))
- (let ((numlen (+ format:fn-len 1)))
- (when (or (not format:fn-pos?) (memq modifier '(at colon-at)))
- (set! numlen (+ numlen 1)))
- (when (and mindig (> mindig format:fn-dot))
- (set! numlen (+ numlen (- mindig format:fn-dot))))
- (when (and (= format:fn-dot 0) (not mindig))
- (set! numlen (+ numlen 1)))
- (if (< numlen width)
- (case modifier
- ((colon)
- (unless format:fn-pos?
- (put-char #\-))
- (put-fill-chars (- width numlen) (integer->char padch)))
- ((at)
- (put-fill-chars (- width numlen) (integer->char padch))
- (put-char (if format:fn-pos? #\+ #\-)))
- ((colon-at)
- (put-char (if format:fn-pos? #\+ #\-))
- (put-fill-chars (- width numlen) (integer->char padch)))
- (else
- (put-fill-chars (- width numlen) (integer->char padch))
- (unless format:fn-pos?
- (put-char #\-))))
- (if format:fn-pos?
- (when (memq modifier '(at colon-at))
- (put-char #\+))
- (put-char #\-))))
- (when (and mindig (> mindig format:fn-dot))
- (put-fill-chars (- mindig format:fn-dot) #\0))
- (when (and (= format:fn-dot 0) (not mindig))
- (put-char #\0))
- (put-substring format:fn-str 0 format:fn-dot)
- (put-char #\.)
- (put-substring format:fn-str format:fn-dot format:fn-len))))))
- ; the flonum buffers
- (define format:fn-max 400) ; max. number of number digits
- (define format:fn-str (make-string format:fn-max)) ; number buffer
- (define format:fn-len 0) ; digit length of number
- (define format:fn-dot #f) ; dot position of number
- (define format:fn-pos? #t) ; number positive?
- (define format:en-max 10) ; max. number of exponent digits
- (define format:en-str (make-string format:en-max)) ; exponent buffer
- (define format:en-len 0) ; digit length of exponent
- (define format:en-pos? #t) ; exponent positive?
- (define (format:parse-float num fixed? scale)
- (let ((num-str (if (string? num)
- num
- (number->string (exact->inexact num)))))
- (set! format:fn-pos? #t)
- (set! format:fn-len 0)
- (set! format:fn-dot #f)
- (set! format:en-pos? #t)
- (set! format:en-len 0)
- (do ((i 0 (+ i 1))
- (left-zeros 0)
- (mantissa? #t)
- (all-zeros? #t)
- (num-len (string-length num-str))
- (c #f)) ; current exam. character in num-str
- ((= i num-len)
- (unless format:fn-dot
- (set! format:fn-dot format:fn-len))
- (when all-zeros?
- (set! left-zeros 0)
- (set! format:fn-dot 0)
- (set! format:fn-len 1))
- ;; now format the parsed values according to format's need
- (if fixed?
- (begin ; fixed format m.nnn or .nnn
- (when (and (> left-zeros 0) (> format:fn-dot 0))
- (if (> format:fn-dot left-zeros)
- (begin ; norm 0{0}nn.mm to nn.mm
- (format:fn-shiftleft left-zeros)
- (set! format:fn-dot (- format:fn-dot left-zeros))
- (set! left-zeros 0))
- (begin ; normalize 0{0}.nnn to .nnn
- (format:fn-shiftleft format:fn-dot)
- (set! left-zeros (- left-zeros format:fn-dot))
- (set! format:fn-dot 0))))
- (when (or (not (= scale 0)) (> format:en-len 0))
- (let ((shift (+ scale (format:en-int))))
- (cond
- (all-zeros? #t)
- ((> (+ format:fn-dot shift) format:fn-len)
- (format:fn-zfill
- #f (- shift (- format:fn-len format:fn-dot)))
- (set! format:fn-dot format:fn-len))
- ((< (+ format:fn-dot shift) 0)
- (format:fn-zfill #t (- (- shift) format:fn-dot))
- (set! format:fn-dot 0))
- (else
- (if (> left-zeros 0)
- (if (<= left-zeros shift) ; shift always > 0 here
- (begin
- (format:fn-shiftleft left-zeros)
- (set! format:fn-dot (- shift left-zeros)))
- (format:fn-shiftleft shift)) ; shift out 0s
- (set! format:fn-dot (+ format:fn-dot shift))))))))
- (let ((negexp ; expon format m.nnnEee
- (if (> left-zeros 0)
- (- left-zeros format:fn-dot -1)
- (if (= format:fn-dot 0) 1 0))))
- (if (> left-zeros 0)
- (begin ; normalize 0{0}.nnn to n.nn
- (format:fn-shiftleft left-zeros)
- (set! format:fn-dot 1))
- (when (= format:fn-dot 0)
- (set! format:fn-dot 1)))
- (format:en-set (- (+ (- format:fn-dot scale) (format:en-int))
- negexp))
- (cond
- (all-zeros?
- (format:en-set 0)
- (set! format:fn-dot 1))
- ((< scale 0) ; leading zero
- (format:fn-zfill #t (- scale))
- (set! format:fn-dot 0))
- ((> scale format:fn-dot)
- (format:fn-zfill #f (- scale format:fn-dot))
- (set! format:fn-dot scale))
- (else
- (set! format:fn-dot scale)))))
- #t)
- ;; do body
- (set! c (string-ref num-str i)) ; parse the output of number->string
- (cond ; which can be any valid number
- ((char-numeric? c) ; representation of R4RS except
- (if mantissa? ; complex numbers
- (begin
- (if (char=? c #\0)
- (when all-zeros?
- (set! left-zeros (+ left-zeros 1)))
- (begin
- (set! all-zeros? #f)))
- (string-set! format:fn-str format:fn-len c)
- (set! format:fn-len (+ format:fn-len 1)))
- (begin
- (string-set! format:en-str format:en-len c)
- (set! format:en-len (+ format:en-len 1)))))
- ((or (char=? c #\-) (char=? c #\+))
- (if mantissa?
- (set! format:fn-pos? (char=? c #\+))
- (set! format:en-pos? (char=? c #\+))))
- ((char=? c #\.)
- (set! format:fn-dot format:fn-len))
- ((char=? c #\e)
- (set! mantissa? #f))
- ((char=? c #\E)
- (set! mantissa? #f))
- ((char-whitespace? c) #t)
- ((char=? c #\d) #t) ; decimal radix prefix
- ((char=? c #\#) #t)
- (else
- (format-error "illegal character `~c' in number->string" c))))))
- (define (format:en-int) ; convert exponent string to integer
- (if (= format:en-len 0)
- 0
- (do ((i 0 (+ i 1))
- (n 0))
- ((= i format:en-len)
- (if format:en-pos?
- n
- (- n)))
- (set! n (+ (* n 10) (- (char->integer (string-ref format:en-str i))
- format:zero-ch))))))
- (define (format:en-set en) ; set exponent string number
- (set! format:en-len 0)
- (set! format:en-pos? (>= en 0))
- (let ((en-str (number->string en)))
- (do ((i 0 (+ i 1))
- (en-len (string-length en-str))
- (c #f))
- ((= i en-len))
- (set! c (string-ref en-str i))
- (when (char-numeric? c)
- (string-set! format:en-str format:en-len c)
- (set! format:en-len (+ format:en-len 1))))))
- (define (format:fn-zfill left? n) ; fill current number string with 0s
- (when (> (+ n format:fn-len) format:fn-max) ; from the left or right
- (format-error "number is too long to format (enlarge format:fn-max)"))
- (set! format:fn-len (+ format:fn-len n))
- (if left?
- (do ((i format:fn-len (- i 1))) ; fill n 0s to left
- ((< i 0))
- (string-set! format:fn-str i
- (if (< i n)
- #\0
- (string-ref format:fn-str (- i n)))))
- (do ((i (- format:fn-len n) (+ i 1))) ; fill n 0s to the right
- ((= i format:fn-len))
- (string-set! format:fn-str i #\0))))
- (define (format:fn-shiftleft n) ; shift left current number n positions
- (when (> n format:fn-len)
- (format-error "internal error in format:fn-shiftleft (~d,~d)"
- n format:fn-len))
- (do ((i n (+ i 1)))
- ((= i format:fn-len)
- (set! format:fn-len (- format:fn-len n)))
- (string-set! format:fn-str (- i n) (string-ref format:fn-str i))))
- (define (format:fn-round digits) ; round format:fn-str
- (set! digits (+ digits format:fn-dot))
- (do ((i digits (- i 1)) ; "099",2 -> "10"
- (c 5)) ; "023",2 -> "02"
- ((or (= c 0) (< i 0)) ; "999",2 -> "100"
- (if (= c 1) ; "005",2 -> "01"
- (begin ; carry overflow
- (set! format:fn-len digits)
- (format:fn-zfill #t 1) ; add a 1 before fn-str
- (string-set! format:fn-str 0 #\1)
- (set! format:fn-dot (+ format:fn-dot 1)))
- (set! format:fn-len digits)))
- (set! c (+ (- (char->integer (string-ref format:fn-str i))
- format:zero-ch) c))
- (string-set! format:fn-str i (integer->char
- (if (< c 10)
- (+ c format:zero-ch)
- (+ (- c 10) format:zero-ch))))
- (set! c (if (< c 10) 0 1))))
- (define (format:fn-out modifier add-leading-zero?)
- (if format:fn-pos?
- (when (eq? modifier 'at)
- (put-char #\+))
- (put-char #\-))
- (if (= format:fn-dot 0)
- (when add-leading-zero?
- (put-char #\0))
- (put-substring format:fn-str 0 format:fn-dot))
- (put-char #\.)
- (put-substring format:fn-str format:fn-dot format:fn-len))
- (define (format:en-out edigits expch)
- (put-char (if expch (integer->char expch) #\E))
- (put-char (if format:en-pos? #\+ #\-))
- (when (and edigits (< format:en-len edigits))
- (put-fill-chars (- edigits format:en-len) #\0))
- (put-substring format:en-str 0 format:en-len))
- (define (format:fn-strip) ; strip trailing zeros but one
- (string-set! format:fn-str format:fn-len #\0)
- (do ((i format:fn-len (- i 1)))
- ((or (not (char=? (string-ref format:fn-str i) #\0))
- (<= i format:fn-dot))
- (set! format:fn-len (+ i 1)))))
- (define (format:fn-zlead) ; count leading zeros
- (do ((i 0 (+ i 1)))
- ((or (= i format:fn-len)
- (not (char=? (string-ref format:fn-str i) #\0)))
- (if (= i format:fn-len) ; found a real zero
- 0
- i))))
- ;;; some global functions not found in SLIB
- (define (string-capitalize-first str) ; "hello" -> "Hello"
- (let ((cap-str (string-copy str)) ; "hELLO" -> "Hello"
- (non-first-alpha #f) ; "*hello" -> "*Hello"
- (str-len (string-length str))) ; "hello you" -> "Hello you"
- (do ((i 0 (+ i 1)))
- ((= i str-len) cap-str)
- (let ((c (string-ref str i)))
- (when (char-alphabetic? c)
- (if non-first-alpha
- (string-set! cap-str i (char-downcase c))
- (begin
- (set! non-first-alpha #t)
- (string-set! cap-str i (char-upcase c)))))))))
- (define arg-pos (format:format-work format-string format-args))
- (define arg-len (length format-args))
- (cond
- ((> arg-pos arg-len)
- (set! %arg-pos (+ arg-len 1))
- (display %arg-pos)
- (format-error "~a missing argument~:p" (- arg-pos arg-len)))
- (else
- (when %flush-output?
- (force-output port))
- (if destination
- #t
- (let ((str (get-output-string port)))
- (close-port port)
- str)))))
- ;; Set the format variable in the root module. This is legacy and
- ;; no longer necessary. It means that as soon as (ice-9 format) is
- ;; loaded somewhere by some module, the predefined binding for format
- ;; becomes the extended format function, even in modules where (ice-9 format)
- ;; isn't imported. Because of this, removing this line should be done
- ;; when a backwards compatibility break is allowed.
- (module-set! the-root-module 'format format)
|