racket/collects/racket/pretty.rkt
2011-03-11 08:02:30 -06:00

1572 lines
55 KiB
Racket

;; Originally:
;; "genwrite.scm" generic write used by pp.scm
;; copyright (c) 1991, marc feeley
;; Pretty-printer for Racket
;; Handles structures, cycles, and graphs
;; TO INSTALL this pretty-printer into Racket's read-eval-print loop,
;; require this module and evaluate:
;; (current-print pretty-print-handler)
(module pretty racket/base
(require mzlib/private/port)
(provide pretty-print
pretty-write
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-hasheq))]
[new-ht (make-hasheq)])
(hash-for-each
ht
(lambda (key val)
(hash-set! new-ht key val)))
(for-each
(lambda (symbol like-symbol)
(let ((s (hash-ref ht
like-symbol
(lambda () #f))))
(hash-set! 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 (name display? as-qq?)
(letrec ([pretty-print
(case-lambda
[(obj port qq-depth)
(unless (output-port? port)
(raise-type-error name "output port" port))
(unless (or (equal? qq-depth 0)
(equal? qq-depth 1))
(raise-type-error name "0 or 1" qq-depth))
(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)
(and (not display?) as-qq? (print-as-expression)) qq-depth
(pretty-print-depth)
(lambda (o display?)
(size-hook o display? port)))
(void))]
[(obj port) (pretty-print obj port 0)]
[(obj) (pretty-print obj (current-output-port))])])
pretty-print)))
(define pretty-print (make-pretty-print 'pretty-print #f #t))
(define pretty-display (let ([pp (make-pretty-print 'pretty-display #t #f)])
(case-lambda
[(v) (pp v)]
[(v o) (pp v o)])))
(define pretty-write (let ([pp (make-pretty-print 'pretty-write #f #f)])
(case-lambda
[(v) (pp v)]
[(v o) (pp v o)])))
(define-struct mark (str def) #:mutable)
(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)))]
[first-line? #t]
[check-esc (lambda ()
(let-values ([(l c p) (port-next-location /dev/null)])
(when (or (c . > . width)
(not first-line?))
(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)))])
(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-weak-hasheq))
(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-set! printing-ports p (make-ephemeron p info)))
(define (register-printing-port-like p pport)
(hash-set! printing-ports p
(make-ephemeron p
(ephemeron-value (hash-ref printing-ports pport)))))
(define (get pport selector)
(let ([e (hash-ref printing-ports pport #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))
(void)))
(define (prefab?! obj v)
(let ([d (prefab-struct-key obj)])
(and d
(begin
(vector-set! v 0 d)
#t))))
(define-struct unquoted (val))
(define struct-ellipses (string->uninterned-symbol "..."))
(define (generic-write obj display? width pport
print-graph? print-struct? print-hash-table? print-vec-length?
print-box? print-as-qq? qq-depth
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-hasheq)) ; Hash table for looking for loops
(define show-inexactness? (pretty-print-show-inexactness))
(define exact-as-decimal? (pretty-print-exact-as-decimal))
(define long-bools? (print-boolean-long-form))
(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? obj) print-hash-table?))
(or (hash-ref table obj #f)
(begin
(hash-set! 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? obj)
(for/or ([(k v) (in-hash obj)])
(or (loop v) (loop k)))])])
(hash-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? obj) print-hash-table?))
;; A little confusing: use #t for not-found
(let ([p (hash-ref table obj #t)])
(when (not (mark? p))
(if p
(begin
(hash-set! 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? obj)
(hash-for-each
obj
(lambda (k v)
(loop k)
(loop v)))]))
(begin
(hash-set! table obj
(make-mark #f (box #f)))))))
(void)))))
(define escapes-table
(let* ([table (make-hasheq)]
[local-compound (and print-as-qq?
(make-hasheq))]
[is-compound! (lambda (obj)
(hash-set! local-compound obj #t))]
[escapes! (lambda (obj)
(hash-set! table obj #t)
#t)]
[orf (lambda (a b) (or a b))])
(when print-as-qq?
(let loop ([obj obj])
(cond
[(hash-ref table obj #f)
;; already decided that it escapes
#t]
[(and local-compound
(hash-ref local-compound obj #f))
;; either still deciding (so assume #f) or
;; already decided that no escape is needed
#f]
[else
(cond
[(vector? obj)
(is-compound! obj)
(let ([len (vector-length obj)])
(let vloop ([esc? #f][i 0])
(if (= i len)
(and esc?
(escapes! obj))
(vloop (or (loop (vector-ref obj i)) esc?)
(add1 i)))))]
[(pair? obj)
(is-compound! obj)
(and (orf (loop (car obj))
(loop (cdr obj)))
(escapes! obj))]
[(mpair? obj)
(is-compound! obj)
(loop (mcar obj))
(loop (mcdr obj))
;; always unquoted:
#t]
[(and (box? obj) print-box?)
(is-compound! obj)
(and (loop (unbox obj))
(escapes! obj))]
[(and (custom-write? obj)
(not (struct-type? obj)))
(is-compound! obj)
(let ([kind (if (custom-print-quotable? obj)
(custom-print-quotable-accessor obj)
'self)])
(and (or (and (loop (extract-sub-objects obj pport))
(not (memq kind '(self always))))
(memq kind '(never)))
(escapes! obj)))]
[(struct? obj)
(is-compound! obj)
(and (or (loop (struct->vector obj))
(not (prefab-struct-key obj)))
(escapes! obj))]
[(hash? obj)
(is-compound! obj)
(and (for/fold ([esc? #f]) ([(k v) (in-hash obj)])
(or (orf (loop v)
(loop k))
esc?))
(escapes! obj))]
[else #f])])))
table))
(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-ref found obj #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 qd multi-line?)
(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 #f))]
[displayer (lambda (v port)
(recur port v (dsub1 depth) #t #f))]
[printer (case-lambda
[(v port) (recur port v (dsub1 depth) #f qd)]
[(v port qd) (recur port v (dsub1 depth) #f qd)])])
(port-write-handler p writer)
(port-display-handler p displayer)
(port-print-handler p printer))
(register-printing-port-like p pport)
(parameterize ([pretty-printing multi-line?]
[pretty-print-columns (or width 'infinity)])
((custom-write-accessor obj) obj p (or qd (not display?)))))))
;; ------------------------------------------------------------
(define (convert-pair obj)
(cond
[(list? obj) (cons (make-unquoted 'list)
;; reconstruct first pair in case it
;; starts a cycle:
(cons (car obj) (cdr obj)))]
[(and (pair? (cdr obj))
(not (and found
(hash-ref found (cdr obj) #f))))
(cons (make-unquoted 'list*)
(cons
(car obj)
(let loop ([obj (cdr obj)])
(cond
[(and found (hash-ref found obj #f)) (list obj)]
[(pair? obj) (cons (car obj) (loop (cdr obj)))]
[else (list obj)]))))]
[else (list (make-unquoted 'cons) (car obj) (cdr obj))]))
(define (convert-hash obj expr?)
(let ([l (hash-map obj (lambda (k v)
(if expr?
(list k v)
(cons k (make-hide v)))))])
(if expr?
(cons (make-unquoted
(if (hash-eq? obj)
'hasheq
(if (hash-eqv? obj)
'hasheqv
'hash)))
(apply append l))
l)))
;; ------------------------------------------------------------
;; wr: write on a single line
(define (wr* pport obj depth display? qd)
(define (out str)
(write-string str pport))
(define (wr obj depth qd)
(wr* pport obj depth display? qd))
(define (wr-expr expr depth pair? car cdr open close qd)
(if (and (read-macro? expr pair? car cdr qd)
(equal? open "("))
(begin
(out (read-macro-prefix expr car))
(wr (read-macro-body expr car cdr) depth qd))
(wr-lst expr #t depth pair? car cdr open close qd)))
(define (wr-lst l check? depth pair? car cdr open close qd)
(if (pair? l)
(if (and depth (zero? depth))
(begin
(out open)
(out "...")
(out close))
(begin
(out open)
(wr (car l) (dsub1 depth) qd)
(let loop ([l (cdr l)])
(check-expr-found
l pport (and check? (pair? l))
(lambda (s) (out " . ") (out s) (out close))
(lambda ()
(out " . ")
(check-expr-found ;; will find it!
l pport #t
#f #f
(lambda ()
(wr-lst l check? (dsub1 depth) pair? car cdr open close qd)))
(out close))
(lambda ()
(cond
[(pair? l)
(if (and (eq? (do-remap (car l)) 'unquote)
(not (equal? qd 1))
(pair? (cdr l))
(null? (cdr (cdr l))))
(begin
(out " . ,")
(wr (car (cdr l)) (dsub1 depth) qd)
(out close))
(begin
(out " ")
(wr (car l) (dsub1 depth) qd)
(loop (cdr l))))]
[(null? l) (out close)]
[else
(out " . ")
(wr l (dsub1 depth) qd)
(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)
(check-expr-found
obj pport #t
#f #f
(lambda ()
(let* ([qd (to-quoted out qd obj)]
[pair (if (and qd (zero? qd))
(convert-pair obj)
obj)])
(wr-expr pair depth pair? car cdr pair-open pair-close qd))))]
[(mpair? obj)
(check-expr-found
obj pport #t
#f #f
(lambda ()
(if (and qd (zero? qd))
(wr-expr (list (make-unquoted 'mcons) (mcar obj) (mcdr obj))
depth pair? car cdr pair-open pair-close qd)
(wr-expr obj depth mpair? mcar mcdr mpair-open mpair-close qd))))]
[(null? obj)
(let ([qd (to-quoted out qd obj)])
(wr-lst obj #f depth pair? car cdr "(" ")" qd))]
[(vector? obj)
(check-expr-found
obj pport #t
#f #f
(lambda ()
(let ([qd (to-quoted out qd obj)]
[vecl (vector->repeatless-list obj)])
(if (and qd (zero? qd))
(wr-lst (cons (make-unquoted 'vector) vecl)
#f depth pair? car cdr "(" ")" qd)
(begin
(out "#")
(when print-vec-length?
(out (number->string (vector-length obj))))
(wr-lst vecl #f depth pair? car cdr "(" ")" qd))))))]
[(and (box? obj)
print-box?)
(check-expr-found
obj pport #t
#f #f
(lambda ()
(let ([qd (to-quoted out qd obj)])
(if (and qd (zero? qd))
(wr-lst (list (make-unquoted 'box) (unbox obj))
#f depth pair? car cdr "(" ")" qd)
(begin
(out "#&")
(wr (unbox obj) (dsub1 depth) qd))))))]
[(and (custom-write? obj)
(not (struct-type? obj)))
(check-expr-found
obj pport #t
#f #f
(lambda ()
(parameterize ([pretty-print-columns 'infinity])
(let ([qd (let ([kind (if (custom-print-quotable? obj)
(custom-print-quotable-accessor obj)
'self)])
(if (memq kind '(self never))
qd
(to-quoted out qd obj)))])
(write-custom wr* obj pport depth display? width qd #f)))))]
[(struct? obj)
(if (and print-struct?
(not (and depth
(zero? depth))))
(check-expr-found
obj pport #t
#f #f
(lambda ()
(let* ([v (struct->vector obj struct-ellipses)]
[pf? (prefab?! obj v)])
(let ([qd (if pf?
(to-quoted out qd obj)
qd)])
(when (or (not qd) (positive? qd))
(out "#")
(when pf? (out "s")))
(wr-lst (let ([l (vector->list v)])
(if (and qd (zero? qd))
(cons (make-unquoted (object-name obj))
(cdr l))
l))
#f (dsub1 depth) pair? car cdr "(" ")"
qd)))))
(parameterize ([print-struct #f])
((if display? orig-display orig-write) obj pport)))]
[(hash? obj)
(if (and print-hash-table?
(not (and depth
(zero? depth))))
(check-expr-found
obj pport #t
#f #f
(lambda ()
(let* ([qd (to-quoted out qd obj)]
[expr? (and qd (zero? qd))])
(unless expr?
(out (if (hash-eq? obj)
"#hasheq"
(if (hash-eqv? obj)
"#hasheqv"
"#hash"))))
(wr-lst (convert-hash obj expr?)
#f depth
pair? car cdr "(" ")" qd))))
(parameterize ([print-hash-table #f])
((if display? orig-display orig-write) obj pport)))]
[(hide? obj)
(wr* pport (hide-val obj) depth display? qd)]
[(boolean? obj)
(out (if long-bools?
(if obj "#true" "#false")
(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 ".")]
[(and qd (or (symbol? obj)
(keyword? obj)))
(unless (eq? obj struct-ellipses)
(to-quoted out qd obj))
(orig-write obj pport)]
[(unquoted? obj)
(orig-write (unquoted-val obj) pport)]
[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? qd)
(define (pp obj depth)
(pp* pport obj depth display? qd))
(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 qd)
;; may have to split on multiple lines
(let* ([obj (if (hide? obj) (hide-val obj) obj)]
[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? obj) print-hash-table?)))]
[graph-ref (if can-multi
(and found (hash-ref found obj #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? qd)
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)
(let* ([qd (to-quoted out qd obj)]
[pair (if (and qd (zero? qd))
(convert-pair obj)
obj)])
(pp-pair pair extra depth
pair? car cdr pair-open pair-close
qd))]
[(mpair? obj)
(if (and qd (zero? qd))
(pp-pair (list (make-unquoted 'mcons) (mcar obj) (mcdr obj))
extra depth
pair? car cdr pair-open pair-close
qd)
(pp-pair obj extra depth
mpair? mcar mcdr mpair-open mpair-close
qd))]
[(vector? obj)
(let ([qd (to-quoted out qd obj)]
[vecl (vector->repeatless-list obj)])
(if (and qd (zero? qd))
(pp-pair (cons (make-unquoted 'vector) vecl)
extra depth
pair? car cdr pair-open pair-close
qd)
(begin
(out "#")
(when print-vec-length?
(out (number->string (vector-length obj))))
(pp-list vecl extra pp-expr #f depth
pair? car cdr pair-open pair-close
qd))))]
[(and (custom-write? obj)
(not (struct-type? obj)))
(let ([qd (let ([kind (if (custom-print-quotable? obj)
(custom-print-quotable-accessor obj)
'self)])
(if (memq kind '(self never))
qd
(to-quoted out qd obj)))])
(write-custom pp* obj pport depth display? width qd #t))]
[(struct? obj) ; print-struct is on if we got here
(let* ([v (struct->vector obj struct-ellipses)]
[pf? (prefab?! obj v)])
(let ([qd (if pf?
(to-quoted out qd obj)
qd)])
(when (or (not qd) (positive? qd))
(out "#")
(when pf? (out "s")))
(pp-list (let ([l (vector->list v)])
(if (and qd (zero? qd))
(cons (make-unquoted (object-name obj))
(cdr l))
l))
extra pp-expr #f depth
pair? car cdr pair-open pair-close
qd)))]
[(hash? obj)
(let* ([qd (to-quoted out qd obj)]
[expr? (and qd (zero? qd))])
(unless expr?
(out (if (hash-eq? obj)
"#hasheq"
(if (hash-eqv? obj)
"#hasheqv"
"#hash"))))
(pp-list (convert-hash obj expr?) extra pp-expr #f depth
pair? car cdr pair-open pair-close
qd))]
[(and (box? obj) print-box?)
(let ([qd (to-quoted out qd obj)])
(if (and qd (zero? qd))
(pp-pair (list (make-unquoted 'box) (unbox obj))
extra depth
pair? car cdr pair-open pair-close
qd)
(begin
(out "#&")
(pr (unbox obj) extra pp-pair depth qd))))])
(post-print pport obj)))))
;; Not possible to split obj across lines; so just write directly
(wr* pport obj depth display? qd))))
(define (pp-expr expr extra depth
apair? acar acdr open close
qd)
(if (and (read-macro? expr apair? acar acdr qd)
(equal? open "(")
(not (and found (hash-ref found (acdr expr) #f))))
(begin
(out (read-macro-prefix expr acar))
(pr (read-macro-body expr acar acdr)
extra
pp-expr
depth
qd))
(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
(let* ([qd (to-quoted out qd expr)]
[pair (if (and qd (zero? qd))
(cons (make-unquoted 'list) obj)
obj)])
(proc expr extra depth
apair? acar acdr open close
qd))
(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
qd)
(pp-list expr extra pp-expr #t depth
apair? acar acdr open close
qd))))
(pp-list expr extra pp-expr #t depth
apair? acar acdr open close
qd)))))
(define (wr obj depth qd)
(wr* pport obj depth display? qd))
;; (head item1
;; item2
;; item3)
(define (pp-call expr extra pp-item depth
apair? acar acdr open close
qd)
(out open)
(wr (acar expr) (dsub1 depth) qd)
(let ([col (+ (ccol) 1)])
(pp-down close (acdr expr) col col extra pp-item #t #t depth
apair? acar acdr open close
qd)))
;; (head item1 item2
;; item3
;; item4)
(define (pp-two-up expr extra pp-item depth
apair? acar acdr open close
qd)
(out open)
(let ([col (ccol)])
(wr (acar expr) (dsub1 depth) qd)
(out " ")
(wr (acar (acdr expr)) (dsub1 depth) qd)
(pp-down close (acdr (acdr expr)) (+ (ccol) 1) (+ col 1) extra pp-item #t #t depth
apair? acar acdr open close
qd)))
;; (head item1
;; item2
;; item3)
(define (pp-one-up expr extra pp-item depth
apair? acar acdr open close
qd)
(out open)
(let ([col (ccol)])
(wr (acar expr) (dsub1 depth) qd)
(pp-down close (acdr expr) (+ (ccol) 1) (+ col 1) extra pp-item #t #t depth
apair? acar acdr open close
qd)))
;; (item1
;; item2
;; item3)
(define (pp-list l extra pp-item check? depth
apair? acar acdr open close
qd)
(out open)
(let ([col (ccol)])
(pp-down close l col col extra pp-item #f check? depth
apair? acar acdr open close
qd)))
(define (pp-down closer l col1 col2 extra pp-item check-first? check-rest? depth
apair? acar acdr open close
qd)
(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 qd)
(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) qd)
(loop rest col2 check-rest?)))]
[(null? l)
(out closer)]
[else
(indent col2)
(out ".")
(indent col2)
(pr l (+ extra 1) pp-item (dsub1 depth) qd)
(out closer)])))))
(define (pp-general expr extra named? pp-1 pp-2 pp-3 depth
apair? acar acdr open close
qd)
(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 qd)
(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 qd)
(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
qd))
(let* ([head (acar expr)]
[rest (acdr expr)]
[col (ccol)])
(out open)
(wr head (dsub1 depth) qd)
(if (and named? (apair? rest))
(let* ((name (acar rest))
(rest (acdr rest)))
(out " ")
(wr name (dsub1 depth) qd)
(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
qd)
(pp-list l extra pp-expr #t depth
apair? acar acdr open close
qd))
(define (pp-lambda expr extra depth
apair? acar acdr open close
qd)
(pp-general expr extra #f pp-expr-list #f pp-expr depth
apair? acar acdr open close
qd))
(define (pp-if expr extra depth
apair? acar acdr open close
qd)
(pp-general expr extra #f pp-expr #f pp-expr depth
apair? acar acdr open close
qd))
(define (pp-cond expr extra depth
apair? acar acdr open close
qd)
(pp-list expr extra pp-expr-list #t depth
apair? acar acdr open close
qd))
(define (pp-syntax-case expr extra depth
apair? acar acdr open close
qd)
(pp-two-up expr extra pp-expr-list depth
apair? acar acdr open close
qd))
(define (pp-module expr extra depth
apair? acar acdr open close
qd)
(pp-two-up expr extra pp-expr depth
apair? acar acdr open close
qd))
(define (pp-make-object expr extra depth
apair? acar acdr open close
qd)
(pp-one-up expr extra pp-expr-list depth
apair? acar acdr open close
qd))
(define (pp-case expr extra depth
apair? acar acdr open close
qd)
(pp-general expr extra #f pp-expr #f pp-expr-list depth
apair? acar acdr open close
qd))
(define (pp-and expr extra depth
apair? acar acdr open close
qd)
(pp-call expr extra pp-expr depth
apair? acar acdr open close
qd))
(define (pp-let expr extra depth
apair? acar acdr open close
qd)
(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
qd)))
(define (pp-begin expr extra depth
apair? acar acdr open close
qd)
(pp-general expr extra #f #f #f pp-expr depth
apair? acar acdr open close
qd))
(define (pp-do expr extra depth
apair? acar acdr open close
qd)
(pp-general expr extra #f pp-expr-list pp-expr-list pp-expr depth
apair? acar acdr open close
qd))
;; 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-ref 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 qd))
(define (to-quoted out qd obj)
(and qd
(if (zero? qd)
(if (hash-ref escapes-table obj #f)
qd
(begin
(out "'")
(add1 qd)))
qd)))
;; ------------------------------------------------------------
;; This is where generic-write's body expressions start
((printing-port-print-line pport) #t 0 width)
(let ([qd (if print-as-qq? qq-depth #f)])
(let-values ([(l col p) (port-next-location pport)])
(if (and width (not (eq? width 'infinity)))
(pp* pport obj depth display? qd)
(wr* pport obj depth display? qd))))
(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-ref (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 qd)
(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 syntax
quasisyntax unsyntax unsyntax-splicing
unquote unquote-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) "#'")
((quasisyntax) "#`")
((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))))))]))
)