-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpprint.scm
212 lines (195 loc) · 6.95 KB
/
pprint.scm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
;;;; pprint.scm --- pretty-printing objects for CGEN
;;;; Copyright (C) 2005, 2009 Red Hat, Inc.
;;;; This file is part of CGEN.
;;;; See file COPYING.CGEN for details.
;;; This file defines a printing function PPRINT, and some hooks to
;;; let you print certain kind of objects in a summary way, and get at
;;; their full values later.
;;; PPRINT is a printer for Scheme objects that prints lists or
;;; vectors that contain shared structure or cycles and prints them in
;;; a finite, legible way.
;;;
;;; Ordinary values print in the usual way:
;;;
;;; guile> (pprint '(1 #(2 3) 4))
;;; (1 #(2 3) 4)
;;;
;;; Values can share structure:
;;;
;;; guile> (let* ((c (list 1 2))
;;; (d (list c c)))
;;; (write d)
;;; (newline))
;;; ((1 2) (1 2))
;;;
;;; In that list, the two instances of (1 2) are actually the same object;
;;; the top-level list refers to the same object twice.
;;;
;;; Printing that structure with PPRINT shows the sharing:
;;;
;;; guile> (let* ((c (list 1 2))
;;; (d (list c c)))
;;; (pprint d))
;;; (#0=(1 2) #0#)
;;;
;;; Here the "#0=" before the list (1 2) labels it with the number
;;; zero. Then, the "#0#" as the second element of the top-level list
;;; indicates that the object appears here, too, referring to it by
;;; its label.
;;;
;;; If you have several objects that appear more than once, they each
;;; get a separate label:
;;;
;;; guile> (let* ((a (list 1 2))
;;; (b (list 3 4))
;;; (c (list a b a b)))
;;; (pprint c))
;;; (#0=(1 2) #1=(3 4) #0# #1#)
;;;
;;; Cyclic values just share structure with themselves:
;;;
;;; guile> (let* ((a (list 1 #f)))
;;; (set-cdr! a a)
;;; (pprint a))
;;; #0=(1 . #0#)
;;;
;;;
;;; PPRINT also consults the function ELIDE? and ELIDED-NAME to see
;;; whether it should print a value in a summary form. You can
;;; re-define those functions to customize PPRINT's behavior;
;;; cos-pprint.scm defines them to handle COS objects and classes
;;; nicely.
;;;
;;; (ELIDE? OBJ) should return true if OBJ should be elided.
;;; (ELIDED-NAME OBJ) should return a (non-cyclic!) object to be used
;;; as OBJ's abbreviated form.
;;;
;;; PPRINT prints an elided object as a list ($ N NAME), where NAME is
;;; the value returned by ELIDED-NAME to stand for the object, and N
;;; is a number; each elided object gets its own number. You can refer
;;; to the elided object number N as ($ N).
;;;
;;; For example, if you've loaded CGEN, pprint.scm, and cos-pprint.scm
;;; (you must load cos-pprint.scm *after* loading pprint.scm), you can
;;; print a list containing the <insn> and <ident> classes:
;;;
;;; guile> (pprint (list <insn> <ident>))
;;; (($ 1 (class <insn>)) ($ 2 (class <ident>)))
;;; guile> (class-name ($ 1))
;;; <insn>
;;; guile> (class-name ($ 2))
;;; <ident>
;;;
;;; As a special case, PPRINT never elides the object that was passed
;;; to it directly. So you can look inside an elided object by doing
;;; just that:
;;;
;;; guile> (pprint ($ 2))
;;; #0=#("class" <ident> () ((name #:unbound #f . 0) ...
;;;
;;; A list of elided objects, larger numbers first, and the number of
;;; the first element.
(define elide-table '())
(define elide-table-last -1)
;;; Add OBJ to the elided object list, and return its number.
(define (add-elided-object obj)
(set! elide-table (cons obj elide-table))
(set! elide-table-last (+ elide-table-last 1))
elide-table-last)
;;; Referencing elided objects.
(define ($ n)
(if (<= 0 n elide-table-last)
(list-ref elide-table (- elide-table-last n))
"no such object"))
;;; A default predicate for elision.
(define (elide? obj) #f)
;;; If (elide? OBJ) is true, return some sort of abbreviated list
;;; structure that might be helpful to the user in identifying the
;;; elided object.
;;; A default definition.
(define (elided-name obj) "")
;;; This is a pretty-printer that handles cyclic and shared structure.
(define (pprint original-obj)
;; Return true if OBJ should be elided in this call to pprint.
;; (We never elide the object we were passed itself.)
(define (elide-this-call? obj)
(and (not (eq? obj original-obj))
(elide? obj)))
;; First, traverse OBJ and build a hash table mapping objects
;; referenced more than once to #t, and everything else to #f.
;; (Only include entries for objects that might be interior nodes:
;; pairs and vectors.)
(let ((shared
;; Guile's stupid hash tables don't resize the table; the
;; chains just get longer and longer. So we need a big value here.
(let ((seen (make-hash-table 65521))
(shared (make-hash-table 4093)))
(define (walk! obj)
(if (or (pair? obj) (vector? obj))
(if (hashq-ref seen obj)
(hashq-set! shared obj #t)
(begin
(hashq-set! seen obj #t)
(cond ((elide-this-call? obj))
((pair? obj) (begin (walk! (car obj))
(walk! (cdr obj))))
((vector? obj) (do ((i 0 (+ i 1)))
((>= i (vector-length obj)))
(walk! (vector-ref obj i))))
(else (error "unhandled interior type")))))))
(walk! original-obj)
shared)))
;; A counter for shared structure labels.
(define fresh-shared-label
(let ((n 0))
(lambda ()
(let ((l n))
(set! n (+ n 1))
l))))
(define (print obj)
(print-with-label obj (hashq-ref shared obj)))
;; Print an object OBJ, which SHARED maps to L.
;; L is always (hashq-ref shared obj), but we have that value handy
;; at times, so this entry point lets us avoid looking it up again.
(define (print-with-label obj label)
(if (number? label)
;; If we've already visited this object, just print a
;; reference to its label.
(map display `("#" ,label "#"))
(begin
;; If it needs a label, attach one now.
(if (eqv? label #t) (let ((label (fresh-shared-label)))
(hashq-set! shared obj label)
(map display `("#" ,label "="))))
;; Print the object.
(cond ((elide-this-call? obj)
(write (list '$ (add-elided-object obj) (elided-name obj))))
((pair? obj) (begin (display "(")
(print-tail obj)))
((vector? obj) (begin (display "#(")
(do ((i 0 (+ i 1)))
((>= i (vector-length obj)))
(print (vector-ref obj i))
(if (< (+ i 1) (vector-length obj))
(display " ")))
(display ")")))
(else (write obj))))))
;; Print a pair P as if it were the tail of a list; assume the
;; opening paren and any previous elements have been printed.
(define (print-tail obj)
(print (car obj))
(force-output)
(let ((tail (cdr obj)))
(if (null? tail)
(display ")")
;; We use the dotted pair syntax if the cdr isn't a pair, but
;; also if it needs to be labeled.
(let ((tail-label (hashq-ref shared tail)))
(if (or (not (pair? tail)) tail-label)
(begin (display " . ")
(print-with-label tail tail-label)
(display ")"))
(begin (display " ")
(print-tail tail)))))))
(print original-obj)
(newline)))