808 lines
27 KiB
Scheme
808 lines
27 KiB
Scheme
;;; Ikarus Scheme -- A compiler for R6RS Scheme.
|
|
;;; Copyright (C) 2006,2007,2008 Abdulaziz Ghuloum
|
|
;;;
|
|
;;; This program is free software: you can redistribute it and/or modify
|
|
;;; it under the terms of the GNU General Public License version 3 as
|
|
;;; published by the Free Software Foundation.
|
|
;;;
|
|
;;; This program is distributed in the hope that it will be useful, but
|
|
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
;;; General Public License for more details.
|
|
;;;
|
|
;;; You should have received a copy of the GNU General Public License
|
|
;;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
(library (ikarus writer)
|
|
|
|
(export write display format printf fprintf print-error
|
|
print-unicode print-graph put-datum traverse
|
|
traversal-helpers)
|
|
|
|
(import
|
|
(rnrs hashtables)
|
|
(ikarus system $chars)
|
|
(ikarus system $strings)
|
|
(ikarus system $vectors)
|
|
(ikarus system $fx)
|
|
(ikarus system $pairs)
|
|
(ikarus system $symbols)
|
|
(ikarus system $bytevectors)
|
|
(ikarus system $transcoders)
|
|
(only (ikarus system $foreign) pointer? pointer->integer)
|
|
(only (ikarus.pretty-formats) get-fmt)
|
|
(except (ikarus)
|
|
write display format printf fprintf print-error print-unicode print-graph
|
|
put-datum))
|
|
|
|
(define print-unicode
|
|
(make-parameter #t))
|
|
|
|
|
|
(module traversal-helpers
|
|
(cyclic-set? shared-set? mark-set? set-mark! set-shared! shared?
|
|
shared-bit cyclic-bit marked-bit mark-shift
|
|
make-cache cache-string cache-object cache-next)
|
|
;;; association list in hash table is one of the following forms:
|
|
;;;
|
|
;;; a fixnum:
|
|
(define cyclic-bit #b001)
|
|
(define shared-bit #b010)
|
|
(define marked-bit #b100)
|
|
(define mark-shift 3)
|
|
;;;
|
|
;;; or a pair of a fixnum (above) and a cache:
|
|
(define-struct cache (string object next))
|
|
(define (cyclic-set? b)
|
|
(fx= (fxand b cyclic-bit) cyclic-bit))
|
|
(define (shared-set? b)
|
|
(fx= (fxand b shared-bit) shared-bit))
|
|
(define (mark-set? b)
|
|
(fx= (fxand b marked-bit) marked-bit))
|
|
|
|
(define (set-mark! x h n)
|
|
(let ([b (hashtable-ref h x #f)])
|
|
(cond
|
|
[(fixnum? b)
|
|
(hashtable-set! h x
|
|
(fxior (fxsll n mark-shift) marked-bit b))]
|
|
[else
|
|
(set-car! b
|
|
(fxior (fxsll n mark-shift) marked-bit (car b)))])))
|
|
|
|
(define (set-shared! x h)
|
|
(let ([b (hashtable-ref h x #f)])
|
|
(cond
|
|
[(fixnum? b)
|
|
(hashtable-set! h x (fxior shared-bit b))]
|
|
[else
|
|
(set-car! b (fxior shared-bit (car b)))])))
|
|
|
|
(define (shared? x h)
|
|
(cond
|
|
[(hashtable-ref h x #f) =>
|
|
(lambda (b)
|
|
(if (fixnum? b)
|
|
(shared-set? b)
|
|
(let ([b (car b)])
|
|
(shared-set? b))))]
|
|
[else #f])))
|
|
(import traversal-helpers)
|
|
|
|
|
|
(define (cannot-happen)
|
|
(error 'ikarus-writer "internal error"))
|
|
|
|
(define (traverse x h)
|
|
(define (traverse-pair x h)
|
|
(traverse (car x) h)
|
|
(traverse (cdr x) h))
|
|
(define (traverse-vector x h)
|
|
(let f ([i 0] [n (vector-length x)])
|
|
(unless (fx=? i n)
|
|
(traverse (vector-ref x i) h)
|
|
(f (fx+ i 1) n))))
|
|
(define (traverse-noop x h) (void))
|
|
(define (traverse-struct x h)
|
|
(define (traverse-vanilla-struct x h)
|
|
(let ([rtd (struct-type-descriptor x)])
|
|
(unless
|
|
(and (record-type-descriptor? rtd)
|
|
(record-type-opaque? rtd))
|
|
(traverse (struct-name x) h)
|
|
(let ([n (struct-length x)])
|
|
(let f ([idx 0])
|
|
(unless (fx= idx n)
|
|
(traverse (struct-ref x idx) h)
|
|
(f (fxadd1 idx))))))))
|
|
(define (traverse-custom-struct x h printer)
|
|
(let-values ([(p e) (open-string-output-port)])
|
|
(let ([cache #f])
|
|
(printer x p
|
|
(lambda (v)
|
|
(let ([str (e)])
|
|
(set! cache (make-cache str v cache))
|
|
(traverse v h))))
|
|
(let ([cache (cons (e) cache)]
|
|
[b (hashtable-ref h x #f)])
|
|
(if (fixnum? b)
|
|
(hashtable-set! h x (cons b cache))
|
|
(cannot-happen))))))
|
|
(let ([printer (struct-printer x)])
|
|
(if (procedure? printer)
|
|
(traverse-custom-struct x h printer)
|
|
(traverse-vanilla-struct x h))))
|
|
(define (traverse-shared x h k)
|
|
(cond
|
|
[(hashtable-ref h x #f) =>
|
|
(lambda (b)
|
|
(cond
|
|
[(fixnum? b)
|
|
(hashtable-set! h x (fxior b shared-bit))]
|
|
[else
|
|
(set-car! b (fxior (car b) shared-bit))]))]
|
|
[else
|
|
(hashtable-set! h x 0)
|
|
(k x h)
|
|
(let ([b (hashtable-ref h x #f)])
|
|
(cond
|
|
[(fixnum? b)
|
|
(when (shared-set? b)
|
|
(hashtable-set! h x (fxior b cyclic-bit)))]
|
|
[else
|
|
(let ([a (car b)])
|
|
(when (shared-set? a)
|
|
(set-car! b (fxior a cyclic-bit))))]))]))
|
|
(define (traverse x h)
|
|
(cond
|
|
[(pair? x) (traverse-shared x h traverse-pair)]
|
|
[(vector? x) (traverse-shared x h traverse-vector)]
|
|
[(struct? x) (traverse-shared x h traverse-struct)]
|
|
[(bytevector? x) (traverse-shared x h traverse-noop)]
|
|
[(gensym? x) (traverse-shared x h traverse-noop)]
|
|
[else (void)]))
|
|
(traverse x h))
|
|
|
|
(define (wr x p m h i)
|
|
(define (write-fixnum x p)
|
|
(define loop
|
|
(lambda (x p)
|
|
(unless (fxzero? x)
|
|
(loop (fxquotient x 10) p)
|
|
(write-char
|
|
(integer->char
|
|
(fx+ (fxremainder x 10)
|
|
(char->integer #\0)))
|
|
p))))
|
|
(cond
|
|
[(fxzero? x) (write-char #\0 p)]
|
|
[(fx< x 0)
|
|
(write-char* (fixnum->string x) p)]
|
|
[else (loop x p)]))
|
|
(define (write-pair x p m h i)
|
|
(define (macro x h)
|
|
(and
|
|
(pair? x)
|
|
(let ([a (car x)])
|
|
(and (symbol? a)
|
|
(let ([d (cdr x)])
|
|
(and (pair? d)
|
|
(null? (cdr d))
|
|
(not (shared? d h))))
|
|
(let ([p ((pretty-format a))])
|
|
(and (pair? p)
|
|
(eq? (car p) 'read-macro)
|
|
(let ([d (cdr p)])
|
|
(and (string? d) d))))))))
|
|
(define (f d i)
|
|
(cond
|
|
[(null? d) i]
|
|
[(or (not (pair? d)) (shared? d h))
|
|
(write-char #\space p)
|
|
(write-char #\. p)
|
|
(write-char #\space p)
|
|
(wr d p m h i)]
|
|
[else
|
|
(write-char #\space p)
|
|
(let ([i (wr (car d) p m h i)])
|
|
(f (cdr d) i))]))
|
|
(cond
|
|
[(macro x h) =>
|
|
(lambda (a)
|
|
(write-string a p #f)
|
|
(wr (cadr x) p m h i))]
|
|
[else
|
|
(write-char #\( p)
|
|
(let ([i (f (cdr x) (wr (car x) p m h i))])
|
|
(write-char #\) p)
|
|
i)]))
|
|
(define (write-vector x p m h i)
|
|
(define (f x p m h i idx n)
|
|
(cond
|
|
[(fx= idx n) i]
|
|
[else
|
|
(write-char #\space p)
|
|
(let ([i (wr (vector-ref x idx) p m h i)])
|
|
(f x p m h i (fx+ idx 1) n))]))
|
|
(write-char #\# p)
|
|
(let ([n (vector-length x)])
|
|
(cond
|
|
[(fx=? n 0)
|
|
(write-char #\( p)
|
|
(write-char #\) p)
|
|
i]
|
|
[else
|
|
(write-char #\( p)
|
|
(let ([i (wr (vector-ref x 0) p m h i)])
|
|
(f x p m h i 1 n)
|
|
(write-char #\) p)
|
|
i)])))
|
|
(define (write-bytevector x p m h i)
|
|
(write-char #\# p)
|
|
(write-char #\v p)
|
|
(write-char #\u p)
|
|
(write-char #\8 p)
|
|
(write-char #\( p)
|
|
(let ([n (bytevector-length x)])
|
|
(when (fx> n 0)
|
|
(write-fixnum (bytevector-u8-ref x 0) p)
|
|
(let f ([idx 1] [n n] [x x] [p p])
|
|
(unless (fx= idx n)
|
|
(write-char #\space p)
|
|
(write-fixnum (bytevector-u8-ref x idx) p)
|
|
(f (fxadd1 idx) n x p)))))
|
|
(write-char #\) p)
|
|
i)
|
|
(define (write-positive-hex-fx n p)
|
|
(unless (fx= n 0)
|
|
(write-positive-hex-fx (fxsra n 4) p)
|
|
(let ([n (fxand n #xF)])
|
|
(cond
|
|
[(fx<= n 9)
|
|
(write-char (integer->char
|
|
(fx+ (char->integer #\0) n))
|
|
p)]
|
|
[else
|
|
(write-char (integer->char
|
|
(fx+ (char->integer #\A) (fx- n 10)))
|
|
p)]))))
|
|
(define (write-inline-hex b p)
|
|
(write-char #\\ p)
|
|
(write-char #\x p)
|
|
(if (fxzero? b)
|
|
(write-char #\0 p)
|
|
(write-positive-hex-fx b p))
|
|
(write-char #\; p))
|
|
(define (write-character x p m)
|
|
(define char-table ; first nonprintable chars
|
|
'#("nul" "x1" "x2" "x3" "x4" "x5" "x6" "alarm"
|
|
"backspace" "tab" "linefeed" "vtab" "page" "return" "xE" "xF"
|
|
"x10" "x11" "x12" "x13" "x14" "x15" "x16" "x17"
|
|
"x18" "x19" "x1A" "esc" "x1C" "x1D" "x1E" "x1F"
|
|
"space"))
|
|
(if m
|
|
(let ([i (char->integer x)])
|
|
(write-char #\# p)
|
|
(cond
|
|
[(fx< i (vector-length char-table))
|
|
(write-char #\\ p)
|
|
(write-char* (vector-ref char-table i) p)]
|
|
[(fx< i 127)
|
|
(write-char #\\ p)
|
|
(write-char x p)]
|
|
[(fx= i 127)
|
|
(write-char #\\ p)
|
|
(write-char* "delete" p)]
|
|
[(and (print-unicode) (unicode-printable-char? x))
|
|
(write-char #\\ p)
|
|
(write-char x p)]
|
|
[else
|
|
(write-char #\\ p)
|
|
(write-char #\x p)
|
|
(write-positive-hex-fx i p)]))
|
|
(write-char x p)))
|
|
(define (write-string x p m)
|
|
(define (write-string-escape x p)
|
|
;;; commonize with write-symbol-bar-escape
|
|
(define (loop x i n p)
|
|
(unless (fx= i n)
|
|
(let* ([c (string-ref x i)]
|
|
[b (char->integer c)])
|
|
(cond
|
|
[(fx< b 32)
|
|
(cond
|
|
[(fx< b 7)
|
|
(write-inline-hex b p)]
|
|
[(fx< b 14)
|
|
(write-char #\\ p)
|
|
(write-char (string-ref "abtnvfr" (fx- b 7)) p)]
|
|
[else
|
|
(write-inline-hex b p)])]
|
|
[(or (char=? #\" c) (char=? #\\ c))
|
|
(write-char #\\ p)
|
|
(write-char c p)]
|
|
[(fx< b 127)
|
|
(write-char c p)]
|
|
[(print-unicode)
|
|
(write-char c p)]
|
|
[else
|
|
(write-inline-hex b p)]))
|
|
(loop x (fxadd1 i) n p)))
|
|
(write-char #\" p)
|
|
(loop x 0 (string-length x) p)
|
|
(write-char #\" p))
|
|
(if m
|
|
(write-string-escape x p)
|
|
(write-char* x p)))
|
|
(module (write-gensym write-symbol)
|
|
(define (write-gensym x p m h i)
|
|
(cond
|
|
[(and m (print-gensym)) =>
|
|
(lambda (gensym-how)
|
|
(case gensym-how
|
|
[(pretty)
|
|
(let ([str (symbol->string x)])
|
|
(write-char #\# p)
|
|
(write-char #\: p)
|
|
(write-symbol-string str p m))]
|
|
[else
|
|
(let ([str (symbol->string x)]
|
|
[ustr (gensym->unique-string x)])
|
|
(write-char #\# p)
|
|
(write-char #\{ p)
|
|
(write-symbol-string str p m)
|
|
(write-char #\space p)
|
|
(write-symbol-bar-esc ustr p)
|
|
(write-char #\} p))])
|
|
i)]
|
|
[else
|
|
(write-symbol x p m)
|
|
i]))
|
|
(define write-symbol-bar-esc
|
|
(lambda (x p)
|
|
(define write-symbol-bar-esc-loop
|
|
(lambda (x i n p)
|
|
(unless (fx= i n)
|
|
(let* ([c (string-ref x i)]
|
|
[b (char->integer c)])
|
|
(cond
|
|
[(fx< b 32)
|
|
(cond
|
|
[(fx< b 7)
|
|
(write-inline-hex b p)]
|
|
[(fx< b 14)
|
|
(write-char #\\ p)
|
|
(write-char (string-ref "abtnvfr" (fx- b 7)) p)]
|
|
[else
|
|
(write-inline-hex b p)])]
|
|
[(memq c '(#\\ #\|))
|
|
(write-char #\\ p)
|
|
(write-char c p)]
|
|
[(fx< b 127)
|
|
(write-char c p)]
|
|
[else
|
|
(write-inline-hex b p)]))
|
|
(write-symbol-bar-esc-loop x (fxadd1 i) n p))))
|
|
(write-char #\| p)
|
|
(write-symbol-bar-esc-loop x 0 (string-length x) p)
|
|
(write-char #\| p)))
|
|
(define (write-symbol-string str p m)
|
|
(define-syntax ascii-map
|
|
(lambda (x)
|
|
(syntax-case x ()
|
|
[(stx str) (string? (syntax->datum #'str))
|
|
(let ([s (syntax->datum #'str)]
|
|
[bv (make-bytevector 16 0)])
|
|
(for-each
|
|
(lambda (c)
|
|
(let ([b (char->integer c)])
|
|
(let ([i (fxlogand b 7)]
|
|
[j (fxsra b 3)])
|
|
(bytevector-u8-set! bv j
|
|
(fxlogor (bytevector-u8-ref bv j)
|
|
(fxsll 1 i))))))
|
|
(string->list s))
|
|
(with-syntax ([bv (datum->syntax #'stx bv)])
|
|
#'(quote bv)))])))
|
|
(define subsequents-map
|
|
(ascii-map
|
|
"!$%&*/:<=>?^_~+-.@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"))
|
|
(define initials-map
|
|
(ascii-map
|
|
"!$%&*/:<=>?^_~abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"))
|
|
(define initial-categories
|
|
'(Lu Ll Lt Lm Lo Mn Nl No Pd Pc Po Sc Sm Sk So Co))
|
|
(define subsequent-categories
|
|
'(Nd Mc Me))
|
|
(define (in-map? byte map)
|
|
(let ([i (fxand byte 7)]
|
|
[j (fxsra byte 3)])
|
|
(and
|
|
(fx< j (bytevector-length map))
|
|
(let ([mask (fxsll 1 i)])
|
|
(not (fxzero?
|
|
(fxlogand mask
|
|
(bytevector-u8-ref map j))))))))
|
|
(define (subsequent*? str i n)
|
|
(or (fx= i n)
|
|
(and (subsequent? (string-ref str i))
|
|
(subsequent*? str (fxadd1 i) n))))
|
|
(define (subsequent? x)
|
|
(define (digit? c)
|
|
(and (char<=? #\0 c) (char<=? c #\9)))
|
|
(define (special-subsequent? x)
|
|
(memq x '(#\+ #\- #\. #\@)))
|
|
(define (special-initial? x)
|
|
(memq x '(#\! #\$ #\% #\& #\* #\/ #\: #\< #\= #\> #\? #\^ #\_ #\~)))
|
|
(define (letter? c)
|
|
(or (and (char<=? #\a c) (char<=? c #\z))
|
|
(and (char<=? #\A c) (char<=? c #\Z))))
|
|
(define (initial? c)
|
|
(or (letter? c) (special-initial? c)))
|
|
(or (initial? x)
|
|
(digit? x)
|
|
(special-subsequent? x)))
|
|
(define (peculiar-symbol-string? str)
|
|
(let ([n (string-length str)])
|
|
(cond
|
|
[(fx= n 1)
|
|
(memq (string-ref str 0) '(#\+ #\-))]
|
|
[(fx>= n 2)
|
|
(or (and (char=? (string-ref str 0) #\-)
|
|
(char=? (string-ref str 1) #\>)
|
|
(subsequent*? str 2 n))
|
|
(string=? str "..."))]
|
|
[else #f])))
|
|
(define (write-symbol-hex-esc str p)
|
|
(let ([n (string-length str)])
|
|
(cond
|
|
[(fx= n 0)
|
|
(write-char #\| p)
|
|
(write-char #\| p)]
|
|
[else
|
|
(let* ([c0 (string-ref str 0)]
|
|
[b0 (char->integer c0)])
|
|
(cond
|
|
[(in-map? b0 initials-map)
|
|
(write-char c0 p)]
|
|
[(fx< b0 128) (write-inline-hex b0 p)]
|
|
[(and (print-unicode)
|
|
(memq (char-general-category c0) initial-categories))
|
|
(write-char c0 p)]
|
|
[else (write-inline-hex b0 p)])
|
|
(write-subsequent* str 1 n p))])))
|
|
(define (write-subsequent* str i j p)
|
|
(unless (fx= i j)
|
|
(let* ([c (string-ref str i)]
|
|
[b (char->integer c)])
|
|
(cond
|
|
[(in-map? b subsequents-map)
|
|
(write-char c p)]
|
|
[(fx< b 128)
|
|
(write-inline-hex b p)]
|
|
[(and (print-unicode)
|
|
(let ([cat (char-general-category c)])
|
|
(or (memq cat initial-categories)
|
|
(memq cat subsequent-categories))))
|
|
(write-char c p)]
|
|
[else
|
|
(write-inline-hex b p)]))
|
|
(write-subsequent* str (fxadd1 i) j p)))
|
|
(define (write-peculiar str p)
|
|
(let ([n (string-length str)])
|
|
(cond
|
|
[(fx= n 1)
|
|
(write-char (string-ref str 0) p)]
|
|
[(and (fx>= n 2)
|
|
(char=? (string-ref str 0) #\-)
|
|
(char=? (string-ref str 1) #\>))
|
|
(write-char #\- p)
|
|
(write-char #\> p)
|
|
(write-subsequent* str 2 n p)]
|
|
[(string=? str "...")
|
|
(write-char #\. p)
|
|
(write-char #\. p)
|
|
(write-char #\. p)]
|
|
[else (error 'write-peculiear "BUG")])))
|
|
(if m
|
|
(if (peculiar-symbol-string? str)
|
|
(write-peculiar str p)
|
|
(write-symbol-hex-esc str p))
|
|
(write-char* str p)))
|
|
(define (write-symbol x p m)
|
|
(write-symbol-string (symbol->string x) p m)))
|
|
(define (write-struct x p m h i)
|
|
(define (write-vanilla-struct x p m h i)
|
|
(cond
|
|
[(let ([rtd (struct-type-descriptor x)])
|
|
(and (record-type-descriptor? rtd)
|
|
(record-type-opaque? rtd)))
|
|
(write-char* "#<unknown>" p)
|
|
i]
|
|
[else
|
|
(write-char #\# p)
|
|
(write-char #\[ p)
|
|
(let ([i (wr (struct-name x) p m h i)])
|
|
(let ([n (struct-length x)])
|
|
(let f ([idx 0] [i i])
|
|
(cond
|
|
[(fx= idx n)
|
|
(write-char #\] p)
|
|
i]
|
|
[else
|
|
(write-char #\space p)
|
|
(f (fxadd1 idx)
|
|
(wr (struct-ref x idx) p m h i))]))))]))
|
|
(define (write-custom-struct out p m h i)
|
|
(let ([i
|
|
(let f ([cache (cdr out)])
|
|
(cond
|
|
[(not cache) i]
|
|
[else
|
|
(let ([i (f (cache-next cache))])
|
|
(write-char* (cache-string cache) p)
|
|
(wr (cache-object cache) p m h i))]))])
|
|
(write-char* (car out) p)
|
|
i))
|
|
(let ([b (hashtable-ref h x #f)])
|
|
(cond
|
|
[(pair? b)
|
|
(write-custom-struct (cdr b) p m h i)]
|
|
[else (write-vanilla-struct x p m h i)])))
|
|
(define (write-char* x p)
|
|
(let f ([x x] [p p] [i 0] [n (string-length x)])
|
|
(unless (fx=? i n)
|
|
(write-char (string-ref x i) p)
|
|
(f x p (fx+ i 1) n))))
|
|
(define (write-procedure x p)
|
|
(cond
|
|
[(let ([name (procedure-annotation x)])
|
|
(and (symbol? name) name)) =>
|
|
(lambda (name)
|
|
(write-char* "#<procedure " p)
|
|
(write-string (symbol->string name) p #f)
|
|
(write-char* ">" p))]
|
|
[else (write-char* "#<procedure>" p)]))
|
|
(define (write-port x p)
|
|
(write-char* "#<" p)
|
|
(write-char* (if (output-port? x) "output" "input") p)
|
|
(write-char* "-port " p)
|
|
(write-char* (if (binary-port? x) "(binary) " "(textual) ") p)
|
|
(let ([i (wr (port-id x) p #t h i)])
|
|
(write-char #\> p)
|
|
i))
|
|
(define (write-hex x n p)
|
|
(define s "0123456789ABCDEF")
|
|
(unless (zero? n)
|
|
(write-hex (sra x 4) (- n 1) p)
|
|
(write-char (string-ref s (bitwise-and x #xF)) p)))
|
|
(define (write-shared x p m h i k)
|
|
(let ([b (hashtable-ref h x #f)])
|
|
(let ([b (if (fixnum? b) b (car b))])
|
|
(cond
|
|
[(mark-set? b)
|
|
(write-char #\# p)
|
|
(write-fixnum (fxsra b mark-shift) p)
|
|
(write-char #\# p)
|
|
i]
|
|
[(or (cyclic-set? b)
|
|
(and (shared-set? b) (print-graph)))
|
|
(let ([n i])
|
|
(set-mark! x h n)
|
|
(write-char #\# p)
|
|
(write-fixnum n p)
|
|
(write-char #\= p)
|
|
(k x p m h (+ i 1)))]
|
|
[else
|
|
(k x p m h i)]))))
|
|
(define (wr x p m h i)
|
|
(cond
|
|
[(pair? x) (write-shared x p m h i write-pair)]
|
|
[(symbol? x)
|
|
(if (gensym? x)
|
|
(write-shared x p m h i write-gensym)
|
|
(begin (write-symbol x p m) i))]
|
|
[(fixnum? x) (write-fixnum x p) i]
|
|
[(string? x) (write-string x p m) i]
|
|
[(boolean? x)
|
|
(write-char #\# p)
|
|
(write-char (if x #\t #\f) p)
|
|
i]
|
|
[(char? x) (write-character x p m) i]
|
|
[(null? x) (write-char #\( p) (write-char #\) p) i]
|
|
[(number? x) (write-char* (number->string x) p) i]
|
|
[(vector? x) (write-shared x p m h i write-vector)]
|
|
[(bytevector? x) (write-shared x p m h i write-bytevector)]
|
|
[(procedure? x) (write-procedure x p) i]
|
|
[(port? x) (write-port x p) i]
|
|
[(eq? x (void)) (write-char* "#<void>" p) i]
|
|
[(eof-object? x) (write-char* "#!eof" p) i]
|
|
[(bwp-object? x) (write-char* "#!bwp" p) i]
|
|
[(hashtable? x) (write-char* "#<hashtable>" p) i]
|
|
[(transcoder? x) (write-char* "#<transcoder>" p) i]
|
|
[(struct? x) (write-shared x p m h i write-struct)]
|
|
[(code? x) (write-char* "#<code>" p) i]
|
|
[(pointer? x)
|
|
(write-char* "#<pointer #x" p)
|
|
(write-hex
|
|
(pointer->integer x)
|
|
(if (<= (fixnum-width) 32) 8 16)
|
|
p)
|
|
(write-char* ">" p)]
|
|
[($unbound-object? x) (write-char* "#<unbound-object>" p) i]
|
|
[else (write-char* "#<unknown>" p) i]))
|
|
(wr x p m h i))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(define print-graph (make-parameter #f))
|
|
|
|
(define (write-to-port x p)
|
|
(let ([h (make-eq-hashtable)])
|
|
(traverse x h)
|
|
(wr x p #t h 0))
|
|
(flush-output-port p))
|
|
|
|
(define (display-to-port x p)
|
|
(let ([h (make-eq-hashtable)])
|
|
(traverse x h)
|
|
(wr x p #f h 0))
|
|
(flush-output-port p))
|
|
|
|
(define formatter
|
|
(lambda (who p fmt args)
|
|
;;; first check
|
|
(let f ([i 0] [args args])
|
|
(cond
|
|
[(fx= i (string-length fmt))
|
|
(unless (null? args)
|
|
(die who
|
|
(format
|
|
"extra arguments given for format string \x2036;~a\x2033;"
|
|
fmt)))]
|
|
[else
|
|
(let ([c (string-ref fmt i)])
|
|
(cond
|
|
[(eqv? c #\~)
|
|
(let ([i (fxadd1 i)])
|
|
(when (fx= i (string-length fmt))
|
|
(die who "invalid ~ at end of format string" fmt))
|
|
(let ([c (string-ref fmt i)])
|
|
(cond
|
|
[(memv c '(#\~ #\%)) (f (fxadd1 i) args)]
|
|
[(memv c '(#\a #\s))
|
|
(when (null? args)
|
|
(die who "insufficient arguments"))
|
|
(f (fxadd1 i) (cdr args))]
|
|
[(memv c '(#\b #\o #\x #\d))
|
|
(when (null? args)
|
|
(die who "insufficient arguments"))
|
|
(let ([a (car args)])
|
|
(cond
|
|
[(or (fixnum? a) (bignum? a) (ratnum? a))
|
|
(void)]
|
|
[(flonum? a)
|
|
(unless (eqv? c #\d)
|
|
(die who
|
|
(format "flonums cannot be printed with ~~~a" c)))]
|
|
[else
|
|
(die who "not a number" a)]))
|
|
(f (fxadd1 i) (cdr args))]
|
|
[else
|
|
(die who "invalid sequence character after ~" c)])))]
|
|
[else (f (fxadd1 i) args)]))]))
|
|
;;; then format
|
|
(let f ([i 0] [args args])
|
|
(unless (fx= i (string-length fmt))
|
|
(let ([c (string-ref fmt i)])
|
|
(cond
|
|
[(eqv? c #\~)
|
|
(let ([i (fxadd1 i)])
|
|
(let ([c (string-ref fmt i)])
|
|
(cond
|
|
[(eqv? c #\~)
|
|
(write-char #\~ p)
|
|
(f (fxadd1 i) args)]
|
|
[(eqv? c #\%)
|
|
(write-char #\newline p)
|
|
(f (fxadd1 i) args)]
|
|
[(eqv? c #\a)
|
|
(display-to-port (car args) p)
|
|
(f (fxadd1 i) (cdr args))]
|
|
[(eqv? c #\s)
|
|
(write-to-port (car args) p)
|
|
(f (fxadd1 i) (cdr args))]
|
|
[(assv c '([#\b . 2] [#\o . 8] [#\x . 16] [#\d . 10]))
|
|
=>
|
|
(lambda (x)
|
|
(let ([a (car args)])
|
|
(cond
|
|
[(or (fixnum? a) (bignum? a) (ratnum? a))
|
|
(display-to-port (number->string a (cdr x)) p)]
|
|
[(flonum? a)
|
|
(display-to-port (number->string a) p)]
|
|
[else
|
|
(die who "BUG: not a number" a)]))
|
|
(f (fxadd1 i) (cdr args)))]
|
|
[else (die who "BUG" c)])))]
|
|
[else
|
|
(write-char c p)
|
|
(f (fxadd1 i) args)]))))
|
|
;;; then flush
|
|
(flush-output-port p)))
|
|
|
|
(define fprintf
|
|
(lambda (p fmt . args)
|
|
(assert-open-textual-output-port p 'fprintf)
|
|
(unless (string? fmt)
|
|
(die 'fprintf "not a string" fmt))
|
|
(formatter 'fprintf p fmt args)))
|
|
|
|
(define display-error
|
|
(lambda (errname who fmt args)
|
|
(unless (string? fmt)
|
|
(die 'print-error "not a string" fmt))
|
|
(let ([p (standard-error-port)])
|
|
(if who
|
|
(fprintf p "~a in ~a: " errname who)
|
|
(fprintf p "~a: " errname))
|
|
(formatter 'print-error p fmt args)
|
|
(write-char #\. p)
|
|
(newline p))))
|
|
|
|
(define format
|
|
(lambda (fmt . args)
|
|
(unless (string? fmt)
|
|
(die 'format "not a string" fmt))
|
|
(let-values ([(p e) (open-string-output-port)])
|
|
(formatter 'format p fmt args)
|
|
(e))))
|
|
|
|
(define printf
|
|
(lambda (fmt . args)
|
|
(unless (string? fmt)
|
|
(die 'printf "not a string" fmt))
|
|
(formatter 'printf (current-output-port) fmt args)))
|
|
|
|
(define write
|
|
(case-lambda
|
|
[(x) (write-to-port x (current-output-port))]
|
|
[(x p)
|
|
(assert-open-textual-output-port p 'write)
|
|
(write-to-port x p)]))
|
|
|
|
(define (put-datum p x)
|
|
(assert-open-textual-output-port p 'put-datum)
|
|
(write-to-port x p))
|
|
|
|
(define display
|
|
(case-lambda
|
|
[(x) (display-to-port x (current-output-port))]
|
|
[(x p)
|
|
(assert-open-textual-output-port p 'display)
|
|
(display-to-port x p)]))
|
|
|
|
(define print-error
|
|
(lambda (who fmt . args)
|
|
(display-error "Error" who fmt args)))
|
|
|
|
(define warning
|
|
(lambda (who fmt . args)
|
|
(display-error "Warning" who fmt args)))
|
|
|
|
(define (assert-open-textual-output-port p who)
|
|
(unless (output-port? p)
|
|
(die who "not an output port" p))
|
|
(unless (textual-port? p)
|
|
(die who "not a textual port" p))
|
|
(when (port-closed? p)
|
|
(die who "port is closed" p)))
|
|
|
|
)
|
|
|