1278 lines
40 KiB
Scheme
1278 lines
40 KiB
Scheme
;; Originally:
|
|
;; "genwrite.scm" generic write used by pp.scm
|
|
;; copyright (c) 1991, marc feeley
|
|
|
|
;; Pretty-printer for MzScheme
|
|
;; Handles structures, cycles, and graphs
|
|
|
|
;; TO INSTALL this pretty-printer into a MzScheme's read-eval-print loop,
|
|
;; require this module and evaluate:
|
|
;; (current-print pretty-print-handler)
|
|
|
|
(module pretty mzscheme
|
|
(require mzlib/private/port)
|
|
|
|
(provide pretty-print
|
|
pretty-display
|
|
pretty-print-columns
|
|
pretty-print-depth
|
|
pretty-print-handler
|
|
pretty-print-size-hook
|
|
pretty-print-print-hook
|
|
pretty-print-pre-print-hook
|
|
pretty-print-post-print-hook
|
|
pretty-print-print-line
|
|
pretty-print-show-inexactness
|
|
pretty-print-exact-as-decimal
|
|
pretty-print-.-symbol-without-bars
|
|
pretty-print-abbreviate-read-macros
|
|
|
|
pretty-print-style-table?
|
|
pretty-print-current-style-table
|
|
pretty-print-extend-style-table
|
|
pretty-print-remap-stylable
|
|
|
|
pretty-format
|
|
pretty-printing
|
|
pretty-print-newline
|
|
make-tentative-pretty-print-output-port
|
|
tentative-pretty-print-port-transfer
|
|
tentative-pretty-print-port-cancel)
|
|
|
|
(define-struct pretty-print-style-table (hash))
|
|
|
|
(define pretty-print-extend-style-table
|
|
(lambda (table symbols like-symbols)
|
|
(let ([terr (lambda (kind which)
|
|
(raise-type-error
|
|
'pretty-print-extend-style-table
|
|
kind
|
|
which
|
|
table symbols like-symbols))])
|
|
(unless (or (not table) (pretty-print-style-table? table))
|
|
(terr "pretty-print style table or #f" 0))
|
|
(unless (and (list? symbols)
|
|
(andmap symbol? symbols))
|
|
(terr "list of symbols" 1))
|
|
(unless (and (list? like-symbols)
|
|
(andmap symbol? like-symbols))
|
|
(terr "list of symbols" 1))
|
|
(unless (= (length symbols) (length like-symbols))
|
|
(raise-mismatch-error
|
|
'pretty-print-extend-style-table
|
|
(format "length of first list (~a) doesn't match the length of the second list (~a): "
|
|
(length symbols) (length like-symbols))
|
|
like-symbols)))
|
|
(let ([ht (if table (pretty-print-style-table-hash table) (make-hash-table))]
|
|
[new-ht (make-hash-table)])
|
|
(hash-table-for-each
|
|
ht
|
|
(lambda (key val)
|
|
(hash-table-put! new-ht key val)))
|
|
(for-each
|
|
(lambda (symbol like-symbol)
|
|
(let ((s (hash-table-get ht
|
|
like-symbol
|
|
(lambda () #f))))
|
|
(hash-table-put! new-ht symbol (or s like-symbol))))
|
|
symbols like-symbols)
|
|
(make-pretty-print-style-table new-ht))))
|
|
|
|
(define pretty-print-abbreviate-read-macros (make-parameter #t))
|
|
|
|
(define pretty-print-current-style-table
|
|
(make-parameter
|
|
(pretty-print-extend-style-table #f null null)
|
|
(lambda (s)
|
|
(unless (pretty-print-style-table? s)
|
|
(raise-type-error
|
|
'pretty-print-current-style-table
|
|
"pretty-print style table"
|
|
s))
|
|
s)))
|
|
|
|
(define pretty-print-.-symbol-without-bars
|
|
(make-parameter #f (lambda (x) (and x #t))))
|
|
|
|
(define pretty-print-show-inexactness
|
|
(make-parameter #f
|
|
(lambda (x) (and x #t))))
|
|
|
|
(define pretty-print-exact-as-decimal
|
|
(make-parameter #f
|
|
(lambda (x) (and x #t))))
|
|
|
|
(define pretty-print-columns
|
|
(make-parameter 79
|
|
(lambda (x)
|
|
(unless (or (eq? x 'infinity)
|
|
(integer? x))
|
|
(raise-type-error
|
|
'pretty-print-columns
|
|
"integer or 'infinity"
|
|
x))
|
|
x)))
|
|
|
|
(define pretty-print-depth
|
|
(make-parameter #f
|
|
(lambda (x)
|
|
(unless (or (not x) (number? x))
|
|
(raise-type-error
|
|
'pretty-print-depth
|
|
"number or #f"
|
|
x))
|
|
x)))
|
|
|
|
(define can-accept-n?
|
|
(lambda (n x)
|
|
(procedure-arity-includes? x n)))
|
|
|
|
(define pretty-print-size-hook
|
|
(make-parameter (lambda (x display? port) #f)
|
|
(lambda (x)
|
|
(unless (can-accept-n? 3 x)
|
|
(raise-type-error
|
|
'pretty-print-size-hook
|
|
"procedure of 3 arguments"
|
|
x))
|
|
x)))
|
|
|
|
(define pretty-print-print-hook
|
|
(make-parameter void
|
|
(lambda (x)
|
|
(unless (can-accept-n? 3 x)
|
|
(raise-type-error
|
|
'pretty-print-print-hook
|
|
"procedure of 3 arguments"
|
|
x))
|
|
x)))
|
|
|
|
(define pretty-print-print-line
|
|
(make-parameter (lambda (line port offset width)
|
|
(when (and (number? width)
|
|
(not (eq? 0 line)))
|
|
(newline port))
|
|
0)
|
|
(lambda (x)
|
|
(unless (can-accept-n? 4 x)
|
|
(raise-type-error
|
|
'pretty-print-print-line
|
|
"procedure of 4 arguments"
|
|
x))
|
|
x)))
|
|
|
|
(define pretty-print-pre-print-hook
|
|
(make-parameter void
|
|
(lambda (x)
|
|
(unless (can-accept-n? 2 x)
|
|
(raise-type-error
|
|
'pretty-print-pre-print-hook
|
|
"procedure of 2 arguments"
|
|
x))
|
|
x)))
|
|
|
|
(define pretty-print-post-print-hook
|
|
(make-parameter void
|
|
(lambda (x)
|
|
(unless (can-accept-n? 2 x)
|
|
(raise-type-error
|
|
'pretty-print-post-print-hook
|
|
"procedure of 2 arguments"
|
|
x))
|
|
x)))
|
|
|
|
(define pretty-printing
|
|
(make-parameter #f (lambda (x) (and x #t))))
|
|
|
|
(define pretty-print-remap-stylable
|
|
(make-parameter (λ (x) #f)
|
|
(λ (f)
|
|
(unless (can-accept-n? 1 f)
|
|
(raise-type-error
|
|
'pretty-print-remap-stylable
|
|
"procedure of 1 argument"
|
|
f))
|
|
(λ (x)
|
|
(let ([res (f x)])
|
|
(unless (or (not res) (symbol? res))
|
|
(raise-type-error
|
|
'pretty-print-remap-stylable
|
|
"result of parameter function to be a symbol or #f"
|
|
res))
|
|
res)))))
|
|
|
|
(define make-pretty-print
|
|
(lambda (display?)
|
|
(letrec ([pretty-print
|
|
(case-lambda
|
|
[(obj port)
|
|
(let ([width (pretty-print-columns)]
|
|
[size-hook (pretty-print-size-hook)]
|
|
[print-hook (pretty-print-print-hook)]
|
|
[pre-hook (pretty-print-pre-print-hook)]
|
|
[post-hook (pretty-print-post-print-hook)])
|
|
(generic-write obj display?
|
|
width
|
|
(make-printing-port port
|
|
pre-hook
|
|
post-hook
|
|
print-hook
|
|
(pretty-print-print-line))
|
|
(print-graph) (print-struct) (print-hash-table)
|
|
(and (not display?) (print-vector-length)) (print-box)
|
|
(pretty-print-depth)
|
|
(lambda (o display?)
|
|
(size-hook o display? port)))
|
|
(void))]
|
|
[(obj) (pretty-print obj (current-output-port))])])
|
|
pretty-print)))
|
|
|
|
(define pretty-print (make-pretty-print #f))
|
|
(define pretty-display (make-pretty-print #t))
|
|
|
|
(define-struct mark (str def))
|
|
(define-struct hide (val))
|
|
|
|
(define (make-tentative-output-port pport width esc)
|
|
(let* ([content null]
|
|
[special-ok? (port-writes-special? pport)]
|
|
;; The null device counts for us:
|
|
[/dev/null (let-values ([(line col pos) (port-next-location pport)])
|
|
(relocate-output-port
|
|
(let ([p (open-output-nowhere special-ok?)])
|
|
(port-count-lines! p)
|
|
p)
|
|
(or line 1) (or col 0) (or pos 1)))]
|
|
[check-esc (lambda ()
|
|
(let-values ([(l c p) (port-next-location /dev/null)])
|
|
(when (c . > . width)
|
|
(esc))))]
|
|
[p (make-output-port
|
|
'tentative
|
|
always-evt
|
|
(lambda (s start end block? break?)
|
|
(write-bytes s /dev/null start end)
|
|
(check-esc)
|
|
(set! content (cons (subbytes s start end) content))
|
|
(- end start))
|
|
void
|
|
(and special-ok?
|
|
(lambda (special block break?)
|
|
(write-special special /dev/null)
|
|
(check-esc)
|
|
(set! content (cons (cons 'special special) content))
|
|
#t))
|
|
#f #f
|
|
(lambda ()
|
|
(port-next-location /dev/null)))]
|
|
[first-line? #t])
|
|
(port-count-lines! /dev/null)
|
|
(port-count-lines! p)
|
|
(register-printing-port p
|
|
(make-print-port-info
|
|
(lambda () (reverse content))
|
|
(box #t)
|
|
(lambda (v)
|
|
(set! content (cons (cons 'pre v) content)))
|
|
(lambda (v)
|
|
(set! content (cons (cons 'post v) content)))
|
|
(lambda (v len display?)
|
|
(display (make-string len #\.) /dev/null)
|
|
(set! content (cons (list* 'hooked v len display?)
|
|
content)))
|
|
(lambda (use-line? offset width)
|
|
(when (and (number? width)
|
|
(not first-line?))
|
|
(newline p))
|
|
(set! first-line? #f)
|
|
0)
|
|
esc))
|
|
p))
|
|
|
|
(define (make-tentative-pretty-print-output-port pport width esc)
|
|
(let ([p (make-tentative-output-port pport width esc)])
|
|
(port-write-handler p (port-write-handler pport))
|
|
(port-display-handler p (port-display-handler pport))
|
|
(port-print-handler p (port-print-handler pport))
|
|
p))
|
|
|
|
(define (make-printing-port port pre-print post-print output-hooked print-line)
|
|
(let-values ([(line col pos) (port-next-location port)])
|
|
(let* ([orig-counts? (and line col pos)]
|
|
[p (if orig-counts?
|
|
(relocate-output-port port line col pos #f)
|
|
(transplant-output-port port #f 1 #f))]
|
|
[line -1])
|
|
(port-count-lines! p)
|
|
(register-printing-port p
|
|
(make-print-port-info
|
|
(lambda () null)
|
|
(box #t)
|
|
(lambda (v)
|
|
(pre-print v port))
|
|
(lambda (v)
|
|
(post-print v port))
|
|
(lambda (v len display?)
|
|
(output-hooked v display? p))
|
|
(lambda (use-line? offset width)
|
|
(set! line (add1 line))
|
|
(print-line (and use-line? line) p offset width))
|
|
void))
|
|
p)))
|
|
|
|
(define printing-ports (make-hash-table 'weak))
|
|
|
|
(define-struct print-port-info (get-content
|
|
def-box
|
|
pre-print
|
|
post-print
|
|
output-hooked
|
|
print-line
|
|
esc))
|
|
|
|
(define (register-printing-port p info)
|
|
(hash-table-put! printing-ports p (make-ephemeron p info)))
|
|
|
|
(define (register-printing-port-like p pport)
|
|
(hash-table-put! printing-ports p
|
|
(make-ephemeron p
|
|
(ephemeron-value (hash-table-get printing-ports pport)))))
|
|
|
|
(define (get pport selector)
|
|
(let ([e (hash-table-get printing-ports pport (lambda () #f))])
|
|
(selector (if e
|
|
(ephemeron-value e)
|
|
(make-print-port-info
|
|
(lambda () null)
|
|
(box #t)
|
|
void void void void void)))))
|
|
|
|
(define (printing-port-pre-print pport)
|
|
(get pport print-port-info-pre-print))
|
|
(define (printing-port-post-print pport)
|
|
(get pport print-port-info-post-print))
|
|
(define (printing-port-def-box pport)
|
|
(get pport print-port-info-def-box))
|
|
(define (printing-port-output-hooked pport)
|
|
(get pport print-port-info-output-hooked))
|
|
(define (printing-port-print-line pport)
|
|
(get pport print-port-info-print-line))
|
|
(define (printing-port-esc pport)
|
|
(get pport print-port-info-esc))
|
|
|
|
(define orig-display (port-display-handler (open-output-string)))
|
|
(define orig-write (port-write-handler (open-output-string)))
|
|
|
|
(define (pretty-print-newline pport width)
|
|
(let-values ([(l col p) (port-next-location pport)])
|
|
((printing-port-print-line pport) #t (or col 0) width)))
|
|
|
|
(define (tentative-pretty-print-port-transfer a-pport pport)
|
|
(let ([content ((get a-pport print-port-info-get-content))])
|
|
(for-each (lambda (elem)
|
|
(if (bytes? elem)
|
|
(write-bytes elem pport)
|
|
(case (car elem)
|
|
[(special) (write-special (cdr elem) pport)]
|
|
[(pre) ((printing-port-pre-print pport) (cdr elem))]
|
|
[(post) ((printing-port-post-print pport) (cdr elem))]
|
|
[(hooked) ((printing-port-output-hooked pport)
|
|
(cadr elem) (caddr elem) (cdddr elem))])))
|
|
content)))
|
|
|
|
(define (tentative-pretty-print-port-cancel pport)
|
|
(set-box! (get pport print-port-info-def-box) #f))
|
|
|
|
(define (add-spaces n port)
|
|
(if (> n 0)
|
|
(if (> n 7)
|
|
(begin
|
|
(write-string " " port)
|
|
(add-spaces (- n 8) port))
|
|
(write-string " " port 0 n))))
|
|
|
|
(define (prefab?! obj v)
|
|
(let ([d (prefab-struct-key obj)])
|
|
(and d
|
|
(begin
|
|
(vector-set! v 0 d)
|
|
#t))))
|
|
|
|
(define (generic-write obj display? width pport
|
|
print-graph? print-struct? print-hash-table? print-vec-length? print-box?
|
|
depth size-hook)
|
|
|
|
(define pair-open (if (print-pair-curly-braces) "{" "("))
|
|
(define pair-close (if (print-pair-curly-braces) "}" ")"))
|
|
(define mpair-open (if (print-mpair-curly-braces) "{" "("))
|
|
(define mpair-close (if (print-mpair-curly-braces) "}" ")"))
|
|
|
|
(define table (make-hash-table)) ; Hash table for looking for loops
|
|
|
|
(define show-inexactness? (pretty-print-show-inexactness))
|
|
(define exact-as-decimal? (pretty-print-exact-as-decimal))
|
|
|
|
(define vector->repeatless-list
|
|
(if print-vec-length?
|
|
(lambda (v)
|
|
(let ([len (vector-length v)])
|
|
(if (zero? len)
|
|
null
|
|
(let ([last (vector-ref v (sub1 len))])
|
|
(let loop ([i (- len 2)])
|
|
(if (i . < . 0)
|
|
(list last)
|
|
(let ([e (vector-ref v i)])
|
|
(if (eq? e last)
|
|
(loop (sub1 i))
|
|
(let loop ([i (sub1 i)][r (list e last)])
|
|
(if (i . < . 0)
|
|
r
|
|
(loop (sub1 i) (cons (vector-ref v i) r))))))))))))
|
|
vector->list))
|
|
|
|
(define (extract-sub-objects obj pport)
|
|
(let ([p (open-output-nowhere 'null (port-writes-special? pport))]
|
|
[l null])
|
|
(let ([record (lambda (o p) (set! l (cons o l)))])
|
|
(port-write-handler p record)
|
|
(port-display-handler p record)
|
|
(port-print-handler p record))
|
|
(parameterize ([pretty-printing #f])
|
|
((custom-write-accessor obj) obj p #f))
|
|
l))
|
|
|
|
(define found-cycle
|
|
(or print-graph?
|
|
(let loop ([obj obj])
|
|
(and (or (vector? obj)
|
|
(pair? obj)
|
|
(mpair? obj)
|
|
(and (box? obj)
|
|
print-box?)
|
|
(and (custom-write? obj)
|
|
(not (struct-type? obj)))
|
|
(and (struct? obj) print-struct?)
|
|
(and (hash-table? obj) print-hash-table?))
|
|
(or (hash-table-get table obj (lambda () #f))
|
|
(begin
|
|
(hash-table-put! table obj #t)
|
|
(let ([cycle
|
|
(cond
|
|
[(vector? obj)
|
|
(let ([len (vector-length obj)])
|
|
(let vloop ([i 0])
|
|
(if (= i len)
|
|
#f
|
|
(or (loop (vector-ref obj i))
|
|
(vloop (add1 i))))))]
|
|
[(pair? obj)
|
|
(or (loop (car obj))
|
|
(loop (cdr obj)))]
|
|
[(mpair? obj)
|
|
(or (loop (mcar obj))
|
|
(loop (mcdr obj)))]
|
|
[(and (box? obj) print-box?) (loop (unbox obj))]
|
|
[(and (custom-write? obj)
|
|
(not (struct-type? obj)))
|
|
(loop (extract-sub-objects obj pport))]
|
|
[(struct? obj)
|
|
(ormap loop
|
|
(vector->list (struct->vector obj)))]
|
|
[(hash-table? obj)
|
|
(let/ec esc
|
|
(hash-table-for-each
|
|
obj
|
|
(lambda (v k)
|
|
(when (or (loop v)
|
|
(loop k))
|
|
(esc #t))))
|
|
#f)])])
|
|
(hash-table-remove! table obj)
|
|
cycle)))))))
|
|
|
|
(define __dummy__
|
|
(when found-cycle
|
|
(let loop ([obj obj])
|
|
(if (or (vector? obj)
|
|
(pair? obj)
|
|
(mpair? obj)
|
|
(and (box? obj)
|
|
print-box?)
|
|
(and (custom-write? obj)
|
|
(not (struct-type? obj)))
|
|
(and (struct? obj) print-struct?)
|
|
(and (hash-table? obj) print-hash-table?))
|
|
;; A little confusing: use #t for not-found
|
|
(let ([p (hash-table-get table obj (lambda () #t))])
|
|
(when (not (mark? p))
|
|
(if p
|
|
(begin
|
|
(hash-table-put! table obj #f)
|
|
(cond
|
|
[(vector? obj)
|
|
(let ([len (vector-length obj)])
|
|
(let vloop ([i 0])
|
|
(unless (= i len)
|
|
(loop (vector-ref obj i))
|
|
(vloop (add1 i)))))]
|
|
[(pair? obj)
|
|
(loop (car obj))
|
|
(loop (cdr obj))]
|
|
[(mpair? obj)
|
|
(loop (mcar obj))
|
|
(loop (mcdr obj))]
|
|
[(and (box? obj) print-box?) (loop (unbox obj))]
|
|
[(and (custom-write? obj)
|
|
(not (struct-type? obj)))
|
|
(loop (extract-sub-objects obj pport))]
|
|
[(struct? obj)
|
|
(for-each loop
|
|
(vector->list (struct->vector obj)))]
|
|
[(hash-table? obj)
|
|
(hash-table-for-each
|
|
obj
|
|
(lambda (k v)
|
|
(loop k)
|
|
(loop v)))]))
|
|
(begin
|
|
(hash-table-put! table obj
|
|
(make-mark #f (box #f)))))))))))
|
|
|
|
(define cycle-counter 0)
|
|
|
|
(define found (if found-cycle
|
|
table
|
|
#f))
|
|
|
|
(define dsub1 (lambda (d)
|
|
(if d
|
|
(sub1 d)
|
|
#f)))
|
|
|
|
(define (pre-print pport obj)
|
|
((printing-port-pre-print pport) obj))
|
|
(define (post-print pport obj)
|
|
((printing-port-post-print pport)
|
|
obj))
|
|
(define (output-hooked pport obj len display?)
|
|
((printing-port-output-hooked pport)
|
|
obj len display?))
|
|
|
|
(define expr-found
|
|
(lambda (pport ref)
|
|
(let ([n cycle-counter])
|
|
(set! cycle-counter (add1 cycle-counter))
|
|
(set-mark-str! ref
|
|
(string-append "#"
|
|
(number->string n)
|
|
"#"))
|
|
(set-mark-def! ref (printing-port-def-box pport))
|
|
(display (string-append "#"
|
|
(number->string n)
|
|
"=")
|
|
pport))))
|
|
|
|
(define check-expr-found
|
|
(lambda (obj pport check? c-k d-k n-k)
|
|
(let ([ref (and check?
|
|
found
|
|
(hash-table-get found obj (lambda () #f)))])
|
|
(if (and ref (unbox (mark-def ref)))
|
|
(if c-k
|
|
(c-k (mark-str ref))
|
|
(display (mark-str ref) pport))
|
|
(if (and ref d-k)
|
|
(d-k)
|
|
(begin
|
|
(when ref
|
|
(expr-found pport ref))
|
|
(n-k)))))))
|
|
|
|
(define (write-custom recur obj pport depth display? width)
|
|
(let-values ([(l c p) (port-next-location pport)])
|
|
(let ([p (relocate-output-port pport l c p)])
|
|
(port-count-lines! p)
|
|
(let ([writer (lambda (v port)
|
|
(recur port v (dsub1 depth) #f))]
|
|
[displayer (lambda (v port)
|
|
(recur port v (dsub1 depth) #t))])
|
|
(port-write-handler p writer)
|
|
(port-display-handler p displayer)
|
|
(port-print-handler p writer))
|
|
(register-printing-port-like p pport)
|
|
(parameterize ([pretty-printing #t]
|
|
[pretty-print-columns (or width 'infinity)])
|
|
((custom-write-accessor obj) obj p (not display?))))))
|
|
|
|
;; ------------------------------------------------------------
|
|
;; wr: write on a single line
|
|
(define (wr* pport obj depth display?)
|
|
|
|
(define (out str)
|
|
(write-string str pport))
|
|
|
|
(define (wr obj depth)
|
|
(wr* pport obj depth display?))
|
|
|
|
(define (wr-expr expr depth pair? car cdr open close)
|
|
(if (and (read-macro? expr pair? car cdr)
|
|
(equal? open "("))
|
|
(begin
|
|
(out (read-macro-prefix expr car))
|
|
(wr (read-macro-body expr car cdr) depth))
|
|
(wr-lst expr #t depth pair? car cdr open close)))
|
|
|
|
(define (wr-lst l check? depth pair? car cdr open close)
|
|
(if (pair? l)
|
|
(check-expr-found
|
|
l pport check?
|
|
#f #f
|
|
(lambda ()
|
|
(if (and depth (zero? depth))
|
|
(begin
|
|
(out open)
|
|
(out "...")
|
|
(out close))
|
|
(begin
|
|
(out open)
|
|
(wr (car l) (dsub1 depth))
|
|
(let loop ([l (cdr l)])
|
|
(check-expr-found
|
|
l pport (and check? (pair? l))
|
|
(lambda (s) (out " . ") (out s) (out close))
|
|
(lambda ()
|
|
(out " . ")
|
|
(wr-lst l check? (dsub1 depth) pair? car cdr open close)
|
|
(out close))
|
|
(lambda ()
|
|
(cond
|
|
[(pair? l)
|
|
(if (and (eq? (car l) 'unquote)
|
|
(pair? (cdr l))
|
|
(null? (cdr (cdr l))))
|
|
(begin
|
|
(out " . ,")
|
|
(wr (car (cdr l)) (dsub1 depth))
|
|
(out close))
|
|
(begin
|
|
(out " ")
|
|
(wr (car l) (dsub1 depth))
|
|
(loop (cdr l))))]
|
|
[(null? l) (out close)]
|
|
[else
|
|
(out " . ")
|
|
(wr l (dsub1 depth))
|
|
(out close)]))))))))
|
|
(begin
|
|
(out open)
|
|
(out close))))
|
|
|
|
(unless (hide? obj)
|
|
(pre-print pport obj))
|
|
(if (and depth
|
|
(negative? depth)
|
|
(not (hide? obj)))
|
|
(out "...")
|
|
|
|
(cond
|
|
[(size-hook obj display?)
|
|
=> (lambda (len)
|
|
(output-hooked pport obj len display?))]
|
|
|
|
[(pair? obj)
|
|
(wr-expr obj depth pair? car cdr pair-open pair-close)]
|
|
[(mpair? obj)
|
|
(wr-expr obj depth mpair? mcar mcdr mpair-open mpair-close)]
|
|
[(null? obj)
|
|
(wr-lst obj #f depth pair? car cdr "(" ")")]
|
|
[(vector? obj)
|
|
(check-expr-found
|
|
obj pport #t
|
|
#f #f
|
|
(lambda ()
|
|
(out "#")
|
|
(when print-vec-length?
|
|
(out (number->string (vector-length obj))))
|
|
(wr-lst (vector->repeatless-list obj) #f depth pair? car cdr "(" ")")))]
|
|
[(and (box? obj)
|
|
print-box?)
|
|
(check-expr-found
|
|
obj pport #t
|
|
#f #f
|
|
(lambda ()
|
|
(out "#&")
|
|
(wr (unbox obj) (dsub1 depth))))]
|
|
[(and (custom-write? obj)
|
|
(not (struct-type? obj)))
|
|
(check-expr-found
|
|
obj pport #t
|
|
#f #f
|
|
(lambda ()
|
|
(parameterize ([pretty-print-columns 'infinity])
|
|
(write-custom wr* obj pport depth display? width))))]
|
|
[(struct? obj)
|
|
(if (and print-struct?
|
|
(not (and depth
|
|
(zero? depth))))
|
|
(check-expr-found
|
|
obj pport #t
|
|
#f #f
|
|
(lambda ()
|
|
(out "#")
|
|
(let ([v (struct->vector obj)])
|
|
(when (prefab?! obj v)
|
|
(out "s"))
|
|
(wr-lst (vector->list v) #f (dsub1 depth) pair? car cdr "(" ")"))))
|
|
(parameterize ([print-struct #f])
|
|
((if display? orig-display orig-write) obj pport)))]
|
|
[(hash-table? obj)
|
|
(if (and print-hash-table?
|
|
(not (and depth
|
|
(zero? depth))))
|
|
(check-expr-found
|
|
obj pport #t
|
|
#f #f
|
|
(lambda ()
|
|
(out (if (hash-table? obj 'equal)
|
|
"#hash"
|
|
(if (hash-table? obj 'eqv)
|
|
"#hasheqv"
|
|
"#hasheq")))
|
|
(wr-lst (hash-table-map obj (lambda (k v)
|
|
(cons k (make-hide v))))
|
|
#f depth
|
|
pair? car cdr "(" ")")))
|
|
(parameterize ([print-hash-table #f])
|
|
((if display? orig-display orig-write) obj pport)))]
|
|
[(hide? obj)
|
|
(wr* pport (hide-val obj) depth display?)]
|
|
[(boolean? obj)
|
|
(out (if obj "#t" "#f"))]
|
|
[(number? obj)
|
|
(when (and show-inexactness?
|
|
(inexact? obj))
|
|
(out "#i"))
|
|
(out ((if exact-as-decimal?
|
|
number->decimal-string
|
|
number->string)
|
|
obj))]
|
|
[(and (pretty-print-.-symbol-without-bars)
|
|
(eq? obj '|.|))
|
|
(out ".")]
|
|
[else
|
|
((if display? orig-display orig-write) obj pport)]))
|
|
(unless (hide? obj)
|
|
(post-print pport obj)))
|
|
|
|
;; ------------------------------------------------------------
|
|
;; pp: write on (potentially) multiple lines
|
|
(define (pp* pport obj depth display?)
|
|
|
|
(define (pp obj depth)
|
|
(pp* pport obj depth display?))
|
|
|
|
(define (out str)
|
|
(write-string str pport))
|
|
|
|
(define (spaces n)
|
|
(add-spaces n pport))
|
|
|
|
(define (ccol)
|
|
(let-values ([(l col p) (port-next-location pport)])
|
|
col))
|
|
|
|
(define (indent to)
|
|
(let ([col (ccol)])
|
|
(if (< to col)
|
|
(begin
|
|
(let ([col ((printing-port-print-line pport) #t col width)])
|
|
(spaces (- to col))))
|
|
(spaces (max 0 (- to col))))))
|
|
|
|
(define (pr obj extra pp-pair depth)
|
|
;; may have to split on multiple lines
|
|
(let* ([can-multi (and width
|
|
(not (size-hook obj display?))
|
|
(or (pair? obj)
|
|
(mpair? obj)
|
|
(vector? obj)
|
|
(and (box? obj) print-box?)
|
|
(and (custom-write? obj)
|
|
(not (struct-type? obj)))
|
|
(and (struct? obj) print-struct?)
|
|
(and (hash-table? obj) print-hash-table?)))]
|
|
[graph-ref (if can-multi
|
|
(and found (hash-table-get found obj (lambda () #f)))
|
|
#f)]
|
|
[old-counter cycle-counter])
|
|
(if (and can-multi
|
|
(or (not graph-ref)
|
|
(not (unbox (mark-def graph-ref)))))
|
|
;; It might be possible to split obj across lines.
|
|
;; Try writing the obj, but accumulate the info that goes out
|
|
;; into a-pport
|
|
(let ([a-pport
|
|
(let/ec esc
|
|
(letrec ([a-pport (make-tentative-output-port
|
|
pport
|
|
(- width extra)
|
|
(lambda () (esc a-pport)))])
|
|
;; Here's the attempt to write on one line:
|
|
(wr* a-pport obj depth display?)
|
|
a-pport))])
|
|
(let-values ([(l c p) (port-next-location a-pport)])
|
|
(if (<= c (- width extra))
|
|
;; All can be printed on one line, so just dump the
|
|
;; accumulated text
|
|
(tentative-pretty-print-port-transfer a-pport pport)
|
|
;; Doesn't fit on one line, so start over
|
|
(begin
|
|
(tentative-pretty-print-port-cancel a-pport)
|
|
(set! cycle-counter old-counter)
|
|
(when graph-ref
|
|
(expr-found pport graph-ref))
|
|
(pre-print pport obj)
|
|
(cond
|
|
[(pair? obj) (pp-pair obj extra depth
|
|
pair? car cdr pair-open pair-close)]
|
|
[(mpair? obj) (pp-pair obj extra depth
|
|
mpair? mcar mcdr mpair-open mpair-close)]
|
|
[(vector? obj)
|
|
(out "#")
|
|
(when print-vec-length?
|
|
(out (number->string (vector-length obj))))
|
|
(pp-list (vector->repeatless-list obj) extra pp-expr #f depth
|
|
pair? car cdr pair-open pair-close)]
|
|
[(and (custom-write? obj)
|
|
(not (struct-type? obj)))
|
|
(write-custom pp* obj pport depth display? width)]
|
|
[(struct? obj) ; print-struct is on if we got here
|
|
(out "#")
|
|
(let ([v (struct->vector obj)])
|
|
(when (prefab?! obj v)
|
|
(out "s"))
|
|
(pp-list (vector->list v) extra pp-expr #f depth
|
|
pair? car cdr pair-open pair-close))]
|
|
[(hash-table? obj)
|
|
(out (if (hash-table? obj 'equal)
|
|
"#hash"
|
|
(if (hash-table? obj 'eqv)
|
|
"#hasheqv"
|
|
"#hasheq")))
|
|
(pp-list (hash-table-map obj cons) extra pp-expr #f depth
|
|
pair? car cdr pair-open pair-close)]
|
|
[(and (box? obj) print-box?)
|
|
(out "#&")
|
|
(pr (unbox obj) extra pp-pair depth)])
|
|
(post-print pport obj)))))
|
|
;; Not possible to split obj across lines; so just write directly
|
|
(wr* pport obj depth display?))))
|
|
|
|
(define (pp-expr expr extra depth
|
|
apair? acar acdr open close)
|
|
(if (and (read-macro? expr apair? acar acdr)
|
|
(equal? open "(")
|
|
(not (and found (hash-table-get found (acdr expr) #f))))
|
|
(begin
|
|
(out (read-macro-prefix expr acar))
|
|
(pr (read-macro-body expr acar acdr)
|
|
extra
|
|
pp-expr
|
|
depth))
|
|
(let ((head (acar expr)))
|
|
(if (or (and (symbol? head)
|
|
(not (size-hook head display?)))
|
|
((pretty-print-remap-stylable) head))
|
|
(let ((proc (style head expr apair? acar acdr)))
|
|
(if proc
|
|
(proc expr extra depth
|
|
apair? acar acdr open close)
|
|
(if (and #f
|
|
;; Why this special case? Currently disabled.
|
|
(> (string-length
|
|
(symbol->string
|
|
(if (symbol? head)
|
|
head
|
|
((pretty-print-remap-stylable) head))))
|
|
max-call-head-width))
|
|
(pp-general expr extra #f #f #f pp-expr depth
|
|
apair? acar acdr open close)
|
|
(pp-list expr extra pp-expr #t depth
|
|
apair? acar acdr open close))))
|
|
(pp-list expr extra pp-expr #t depth
|
|
apair? acar acdr open close)))))
|
|
|
|
(define (wr obj depth)
|
|
(wr* pport obj depth display?))
|
|
|
|
;; (head item1
|
|
;; item2
|
|
;; item3)
|
|
(define (pp-call expr extra pp-item depth
|
|
apair? acar acdr open close)
|
|
(out open)
|
|
(wr (acar expr) (dsub1 depth))
|
|
(let ([col (+ (ccol) 1)])
|
|
(pp-down close (acdr expr) col col extra pp-item #t #t depth
|
|
apair? acar acdr open close)))
|
|
|
|
;; (head item1 item2
|
|
;; item3
|
|
;; item4)
|
|
(define (pp-two-up expr extra pp-item depth
|
|
apair? acar acdr open close)
|
|
(out open)
|
|
(let ([col (ccol)])
|
|
(wr (acar expr) (dsub1 depth))
|
|
(out " ")
|
|
(wr (acar (acdr expr)) (dsub1 depth))
|
|
(pp-down close (acdr (acdr expr)) (+ (ccol) 1) (+ col 1) extra pp-item #t #t depth
|
|
apair? acar acdr open close)))
|
|
|
|
;; (head item1
|
|
;; item2
|
|
;; item3)
|
|
(define (pp-one-up expr extra pp-item depth
|
|
apair? acar acdr open close)
|
|
(out open)
|
|
(let ([col (ccol)])
|
|
(wr (acar expr) (dsub1 depth))
|
|
(pp-down close (acdr expr) (+ (ccol) 1) (+ col 1) extra pp-item #t #t depth
|
|
apair? acar acdr open close)))
|
|
|
|
;; (item1
|
|
;; item2
|
|
;; item3)
|
|
(define (pp-list l extra pp-item check? depth
|
|
apair? acar acdr open close)
|
|
(out open)
|
|
(let ([col (ccol)])
|
|
(pp-down close l col col extra pp-item #f check? depth
|
|
apair? acar acdr open close)))
|
|
|
|
(define (pp-down closer l col1 col2 extra pp-item check-first? check-rest? depth
|
|
apair? acar acdr open close)
|
|
(let loop ([l l] [icol col1] [check? check-first?])
|
|
(check-expr-found
|
|
l pport (and check? (apair? l))
|
|
(lambda (s)
|
|
(indent col2)
|
|
(out ".")
|
|
(indent col2)
|
|
(out s)
|
|
(out closer))
|
|
(lambda ()
|
|
(indent col2)
|
|
(out ".")
|
|
(indent col2)
|
|
(pr l extra pp-item depth)
|
|
(out closer))
|
|
(lambda ()
|
|
(cond
|
|
[(apair? l)
|
|
(let ([rest (acdr l)])
|
|
(let ([extra (if (null? rest) (+ extra 1) 0)])
|
|
(indent icol)
|
|
(pr (acar l) extra pp-item (dsub1 depth))
|
|
(loop rest col2 check-rest?)))]
|
|
[(null? l)
|
|
(out closer)]
|
|
[else
|
|
(indent col2)
|
|
(out ".")
|
|
(indent col2)
|
|
(pr l (+ extra 1) pp-item (dsub1 depth))
|
|
(out closer)])))))
|
|
|
|
(define (pp-general expr extra named? pp-1 pp-2 pp-3 depth
|
|
apair? acar acdr open close)
|
|
|
|
(define (tail1 rest col1 col3)
|
|
(if (and pp-1 (apair? rest))
|
|
(let* ((val1 (acar rest))
|
|
(rest (acdr rest))
|
|
(extra (if (null? rest) (+ extra 1) 0)))
|
|
(indent col3)
|
|
(pr val1 extra pp-1 depth)
|
|
(tail2 rest col1 col3))
|
|
(tail2 rest col1 col3)))
|
|
|
|
(define (tail2 rest col1 col3)
|
|
(if (and pp-2 (apair? rest))
|
|
(let* ((val1 (acar rest))
|
|
(rest (acdr rest))
|
|
(extra (if (null? rest) (+ extra 1) 0)))
|
|
(indent col3)
|
|
(pr val1 extra pp-2 depth)
|
|
(tail3 rest col1))
|
|
(tail3 rest col1)))
|
|
|
|
(define (tail3 rest col1)
|
|
(pp-down close rest col1 col1 extra pp-3 #f #t depth
|
|
apair? acar acdr open close))
|
|
|
|
(let* ([head (acar expr)]
|
|
[rest (acdr expr)]
|
|
[col (ccol)])
|
|
(out open)
|
|
(wr head (dsub1 depth))
|
|
(if (and named? (apair? rest))
|
|
(let* ((name (acar rest))
|
|
(rest (acdr rest)))
|
|
(out " ")
|
|
(wr name (dsub1 depth))
|
|
(tail1 rest (+ col indent-general) (+ (ccol) 1)))
|
|
(tail1 rest (+ col indent-general) (+ (ccol) 1)))))
|
|
|
|
(define (pp-expr-list l extra depth
|
|
apair? acar acdr open close)
|
|
(pp-list l extra pp-expr #t depth
|
|
apair? acar acdr open close))
|
|
|
|
(define (pp-lambda expr extra depth
|
|
apair? acar acdr open close)
|
|
(pp-general expr extra #f pp-expr-list #f pp-expr depth
|
|
apair? acar acdr open close))
|
|
|
|
(define (pp-if expr extra depth
|
|
apair? acar acdr open close)
|
|
(pp-general expr extra #f pp-expr #f pp-expr depth
|
|
apair? acar acdr open close))
|
|
|
|
(define (pp-cond expr extra depth
|
|
apair? acar acdr open close)
|
|
(pp-list expr extra pp-expr-list #t depth
|
|
apair? acar acdr open close))
|
|
|
|
(define (pp-syntax-case expr extra depth
|
|
apair? acar acdr open close)
|
|
(pp-two-up expr extra pp-expr-list depth
|
|
apair? acar acdr open close))
|
|
|
|
(define (pp-module expr extra depth
|
|
apair? acar acdr open close)
|
|
(pp-two-up expr extra pp-expr depth
|
|
apair? acar acdr open close))
|
|
|
|
(define (pp-make-object expr extra depth
|
|
apair? acar acdr open close)
|
|
(pp-one-up expr extra pp-expr-list depth
|
|
apair? acar acdr open close))
|
|
|
|
(define (pp-case expr extra depth
|
|
apair? acar acdr open close)
|
|
(pp-general expr extra #f pp-expr #f pp-expr-list depth
|
|
apair? acar acdr open close))
|
|
|
|
(define (pp-and expr extra depth
|
|
apair? acar acdr open close)
|
|
(pp-call expr extra pp-expr depth
|
|
apair? acar acdr open close))
|
|
|
|
(define (pp-let expr extra depth
|
|
apair? acar acdr open close)
|
|
(let* ((rest (acdr expr))
|
|
(named? (and (apair? rest) (symbol? (do-remap (acar rest))))))
|
|
(pp-general expr extra named? pp-expr-list #f pp-expr depth
|
|
apair? acar acdr open close)))
|
|
|
|
(define (pp-begin expr extra depth
|
|
apair? acar acdr open close)
|
|
(pp-general expr extra #f #f #f pp-expr depth
|
|
apair? acar acdr open close))
|
|
|
|
(define (pp-do expr extra depth
|
|
apair? acar acdr open close)
|
|
(pp-general expr extra #f pp-expr-list pp-expr-list pp-expr depth
|
|
apair? acar acdr open close))
|
|
|
|
;; define formatting style (change these to suit your style)
|
|
|
|
(define indent-general 2)
|
|
|
|
(define max-call-head-width 5)
|
|
|
|
(define (no-sharing? expr count apair? acdr)
|
|
(if (apair? expr)
|
|
(if (and found
|
|
(hash-table-get found (acdr expr) #f))
|
|
#f
|
|
(or (zero? count)
|
|
(no-sharing? (acdr expr) (sub1 count) apair? acdr)))
|
|
#f))
|
|
|
|
(define (style head expr apair? acar acdr)
|
|
(case (look-in-style-table head)
|
|
((lambda λ define define-macro define-syntax
|
|
syntax-rules
|
|
shared
|
|
unless when)
|
|
(and (no-sharing? expr 1 apair? acdr)
|
|
pp-lambda))
|
|
((if set! set!-values)
|
|
(and (no-sharing? expr 1 apair? acdr)
|
|
pp-if))
|
|
((cond case-lambda)
|
|
(and (no-sharing? expr 0 apair? acdr)
|
|
pp-cond))
|
|
((case class)
|
|
(and (no-sharing? expr 1 apair? acdr)
|
|
pp-case))
|
|
((and or import export
|
|
require require-for-syntax require-for-template
|
|
provide link
|
|
public private override rename inherit field init)
|
|
(and (no-sharing? expr 0 apair? acdr)
|
|
pp-and))
|
|
((let letrec let*
|
|
let-values letrec-values let*-values
|
|
let-syntax letrec-syntax
|
|
let-syntaxes letrec-syntaxes)
|
|
(and (no-sharing? expr
|
|
(if (and (apair? (acdr expr))
|
|
(symbol? (acar (acdr expr))))
|
|
2
|
|
1)
|
|
apair?
|
|
acdr)
|
|
pp-let))
|
|
((begin begin0)
|
|
(and (no-sharing? expr 0 apair? acdr)
|
|
pp-begin))
|
|
((do letrec-syntaxes+values)
|
|
(and (no-sharing? expr 2 apair? acdr)
|
|
pp-do))
|
|
((module)
|
|
(and (no-sharing? expr 2 apair? acdr)
|
|
pp-module))
|
|
((send syntax-case instantiate)
|
|
(and (no-sharing? expr 2 apair? acdr)
|
|
pp-syntax-case))
|
|
((make-object)
|
|
(and (no-sharing? expr 1 apair? acdr)
|
|
pp-make-object))
|
|
|
|
(else #f)))
|
|
|
|
(pr obj 0 pp-expr depth))
|
|
|
|
;; ------------------------------------------------------------
|
|
;; This is where generic-write's body expressions start
|
|
|
|
((printing-port-print-line pport) #t 0 width)
|
|
(let-values ([(l col p) (port-next-location pport)])
|
|
(if (and width (not (eq? width 'infinity)))
|
|
(pp* pport obj depth display?)
|
|
(wr* pport obj depth display?)))
|
|
(let-values ([(l col p) (port-next-location pport)])
|
|
((printing-port-print-line pport) #f col width)))
|
|
|
|
(define (look-in-style-table raw-head)
|
|
(let ([head (do-remap raw-head)])
|
|
(or (hash-table-get (pretty-print-style-table-hash
|
|
(pretty-print-current-style-table))
|
|
head
|
|
#f)
|
|
head)))
|
|
|
|
(define (do-remap raw-head)
|
|
(cond
|
|
[((pretty-print-remap-stylable) raw-head)
|
|
=>
|
|
values]
|
|
[else raw-head]))
|
|
|
|
(define (read-macro? l pair? car cdr)
|
|
(define (length1? l) (and (pair? l) (null? (cdr l))))
|
|
(and (pretty-print-abbreviate-read-macros)
|
|
(let ((head (do-remap (car l))) (tail (cdr l)))
|
|
(case head
|
|
((quote quasiquote unquote unquote-splicing syntax unsyntax unsyntax-splicing)
|
|
(length1? tail))
|
|
(else #f)))))
|
|
|
|
(define (read-macro-body l car cdr)
|
|
(car (cdr l)))
|
|
|
|
(define (read-macro-prefix l car)
|
|
(let ((head (do-remap (car l))))
|
|
(case head
|
|
((quote) "'")
|
|
((quasiquote) "`")
|
|
((unquote) ",")
|
|
((unquote-splicing) ",@")
|
|
((syntax) "#'")
|
|
((unsyntax) "#,")
|
|
((unsyntax-splicing) "#,@"))))
|
|
|
|
(define pretty-print-handler
|
|
(lambda (v)
|
|
(unless (void? v)
|
|
(pretty-print v))))
|
|
|
|
(define (number->decimal-string x)
|
|
(cond
|
|
[(or (inexact? x)
|
|
(integer? x))
|
|
(number->string x)]
|
|
[(not (real? x))
|
|
(let ([r (real-part x)]
|
|
[i (imag-part x)])
|
|
(format "~a~a~ai"
|
|
(number->decimal-string r)
|
|
(if (negative? i)
|
|
""
|
|
"+")
|
|
(number->decimal-string i)))]
|
|
[else
|
|
(let ([n (numerator x)]
|
|
[d (denominator x)])
|
|
;; Count powers of 2 in denomintor
|
|
(let loop ([v d][2-power 0])
|
|
(if (and (positive? v)
|
|
(even? v))
|
|
(loop (arithmetic-shift v -1) (add1 2-power))
|
|
;; Count powers of 5 in denominator
|
|
(let loop ([v v][5-power 0])
|
|
(if (zero? (remainder v 5))
|
|
(loop (quotient v 5) (add1 5-power))
|
|
;; No more 2s or 5s. Anything left?
|
|
(if (= v 1)
|
|
;; Denominator = (* (expt 2 2-power) (expt 5 5-power)).
|
|
;; Print number as decimal.
|
|
(let* ([10-power (max 2-power 5-power)]
|
|
[scale (* (expt 2 (- 10-power 2-power))
|
|
(expt 5 (- 10-power 5-power)))]
|
|
[s (number->string (* (abs n) scale))]
|
|
[orig-len (string-length s)]
|
|
[len (max (add1 10-power) orig-len)]
|
|
[padded-s (if (< orig-len len)
|
|
(string-append
|
|
(make-string (- len orig-len) #\0)
|
|
s)
|
|
s)])
|
|
(format "~a~a.~a"
|
|
(if (negative? n) "-" "")
|
|
(substring padded-s 0 (- len 10-power))
|
|
(substring padded-s (- len 10-power) len)))
|
|
;; d has factor(s) other than 2 and 5.
|
|
;; Print as a fraction.
|
|
(number->string x)))))))]))
|
|
|
|
(define pretty-format
|
|
(case-lambda
|
|
[(t) (pretty-format t (pretty-print-columns))]
|
|
[(t w)
|
|
(parameterize ([pretty-print-columns w])
|
|
(let ([op (open-output-string)])
|
|
(pretty-print t op)
|
|
(let ([s (get-output-string op)])
|
|
(if (eq? w 'infinity)
|
|
s
|
|
(substring s 0 (- (string-length s) 1))))))]))
|
|
|
|
|
|
)
|
|
|