The pretty-printer can now be made to print circular lists
authorsybok <sybok>
Thu, 29 Aug 1991 17:40:09 +0000 (17:40 +0000)
committersybok <sybok>
Thu, 29 Aug 1991 17:40:09 +0000 (17:40 +0000)
without hanging up.  Just fluid-let the variable
*pp-avoid-circularity?* to #t when needed.  (It defaults
to false.)

v7/src/runtime/pp.scm

index 3910b6ee222dae78b6e6ab66bad70bd0ccf20f56..afec1f23794dbea75b98f711aa40cb25a8b1d49c 100644 (file)
@@ -1,6 +1,6 @@
 #| -*-Scheme-*-
 
-$Header: /Users/cph/tmp/foo/mit-scheme/mit-scheme/v7/src/runtime/pp.scm,v 14.19 1991/08/21 16:57:02 jinx Exp $
+$Header: /Users/cph/tmp/foo/mit-scheme/mit-scheme/v7/src/runtime/pp.scm,v 14.20 1991/08/29 17:40:09 sybok Exp $
 
 Copyright (c) 1988-1991 Massachusetts Institute of Technology
 
@@ -56,6 +56,9 @@ MIT in each case. |#
   (set! dispatch-default print-combination)
   unspecific)
 
+
+
+  
 (define-structure (pretty-printer-highlight
                   (conc-name pph/)
                   (constructor
@@ -78,6 +81,7 @@ MIT in each case. |#
 (define *pp-save-vertical-space?* false)
 (define *pp-lists-as-tables?* true)
 (define *pp-forced-x-size* false)
+(define *pp-avoid-circularity?* false)
 
 (define (pp object #!optional port . rest)
   (let ((port (if (default-object? port) (current-output-port) port)))
@@ -119,7 +123,9 @@ MIT in each case. |#
 (define (pp-top-level expression port as-code? indentation list-depth)
   (fluid-let ((x-size (or *pp-forced-x-size* (output-port/x-size port)))
              (output-port port))
-    (let ((node (numerical-walk expression list-depth)))
+    (let* ((numerical-walk 
+           (if *pp-avoid-circularity?* numerical-walk-avoid-circularities numerical-walk))
+          (node (numerical-walk expression list-depth)))
       (if (positive? indentation)
          (*unparse-string (make-string indentation #\space)))
       (if as-code?
@@ -614,7 +620,9 @@ MIT in each case. |#
         false)))
 
 (define (walk-highlighted-object object list-depth)
-  (let ((dl (pph/depth-limit object)))
+  (let ((dl (pph/depth-limit object))
+       (numerical-walk 
+        (if *pp-avoid-circularity?*  numerical-walk-avoid-circularities numerical-walk)))
     (fluid-let ((*unparser-list-breadth-limit*
                 (let ((bl (pph/breadth-limit object)))
                   (if (eq? bl 'default)
@@ -628,6 +636,314 @@ MIT in each case. |#
                      (if (eq? dl 'default)
                          list-depth
                          0)))))
+
+\f
+;;;  The following are circular list/vector handing procedures.  They allow
+;;;  arbitary circular constructions made from pairs and vectors to be printed
+;;;  in closed form.  The term "current parenthetical level" means the lowest
+;;;  parethetical level which contains the circularity object.  Expressions like
+;;;  "up 1 parenthetical level" refer to the object which is one parenthetical
+;;;  level above the lowest parenthetical level which contains the circularity
+;;;  object--i.e., the second lowest parenthetical level which contains the 
+;;;  circularity object.
+;;;       Finally, the expression, "up 1 parenthetical level, downstream 1 cdr,"
+;;;  means that to find the object being referred to, you should go to the
+;;;  parenthetical level one level above the lowest parenthetical level which
+;;;  contains the circularity object, and then take the cdr of that list.
+;;;  This notation must be used because while a new parenthetical level is
+;;;  generated for each car and each vector-ref, a new parenthetical level
+;;;  obtains from cdring iff the result of said cdring is NOT a pair.
+
+;; This is the master procedure which all circularity-proof printing
+;; goes through.
+
+(define (numerical-walk-avoid-circularities exp list-depth)
+  (numerical-walk-terminating exp (cons exp (make-queue)) list-depth))
+
+
+;; This numerical walker has special pair and vector walkers to guarantee proper termination.
+
+(define (numerical-walk-terminating object half-pointer/queue list-depth)
+  (define queue (cdr half-pointer/queue))
+  (define half-pointer (car half-pointer/queue))
+  (cond ((pair? object)
+        (let ((prefix (unparse-list/prefix-pair? object)))
+          (if prefix
+              (make-prefix-node prefix
+                                (numerical-walk-terminating (cadr object) 
+                                                            (advance half-pointer (update-queue queue '(cdr car)))
+                                                            list-depth))
+              (let ((unparser (unparse-list/unparser object)))
+                (if unparser
+                    (walk-custom unparser object list-depth)
+                    (walk-pair-terminating object half-pointer/queue list-depth))))))
+       ((symbol? object)
+        (if (or *pp-uninterned-symbols-by-name*
+                (object-type? (ucode-type interned-symbol) object))
+            object
+            (walk-custom unparse-object object list-depth)))
+       ((pretty-printer-highlight? object)
+        (let ((rest (walk-highlighted-object object list-depth))
+              (start (pph/start-string object))
+              (end (pph/end-string object)))
+          (make-highlighted-node
+           (+ (string-length start)
+              (string-length end)
+              (node-size rest))
+           object
+           rest)))
+       ((vector? object)
+        (if (zero? (vector-length object))
+            (walk-custom unparse-object object list-depth)
+            (let ((unparser (unparse-vector/unparser object)))
+              (if unparser
+                  (walk-custom unparser object list-depth)
+                  (make-prefix-node "#" (walk-vector-terminating (vector->list object) half-pointer/queue list-depth))))))
+       ((primitive-procedure? object)
+        (if *pp-primitives-by-name*
+            (primitive-procedure-name object)
+            (walk-custom unparse-object object list-depth)))
+       (else
+        (walk-custom unparse-object object list-depth))))
+
+;; The following two procedures walk lists and vectors, respectively.
+
+(define (walk-pair-terminating pair half-pointer/queue list-depth)
+       (if (and *unparser-list-depth-limit*
+          (>= list-depth *unparser-list-depth-limit*)
+          (no-highlights? pair))
+      "..."
+      (let ((list-depth (+ list-depth 1)))
+       (let loop ((pair pair) (list-breadth 0) (half-pointer/queue half-pointer/queue))
+         (cond ((and *unparser-list-breadth-limit*
+                     (>= list-breadth *unparser-list-breadth-limit*)
+                     (no-highlights? pair))
+                (make-singleton-list-node "..."))
+               ((null? (cdr pair))
+                (make-singleton-list-node
+                 (let ((half-pointer/queue (advance (car half-pointer/queue) (update-queue (cdr half-pointer/queue) '(car)))))
+                   (if (eq? (car half-pointer/queue) (car pair))
+                       (circularity-string (cdr half-pointer/queue))
+                       (numerical-walk-terminating (car pair) half-pointer/queue list-depth)))))
+               (else
+                (make-list-node
+                 (let ((half-pointer/queue (advance (car half-pointer/queue) (update-queue (cdr half-pointer/queue) '(car)))))
+                   (if (eq? (car half-pointer/queue) (car pair))
+                       (circularity-string (cdr half-pointer/queue))
+                       (numerical-walk-terminating (car pair) half-pointer/queue list-depth)))
+                 (let ((list-breadth (+ list-breadth 1)))
+                   (if (and (pair? (cdr pair))
+                            (not (unparse-list/unparser (cdr pair))))
+                       (let ((half-pointer/queue (advance (car half-pointer/queue) (update-queue (cdr half-pointer/queue) '(cdr)))))
+                         (if (eq? (car half-pointer/queue) (cdr pair))
+                             (make-singleton-list-node 
+                              (string-append ". " (circularity-string (cdr half-pointer/queue))))
+                             (loop (cdr pair) list-breadth half-pointer/queue)))
+                       (make-list-node
+                        "."
+                        (make-singleton-list-node
+                         (if (and *unparser-list-breadth-limit*
+                                  (>= list-breadth
+                                      *unparser-list-breadth-limit*)
+                                  (no-highlights? pair))
+                             "..."
+                             (let ((half-pointer/queue (advance (car half-pointer/queue) (update-queue (cdr half-pointer/queue) '(cdr)))))
+                               (if (eq? (car half-pointer/queue) (cdr pair))
+                                 (circularity-string (cdr half-pointer/queue))
+                                 (numerical-walk-terminating (cdr pair) half-pointer/queue list-depth)))))))))))))))
+
+(define (walk-vector-terminating pair half-pointer/queue list-depth)
+  (if (and *unparser-list-depth-limit*
+          (>= list-depth *unparser-list-depth-limit*)
+          (no-highlights? pair))
+      "..."
+      (let ((list-depth (+ list-depth 1)))
+       (let loop ((pair pair) (list-breadth 0))
+         (cond ((and *unparser-list-breadth-limit*
+                     (>= list-breadth *unparser-list-breadth-limit*)
+                     (no-highlights? pair))
+                (make-singleton-list-node "..."))
+               ((null? (cdr pair))
+                (make-singleton-list-node
+                 (let ((half-pointer/queue (advance (car half-pointer/queue) (update-queue (cdr half-pointer/queue) (list list-breadth)))))
+                   (if (eq? (car half-pointer/queue) (car pair))
+                       (circularity-string (cdr half-pointer/queue))
+                       (numerical-walk-terminating (car pair) half-pointer/queue list-depth)))))
+               (else
+                (make-list-node
+                 (let ((half-pointer/queue (advance (car half-pointer/queue) (update-queue (cdr half-pointer/queue) (list list-breadth)))))
+                   (if (eq? (car half-pointer/queue) (car pair))
+                       (circularity-string (cdr half-pointer/queue))
+                       (numerical-walk-terminating (car pair) half-pointer/queue list-depth)))
+                 (let ((list-breadth (+ list-breadth 1)))
+                   (if (not (unparse-list/unparser (cdr pair)))
+                       (loop (cdr pair) list-breadth)
+                       (make-list-node
+                        "."
+                        (make-singleton-list-node
+                         (if (and *unparser-list-breadth-limit*
+                                  (>= list-breadth
+                                      *unparser-list-breadth-limit*)
+                                  (no-highlights? pair))
+                             "..."
+                             (numerical-walk-terminating (cdr pair) half-pointer/queue list-depth)))))))))))))
+
+
+\f
+;;;; These procedures allow the walkers to interact with the queue.
+
+(define cocked-object (generate-uninterned-symbol 'cocked-object))
+
+(define (advance half-object queue)
+  (cond ((vector? half-object)
+        (cons (cons cocked-object half-object) queue))
+       ((not (pair? half-object))
+        (cons half-object queue))
+       ((eq? (car half-object) cocked-object)
+        (cons (let ((directive (queue-car queue)))
+                (cond ((>= directive 0)
+                       (vector-ref (cdr half-object) directive))
+                      ((= directive -1)
+                       (cadr half-object))
+                      (else
+                       (cddr half-object))))
+              (queue-cdr queue)))
+       (else
+        (cons (cons cocked-object half-object) queue))))
+
+(define (update-queue queue command-list)
+  (define (uq-iter queue command-list)
+    (cond ((null? command-list) queue)
+         ((eq? (car command-list) 'car)
+          (uq-iter (add-car queue) (cdr command-list)))
+         ((eq? (car command-list) 'cdr)
+          (uq-iter (add-cdr queue) (cdr command-list)))
+         (else
+          (uq-iter (add-vector-ref (car command-list) queue) (cdr command-list)))))
+  (uq-iter queue command-list))
+
+(define (add-car queue)
+  (queue-cons queue -1))
+
+(define (add-cdr queue)
+  (queue-cons queue -2))
+
+(define (add-vector-ref n queue)
+  (queue-cons queue n))
+
+\f
+;;;; The Queue Abstraction.  Queues are data structures allowing fifo
+;;;  access without mutation.  The following procedures implement them.
+
+(define-structure (queue 
+                  (conc-name queue/)
+                  (constructor
+                   make-queue
+                   (#!optional cons-cell past-cdrs)))
+  (cons-cell (let* ((new-vector (make-fluid-vector))
+                   (pointer (cons 0 new-vector)))
+              (cons pointer pointer)))
+  (past-cdrs 0))
+
+;;; Fluid Vectors.
+;;  Queues are built on a subabstraction, "fluid-vectors," which
+;;  are actually nested vectors of a default length.
+
+(define default-fluid-vector-length 10)
+(define virtual-fluid-vector-length (-1+ default-fluid-vector-length))
+
+(define (fluid-vector-extend fluid-vector)
+  (define new-fluid-vector (make-fluid-vector))
+  (vector-set! fluid-vector virtual-fluid-vector-length new-fluid-vector)
+  new-fluid-vector)
+
+(define (fluid-vector-set! fluid-vector index object)
+  (define tail (vector-ref fluid-vector virtual-fluid-vector-length))
+  (if (< index virtual-fluid-vector-length)
+      (vector-set! fluid-vector index object)
+      (fluid-vector-set! tail (- index virtual-fluid-vector-length) object)))
+
+(define (make-fluid-vector)
+  (make-vector default-fluid-vector-length #f))
+
+;;; The actual queue constructors/extractors
+
+(define (queue-cons queue object)
+  (let* ((old-cell (queue/cons-cell queue))
+        (head (car old-cell))
+        (tail (cdr old-cell)))
+    (if (eq? head tail)
+       (begin
+         (fluid-vector-set! (cdr tail) 0 object)
+         (make-queue (cons head (cons 1 (cdr tail))) (queue/past-cdrs queue)))
+       (begin
+         (fluid-vector-set! (cdr tail) (car tail) object)
+         (make-queue (cons head
+                           (if (= (car tail) (-1+ virtual-fluid-vector-length))
+                               (cons 0 (fluid-vector-extend (cdr tail)))
+                               (cons (1+ (car tail)) (cdr tail))))
+                     (queue/past-cdrs queue))))))
+
+(define (queue-car queue)
+  (define head (car (queue/cons-cell queue)))
+  (vector-ref (cdr head) (car head)))
+
+(define (queue-cdr queue)
+  (define head (car (queue/cons-cell queue)))
+  (define tail (cdr (queue/cons-cell queue)))
+  (make-queue
+   (cons 
+    (if (= (car head) (-1+ virtual-fluid-vector-length))
+       (cons 0 (vector-ref (cdr head) virtual-fluid-vector-length))
+       (cons (1+ (car head)) (cdr head)))
+    tail)
+   (if (= (queue-car queue) -2)
+       (1+ (queue/past-cdrs queue))
+       0)))
+
+;; Auxilary queue handlers.
+
+(define (null-queue? queue)
+  (define cell (queue/cons-cell queue))
+  (eq? (car cell) (cdr cell)))
+
+(define (queue-depth queue)
+  (define (flatten starting-vector starting-n ending-vector ending-n)
+    (if (eq? starting-vector ending-vector)
+       (vector->list (subvector starting-vector starting-n ending-n))
+       (append (vector->list (subvector starting-vector starting-n virtual-fluid-vector-length))
+               (flatten (vector-ref starting-vector virtual-fluid-vector-length) 0 ending-vector ending-n))))
+  (define (proc-list-iter list code-cache)
+    (cond ((null? list) (if (eq? code-cache -2) 1 0))
+         ((>= (car list) 0)
+          (+ (if (eq? code-cache -2) 2 1)
+             (proc-list-iter (cdr list) (car list))))
+         ((= (car list) -1)
+          (1+ (proc-list-iter (cdr list) (car list))))
+         (else
+          (proc-list-iter (cdr list) (car list)))))
+  (let* ((cell (queue/cons-cell queue))
+        (head (car cell))
+        (tail (cdr cell))
+        (operating-list (flatten (cdr head) (car head) (cdr tail) (car tail))))
+    (proc-list-iter operating-list #f)))
+
+\f
+;;;; This procedure creates the circularity object which is printed within circular structures.
+
+(define (circularity-string queue)
+       (let ((depth (queue-depth queue))
+             (cdrs (queue/past-cdrs queue)))
+         (string-append
+          (cond ((= depth 1) "#[circularity (current parenthetical level")
+                ((= depth 2) "#[circularity (up 1 parenthetical level")
+                (else
+                 (string-append "#[circularity (up " (number->string (-1+ depth)) " parenthetical levels")))
+          (cond ((= cdrs 0) ")]")
+                ((= cdrs 1) ", downstream 1 cdr.)]")
+                (else
+                 (string-append ", downstream " (number->string cdrs) " cdrs.)]"))))))
+
 \f
 ;;;; Node Model
 ;;;  Carefully crafted to use the least amount of memory, while at the