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