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