123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- ; Part of Scheme 48 1.9. See file COPYING for notices and license.
- ; Authors: Richard Kelsey, Jonathan Rees, Mike Sperber
- ; Quicky FORMAT
- ;
- ; (FORMAT port string . args)
- ;
- ; PORT is one of:
- ; an output port, in which case FORMAT prints to the port;
- ; #T, FORMAT prints to the current output port;
- ; #F, FORMAT returns a string.
- ;
- ; The following format directives have been implemented:
- ; ~~ -prints a single ~
- ; ~A -prints the next argument using DISPLAY
- ; ~D -prints the next argument using NUMBER->STRING (`D'ecimal)
- ; ~S -prints the next argument using WRITE
- ; ~% -prints a NEWLINE character
- ; ~& -prints a NEWLINE character if the previous printed character was not one
- ; (this is implemented using FRESH-LINE)
- ; ~? -performs a recursive call to FORMAT using the next two arguments as the
- ; string and the list of arguments
- ;
- ; FORMAT is case-insensitive with respect to letter directives (~a and ~A have
- ; the same effect).
- ; The entry point. Gets the port and writes the output.
- ; Get the appropriate writer for the port specification.
- (define (format port string . args)
- (cond ((not port)
- (call-with-string-output-port
- (lambda (port)
- (real-format port string args))))
- ((eq? port #t)
- (real-format (current-output-port) string args))
- ((output-port? port)
- (real-format port string args))
- (else
- (assertion-violation 'format "invalid port argument" port))))
- ; Loop down the format string printing characters and dispatching on directives
- ; as required. Procedures for the directives are in a vector indexed by
- ; character codes. Each procedure takes four arguments: the format string,
- ; the index of the next unused character in the format string, the list of
- ; remaining arguments, and the writer. Each should return a list of the unused
- ; arguments.
- (define (real-format out string all-args)
- (let loop ((i 0) (args all-args))
- (cond ((>= i (string-length string))
- (if (null? args)
- #f
- (assertion-violation 'format "too many arguments" string all-args)))
- ((char=? #\~ (string-ref string i))
- (if (= (+ i 1) (string-length string))
- (assertion-violation 'format "invalid format string" string i)
- (loop (+ i 2)
- ((vector-ref format-dispatch-vector
- (char->ascii (string-ref string (+ i 1))))
- string
- (+ i 2)
- args
- out))))
- (else
- (write-char (string-ref string i) out)
- (loop (+ i 1) args)))))
- ; One more than the highest integer that CHAR->ASCII may return.
- (define number-of-char-codes ascii-limit)
- ; The vector of procedures implementing format directives.
- (define format-dispatch-vector
- (make-vector number-of-char-codes
- (lambda (string i args out)
- (assertion-violation 'format
- "illegal format command"
- string
- (string-ref string (- i 1))))))
- ; This implements FORMAT's case-insensitivity.
- (define (define-format-command char proc)
- (vector-set! format-dispatch-vector (char->ascii char) proc)
- (if (char-alphabetic? char)
- (vector-set! format-dispatch-vector
- (char->ascii (if (char-lower-case? char)
- (char-upcase char)
- (char-downcase char)))
- proc)))
- ; Write a single ~ character.
- (define-format-command #\~
- (lambda (string i args out)
- (write-char #\~ out)
- args))
- ; Newline
- (define-format-command #\%
- (lambda (string i args out)
- (newline out)
- args))
- ; Fresh-Line
- (define-format-command #\&
- (lambda (string i args out)
- (fresh-line out)
- args))
- ; Display (`A' is for ASCII)
- (define-format-command #\a
- (lambda (string i args out)
- (check-for-format-arg args)
- (display (car args) out)
- (cdr args)))
- ; Decimals
- (define-format-command #\d
- (lambda (string i args out)
- (check-for-format-arg args)
- (if (not (number? (car args)))
- (assertion-violation 'format "invalid number argument to ~D" string (car args)))
- (display (number->string (car args) 10) out)
- (cdr args)))
- ; Write (`S' is for S-expression)
- (define-format-command #\s
- (lambda (string i args out)
- (check-for-format-arg args)
- (write (car args) out)
- (cdr args)))
- ; Recursion
- (define-format-command #\?
- (lambda (string i args out)
- (check-for-format-arg args)
- (check-for-format-arg (cdr args))
- (real-format out (car args) (cadr args))
- (cddr args)))
- ; Signal an error if ARGS is empty.
- (define (check-for-format-arg args)
- (if (null? args)
- (assertion-violation 'format "insufficient number of arguments")))
|