merge from 1.8 branch
[bpt/guile.git] / ice-9 / format.scm
CommitLineData
7da43e41
JB
1;;;; "format.scm" Common LISP text output formatter for SLIB
2;;; Written 1992-1994 by Dirk Lutzebaeck (lutzeb@cs.tu-berlin.de)
3;;; Assimilated into Guile May 1999
093d2ca9
MV
4;;
5;; This code is in the public domain.
7da43e41 6
093d2ca9
MV
7;; Authors of the original version (< 1.4) were Ken Dickey and Aubrey Jaffer.
8;; Please send error reports to bug-guile@gnu.org.
9;; For documentation see slib.texi and format.doc.
10;; For testing load formatst.scm.
11;;
12;; Version 3.0
7da43e41 13
b337528f 14(define-module (ice-9 format)
3cc34e16 15 :use-module (ice-9 and-let-star)
d02655f7 16 :use-module (ice-9 threads)
f595ccfe
MD
17 :autoload (ice-9 pretty-print) (pretty-print)
18 :replace (format)
19 :export (format:symbol-case-conv
20 format:iobj-case-conv
21 format:expch))
7da43e41
JB
22
23;;; Configuration ------------------------------------------------------------
24
25(define format:symbol-case-conv #f)
26;; Symbols are converted by symbol->string so the case of the printed
27;; symbols is implementation dependent. format:symbol-case-conv is a
28;; one arg closure which is either #f (no conversion), string-upcase!,
29;; string-downcase! or string-capitalize!.
30
31(define format:iobj-case-conv #f)
32;; As format:symbol-case-conv but applies for the representation of
33;; implementation internal objects.
34
35(define format:expch #\E)
36;; The character prefixing the exponent value in ~e printing.
37
38(define format:floats (provided? 'inexact))
39;; Detects if the scheme system implements flonums (see at eof).
40
41(define format:complex-numbers (provided? 'complex))
42;; Detects if the scheme system implements complex numbers.
43
44(define format:radix-pref (char=? #\# (string-ref (number->string 8 8) 0)))
45;; Detects if number->string adds a radix prefix.
46
47(define format:ascii-non-printable-charnames
48 '#("nul" "soh" "stx" "etx" "eot" "enq" "ack" "bel"
49 "bs" "ht" "nl" "vt" "np" "cr" "so" "si"
50 "dle" "dc1" "dc2" "dc3" "dc4" "nak" "syn" "etb"
51 "can" "em" "sub" "esc" "fs" "gs" "rs" "us" "space"))
52
53;;; End of configuration ----------------------------------------------------
54
093d2ca9
MV
55(define (format . args)
56 (letrec
57 ((format:version "3.0")
58 (format:port #f) ; curr. format output port
59 (format:output-col 0) ; curr. format output tty column
60 (format:flush-output #f) ; flush output at end of formatting
61 (format:case-conversion #f)
62 (format:args #f)
63 (format:pos 0) ; curr. format string parsing position
64 (format:arg-pos 0) ; curr. format argument position
7da43e41 65 ; this is global for error presentation
093d2ca9
MV
66
67 ;; format string and char output routines on format:port
68
69 (format:out-str
70 (lambda (str)
71 (if format:case-conversion
72 (display (format:case-conversion str) format:port)
73 (display str format:port))
74 (set! format:output-col
75 (+ format:output-col (string-length str)))))
76
77 (format:out-char
78 (lambda (ch)
79 (if format:case-conversion
80 (display (format:case-conversion (string ch))
81 format:port)
82 (write-char ch format:port))
83 (set! format:output-col
84 (if (char=? ch #\newline)
85 0
86 (+ format:output-col 1)))))
87
88 ;;(define (format:out-substr str i n) ; this allocates a new string
89 ;; (display (substring str i n) format:port)
90 ;; (set! format:output-col (+ format:output-col n)))
91
92 (format:out-substr
93 (lambda (str i n)
94 (do ((k i (+ k 1)))
95 ((= k n))
96 (write-char (string-ref str k) format:port))
97 (set! format:output-col (+ format:output-col (- n i)))))
98
99 ;;(define (format:out-fill n ch) ; this allocates a new string
100 ;; (format:out-str (make-string n ch)))
101
102 (format:out-fill
103 (lambda (n ch)
104 (do ((i 0 (+ i 1)))
105 ((= i n))
106 (write-char ch format:port))
107 (set! format:output-col (+ format:output-col n))))
108
109 ;; format's user error handler
110
111 (format:error
112 (lambda args ; never returns!
113 (let ((format-args format:args)
114 (port (current-error-port)))
115 (set! format:error format:intern-error)
116 (if (and (>= (length format:args) 2)
117 (string? (cadr format:args)))
118 (let ((format-string (cadr format-args)))
119 (if (not (zero? format:arg-pos))
120 (set! format:arg-pos (- format:arg-pos 1)))
121 (format port
122 "~%FORMAT: error with call: (format ~a \"~a<===~a\" ~
7da43e41 123 ~{~a ~}===>~{~a ~})~% "
093d2ca9
MV
124 (car format:args)
125 (substring format-string 0 format:pos)
126 (substring format-string format:pos
127 (string-length format-string))
128 (list-head (cddr format:args) format:arg-pos)
129 (list-tail (cddr format:args) format:arg-pos)))
130 (format port
131 "~%FORMAT: error with call: (format~{ ~a~})~% "
132 format:args))
133 (apply format port args)
134 (newline port)
135 (set! format:error format:error-save)
136 (format:abort))))
137
138 (format:intern-error
139 (lambda args
140 ;;if something goes wrong in format:error
141 (display "FORMAT: INTERNAL ERROR IN FORMAT:ERROR!") (newline)
142 (display " format args: ") (write format:args) (newline)
143 (display " error args: ") (write args) (newline)
144 (set! format:error format:error-save)
145 (format:abort)))
146
147 (format:error-save #f)
148
149 (format:format
150 (lambda args ; the formatter entry
151 (set! format:args args)
152 (set! format:arg-pos 0)
153 (set! format:pos 0)
154 (if (< (length args) 1)
155 (format:error "not enough arguments"))
156
157 ;; If the first argument is a string, then that's the format string.
158 ;; (Scheme->C)
159 ;; In this case, put the argument list in canonical form.
160 (let ((args (if (string? (car args))
161 (cons #f args)
162 args)))
163 ;; Use this canonicalized version when reporting errors.
164 (set! format:args args)
165
166 (let ((destination (car args))
167 (arglist (cdr args)))
168 (cond
169 ((or (and (boolean? destination) ; port output
170 destination)
171 (output-port? destination)
172 (number? destination))
173 (format:out (cond
174 ((boolean? destination) (current-output-port))
175 ((output-port? destination) destination)
176 ((number? destination) (current-error-port)))
177 (car arglist) (cdr arglist)))
178 ((and (boolean? destination) ; string output
179 (not destination))
180 (call-with-output-string
181 (lambda (port) (format:out port (car arglist) (cdr arglist)))))
182 (else
183 (format:error "illegal destination `~a'" destination)))))))
184
185 (format:out ; the output handler for a port
186 (lambda (port fmt args)
187 (set! format:port port) ; global port for
188 ; output routines
189 (set! format:case-conversion #f) ; modifier case
190 ; conversion procedure
191 (set! format:flush-output #f) ; ~! reset
192 (and-let* ((col (port-column port))) ; get current column from port
193 (set! format:output-col col))
194 (let ((arg-pos (format:format-work fmt args))
195 (arg-len (length args)))
196 (cond
093d2ca9
MV
197 ((> arg-pos arg-len)
198 (set! format:arg-pos (+ arg-len 1))
199 (display format:arg-pos)
200 (format:error "~a missing argument~:p" (- arg-pos arg-len)))
201 (else
202 (if format:flush-output (force-output port))
203 #t)))))
204
205 (format:parameter-characters
206 '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\- #\+ #\v #\# #\'))
207
208 (format:format-work ; does the formatting work
209 (lambda (format-string arglist)
210 (letrec
211 ((format-string-len (string-length format-string))
212 (arg-pos 0) ; argument position in arglist
213 (arg-len (length arglist)) ; number of arguments
214 (modifier #f) ; 'colon | 'at | 'colon-at | #f
215 (params '()) ; directive parameter list
216 (param-value-found #f) ; a directive
217 ; parameter value
218 ; found
219 (conditional-nest 0) ; conditional nesting level
220 (clause-pos 0) ; last cond. clause
221 ; beginning char pos
222 (clause-default #f) ; conditional default
223 ; clause string
224 (clauses '()) ; conditional clause
225 ; string list
226 (conditional-type #f) ; reflects the
227 ; contional modifiers
228 (conditional-arg #f) ; argument to apply the conditional
229 (iteration-nest 0) ; iteration nesting level
230 (iteration-pos 0) ; iteration string
231 ; beginning char pos
232 (iteration-type #f) ; reflects the
233 ; iteration modifiers
234 (max-iterations #f) ; maximum number of
235 ; iterations
236 (recursive-pos-save format:pos)
237
238 (next-char ; gets the next char
239 ; from format-string
240 (lambda ()
241 (let ((ch (peek-next-char)))
242 (set! format:pos (+ 1 format:pos))
243 ch)))
244
245 (peek-next-char
246 (lambda ()
247 (if (>= format:pos format-string-len)
248 (format:error "illegal format string")
249 (string-ref format-string format:pos))))
250
251 (one-positive-integer?
252 (lambda (params)
253 (cond
254 ((null? params) #f)
255 ((and (integer? (car params))
256 (>= (car params) 0)
257 (= (length params) 1)) #t)
258 (else
259 (format:error
260 "one positive integer parameter expected")))))
261
262 (next-arg
263 (lambda ()
264 (if (>= arg-pos arg-len)
265 (begin
266 (set! format:arg-pos (+ arg-len 1))
267 (format:error "missing argument(s)")))
268 (add-arg-pos 1)
269 (list-ref arglist (- arg-pos 1))))
270
271 (prev-arg
272 (lambda ()
273 (add-arg-pos -1)
274 (if (negative? arg-pos)
275 (format:error "missing backward argument(s)"))
276 (list-ref arglist arg-pos)))
277
278 (rest-args
279 (lambda ()
280 (let loop ((l arglist) (k arg-pos)) ; list-tail definition
281 (if (= k 0) l (loop (cdr l) (- k 1))))))
282
283 (add-arg-pos
284 (lambda (n)
285 (set! arg-pos (+ n arg-pos))
286 (set! format:arg-pos arg-pos)))
287
288 (anychar-dispatch ; dispatches the format-string
289 (lambda ()
290 (if (>= format:pos format-string-len)
291 arg-pos ; used for ~? continuance
292 (let ((char (next-char)))
293 (cond
294 ((char=? char #\~)
295 (set! modifier #f)
296 (set! params '())
297 (set! param-value-found #f)
298 (tilde-dispatch))
299 (else
300 (if (and (zero? conditional-nest)
301 (zero? iteration-nest))
302 (format:out-char char))
303 (anychar-dispatch)))))))
304
305 (tilde-dispatch
306 (lambda ()
307 (cond
308 ((>= format:pos format-string-len)
309 (format:out-str "~") ; tilde at end of
310 ; string is just
311 ; output
312 arg-pos) ; used for ~?
313 ; continuance
314 ((and (or (zero? conditional-nest)
315 (memv (peek-next-char) ; find conditional
316 ; directives
317 (append '(#\[ #\] #\; #\: #\@ #\^)
318 format:parameter-characters)))
319 (or (zero? iteration-nest)
320 (memv (peek-next-char) ; find iteration
321 ; directives
322 (append '(#\{ #\} #\: #\@ #\^)
323 format:parameter-characters))))
324 (case (char-upcase (next-char))
325
326 ;; format directives
327
328 ((#\A) ; Any -- for humans
329 (set! format:read-proof
330 (memq modifier '(colon colon-at)))
331 (format:out-obj-padded (memq modifier '(at colon-at))
332 (next-arg) #f params)
333 (anychar-dispatch))
334 ((#\S) ; Slashified -- for parsers
a6b3219e
MV
335 (set! format:read-proof
336 (memq modifier '(colon colon-at)))
093d2ca9
MV
337 (format:out-obj-padded (memq modifier '(at colon-at))
338 (next-arg) #t params)
339 (anychar-dispatch))
340 ((#\D) ; Decimal
341 (format:out-num-padded modifier (next-arg) params 10)
342 (anychar-dispatch))
343 ((#\X) ; Hexadecimal
344 (format:out-num-padded modifier (next-arg) params 16)
345 (anychar-dispatch))
346 ((#\O) ; Octal
347 (format:out-num-padded modifier (next-arg) params 8)
348 (anychar-dispatch))
349 ((#\B) ; Binary
350 (format:out-num-padded modifier (next-arg) params 2)
351 (anychar-dispatch))
352 ((#\R)
353 (if (null? params)
354 (format:out-obj-padded ; Roman, cardinal,
355 ; ordinal numerals
356 #f
357 ((case modifier
358 ((at) format:num->roman)
359 ((colon-at) format:num->old-roman)
360 ((colon) format:num->ordinal)
361 (else format:num->cardinal))
362 (next-arg))
363 #f params)
364 (format:out-num-padded ; any Radix
365 modifier (next-arg) (cdr params) (car params)))
366 (anychar-dispatch))
367 ((#\F) ; Fixed-format floating-point
368 (if format:floats
369 (format:out-fixed modifier (next-arg) params)
370 (format:out-str (number->string (next-arg))))
371 (anychar-dispatch))
372 ((#\E) ; Exponential floating-point
373 (if format:floats
374 (format:out-expon modifier (next-arg) params)
375 (format:out-str (number->string (next-arg))))
376 (anychar-dispatch))
377 ((#\G) ; General floating-point
378 (if format:floats
379 (format:out-general modifier (next-arg) params)
380 (format:out-str (number->string (next-arg))))
381 (anychar-dispatch))
382 ((#\$) ; Dollars floating-point
383 (if format:floats
384 (format:out-dollar modifier (next-arg) params)
385 (format:out-str (number->string (next-arg))))
386 (anychar-dispatch))
387 ((#\I) ; Complex numbers
388 (if (not format:complex-numbers)
389 (format:error
390 "complex numbers not supported by this scheme system"))
391 (let ((z (next-arg)))
392 (if (not (complex? z))
393 (format:error "argument not a complex number"))
394 (format:out-fixed modifier (real-part z) params)
395 (format:out-fixed 'at (imag-part z) params)
396 (format:out-char #\i))
397 (anychar-dispatch))
398 ((#\C) ; Character
399 (let ((ch (if (one-positive-integer? params)
400 (integer->char (car params))
401 (next-arg))))
402 (if (not (char? ch))
403 (format:error "~~c expects a character"))
404 (case modifier
405 ((at)
406 (format:out-str (format:char->str ch)))
407 ((colon)
408 (let ((c (char->integer ch)))
409 (if (< c 0)
410 (set! c (+ c 256))) ; compensate
411 ; complement
412 ; impl.
413 (cond
414 ((< c #x20) ; assumes that control
415 ; chars are < #x20
416 (format:out-char #\^)
417 (format:out-char
418 (integer->char (+ c #x40))))
419 ((>= c #x7f)
420 (format:out-str "#\\")
421 (format:out-str
422 (if format:radix-pref
423 (let ((s (number->string c 8)))
424 (substring s 2 (string-length s)))
425 (number->string c 8))))
426 (else
427 (format:out-char ch)))))
428 (else (format:out-char ch))))
429 (anychar-dispatch))
430 ((#\P) ; Plural
431 (if (memq modifier '(colon colon-at))
432 (prev-arg))
433 (let ((arg (next-arg)))
434 (if (not (number? arg))
435 (format:error "~~p expects a number argument"))
436 (if (= arg 1)
437 (if (memq modifier '(at colon-at))
438 (format:out-char #\y))
439 (if (memq modifier '(at colon-at))
440 (format:out-str "ies")
441 (format:out-char #\s))))
442 (anychar-dispatch))
443 ((#\~) ; Tilde
444 (if (one-positive-integer? params)
445 (format:out-fill (car params) #\~)
446 (format:out-char #\~))
447 (anychar-dispatch))
448 ((#\%) ; Newline
449 (if (one-positive-integer? params)
450 (format:out-fill (car params) #\newline)
451 (format:out-char #\newline))
452 (set! format:output-col 0)
453 (anychar-dispatch))
454 ((#\&) ; Fresh line
7da43e41 455 (if (one-positive-integer? params)
093d2ca9
MV
456 (begin
457 (if (> (car params) 0)
458 (format:out-fill (- (car params)
459 (if (>
460 format:output-col
461 0) 0 1))
462 #\newline))
463 (set! format:output-col 0))
464 (if (> format:output-col 0)
465 (format:out-char #\newline)))
466 (anychar-dispatch))
467 ((#\_) ; Space character
468 (if (one-positive-integer? params)
469 (format:out-fill (car params) #\space)
470 (format:out-char #\space))
471 (anychar-dispatch))
472 ((#\/) ; Tabulator character
473 (if (one-positive-integer? params)
474 (format:out-fill (car params) #\tab)
475 (format:out-char #\tab))
476 (anychar-dispatch))
477 ((#\|) ; Page seperator
478 (if (one-positive-integer? params)
479 (format:out-fill (car params) #\page)
480 (format:out-char #\page))
481 (set! format:output-col 0)
482 (anychar-dispatch))
483 ((#\T) ; Tabulate
484 (format:tabulate modifier params)
485 (anychar-dispatch))
486 ((#\Y) ; Pretty-print
487 (pretty-print (next-arg) format:port)
488 (set! format:output-col 0)
489 (anychar-dispatch))
490 ((#\? #\K) ; Indirection (is "~K" in T-Scheme)
491 (cond
492 ((memq modifier '(colon colon-at))
493 (format:error "illegal modifier in ~~?"))
494 ((eq? modifier 'at)
495 (let* ((frmt (next-arg))
496 (args (rest-args)))
497 (add-arg-pos (format:format-work frmt args))))
498 (else
499 (let* ((frmt (next-arg))
500 (args (next-arg)))
501 (format:format-work frmt args))))
502 (anychar-dispatch))
503 ((#\!) ; Flush output
504 (set! format:flush-output #t)
505 (anychar-dispatch))
506 ((#\newline) ; Continuation lines
507 (if (eq? modifier 'at)
508 (format:out-char #\newline))
509 (if (< format:pos format-string-len)
510 (do ((ch (peek-next-char) (peek-next-char)))
511 ((or (not (char-whitespace? ch))
512 (= format:pos (- format-string-len 1))))
513 (if (eq? modifier 'colon)
514 (format:out-char (next-char))
515 (next-char))))
516 (anychar-dispatch))
517 ((#\*) ; Argument jumping
7da43e41 518 (case modifier
093d2ca9
MV
519 ((colon) ; jump backwards
520 (if (one-positive-integer? params)
521 (do ((i 0 (+ i 1)))
522 ((= i (car params)))
523 (prev-arg))
524 (prev-arg)))
525 ((at) ; jump absolute
526 (set! arg-pos (if (one-positive-integer? params)
527 (car params) 0)))
528 ((colon-at)
529 (format:error "illegal modifier `:@' in ~~* directive"))
530 (else ; jump forward
531 (if (one-positive-integer? params)
532 (do ((i 0 (+ i 1)))
533 ((= i (car params)))
534 (next-arg))
535 (next-arg))))
536 (anychar-dispatch))
537 ((#\() ; Case conversion begin
538 (set! format:case-conversion
539 (case modifier
540 ((at) string-capitalize-first)
541 ((colon) string-capitalize)
542 ((colon-at) string-upcase)
543 (else string-downcase)))
544 (anychar-dispatch))
545 ((#\)) ; Case conversion end
546 (if (not format:case-conversion)
547 (format:error "missing ~~("))
548 (set! format:case-conversion #f)
549 (anychar-dispatch))
550 ((#\[) ; Conditional begin
551 (set! conditional-nest (+ conditional-nest 1))
7da43e41 552 (cond
093d2ca9
MV
553 ((= conditional-nest 1)
554 (set! clause-pos format:pos)
555 (set! clause-default #f)
556 (set! clauses '())
557 (set! conditional-type
558 (case modifier
559 ((at) 'if-then)
560 ((colon) 'if-else-then)
561 ((colon-at) (format:error "illegal modifier in ~~["))
562 (else 'num-case)))
563 (set! conditional-arg
564 (if (one-positive-integer? params)
565 (car params)
566 (next-arg)))))
567 (anychar-dispatch))
568 ((#\;) ; Conditional separator
569 (if (zero? conditional-nest)
570 (format:error "~~; not in ~~[~~] conditional"))
571 (if (not (null? params))
572 (format:error "no parameter allowed in ~~;"))
573 (if (= conditional-nest 1)
574 (let ((clause-str
575 (cond
576 ((eq? modifier 'colon)
577 (set! clause-default #t)
578 (substring format-string clause-pos
579 (- format:pos 3)))
580 ((memq modifier '(at colon-at))
581 (format:error "illegal modifier in ~~;"))
582 (else
583 (substring format-string clause-pos
584 (- format:pos 2))))))
585 (set! clauses (append clauses (list clause-str)))
586 (set! clause-pos format:pos)))
587 (anychar-dispatch))
588 ((#\]) ; Conditional end
589 (if (zero? conditional-nest) (format:error "missing ~~["))
590 (set! conditional-nest (- conditional-nest 1))
591 (if modifier
592 (format:error "no modifier allowed in ~~]"))
593 (if (not (null? params))
594 (format:error "no parameter allowed in ~~]"))
595 (cond
596 ((zero? conditional-nest)
597 (let ((clause-str (substring format-string clause-pos
598 (- format:pos 2))))
599 (if clause-default
600 (set! clause-default clause-str)
601 (set! clauses (append clauses (list clause-str)))))
602 (case conditional-type
603 ((if-then)
604 (if conditional-arg
605 (format:format-work (car clauses)
606 (list conditional-arg))))
607 ((if-else-then)
608 (add-arg-pos
609 (format:format-work (if conditional-arg
610 (cadr clauses)
611 (car clauses))
612 (rest-args))))
613 ((num-case)
614 (if (or (not (integer? conditional-arg))
615 (< conditional-arg 0))
616 (format:error "argument not a positive integer"))
617 (if (not (and (>= conditional-arg (length clauses))
618 (not clause-default)))
619 (add-arg-pos
620 (format:format-work
621 (if (>= conditional-arg (length clauses))
622 clause-default
623 (list-ref clauses conditional-arg))
624 (rest-args))))))))
625 (anychar-dispatch))
626 ((#\{) ; Iteration begin
627 (set! iteration-nest (+ iteration-nest 1))
628 (cond
629 ((= iteration-nest 1)
630 (set! iteration-pos format:pos)
631 (set! iteration-type
632 (case modifier
633 ((at) 'rest-args)
634 ((colon) 'sublists)
635 ((colon-at) 'rest-sublists)
636 (else 'list)))
637 (set! max-iterations (if (one-positive-integer? params)
638 (car params) #f))))
639 (anychar-dispatch))
640 ((#\}) ; Iteration end
641 (if (zero? iteration-nest) (format:error "missing ~~{"))
642 (set! iteration-nest (- iteration-nest 1))
643 (case modifier
644 ((colon)
645 (if (not max-iterations) (set! max-iterations 1)))
a0dbcd9c 646 ((colon-at at) (format:error "illegal modifier")))
093d2ca9
MV
647 (if (not (null? params))
648 (format:error "no parameters allowed in ~~}"))
649 (if (zero? iteration-nest)
650 (let ((iteration-str
651 (substring format-string iteration-pos
652 (- format:pos (if modifier 3 2)))))
653 (if (string=? iteration-str "")
654 (set! iteration-str (next-arg)))
655 (case iteration-type
656 ((list)
657 (let ((args (next-arg))
658 (args-len 0))
659 (if (not (list? args))
660 (format:error "expected a list argument"))
661 (set! args-len (length args))
662 (do ((arg-pos 0 (+ arg-pos
663 (format:format-work
664 iteration-str
665 (list-tail args arg-pos))))
666 (i 0 (+ i 1)))
667 ((or (>= arg-pos args-len)
a0dbcd9c
KR
668 (and max-iterations
669 (>= i max-iterations)))))))
093d2ca9
MV
670 ((sublists)
671 (let ((args (next-arg))
672 (args-len 0))
673 (if (not (list? args))
674 (format:error "expected a list argument"))
675 (set! args-len (length args))
676 (do ((arg-pos 0 (+ arg-pos 1)))
677 ((or (>= arg-pos args-len)
a0dbcd9c
KR
678 (and max-iterations
679 (>= arg-pos max-iterations))))
093d2ca9
MV
680 (let ((sublist (list-ref args arg-pos)))
681 (if (not (list? sublist))
682 (format:error
683 "expected a list of lists argument"))
684 (format:format-work iteration-str sublist)))))
685 ((rest-args)
686 (let* ((args (rest-args))
687 (args-len (length args))
688 (usedup-args
689 (do ((arg-pos 0 (+ arg-pos
690 (format:format-work
691 iteration-str
692 (list-tail
693 args arg-pos))))
694 (i 0 (+ i 1)))
695 ((or (>= arg-pos args-len)
a0dbcd9c
KR
696 (and max-iterations
697 (>= i max-iterations)))
093d2ca9
MV
698 arg-pos))))
699 (add-arg-pos usedup-args)))
700 ((rest-sublists)
701 (let* ((args (rest-args))
702 (args-len (length args))
703 (usedup-args
704 (do ((arg-pos 0 (+ arg-pos 1)))
705 ((or (>= arg-pos args-len)
a0dbcd9c
KR
706 (and max-iterations
707 (>= arg-pos max-iterations)))
093d2ca9
MV
708 arg-pos)
709 (let ((sublist (list-ref args arg-pos)))
710 (if (not (list? sublist))
711 (format:error "expected list arguments"))
712 (format:format-work iteration-str sublist)))))
713 (add-arg-pos usedup-args)))
714 (else (format:error "internal error in ~~}")))))
715 (anychar-dispatch))
716 ((#\^) ; Up and out
717 (let* ((continue
718 (cond
719 ((not (null? params))
720 (not
721 (case (length params)
722 ((1) (zero? (car params)))
723 ((2) (= (list-ref params 0) (list-ref params 1)))
724 ((3) (<= (list-ref params 0)
725 (list-ref params 1)
726 (list-ref params 2)))
727 (else (format:error "too much parameters")))))
728 (format:case-conversion ; if conversion stop conversion
729 (set! format:case-conversion string-copy) #t)
730 ((= iteration-nest 1) #t)
731 ((= conditional-nest 1) #t)
732 ((>= arg-pos arg-len)
733 (set! format:pos format-string-len) #f)
734 (else #t))))
735 (if continue
736 (anychar-dispatch))))
737
738 ;; format directive modifiers and parameters
739
740 ((#\@) ; `@' modifier
741 (if (memq modifier '(at colon-at))
742 (format:error "double `@' modifier"))
743 (set! modifier (if (eq? modifier 'colon) 'colon-at 'at))
744 (tilde-dispatch))
745 ((#\:) ; `:' modifier
746 (if (memq modifier '(colon colon-at))
747 (format:error "double `:' modifier"))
748 (set! modifier (if (eq? modifier 'at) 'colon-at 'colon))
749 (tilde-dispatch))
750 ((#\') ; Character parameter
751 (if modifier (format:error "misplaced modifier"))
752 (set! params (append params (list (char->integer (next-char)))))
753 (set! param-value-found #t)
754 (tilde-dispatch))
755 ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\- #\+) ; num. paramtr
756 (if modifier (format:error "misplaced modifier"))
757 (let ((num-str-beg (- format:pos 1))
758 (num-str-end format:pos))
759 (do ((ch (peek-next-char) (peek-next-char)))
760 ((not (char-numeric? ch)))
761 (next-char)
762 (set! num-str-end (+ 1 num-str-end)))
763 (set! params
764 (append params
765 (list (string->number
766 (substring format-string
767 num-str-beg
768 num-str-end))))))
769 (set! param-value-found #t)
770 (tilde-dispatch))
771 ((#\V) ; Variable parameter from next argum.
772 (if modifier (format:error "misplaced modifier"))
773 (set! params (append params (list (next-arg))))
774 (set! param-value-found #t)
775 (tilde-dispatch))
776 ((#\#) ; Parameter is number of remaining args
777 (if param-value-found (format:error "misplaced '#'"))
778 (if modifier (format:error "misplaced modifier"))
779 (set! params (append params (list (length (rest-args)))))
780 (set! param-value-found #t)
781 (tilde-dispatch))
782 ((#\,) ; Parameter separators
783 (if modifier (format:error "misplaced modifier"))
784 (if (not param-value-found)
785 (set! params (append params '(#f)))) ; append empty paramtr
786 (set! param-value-found #f)
787 (tilde-dispatch))
788 ((#\Q) ; Inquiry messages
789 (if (eq? modifier 'colon)
790 (format:out-str format:version)
791 (let ((nl (string #\newline)))
792 (format:out-str
793 (string-append
794 "SLIB Common LISP format version " format:version nl
795 " (C) copyright 1992-1994 by Dirk Lutzebaeck" nl
796 " please send bug reports to `lutzeb@cs.tu-berlin.de'"
797 nl))))
798 (anychar-dispatch))
799 (else ; Unknown tilde directive
800 (format:error "unknown control character `~c'"
801 (string-ref format-string (- format:pos 1))))))
802 (else (anychar-dispatch)))))) ; in case of conditional
803
804 (set! format:pos 0)
805 (set! format:arg-pos 0)
806 (anychar-dispatch) ; start the formatting
807 (set! format:pos recursive-pos-save)
808 arg-pos))) ; return the position in the arg. list
809
a6b3219e
MV
810 ;; when format:read-proof is true, format:obj->str will wrap
811 ;; result strings starting with "#<" in an extra pair of double
812 ;; quotes.
813
093d2ca9
MV
814 (format:read-proof #f)
815
a6b3219e
MV
816 ;; format:obj->str returns a R4RS representation as a string of
817 ;; an arbitrary scheme object.
093d2ca9 818
a6b3219e
MV
819 (format:obj->str
820 (lambda (obj slashify)
821 (let ((res (if slashify
822 (object->string obj)
823 (with-output-to-string (lambda () (display obj))))))
824 (if (and format:read-proof (string-prefix? "#<" res))
825 (object->string res)
826 res))))
093d2ca9
MV
827
828 ;; format:char->str converts a character into a slashified string as
829 ;; done by `write'. The procedure is dependent on the integer
830 ;; representation of characters and assumes a character number according to
831 ;; the ASCII character set.
832
833 (format:char->str
834 (lambda (ch)
835 (let ((int-rep (char->integer ch)))
836 (if (< int-rep 0) ; if chars are [-128...+127]
837 (set! int-rep (+ int-rep 256)))
838 (string-append
839 "#\\"
840 (cond
841 ((char=? ch #\newline) "newline")
842 ((and (>= int-rep 0) (<= int-rep 32))
843 (vector-ref format:ascii-non-printable-charnames int-rep))
844 ((= int-rep 127) "del")
845 ((>= int-rep 128) ; octal representation
846 (if format:radix-pref
847 (let ((s (number->string int-rep 8)))
848 (substring s 2 (string-length s)))
849 (number->string int-rep 8)))
850 (else (string ch)))))))
851
852 (format:space-ch (char->integer #\space))
853 (format:zero-ch (char->integer #\0))
854
855 (format:par
856 (lambda (pars length index default name)
857 (if (> length index)
858 (let ((par (list-ref pars index)))
859 (if par
860 (if name
861 (if (< par 0)
862 (format:error
863 "~s parameter must be a positive integer" name)
864 par)
865 par)
866 default))
867 default)))
868
869 (format:out-obj-padded
870 (lambda (pad-left obj slashify pars)
871 (if (null? pars)
872 (format:out-str (format:obj->str obj slashify))
873 (let ((l (length pars)))
874 (let ((mincol (format:par pars l 0 0 "mincol"))
875 (colinc (format:par pars l 1 1 "colinc"))
876 (minpad (format:par pars l 2 0 "minpad"))
877 (padchar (integer->char
878 (format:par pars l 3 format:space-ch #f)))
879 (objstr (format:obj->str obj slashify)))
880 (if (not pad-left)
881 (format:out-str objstr))
882 (do ((objstr-len (string-length objstr))
883 (i minpad (+ i colinc)))
884 ((>= (+ objstr-len i) mincol)
885 (format:out-fill i padchar)))
886 (if pad-left
887 (format:out-str objstr)))))))
888
889 (format:out-num-padded
890 (lambda (modifier number pars radix)
891 (if (not (integer? number)) (format:error "argument not an integer"))
892 (let ((numstr (number->string number radix)))
893 (if (and format:radix-pref (not (= radix 10)))
894 (set! numstr (substring numstr 2 (string-length numstr))))
895 (if (and (null? pars) (not modifier))
896 (format:out-str numstr)
897 (let ((l (length pars))
898 (numstr-len (string-length numstr)))
899 (let ((mincol (format:par pars l 0 #f "mincol"))
900 (padchar (integer->char
901 (format:par pars l 1 format:space-ch #f)))
902 (commachar (integer->char
903 (format:par pars l 2 (char->integer #\,) #f)))
904 (commawidth (format:par pars l 3 3 "commawidth")))
905 (if mincol
906 (let ((numlen numstr-len)) ; calc. the output len of number
260ffd61 907 (if (and (memq modifier '(at colon-at)) (>= number 0))
093d2ca9
MV
908 (set! numlen (+ numlen 1)))
909 (if (memq modifier '(colon colon-at))
910 (set! numlen (+ (quotient (- numstr-len
911 (if (< number 0) 2 1))
912 commawidth)
913 numlen)))
914 (if (> mincol numlen)
915 (format:out-fill (- mincol numlen) padchar))))
916 (if (and (memq modifier '(at colon-at))
260ffd61 917 (>= number 0))
093d2ca9
MV
918 (format:out-char #\+))
919 (if (memq modifier '(colon colon-at)) ; insert comma character
920 (let ((start (remainder numstr-len commawidth))
921 (ns (if (< number 0) 1 0)))
922 (format:out-substr numstr 0 start)
923 (do ((i start (+ i commawidth)))
924 ((>= i numstr-len))
925 (if (> i ns)
926 (format:out-char commachar))
927 (format:out-substr numstr i (+ i commawidth))))
928 (format:out-str numstr))))))))
929
930 (format:tabulate
931 (lambda (modifier pars)
932 (let ((l (length pars)))
933 (let ((colnum (format:par pars l 0 1 "colnum"))
934 (colinc (format:par pars l 1 1 "colinc"))
935 (padch (integer->char (format:par pars l 2 format:space-ch #f))))
936 (case modifier
937 ((colon colon-at)
938 (format:error "unsupported modifier for ~~t"))
939 ((at) ; relative tabulation
940 (format:out-fill
941 (if (= colinc 0)
942 colnum ; colnum = colrel
943 (do ((c 0 (+ c colinc))
944 (col (+ format:output-col colnum)))
945 ((>= c col)
946 (- c format:output-col))))
947 padch))
948 (else ; absolute tabulation
949 (format:out-fill
950 (cond
951 ((< format:output-col colnum)
952 (- colnum format:output-col))
953 ((= colinc 0)
954 0)
955 (else
956 (do ((c colnum (+ c colinc)))
957 ((>= c format:output-col)
958 (- c format:output-col)))))
959 padch)))))))
960
961
962 ;; roman numerals (from dorai@cs.rice.edu).
963
964 (format:roman-alist
965 '((1000 #\M) (500 #\D) (100 #\C) (50 #\L)
966 (10 #\X) (5 #\V) (1 #\I)))
967
968 (format:roman-boundary-values
969 '(100 100 10 10 1 1 #f))
970
971 (format:num->old-roman
972 (lambda (n)
973 (if (and (integer? n) (>= n 1))
7da43e41 974 (let loop ((n n)
093d2ca9 975 (romans format:roman-alist)
7da43e41 976 (s '()))
093d2ca9
MV
977 (if (null? romans) (list->string (reverse s))
978 (let ((roman-val (caar romans))
979 (roman-dgt (cadar romans)))
980 (do ((q (quotient n roman-val) (- q 1))
981 (s s (cons roman-dgt s)))
982 ((= q 0)
983 (loop (remainder n roman-val)
984 (cdr romans) s))))))
985 (format:error "only positive integers can be romanized"))))
986
987 (format:num->roman
988 (lambda (n)
989 (if (and (integer? n) (> n 0))
990 (let loop ((n n)
991 (romans format:roman-alist)
992 (boundaries format:roman-boundary-values)
993 (s '()))
994 (if (null? romans)
995 (list->string (reverse s))
996 (let ((roman-val (caar romans))
997 (roman-dgt (cadar romans))
998 (bdry (car boundaries)))
999 (let loop2 ((q (quotient n roman-val))
1000 (r (remainder n roman-val))
1001 (s s))
1002 (if (= q 0)
1003 (if (and bdry (>= r (- roman-val bdry)))
1004 (loop (remainder r bdry) (cdr romans)
1005 (cdr boundaries)
1006 (cons roman-dgt
1007 (append
1008 (cdr (assv bdry romans))
1009 s)))
1010 (loop r (cdr romans) (cdr boundaries) s))
1011 (loop2 (- q 1) r (cons roman-dgt s)))))))
1012 (format:error "only positive integers can be romanized"))))
1013
1014 ;; cardinals & ordinals (from dorai@cs.rice.edu)
1015
1016 (format:cardinal-ones-list
1017 '(#f "one" "two" "three" "four" "five"
1018 "six" "seven" "eight" "nine" "ten" "eleven" "twelve" "thirteen"
1019 "fourteen" "fifteen" "sixteen" "seventeen" "eighteen"
1020 "nineteen"))
1021
1022 (format:cardinal-tens-list
1023 '(#f #f "twenty" "thirty" "forty" "fifty" "sixty" "seventy" "eighty"
1024 "ninety"))
1025
1026 (format:num->cardinal999
1027 (lambda (n)
1028 ;this procedure is inspired by the Bruno Haible's CLisp
1029 ;function format-small-cardinal, which converts numbers
1030 ;in the range 1 to 999, and is used for converting each
1031 ;thousand-block in a larger number
1032 (let* ((hundreds (quotient n 100))
1033 (tens+ones (remainder n 100))
1034 (tens (quotient tens+ones 10))
1035 (ones (remainder tens+ones 10)))
1036 (append
1037 (if (> hundreds 0)
1038 (append
1039 (string->list
1040 (list-ref format:cardinal-ones-list hundreds))
1041 (string->list" hundred")
1042 (if (> tens+ones 0) '(#\space) '()))
1043 '())
1044 (if (< tens+ones 20)
1045 (if (> tens+ones 0)
1046 (string->list
1047 (list-ref format:cardinal-ones-list tens+ones))
1048 '())
1049 (append
1050 (string->list
1051 (list-ref format:cardinal-tens-list tens))
1052 (if (> ones 0)
1053 (cons #\-
1054 (string->list
1055 (list-ref format:cardinal-ones-list ones)))
1056 '())))))))
1057
1058 (format:cardinal-thousand-block-list
1059 '("" " thousand" " million" " billion" " trillion" " quadrillion"
1060 " quintillion" " sextillion" " septillion" " octillion" " nonillion"
1061 " decillion" " undecillion" " duodecillion" " tredecillion"
1062 " quattuordecillion" " quindecillion" " sexdecillion" " septendecillion"
1063 " octodecillion" " novemdecillion" " vigintillion"))
1064
1065 (format:num->cardinal
1066 (lambda (n)
1067 (cond ((not (integer? n))
1068 (format:error
1069 "only integers can be converted to English cardinals"))
1070 ((= n 0) "zero")
1071 ((< n 0) (string-append "minus " (format:num->cardinal (- n))))
1072 (else
1073 (let ((power3-word-limit
1074 (length format:cardinal-thousand-block-list)))
1075 (let loop ((n n)
1076 (power3 0)
1077 (s '()))
1078 (if (= n 0)
1079 (list->string s)
1080 (let ((n-before-block (quotient n 1000))
1081 (n-after-block (remainder n 1000)))
1082 (loop n-before-block
1083 (+ power3 1)
1084 (if (> n-after-block 0)
1085 (append
1086 (if (> n-before-block 0)
1087 (string->list ", ") '())
1088 (format:num->cardinal999 n-after-block)
1089 (if (< power3 power3-word-limit)
1090 (string->list
1091 (list-ref
1092 format:cardinal-thousand-block-list
1093 power3))
1094 (append
1095 (string->list " times ten to the ")
1096 (string->list
1097 (format:num->ordinal
1098 (* power3 3)))
1099 (string->list " power")))
1100 s)
1101 s))))))))))
1102
1103 (format:ordinal-ones-list
1104 '(#f "first" "second" "third" "fourth" "fifth"
1105 "sixth" "seventh" "eighth" "ninth" "tenth" "eleventh" "twelfth"
1106 "thirteenth" "fourteenth" "fifteenth" "sixteenth" "seventeenth"
1107 "eighteenth" "nineteenth"))
1108
1109 (format:ordinal-tens-list
1110 '(#f #f "twentieth" "thirtieth" "fortieth" "fiftieth" "sixtieth"
1111 "seventieth" "eightieth" "ninetieth"))
1112
1113 (format:num->ordinal
1114 (lambda (n)
1115 (cond ((not (integer? n))
1116 (format:error
1117 "only integers can be converted to English ordinals"))
1118 ((= n 0) "zeroth")
1119 ((< n 0) (string-append "minus " (format:num->ordinal (- n))))
1120 (else
1121 (let ((hundreds (quotient n 100))
1122 (tens+ones (remainder n 100)))
1123 (string-append
1124 (if (> hundreds 0)
1125 (string-append
1126 (format:num->cardinal (* hundreds 100))
1127 (if (= tens+ones 0) "th" " "))
1128 "")
1129 (if (= tens+ones 0) ""
1130 (if (< tens+ones 20)
1131 (list-ref format:ordinal-ones-list tens+ones)
1132 (let ((tens (quotient tens+ones 10))
1133 (ones (remainder tens+ones 10)))
1134 (if (= ones 0)
1135 (list-ref format:ordinal-tens-list tens)
1136 (string-append
1137 (list-ref format:cardinal-tens-list tens)
1138 "-"
1139 (list-ref format:ordinal-ones-list ones))))
1140 ))))))))
1141
1142 ;; format inf and nan.
1143
1144 (format:out-inf-nan
1145 (lambda (number width digits edigits overch padch)
1146 ;; inf and nan are always printed exactly as "+inf.0", "-inf.0" or
1147 ;; "+nan.0", suitably justified in their field. We insist on
1148 ;; printing this exact form so that the numbers can be read back in.
1149
1150 (let* ((str (number->string number))
1151 (len (string-length str))
1152 (dot (string-index str #\.))
1153 (digits (+ (or digits 0)
1154 (if edigits (+ edigits 2) 0))))
1155 (if (and width overch (< width len))
a0c39db3 1156 (format:out-fill width (integer->char overch))
093d2ca9
MV
1157 (let* ((leftpad (if width
1158 (max (- width (max len (+ dot 1 digits))) 0)
1159 0))
1160 (rightpad (if width
1161 (max (- width leftpad len) 0)
1162 0))
1163 (padch (integer->char (or padch format:space-ch))))
1164 (format:out-fill leftpad padch)
1165 (format:out-str str)
1166 (format:out-fill rightpad padch))))))
1167
1168 ;; format fixed flonums (~F)
1169
1170 (format:out-fixed
1171 (lambda (modifier number pars)
1172 (if (not (or (number? number) (string? number)))
1173 (format:error "argument is not a number or a number string"))
1174
1175 (let ((l (length pars)))
1176 (let ((width (format:par pars l 0 #f "width"))
1177 (digits (format:par pars l 1 #f "digits"))
1178 (scale (format:par pars l 2 0 #f))
1179 (overch (format:par pars l 3 #f #f))
1180 (padch (format:par pars l 4 format:space-ch #f)))
1181
1182 (cond
1183 ((or (inf? number) (nan? number))
1184 (format:out-inf-nan number width digits #f overch padch))
1185
1186 (digits
1187 (format:parse-float
1188 (if (string? number) number (number->string number)) #t scale)
1189 (if (<= (- format:fn-len format:fn-dot) digits)
1190 (format:fn-zfill #f (- digits (- format:fn-len format:fn-dot)))
1191 (format:fn-round digits))
1192 (if width
1193 (let ((numlen (+ format:fn-len 1)))
1194 (if (or (not format:fn-pos?) (eq? modifier 'at))
1195 (set! numlen (+ numlen 1)))
1196 (if (and (= format:fn-dot 0) (> width (+ digits 1)))
1197 (set! numlen (+ numlen 1)))
1198 (if (< numlen width)
1199 (format:out-fill (- width numlen) (integer->char padch)))
1200 (if (and overch (> numlen width))
a0c39db3 1201 (format:out-fill width (integer->char overch))
093d2ca9
MV
1202 (format:fn-out modifier (> width (+ digits 1)))))
1203 (format:fn-out modifier #t)))
1204
1205 (else
1206 (format:parse-float
1207 (if (string? number) number (number->string number)) #t scale)
1208 (format:fn-strip)
1209 (if width
1210 (let ((numlen (+ format:fn-len 1)))
1211 (if (or (not format:fn-pos?) (eq? modifier 'at))
1212 (set! numlen (+ numlen 1)))
1213 (if (= format:fn-dot 0)
1214 (set! numlen (+ numlen 1)))
1215 (if (< numlen width)
1216 (format:out-fill (- width numlen) (integer->char padch)))
1217 (if (> numlen width) ; adjust precision if possible
1218 (let ((dot-index (- numlen
1219 (- format:fn-len format:fn-dot))))
1220 (if (> dot-index width)
1221 (if overch ; numstr too big for required width
1222 (format:out-fill width (integer->char overch))
1223 (format:fn-out modifier #t))
1224 (begin
1225 (format:fn-round (- width dot-index))
1226 (format:fn-out modifier #t))))
1227 (format:fn-out modifier #t)))
1228 (format:fn-out modifier #t))))))))
1229
1230 ;; format exponential flonums (~E)
1231
1232 (format:out-expon
1233 (lambda (modifier number pars)
1234 (if (not (or (number? number) (string? number)))
1235 (format:error "argument is not a number"))
1236
1237 (let ((l (length pars)))
1238 (let ((width (format:par pars l 0 #f "width"))
1239 (digits (format:par pars l 1 #f "digits"))
1240 (edigits (format:par pars l 2 #f "exponent digits"))
1241 (scale (format:par pars l 3 1 #f))
1242 (overch (format:par pars l 4 #f #f))
1243 (padch (format:par pars l 5 format:space-ch #f))
1244 (expch (format:par pars l 6 #f #f)))
1245
1246 (cond
1247 ((or (inf? number) (nan? number))
1248 (format:out-inf-nan number width digits edigits overch padch))
1249
1250 (digits ; fixed precision
1251
1252 (let ((digits (if (> scale 0)
1253 (if (< scale (+ digits 2))
1254 (+ (- digits scale) 1)
1255 0)
1256 digits)))
1257 (format:parse-float
1258 (if (string? number) number (number->string number)) #f scale)
1259 (if (<= (- format:fn-len format:fn-dot) digits)
1260 (format:fn-zfill #f (- digits (- format:fn-len format:fn-dot)))
1261 (format:fn-round digits))
1262 (if width
1263 (if (and edigits overch (> format:en-len edigits))
1264 (format:out-fill width (integer->char overch))
1265 (let ((numlen (+ format:fn-len 3))) ; .E+
1266 (if (or (not format:fn-pos?) (eq? modifier 'at))
1267 (set! numlen (+ numlen 1)))
1268 (if (and (= format:fn-dot 0) (> width (+ digits 1)))
1269 (set! numlen (+ numlen 1)))
1270 (set! numlen
1271 (+ numlen
1272 (if (and edigits (>= edigits format:en-len))
1273 edigits
1274 format:en-len)))
1275 (if (< numlen width)
1276 (format:out-fill (- width numlen)
1277 (integer->char padch)))
1278 (if (and overch (> numlen width))
1279 (format:out-fill width (integer->char overch))
a0c39db3 1280 (begin
093d2ca9
MV
1281 (format:fn-out modifier (> width (- numlen 1)))
1282 (format:en-out edigits expch)))))
a0c39db3
MV
1283 (begin
1284 (format:fn-out modifier #t)
1285 (format:en-out edigits expch)))))
093d2ca9
MV
1286
1287 (else
1288 (format:parse-float
1289 (if (string? number) number (number->string number)) #f scale)
1290 (format:fn-strip)
1291 (if width
1292 (if (and edigits overch (> format:en-len edigits))
1293 (format:out-fill width (integer->char overch))
1294 (let ((numlen (+ format:fn-len 3))) ; .E+
1295 (if (or (not format:fn-pos?) (eq? modifier 'at))
1296 (set! numlen (+ numlen 1)))
1297 (if (= format:fn-dot 0)
1298 (set! numlen (+ numlen 1)))
1299 (set! numlen
1300 (+ numlen
1301 (if (and edigits (>= edigits format:en-len))
1302 edigits
1303 format:en-len)))
1304 (if (< numlen width)
1305 (format:out-fill (- width numlen)
1306 (integer->char padch)))
1307 (if (> numlen width) ; adjust precision if possible
1308 (let ((f (- format:fn-len format:fn-dot))) ; fract len
1309 (if (> (- numlen f) width)
1310 (if overch ; numstr too big for required width
1311 (format:out-fill width
1312 (integer->char overch))
1313 (begin
1314 (format:fn-out modifier #t)
1315 (format:en-out edigits expch)))
1316 (begin
1317 (format:fn-round (+ (- f numlen) width))
1318 (format:fn-out modifier #t)
1319 (format:en-out edigits expch))))
1320 (begin
1321 (format:fn-out modifier #t)
1322 (format:en-out edigits expch)))))
1323 (begin
1324 (format:fn-out modifier #t)
1325 (format:en-out edigits expch)))))))))
1326
1327 ;; format general flonums (~G)
1328
1329 (format:out-general
1330 (lambda (modifier number pars)
1331 (if (not (or (number? number) (string? number)))
1332 (format:error "argument is not a number or a number string"))
1333
1334 (let ((l (length pars)))
1335 (let ((width (if (> l 0) (list-ref pars 0) #f))
1336 (digits (if (> l 1) (list-ref pars 1) #f))
1337 (edigits (if (> l 2) (list-ref pars 2) #f))
1338 (overch (if (> l 4) (list-ref pars 4) #f))
1339 (padch (if (> l 5) (list-ref pars 5) #f)))
1340 (cond
1341 ((or (inf? number) (nan? number))
1342 ;; FIXME: this isn't right.
1343 (format:out-inf-nan number width digits edigits overch padch))
1344 (else
1345 (format:parse-float
1346 (if (string? number) number (number->string number)) #t 0)
1347 (format:fn-strip)
1348 (let* ((ee (if edigits (+ edigits 2) 4)) ; for the following algorithm
1349 (ww (if width (- width ee) #f)) ; see Steele's CL book p.395
1350 (n (if (= format:fn-dot 0) ; number less than (abs 1.0) ?
1351 (- (format:fn-zlead))
1352 format:fn-dot))
1353 (d (if digits
1354 digits
1355 (max format:fn-len (min n 7)))) ; q = format:fn-len
1356 (dd (- d n)))
1357 (if (<= 0 dd d)
1358 (begin
1359 (format:out-fixed modifier number (list ww dd #f overch padch))
1360 (format:out-fill ee #\space)) ;~@T not implemented yet
1361 (format:out-expon modifier number pars)))))))))
1362
1363 ;; format dollar flonums (~$)
1364
1365 (format:out-dollar
1366 (lambda (modifier number pars)
1367 (if (not (or (number? number) (string? number)))
1368 (format:error "argument is not a number or a number string"))
1369
1370 (let ((l (length pars)))
1371 (let ((digits (format:par pars l 0 2 "digits"))
1372 (mindig (format:par pars l 1 1 "mindig"))
1373 (width (format:par pars l 2 0 "width"))
1374 (padch (format:par pars l 3 format:space-ch #f)))
1375
cd5fea8d
KR
1376 (cond
1377 ((or (inf? number) (nan? number))
1378 (format:out-inf-nan number width digits #f #f padch))
1379
1380 (else
1381 (format:parse-float
1382 (if (string? number) number (number->string number)) #t 0)
1383 (if (<= (- format:fn-len format:fn-dot) digits)
1384 (format:fn-zfill #f (- digits (- format:fn-len format:fn-dot)))
1385 (format:fn-round digits))
1386 (let ((numlen (+ format:fn-len 1)))
1387 (if (or (not format:fn-pos?) (memq modifier '(at colon-at)))
1388 (set! numlen (+ numlen 1)))
1389 (if (and mindig (> mindig format:fn-dot))
1390 (set! numlen (+ numlen (- mindig format:fn-dot))))
1391 (if (and (= format:fn-dot 0) (not mindig))
1392 (set! numlen (+ numlen 1)))
1393 (if (< numlen width)
1394 (case modifier
1395 ((colon)
1396 (if (not format:fn-pos?)
1397 (format:out-char #\-))
1398 (format:out-fill (- width numlen) (integer->char padch)))
1399 ((at)
1400 (format:out-fill (- width numlen) (integer->char padch))
1401 (format:out-char (if format:fn-pos? #\+ #\-)))
1402 ((colon-at)
1403 (format:out-char (if format:fn-pos? #\+ #\-))
1404 (format:out-fill (- width numlen) (integer->char padch)))
1405 (else
1406 (format:out-fill (- width numlen) (integer->char padch))
1407 (if (not format:fn-pos?)
1408 (format:out-char #\-))))
1409 (if format:fn-pos?
1410 (if (memq modifier '(at colon-at)) (format:out-char #\+))
1411 (format:out-char #\-))))
093d2ca9 1412 (if (and mindig (> mindig format:fn-dot))
cd5fea8d 1413 (format:out-fill (- mindig format:fn-dot) #\0))
093d2ca9 1414 (if (and (= format:fn-dot 0) (not mindig))
cd5fea8d
KR
1415 (format:out-char #\0))
1416 (format:out-substr format:fn-str 0 format:fn-dot)
1417 (format:out-char #\.)
1418 (format:out-substr format:fn-str format:fn-dot format:fn-len)))))))
093d2ca9
MV
1419
1420 ; the flonum buffers
1421
1422 (format:fn-max 400) ; max. number of number digits
1423 (format:fn-str #f) ; number buffer
1424 (format:fn-len 0) ; digit length of number
1425 (format:fn-dot #f) ; dot position of number
1426 (format:fn-pos? #t) ; number positive?
1427 (format:en-max 10) ; max. number of exponent digits
1428 (format:en-str #f) ; exponent buffer
1429 (format:en-len 0) ; digit length of exponent
1430 (format:en-pos? #t) ; exponent positive?
1431
1432 (format:parse-float
1433 (lambda (num-str fixed? scale)
1434 (set! format:fn-pos? #t)
1435 (set! format:fn-len 0)
1436 (set! format:fn-dot #f)
1437 (set! format:en-pos? #t)
1438 (set! format:en-len 0)
1439 (do ((i 0 (+ i 1))
1440 (left-zeros 0)
1441 (mantissa? #t)
1442 (all-zeros? #t)
1443 (num-len (string-length num-str))
1444 (c #f)) ; current exam. character in num-str
1445 ((= i num-len)
1446 (if (not format:fn-dot)
1447 (set! format:fn-dot format:fn-len))
1448
1449 (if all-zeros?
1450 (begin
1451 (set! left-zeros 0)
1452 (set! format:fn-dot 0)
1453 (set! format:fn-len 1)))
1454
1455 ;; now format the parsed values according to format's need
1456
1457 (if fixed?
1458
1459 (begin ; fixed format m.nnn or .nnn
1460 (if (and (> left-zeros 0) (> format:fn-dot 0))
1461 (if (> format:fn-dot left-zeros)
1462 (begin ; norm 0{0}nn.mm to nn.mm
1463 (format:fn-shiftleft left-zeros)
1464 (set! left-zeros 0)
1465 (set! format:fn-dot (- format:fn-dot left-zeros)))
1466 (begin ; normalize 0{0}.nnn to .nnn
1467 (format:fn-shiftleft format:fn-dot)
1468 (set! left-zeros (- left-zeros format:fn-dot))
1469 (set! format:fn-dot 0))))
1470 (if (or (not (= scale 0)) (> format:en-len 0))
1471 (let ((shift (+ scale (format:en-int))))
1472 (cond
1473 (all-zeros? #t)
1474 ((> (+ format:fn-dot shift) format:fn-len)
1475 (format:fn-zfill
1476 #f (- shift (- format:fn-len format:fn-dot)))
1477 (set! format:fn-dot format:fn-len))
1478 ((< (+ format:fn-dot shift) 0)
1479 (format:fn-zfill #t (- (- shift) format:fn-dot))
1480 (set! format:fn-dot 0))
1481 (else
1482 (if (> left-zeros 0)
1483 (if (<= left-zeros shift) ; shift always > 0 here
1484 (format:fn-shiftleft shift) ; shift out 0s
1485 (begin
1486 (format:fn-shiftleft left-zeros)
1487 (set! format:fn-dot (- shift left-zeros))))
1488 (set! format:fn-dot (+ format:fn-dot shift))))))))
1489
1490 (let ((negexp ; expon format m.nnnEee
1491 (if (> left-zeros 0)
1492 (- left-zeros format:fn-dot -1)
1493 (if (= format:fn-dot 0) 1 0))))
1494 (if (> left-zeros 0)
1495 (begin ; normalize 0{0}.nnn to n.nn
1496 (format:fn-shiftleft left-zeros)
1497 (set! format:fn-dot 1))
1498 (if (= format:fn-dot 0)
1499 (set! format:fn-dot 1)))
1500 (format:en-set (- (+ (- format:fn-dot scale) (format:en-int))
1501 negexp))
1502 (cond
1503 (all-zeros?
1504 (format:en-set 0)
1505 (set! format:fn-dot 1))
1506 ((< scale 0) ; leading zero
1507 (format:fn-zfill #t (- scale))
1508 (set! format:fn-dot 0))
1509 ((> scale format:fn-dot)
1510 (format:fn-zfill #f (- scale format:fn-dot))
1511 (set! format:fn-dot scale))
1512 (else
1513 (set! format:fn-dot scale)))))
1514 #t)
1515
1516 ;; do body
1517 (set! c (string-ref num-str i)) ; parse the output of number->string
1518 (cond ; which can be any valid number
1519 ((char-numeric? c) ; representation of R4RS except
1520 (if mantissa? ; complex numbers
1521 (begin
1522 (if (char=? c #\0)
1523 (if all-zeros?
1524 (set! left-zeros (+ left-zeros 1)))
1525 (begin
1526 (set! all-zeros? #f)))
1527 (string-set! format:fn-str format:fn-len c)
1528 (set! format:fn-len (+ format:fn-len 1)))
1529 (begin
1530 (string-set! format:en-str format:en-len c)
1531 (set! format:en-len (+ format:en-len 1)))))
1532 ((or (char=? c #\-) (char=? c #\+))
1533 (if mantissa?
1534 (set! format:fn-pos? (char=? c #\+))
1535 (set! format:en-pos? (char=? c #\+))))
1536 ((char=? c #\.)
7da43e41 1537 (set! format:fn-dot format:fn-len))
093d2ca9
MV
1538 ((char=? c #\e)
1539 (set! mantissa? #f))
1540 ((char=? c #\E)
1541 (set! mantissa? #f))
1542 ((char-whitespace? c) #t)
1543 ((char=? c #\d) #t) ; decimal radix prefix
1544 ((char=? c #\#) #t)
7da43e41 1545 (else
093d2ca9
MV
1546 (format:error "illegal character `~c' in number->string" c))))))
1547
1548 (format:en-int
1549 (lambda () ; convert exponent string to integer
1550 (if (= format:en-len 0)
1551 0
1552 (do ((i 0 (+ i 1))
1553 (n 0))
1554 ((= i format:en-len)
1555 (if format:en-pos?
1556 n
1557 (- n)))
1558 (set! n (+ (* n 10) (- (char->integer (string-ref format:en-str i))
1559 format:zero-ch)))))))
1560
1561 (format:en-set ; set exponent string number
1562 (lambda (en)
1563 (set! format:en-len 0)
1564 (set! format:en-pos? (>= en 0))
1565 (let ((en-str (number->string en)))
1566 (do ((i 0 (+ i 1))
1567 (en-len (string-length en-str))
1568 (c #f))
1569 ((= i en-len))
1570 (set! c (string-ref en-str i))
1571 (if (char-numeric? c)
1572 (begin
1573 (string-set! format:en-str format:en-len c)
1574 (set! format:en-len (+ format:en-len 1))))))))
1575
1576 (format:fn-zfill ; fill current number string with 0s
1577 (lambda (left? n)
1578 (if (> (+ n format:fn-len) format:fn-max) ; from the left or right
1579 (format:error "number is too long to format (enlarge format:fn-max)"))
1580 (set! format:fn-len (+ format:fn-len n))
1581 (if left?
1582 (do ((i format:fn-len (- i 1))) ; fill n 0s to left
1583 ((< i 0))
1584 (string-set! format:fn-str i
1585 (if (< i n)
1586 #\0
1587 (string-ref format:fn-str (- i n)))))
1588 (do ((i (- format:fn-len n) (+ i 1))) ; fill n 0s to the right
1589 ((= i format:fn-len))
1590 (string-set! format:fn-str i #\0)))))
1591
1592 (format:fn-shiftleft ; shift left current number n positions
1593 (lambda (n)
1594 (if (> n format:fn-len)
1595 (format:error "internal error in format:fn-shiftleft (~d,~d)"
1596 n format:fn-len))
1597 (do ((i n (+ i 1)))
1598 ((= i format:fn-len)
1599 (set! format:fn-len (- format:fn-len n)))
1600 (string-set! format:fn-str (- i n) (string-ref format:fn-str i)))))
1601
1602 (format:fn-round ; round format:fn-str
1603 (lambda (digits)
1604 (set! digits (+ digits format:fn-dot))
1605 (do ((i digits (- i 1)) ; "099",2 -> "10"
1606 (c 5)) ; "023",2 -> "02"
1607 ((or (= c 0) (< i 0)) ; "999",2 -> "100"
1608 (if (= c 1) ; "005",2 -> "01"
1609 (begin ; carry overflow
1610 (set! format:fn-len digits)
1611 (format:fn-zfill #t 1) ; add a 1 before fn-str
1612 (string-set! format:fn-str 0 #\1)
1613 (set! format:fn-dot (+ format:fn-dot 1)))
1614 (set! format:fn-len digits)))
1615 (set! c (+ (- (char->integer (string-ref format:fn-str i))
1616 format:zero-ch) c))
1617 (string-set! format:fn-str i (integer->char
1618 (if (< c 10)
1619 (+ c format:zero-ch)
1620 (+ (- c 10) format:zero-ch))))
1621 (set! c (if (< c 10) 0 1)))))
1622
1623 (format:fn-out
1624 (lambda (modifier add-leading-zero?)
1625 (if format:fn-pos?
1626 (if (eq? modifier 'at)
1627 (format:out-char #\+))
1628 (format:out-char #\-))
1629 (if (= format:fn-dot 0)
1630 (if add-leading-zero?
1631 (format:out-char #\0))
1632 (format:out-substr format:fn-str 0 format:fn-dot))
1633 (format:out-char #\.)
1634 (format:out-substr format:fn-str format:fn-dot format:fn-len)))
1635
1636 (format:en-out
1637 (lambda (edigits expch)
1638 (format:out-char (if expch (integer->char expch) format:expch))
1639 (format:out-char (if format:en-pos? #\+ #\-))
1640 (if edigits
1641 (if (< format:en-len edigits)
1642 (format:out-fill (- edigits format:en-len) #\0)))
1643 (format:out-substr format:en-str 0 format:en-len)))
1644
1645 (format:fn-strip ; strip trailing zeros but one
1646 (lambda ()
1647 (string-set! format:fn-str format:fn-len #\0)
1648 (do ((i format:fn-len (- i 1)))
1649 ((or (not (char=? (string-ref format:fn-str i) #\0))
1650 (<= i format:fn-dot))
1651 (set! format:fn-len (+ i 1))))))
7da43e41 1652
093d2ca9
MV
1653 (format:fn-zlead ; count leading zeros
1654 (lambda ()
1655 (do ((i 0 (+ i 1)))
1656 ((or (= i format:fn-len)
1657 (not (char=? (string-ref format:fn-str i) #\0)))
1658 (if (= i format:fn-len) ; found a real zero
1659 0
1660 i)))))
7da43e41 1661
7da43e41 1662
093d2ca9 1663;;; some global functions not found in SLIB
7da43e41 1664
093d2ca9
MV
1665 (string-capitalize-first ; "hello" -> "Hello"
1666 (lambda (str)
1667 (let ((cap-str (string-copy str)) ; "hELLO" -> "Hello"
1668 (non-first-alpha #f) ; "*hello" -> "*Hello"
1669 (str-len (string-length str))) ; "hello you" -> "Hello you"
1670 (do ((i 0 (+ i 1)))
1671 ((= i str-len) cap-str)
1672 (let ((c (string-ref str i)))
1673 (if (char-alphabetic? c)
1674 (if non-first-alpha
1675 (string-set! cap-str i (char-downcase c))
1676 (begin
1677 (set! non-first-alpha #t)
1678 (string-set! cap-str i (char-upcase c))))))))))
7da43e41 1679
093d2ca9
MV
1680 ;; Aborts the program when a formatting error occures. This is a null
1681 ;; argument closure to jump to the interpreters toplevel continuation.
7da43e41 1682
093d2ca9
MV
1683 (format:abort (lambda () (error "error in format"))))
1684
1685 (set! format:error-save format:error)
1686 (set! format:fn-str (make-string format:fn-max)) ; number buffer
1687 (set! format:en-str (make-string format:en-max)) ; exponent buffer
1688 (apply format:format args)))
d02655f7 1689
14469b7c 1690;; Thanks to Shuji Narazaki
296ff5e7 1691(module-set! the-root-module 'format format)