~ chicken-r7rs (master) /scheme.write.scm
Trap1(module scheme.write (display
2 write
3 write-shared
4 write-simple)
5 (import (rename scheme (display display-simple) (write write-simple))
6 (only chicken.base foldl when optional)
7 (only chicken.platform feature?)
8 (only chicken.type :)
9 (only chicken.fixnum fx+ fx= fx<=))
10
11 (when (feature? 'csi)
12 (set! ##sys#repl-print-hook
13 (lambda (o p)
14 (write o p)
15 (newline))))
16
17 (define (interesting? o)
18 (or (pair? o)
19 (and (vector? o)
20 (fx<= 1 (vector-length o)))))
21
22 (define (uninteresting? o)
23 (not (interesting? o)))
24
25 (define (display-char c p)
26 ((##sys#slot (##sys#slot p 2) 2) p c))
27
28 (define (display-string s p)
29 ((##sys#slot (##sys#slot p 2) 3) p s))
30
31 ;; Build an alist mapping `interesting?` objects to boolean values
32 ;; indicating whether those objects occur shared in `o`.
33 (define (find-shared o cycles-only?)
34
35 (define seen '())
36 (define (seen? x) (assq x seen))
37 (define (seen! x) (set! seen (cons (cons x 1) seen)))
38
39 ;; Walk the form, tallying the number of times each object is
40 ;; encountered. This has the effect of filling `seen` with
41 ;; occurence counts for all objects satisfying `interesting?`.
42 (let walk! ((o o))
43 (when (interesting? o)
44 (cond ((seen? o) =>
45 (lambda (p)
46 (set-cdr! p (fx+ (cdr p) 1))))
47 ((pair? o)
48 (seen! o)
49 (walk! (car o))
50 (walk! (cdr o)))
51 ((vector? o)
52 (seen! o)
53 (let ((len (vector-length o)))
54 (do ((i 0 (fx+ i 1)))
55 ((fx= i len))
56 (walk! (vector-ref o i))))))
57 ;; If we're only interested in cycles and this object isn't
58 ;; self-referential, discount it (resulting in `write` rather
59 ;; than `write-shared` behavior).
60 (when cycles-only?
61 (let ((p (seen? o)))
62 (when (fx<= (cdr p) 1)
63 (set-cdr! p 0))))))
64
65 ;; Mark shared objects #t, unshared objects #f.
66 (foldl (lambda (a p)
67 (if (fx<= (cdr p) 1)
68 (cons (cons (car p) #f) a)
69 (cons (cons (car p) #t) a)))
70 '()
71 seen))
72
73 (define (write-with-shared-structure writer obj cycles-only? port)
74
75 (define label 0)
76 (define (assign-label! pair)
77 (set-cdr! pair label)
78 (set! label (fx+ label 1)))
79
80 (define shared
81 (find-shared obj cycles-only?))
82
83 (define (write-interesting/shared o)
84 (cond ((pair? o)
85 (display-char #\( port)
86 (write/shared (car o))
87 (let loop ((o (cdr o)))
88 (cond ((null? o)
89 (display-char #\) port))
90 ((and (pair? o)
91 (not (cdr (assq o shared))))
92 (display-char #\space port)
93 (write/shared (car o))
94 (loop (cdr o)))
95 (else
96 (display-string " . " port)
97 (write/shared o)
98 (display-char #\) port)))))
99 ((vector? o)
100 (display-string "#(" port)
101 (write/shared (vector-ref o 0))
102 (let ((len (vector-length o)))
103 (do ((i 1 (fx+ i 1)))
104 ((fx= i len)
105 (display-char #\) port))
106 (display-char #\space port)
107 (write/shared (vector-ref o i)))))))
108
109 (define (write/shared o)
110 (if (uninteresting? o)
111 (writer o port)
112 (let* ((p (assq o shared))
113 (d (cdr p)))
114 (cond ((not d)
115 (write-interesting/shared o))
116 ((number? d)
117 (display-char #\# port)
118 (writer d port)
119 (display-char #\# port))
120 (else
121 (display-char #\# port)
122 (writer label port)
123 (display-char #\= port)
124 (assign-label! p)
125 (write-interesting/shared o))))))
126
127 (write/shared obj))
128
129 (: display (* #!optional output-port -> undefined))
130 (define (display o . p)
131 (write-with-shared-structure
132 display-simple
133 o
134 #t
135 (optional p (current-output-port))))
136
137 (: write (* #!optional output-port -> undefined))
138 (define (write o . p)
139 (write-with-shared-structure
140 write-simple
141 o
142 #t
143 (optional p (current-output-port))))
144
145 (: write-shared (* #!optional output-port -> undefined))
146 (define (write-shared o . p)
147 (write-with-shared-structure
148 write-simple
149 o
150 #f
151 (optional p (current-output-port)))))