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