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