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