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