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