home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / mitsch75.zip / scheme-7_5_17-src.zip / scheme-7.5.17 / src / runtime / pp.scm < prev    next >
Text File  |  2001-07-02  |  39KB  |  1,178 lines

  1. #| -*-Scheme-*-
  2.  
  3. $Id: pp.scm,v 14.42 2001/07/02 18:47:51 cph Exp $
  4.  
  5. Copyright (c) 1988-2001 Massachusetts Institute of Technology
  6.  
  7. This program is free software; you can redistribute it and/or modify
  8. it under the terms of the GNU General Public License as published by
  9. the Free Software Foundation; either version 2 of the License, or (at
  10. your option) any later version.
  11.  
  12. This program is distributed in the hope that it will be useful, but
  13. WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15. General Public License for more details.
  16.  
  17. You should have received a copy of the GNU General Public License
  18. along with this program; if not, write to the Free Software
  19. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
  20. 02111-1307, USA.
  21. |#
  22.  
  23. ;;;; Pretty Printer
  24. ;;; package: (runtime pretty-printer)
  25.  
  26. (declare (usual-integrations))
  27.  
  28. (define (initialize-package!)
  29.   (set! pp-description (make-generic-procedure 1 'PP-DESCRIPTION))
  30.   (set-generic-procedure-default-generator! pp-description
  31.     (lambda (generic tags)
  32.       generic tags
  33.       pp-description/default))
  34.   (set! forced-indentation (special-printer kernel/forced-indentation))
  35.   (set! pressured-indentation (special-printer kernel/pressured-indentation))
  36.   (set! print-procedure (special-printer kernel/print-procedure))
  37.   (set! print-let-expression (special-printer kernel/print-let-expression))
  38.   (set! print-case-expression (special-printer kernel/print-case-expression))
  39.   (set! code-dispatch-list
  40.     `((COND . ,forced-indentation)
  41.       (CASE . ,print-case-expression)
  42.       (IF . ,forced-indentation)
  43.       (OR . ,forced-indentation)
  44.       (AND . ,forced-indentation)
  45.       (LET . ,print-let-expression)
  46.       (LET* . ,print-let-expression)
  47.       (LETREC . ,print-let-expression)
  48.       (FLUID-LET . ,print-let-expression)
  49.       (DEFINE . ,print-procedure)
  50.       (DEFINE-INTEGRABLE . ,print-procedure)
  51.       (LAMBDA . ,print-procedure)
  52.       (NAMED-LAMBDA . ,print-procedure)))
  53.   (set! dispatch-list code-dispatch-list)
  54.   (set! dispatch-default print-combination)
  55.   (set! cocked-object (generate-uninterned-symbol))
  56.   unspecific)
  57.  
  58. (define *pp-named-lambda->define?* #f)
  59. (define *pp-primitives-by-name* #t)
  60. (define *pp-uninterned-symbols-by-name* #t)
  61. (define *pp-no-highlights?* #t)
  62. (define *pp-save-vertical-space?* #f)
  63. (define *pp-lists-as-tables?* #t)
  64. (define *pp-forced-x-size* #f)
  65. (define *pp-avoid-circularity?* #f)
  66. (define *pp-default-as-code?* #t)
  67. (define *pp-auto-highlighter* #f)
  68.  
  69. (define (pp object #!optional port . rest)
  70.   (let ((port (if (default-object? port) (current-output-port) port)))
  71.     (let ((pretty-print
  72.        (lambda (object)
  73.          (fresh-line port)
  74.          (apply pretty-print object port rest))))
  75.       (cond ((pp-description object)
  76.          => (lambda (description)
  77.           (pretty-print object)
  78.           (for-each pretty-print description)))
  79.         ((arity-dispatched-procedure? object)
  80.          (pretty-print (unsyntax-entity object)))
  81.         ((and (procedure? object) (procedure-lambda object))
  82.          => pretty-print)
  83.         (else
  84.          (pretty-print object))))))
  85.  
  86. (define pp-description)
  87.  
  88. (define (pp-description/default object)
  89.   (cond ((named-structure? object)
  90.      (named-structure/description object))
  91.     ((%record? object)        ; unnamed record
  92.      (let loop ((i (- (%record-length object) 1)) (d '()))
  93.        (if (< i 0)
  94.            d
  95.            (loop (- i 1)
  96.              (cons (list i (%record-ref object i)) d)))))
  97.     ((weak-pair? object)
  98.      `((WEAK-CAR ,(weak-car object))
  99.        (WEAK-CDR ,(weak-cdr object))))
  100.     ((cell? object)
  101.      `((CONTENTS ,(cell-contents object))))
  102.     (else
  103.      #f)))
  104.  
  105. ;;; Controls the appearance of procedures in the CASE statement used
  106. ;;; to describe an arity dispatched procedure:
  107. ;;;  FULL:  full bodies of procedures
  108. ;;;  NAMED: just name if the procedure is a named lambda, like FULL if unnamed
  109. ;;;  SHORT: procedures appear in #[...] unparser syntax
  110. (define *pp-arity-dispatched-procedure-style* 'FULL)
  111.  
  112. (define (unsyntax-entity object)
  113.   (define (unsyntax-entry procedure)
  114.     (case *pp-arity-dispatched-procedure-style*
  115.       ((FULL)  (unsyntax-entity procedure))
  116.       ((NAMED)
  117.        (let ((text (unsyntax-entity procedure)))
  118.      (if (and (pair? text)
  119.           (eq? (car text) 'named-lambda)
  120.           (pair? (cdr text))
  121.           (pair? (cadr text)))
  122.          (caadr text)
  123.          text)))
  124.       ((SHORT) procedure)
  125.       (else procedure)))
  126.   (cond ((arity-dispatched-procedure? object)
  127.      (let* ((default  (entity-procedure  object))
  128.         (cases    (cdr (vector->list (entity-extra object))))
  129.         (cases*
  130.          (let loop ((i 0) (tests '()) (cases cases))
  131.            (cond ((null? cases) (reverse tests))
  132.              ((car cases)
  133.               (loop (+ i 1)
  134.                 (cons `((,i) ,(unsyntax-entry (car cases)))
  135.                       tests)
  136.                 (cdr cases)))
  137.              (else
  138.               (loop (+ i 1) tests (cdr cases)))))))
  139.        `(CASE NUMBER-OF-ARGUMENTS
  140.           ,@cases*
  141.           (ELSE
  142.            ,(unsyntax-entry default)))))
  143.     ((and (procedure? object) (procedure-lambda object))
  144.      => unsyntax)
  145.     (else
  146.      object)))
  147.  
  148. (define (pretty-print object #!optional port as-code? indentation)
  149.   (let ((as-code?
  150.      (if (default-object? as-code?)
  151.          (let ((default *pp-default-as-code?*))
  152.            (if (boolean? default)
  153.            default
  154.            (not (scode-constant? object))))
  155.          as-code?)))
  156.     (pp-top-level (let ((sexp
  157.              (if (scode-constant? object)
  158.                  object
  159.                  (unsyntax object))))
  160.             (if (and as-code?
  161.                  (pair? sexp)
  162.                  (eq? (car sexp) 'NAMED-LAMBDA)
  163.                  *pp-named-lambda->define?*)
  164.             (if (and (eq? 'LAMBDA *pp-named-lambda->define?*)
  165.                  (pair? (cdr sexp))
  166.                  (pair? (cadr sexp)))
  167.                 `(LAMBDA ,(cdadr sexp) ,@(cddr sexp))
  168.                 `(DEFINE ,@(cdr sexp)))
  169.             sexp))
  170.           (if (default-object? port) (current-output-port) port)
  171.           as-code?
  172.           (if (default-object? indentation) 0 indentation)
  173.           0)
  174.     unspecific))
  175.  
  176. (define-structure (pretty-printer-highlight
  177.            (conc-name pph/)
  178.            (constructor
  179.             make-pretty-printer-highlight
  180.             (object #!optional
  181.                 start-string end-string
  182.                 as-code? depth-limit
  183.                 breadth-limit)))
  184.   (object #f read-only #t)
  185.   (start-string "*=>" read-only #t)
  186.   (end-string   "<=*" read-only #t)
  187.   (as-code? 'DEFAULT read-only #t)
  188.   (depth-limit 'DEFAULT read-only #t)
  189.   (breadth-limit 'DEFAULT read-only #t))
  190.  
  191. (define (with-highlight-strings-printed pph thunk)
  192.   (let ((print-string
  193.      (lambda (s)
  194.        (if (string? s)
  195.            (*unparse-string s)
  196.            (s output-port)))))
  197.     (print-string (pph/start-string pph))
  198.     (thunk)
  199.     (print-string (pph/end-string pph))))
  200.  
  201. (define (pph/start-string-length pph)
  202.   (let ((start (pph/start-string pph)))
  203.     (if (string? start)
  204.     (string-length start)
  205.     0)))
  206.  
  207. (define (pph/end-string-length pph)
  208.   (let ((end (pph/end-string pph)))
  209.     (if (string? end)
  210.     (string-length end)
  211.     0)))
  212.  
  213. (define (pp-top-level expression port as-code? indentation list-depth)
  214.   (fluid-let ((x-size (or *pp-forced-x-size* (output-port/x-size port)))
  215.           (output-port port))
  216.     (let* ((numerical-walk
  217.         (if *pp-avoid-circularity?*
  218.         numerical-walk-avoid-circularities
  219.         numerical-walk))
  220.        (node (numerical-walk expression list-depth)))
  221.       (if (positive? indentation)
  222.       (*unparse-string (make-string indentation #\space)))
  223.       (if as-code?
  224.       (print-node node indentation list-depth)
  225.       (print-non-code-node node indentation list-depth))
  226.       (output-port/discretionary-flush port))))
  227.  
  228. (define x-size)
  229. (define output-port)
  230.  
  231. (define-integrable (*unparse-char char)
  232.   (output-port/write-char output-port char))
  233.  
  234. (define-integrable (*unparse-string string)
  235.   (output-port/write-string output-port string))
  236.  
  237. (define-integrable (*unparse-open)
  238.   (*unparse-char #\())
  239.  
  240. (define-integrable (*unparse-close)
  241.   (*unparse-char #\)))
  242.  
  243. (define-integrable (*unparse-space)
  244.   (*unparse-char #\space))
  245.  
  246. (define-integrable (*unparse-newline)
  247.   (*unparse-char #\newline))
  248.  
  249. (define (print-non-code-node node column depth)
  250.   (fluid-let ((dispatch-list '())
  251.           (dispatch-default
  252.            (if *pp-lists-as-tables?*
  253.            print-data-table
  254.            print-data-column)))
  255.     (print-node node column depth)))
  256.  
  257. (define (print-code-node node column depth)
  258.   (fluid-let ((dispatch-list code-dispatch-list)
  259.           (dispatch-default print-combination))
  260.     (print-node node column depth)))
  261.  
  262. (define (print-data-column nodes column depth)
  263.   (*unparse-open)
  264.   (print-column nodes (+ column 1) (+ depth 1))
  265.   (*unparse-close))
  266.  
  267. (define (print-data-table nodes column depth)
  268.   (*unparse-open)
  269.   (maybe-print-table nodes (+ column 1) (+ depth 1))
  270.   (*unparse-close))
  271.  
  272. (define (print-node node column depth)
  273.   (cond ((list-node? node)
  274.      (print-list-node node column depth))
  275.     ((symbol? node)
  276.      (*unparse-symbol node))
  277.     ((prefix-node? node)
  278.      (*unparse-string (prefix-node-prefix node))
  279.      (let ((new-column
  280.         (+ column (string-length (prefix-node-prefix node))))
  281.            (subnode (prefix-node-subnode node)))
  282.        (if (null? dispatch-list)
  283.            (print-node subnode new-column depth)
  284.            (print-non-code-node subnode new-column depth))))
  285.     ((highlighted-node? node)
  286.      (let ((highlight (highlighted-node/highlight node)))
  287.        (with-highlight-strings-printed highlight
  288.          (lambda ()
  289.            (let ((handler
  290.               (let ((as-code? (pph/as-code? highlight))
  291.                 (currently-as-code? (not (null? dispatch-list))))
  292.             (cond ((or (eq? as-code? 'DEFAULT)
  293.                    (eq? as-code? currently-as-code?))
  294.                    print-node)
  295.                   (as-code?
  296.                    print-code-node)
  297.                   (else
  298.                    print-non-code-node)))))
  299.          (handler (highlighted-node/subnode node)
  300.               (+ column (pph/start-string-length highlight))
  301.               (+ depth (pph/end-string-length highlight))))))))
  302.     (else
  303.      (*unparse-string node))))
  304.  
  305. (define (print-list-node node column depth)
  306.   (if (and *pp-save-vertical-space?*
  307.        (fits-within? node column depth))
  308.       (print-guaranteed-list-node node)
  309.       (let* ((subnodes (node-subnodes node))
  310.          (association
  311.           (and (not (null? (cdr subnodes)))
  312.            (assq (unhighlight (car subnodes)) dispatch-list))))
  313.     (if (and (not association)
  314.          (fits-within? node column depth))
  315.         (print-guaranteed-list-node node)
  316.         ((if association
  317.          (cdr association)
  318.          dispatch-default)
  319.          subnodes column depth)))))
  320.  
  321. (define (print-guaranteed-node node)
  322.   (cond ((list-node? node)
  323.      (print-guaranteed-list-node node))
  324.     ((symbol? node)
  325.      (*unparse-symbol node))
  326.     ((highlighted-node? node)
  327.      (with-highlight-strings-printed (highlighted-node/highlight node)
  328.        (lambda ()
  329.          (print-guaranteed-node (highlighted-node/subnode node)))))
  330.     ((prefix-node? node)
  331.      (*unparse-string (prefix-node-prefix node))
  332.      (print-guaranteed-node (prefix-node-subnode node)))
  333.     (else
  334.      (*unparse-string node))))
  335.  
  336. (define (print-guaranteed-list-node node)
  337.   (*unparse-open)
  338.   (let loop ((nodes (node-subnodes node)))
  339.     (print-guaranteed-node (car nodes))
  340.     (if (not (null? (cdr nodes)))
  341.     (begin
  342.       (*unparse-space)
  343.       (loop (cdr nodes)))))
  344.   (*unparse-close))
  345.  
  346. (define (print-column nodes column depth)
  347.   (let loop ((nodes nodes))
  348.     (if (null? (cdr nodes))
  349.     (print-node (car nodes) column depth)
  350.     (begin
  351.       (print-node (car nodes) column 0)
  352.       (tab-to column)
  353.       (loop (cdr nodes))))))
  354.  
  355. (define (print-guaranteed-column nodes column)
  356.   (let loop ((nodes nodes))
  357.     (print-guaranteed-node (car nodes))
  358.     (if (not (null? (cdr nodes)))
  359.     (begin
  360.       (tab-to column)
  361.       (loop (cdr nodes))))))
  362.  
  363. (define (print-guaranteed-table nodes column all-widths)
  364.   (define (print-row row widths spaces)
  365.     (cond ((null? row)
  366.        unspecific)
  367.       ((null? widths)
  368.        (tab-to column)
  369.        (print-row row all-widths 0))
  370.       (else
  371.        (let ((next (car row)))
  372.          (pad-with-spaces spaces)
  373.          (print-guaranteed-node next)
  374.          (print-row (cdr row)
  375.             (cdr widths)
  376.             (1+ (- (car widths)
  377.                    (node-size next))))))))
  378.   (print-row nodes all-widths 0))
  379.  
  380. (define (maybe-print-table nodes column depth)
  381.   (define (default)
  382.     (print-column nodes column depth))
  383.  
  384.   (let* ((available-space (- x-size column))
  385.      (n-nodes (length nodes))
  386.      (max-cols (quotient (+ n-nodes 1) 2)))
  387.  
  388.     (define (try-columns n-columns)
  389.       (let* ((nodev (list->vector nodes))
  390.          (last-size (node-size (vector-ref nodev (-1+ n-nodes)))))
  391.  
  392.     (define (fit? n-cols widths)
  393.       ;; This must check that all rows fit.
  394.       ;; The last one must be treated specially because it is
  395.       ;; followed by depth tokens (close parens).
  396.       (and (>= available-space (+ (-1+ n-cols) (reduce + 0 widths)))
  397.            (let ((last-n-1 (remainder (-1+ n-nodes) n-cols)))
  398.          (>= available-space
  399.              (+ (+ last-n-1 (reduce + 0 (list-head widths last-n-1)))
  400.             (+ last-size depth))))))
  401.  
  402.     (define (find-max-width posn step)
  403.       (let loop ((posn posn)
  404.              (width 0))
  405.         (if (>= posn n-nodes)
  406.         width
  407.         (let ((next (node-size (vector-ref nodev posn))))
  408.           (loop (+ posn step)
  409.             (if (> next width) next width))))))
  410.  
  411.     (define (find-widths n)
  412.       (let recur ((start 0))
  413.         (if (= start n)
  414.         '()
  415.         (cons (find-max-width start n) (recur (1+ start))))))
  416.  
  417.     (define (try n)
  418.       (if (< n 2)
  419.           (default)
  420.           (let ((widths (find-widths n)))
  421.         (if (not (fit? n widths))
  422.             (try (- n 1))
  423.             (print-guaranteed-table
  424.              nodes column
  425.              ;; Try to make it look pretty.
  426.              (let ((next-n (-1+ n)))
  427.                (if (or (= n 2)
  428.                    (not (= (quotient (+ n-nodes next-n) n)
  429.                        (quotient (+ n-nodes (-1+ next-n))
  430.                          next-n))))
  431.                widths
  432.                (let ((nwidths (find-widths next-n)))
  433.                  (if (fit? (-1+ n) nwidths)
  434.                  nwidths
  435.                  widths)))))))))
  436.  
  437.     (try n-columns)))
  438.  
  439.     (if (< n-nodes 4)
  440.     ;; It's silly to tabulate 3 or less things.
  441.     (default)
  442.     (let loop ((n 1)
  443.            (nodes (cdr nodes))
  444.            (space (- available-space
  445.                  (node-size (car nodes)))))
  446.       (cond ((> n max-cols)
  447.          ;; Make sure there are at least two relatively full rows.
  448.          ;; This also guarantees that nodes is not NULL?.
  449.          (try-columns max-cols))
  450.         ((>= space 0)
  451.          (loop (1+ n)
  452.                (cdr nodes)
  453.                (- space (1+ (node-size (car nodes))))))
  454.         ((<= n 2)
  455.          (default))
  456.         (else
  457.          (try-columns (-1+ n))))))))
  458.  
  459. ;;;; Printers
  460.  
  461. (define (print-combination nodes column depth)
  462.   (*unparse-open)
  463.   (let ((column (+ column 1))
  464.     (depth (+ depth 1)))
  465.     (cond ((null? (cdr nodes))
  466.        (print-node (car nodes) column depth))
  467.       ((two-on-first-line? nodes column depth)
  468.        (print-guaranteed-node (car nodes))
  469.        (*unparse-space)
  470.        (print-guaranteed-column (cdr nodes)
  471.                     (+ column 1 (node-size (car nodes)))))
  472.       (else
  473.        (print-column nodes column depth))))
  474.   (*unparse-close))
  475.  
  476. (define dispatch-list)
  477. (define dispatch-default)
  478. (define code-dispatch-list)
  479.  
  480. (define ((special-printer procedure) nodes column depth)
  481.   (*unparse-open)
  482.   (print-guaranteed-node (car nodes))    ;(*unparse-symbol (car nodes))
  483.   (*unparse-space)
  484.   (if (not (null? (cdr nodes)))
  485.       (procedure (cdr nodes)
  486.          (+ column 2 (node-size (car nodes)))
  487.          (+ column 2)
  488.          (+ depth 1)))
  489.   (*unparse-close))
  490.  
  491. ;;; Force the indentation to be an optimistic column.
  492.  
  493. (define forced-indentation)
  494. (define (kernel/forced-indentation nodes optimistic pessimistic depth)
  495.   pessimistic
  496.   (print-column nodes optimistic depth))
  497.  
  498. ;;; Pressure the indentation to be an optimistic column; no matter
  499. ;;; what happens, insist on a column, but accept a pessimistic one if
  500. ;;; necessary.
  501.  
  502. (define pressured-indentation)
  503. (define (kernel/pressured-indentation nodes optimistic pessimistic depth)
  504.   (if (fits-as-column? nodes optimistic depth)
  505.       (print-guaranteed-column nodes optimistic)
  506.       (begin
  507.     (tab-to pessimistic)
  508.     (print-column nodes pessimistic depth))))
  509.  
  510. ;;; Print a procedure definition.  The bound variable pattern goes on
  511. ;;; the same line as the keyword, while everything else gets indented
  512. ;;; pessimistically.  We may later want to modify this to make higher
  513. ;;; order procedure patterns be printed more carefully.
  514.  
  515. (define print-procedure)
  516. (define (kernel/print-procedure nodes optimistic pessimistic depth)
  517.   (if (and *unparse-disambiguate-null-lambda-list?*
  518.        (member (car nodes) '("#f" "#F")))
  519.       (*unparse-string "()")
  520.       (print-node (car nodes) optimistic 0))
  521.   (let ((rest (cdr nodes)))
  522.     (if (not (null? rest))
  523.     (begin
  524.       (tab-to pessimistic)
  525.       (print-column (cdr nodes) pessimistic depth)))))
  526.  
  527. ;;; Print a binding form.  There is a great deal of complication here,
  528. ;;; some of which is to gracefully handle the case of a badly-formed
  529. ;;; binder.  But most important is the code that handles the name when
  530. ;;; we encounter a named let; it must go on the same line as the
  531. ;;; keyword.  In that case, the bindings try to fit on that line or
  532. ;;; start on that line if possible; otherwise they line up under the
  533. ;;; name.  The body, of course, is always indented pessimistically.
  534.  
  535. (define print-let-expression)
  536. (define (kernel/print-let-expression nodes optimistic pessimistic depth)
  537.   (let ((print-body
  538.      (lambda (nodes)
  539.        (if (not (null? nodes))
  540.            (begin
  541.          (tab-to pessimistic)
  542.          (print-column nodes pessimistic depth))))))
  543.     (cond ((null? (cdr nodes))
  544.        ;; screw case
  545.        (print-node (car nodes) optimistic depth))
  546.       ((symbol? (car nodes))
  547.        ;; named let
  548.        (*unparse-symbol (car nodes))
  549.        (let ((new-optimistic
  550.           (+ optimistic (+ 1 (symbol-length (car nodes))))))
  551.          (cond ((fits-within? (cadr nodes) new-optimistic 0)
  552.             (*unparse-space)
  553.             (print-guaranteed-node (cadr nodes))
  554.             (print-body (cddr nodes)))
  555.            ((and (list-node? (cadr nodes))
  556.              (fits-as-column? (node-subnodes (cadr nodes))
  557.                       (+ new-optimistic 2)
  558.                       0))
  559.             (*unparse-space)
  560.             (*unparse-open)
  561.             (print-guaranteed-column (node-subnodes (cadr nodes))
  562.                          (+ new-optimistic 1))
  563.             (*unparse-close)
  564.             (print-body (cddr nodes)))
  565.            (else
  566.             (tab-to optimistic)
  567.             (print-node (cadr nodes) optimistic 0)
  568.             (print-body (cddr nodes))))))
  569.       (else
  570.        ;; ordinary let
  571.        (print-node (car nodes) optimistic 0)
  572.        (print-body (cdr nodes))))))
  573.  
  574. (define print-case-expression)
  575. (define (kernel/print-case-expression nodes optimistic pessimistic depth)
  576.   (define (print-cases nodes)
  577.     (if (not (null? nodes))
  578.     (begin
  579.       (tab-to pessimistic)
  580.       (print-column nodes pessimistic depth))))
  581.   (cond ((null? (cdr nodes))
  582.      (print-node (car nodes) optimistic depth))
  583.     ((fits-within? (car nodes) optimistic 0)
  584.      (print-guaranteed-node (car nodes))
  585.      (print-cases (cdr nodes)))
  586.     (else
  587.      (tab-to (+ pessimistic 2))
  588.      (print-node (car nodes) optimistic 0)
  589.      (print-cases (cdr nodes)))))
  590.  
  591. ;;;; Alignment
  592.  
  593. (define-integrable (fits-within? node column depth)
  594.   (> (- x-size depth)
  595.      (+ column (node-size node))))
  596.  
  597. ;;; Fits if each node fits when stacked vertically at the given column.
  598.  
  599. (define (fits-as-column? nodes column depth)
  600.   (let loop ((nodes nodes))
  601.     (if (null? (cdr nodes))
  602.     (fits-within? (car nodes) column depth)
  603.     (and (> x-size
  604.         (+ column (node-size (car nodes))))
  605.          (loop (cdr nodes))))))
  606.  
  607. ;;; Fits if first two nodes fit on same line, and rest fit under the
  608. ;;; second node.  Assumes at least two nodes are given.
  609.  
  610. (define (two-on-first-line? nodes column depth)
  611.   (let ((column (+ column (+ 1 (node-size (car nodes))))))
  612.     (and (> x-size column)
  613.      (fits-as-column? (cdr nodes) column depth))))
  614.  
  615. ;;; Starts a new line with the specified indentation.
  616.  
  617. (define (tab-to column)
  618.   (*unparse-newline)
  619.   (pad-with-spaces column))
  620.  
  621. (define-integrable (pad-with-spaces n-spaces)
  622.   (*unparse-string (make-string n-spaces #\space)))
  623.  
  624. ;;;; Numerical Walk
  625.  
  626. (define (numerical-walk object list-depth)
  627.   (define (numerical-walk-no-auto-highlight object list-depth)
  628.     (cond ((pair? object)
  629.        (let ((prefix (unparse-list/prefix-pair? object)))
  630.          (if prefix
  631.          (make-prefix-node prefix
  632.                    (numerical-walk (cadr object)
  633.                            list-depth))
  634.          (let ((unparser (unparse-list/unparser object)))
  635.            (if unparser
  636.                (walk-custom unparser object list-depth)
  637.                (walk-pair object list-depth))))))
  638.       ((symbol? object)
  639.        (if (or *pp-uninterned-symbols-by-name*
  640.            (interned-symbol? object))
  641.            object
  642.            (walk-custom unparse-object object list-depth)))
  643.       ((pretty-printer-highlight? object)
  644.        ;; (1) see note below.
  645.        (let ((rest (walk-highlighted-object
  646.             object list-depth
  647.             numerical-walk-no-auto-highlight)))
  648.          (make-highlighted-node (+ (pph/start-string-length object)
  649.                        (pph/end-string-length object)
  650.                        (node-size rest))
  651.                     object
  652.                     rest)))
  653.       ((vector? object)
  654.        (if (zero? (vector-length object))
  655.            (walk-custom unparse-object object list-depth)
  656.            (let ((unparser (unparse-vector/unparser object)))
  657.          (if unparser
  658.              (walk-custom unparser object list-depth)
  659.              (make-prefix-node "#"
  660.                        (walk-pair (vector->list object)
  661.                           list-depth))))))
  662.       ((primitive-procedure? object)
  663.        (if *pp-primitives-by-name*
  664.            (primitive-procedure-name object)
  665.            (walk-custom unparse-object object list-depth)))
  666.       (else
  667.        (walk-custom unparse-object object list-depth))))
  668.  
  669.   ;; We do the following test first and the test above at (1) for a
  670.   ;; PRETTY-PRINTER-HIGHLIGHT because the highlighted object may
  671.   ;; itself be a PRETTY-PRINTER-HIGHLIGHT.  It is also important that
  672.   ;; the case (1) above uses NUMERICAL-WALK-NO-AUTO-HIGHLIGHT
  673.   ;; otherwise we would get infinite recursion when the `unwrapped'
  674.   ;; object REST is re-auto-highlighted by the test below.
  675.  
  676.   (cond ((and *pp-auto-highlighter*
  677.           (not (pretty-printer-highlight? object))
  678.           (*pp-auto-highlighter* object))
  679.      => (lambda (highlighted)
  680.           (numerical-walk-no-auto-highlight highlighted list-depth)))
  681.     (else
  682.      (numerical-walk-no-auto-highlight object list-depth))))
  683.  
  684. (define (walk-custom unparser object list-depth)
  685.   (with-string-output-port
  686.    (lambda (port)
  687.      (unparser (make-unparser-state port
  688.                     list-depth
  689.                     #t
  690.                     (current-unparser-table))
  691.            object))))
  692.  
  693. (define (walk-pair pair list-depth)
  694.   (if (and *unparser-list-depth-limit*
  695.        (>= list-depth *unparser-list-depth-limit*)
  696.        (no-highlights? pair))
  697.       "..."
  698.       (let ((list-depth (+ list-depth 1)))
  699.     (let loop ((pair pair) (list-breadth 0))
  700.       (cond ((and *unparser-list-breadth-limit*
  701.               (>= list-breadth *unparser-list-breadth-limit*)
  702.               (no-highlights? pair))
  703.          (make-singleton-list-node "..."))
  704.         ((null? (cdr pair))
  705.          (make-singleton-list-node
  706.           (numerical-walk (car pair) list-depth)))
  707.         (else
  708.          (make-list-node
  709.           (numerical-walk (car pair) list-depth)
  710.           (let ((list-breadth (+ list-breadth 1)))
  711.             (if (and (pair? (cdr pair))
  712.                  (not (unparse-list/unparser (cdr pair))))
  713.             (loop (cdr pair) list-breadth)
  714.             (make-list-node
  715.              "."
  716.              (make-singleton-list-node
  717.               (if (and *unparser-list-breadth-limit*
  718.                    (>= list-breadth
  719.                        *unparser-list-breadth-limit*)
  720.                    (no-highlights? pair))
  721.                   "..."
  722.                   (numerical-walk (cdr pair)
  723.                           list-depth)))))))))))))
  724.  
  725. (define-integrable (no-highlights? object)
  726.   (or *pp-no-highlights?*
  727.       (not (partially-highlighted? object))))
  728.  
  729. (define (partially-highlighted? object)
  730.   (cond ((pair? object)
  731.      (or (partially-highlighted? (car object))
  732.          (partially-highlighted? (cdr object))))
  733.     ((pretty-printer-highlight? object)
  734.      #t)
  735.     ((vector? object)
  736.      (partially-highlighted? (vector->list object)))
  737.     (else
  738.      #f)))
  739.  
  740. (define (walk-highlighted-object object list-depth numerical-walk)
  741.   (let ((dl (pph/depth-limit object)))
  742.     (fluid-let ((*unparser-list-breadth-limit*
  743.          (let ((bl (pph/breadth-limit object)))
  744.            (if (eq? bl 'DEFAULT)
  745.                *unparser-list-breadth-limit*
  746.                bl)))
  747.         (*unparser-list-depth-limit*
  748.          (if (eq? dl 'DEFAULT)
  749.              *unparser-list-depth-limit*
  750.              dl)))
  751.       (numerical-walk (pph/object object)
  752.               (if (eq? dl 'DEFAULT)
  753.               list-depth
  754.               0)))))
  755.  
  756.  
  757. ;;;     The following are circular list/vector handing procedures.  They allow
  758. ;;;  arbitary circular constructions made from pairs and vectors to be printed
  759. ;;;  in closed form.  The term "current parenthetical level" means the lowest
  760. ;;;  parethetical level which contains the circularity object.  Expressions
  761. ;;;  like "up 1 parenthetical level" refer to the object which is one
  762. ;;;  parenthetical level above the lowest parenthetical level which contains
  763. ;;;  the circularity object--i.e., the second lowest parenthetical level
  764. ;;;  which contains the circularity object.
  765. ;;;     Finally, the expression, "up 1 parenthetical level, downstream 1 cdr,"
  766. ;;;  means that to find the object being referred to, you should go to the
  767. ;;;  parenthetical level one level above the lowest parenthetical level which
  768. ;;;  contains the circularity object, and then take the cdr of that list.
  769. ;;;  This notation must be used because while a new parenthetical level is
  770. ;;;  generated for each car and each vector-ref, a new parenthetical level
  771. ;;;  obtains from cdring iff the result of said cdring is not a pair.
  772.  
  773. ;; This is the master procedure which all circularity-proof printing
  774. ;; goes through.
  775.  
  776. (define (numerical-walk-avoid-circularities exp list-depth)
  777.   (numerical-walk-terminating exp (cons exp (make-queue)) list-depth))
  778.  
  779. ;; This numerical walker has special pair and vector walkers to guarantee
  780. ;; proper termination.
  781.  
  782. (define (numerical-walk-terminating object half-pointer/queue list-depth)
  783.   (define queue (cdr half-pointer/queue))
  784.   (define half-pointer (car half-pointer/queue))
  785.   (cond ((pair? object)
  786.      (let ((prefix (unparse-list/prefix-pair? object)))
  787.        (if prefix
  788.            (make-prefix-node
  789.         prefix
  790.         (numerical-walk-terminating
  791.          (cadr object)
  792.          (advance half-pointer (update-queue queue '(CDR CAR)))
  793.          list-depth))
  794.            (let ((unparser (unparse-list/unparser object)))
  795.          (if unparser
  796.              (walk-custom unparser object list-depth)
  797.              (walk-pair-terminating object half-pointer/queue
  798.                         list-depth))))))
  799.     ((symbol? object)
  800.      (if (or *pp-uninterned-symbols-by-name*
  801.          (interned-symbol? object))
  802.          object
  803.          (walk-custom unparse-object object list-depth)))
  804.     ((pretty-printer-highlight? object)
  805.      (let ((rest (walk-highlighted-object object list-depth)))
  806.        (make-highlighted-node (+ (pph/start-string-length object)
  807.                      (pph/end-string-length object)
  808.                      (node-size rest))
  809.                   object
  810.                   rest)))
  811.     ((vector? object)
  812.      (if (zero? (vector-length object))
  813.          (walk-custom unparse-object object list-depth)
  814.          (let ((unparser (unparse-vector/unparser object)))
  815.            (if unparser
  816.            (walk-custom unparser object list-depth)
  817.            (make-prefix-node
  818.             "#"
  819.             (walk-vector-terminating
  820.              (vector->list object)
  821.              half-pointer/queue list-depth))))))
  822.     ((primitive-procedure? object)
  823.      (if *pp-primitives-by-name*
  824.          (primitive-procedure-name object)
  825.          (walk-custom unparse-object object list-depth)))
  826.     (else
  827.      (walk-custom unparse-object object list-depth))))
  828.  
  829. ;;; The following two procedures walk lists and vectors, respectively.
  830.  
  831. (define (walk-pair-terminating pair half-pointer/queue list-depth)
  832.     (if (and *unparser-list-depth-limit*
  833.        (>= list-depth *unparser-list-depth-limit*)
  834.        (no-highlights? pair))
  835.       "..."
  836.       (let ((list-depth (+ list-depth 1)))
  837.     (let loop ((pair pair) (list-breadth 0)
  838.                    (half-pointer/queue half-pointer/queue))
  839.       (cond ((and *unparser-list-breadth-limit*
  840.               (>= list-breadth *unparser-list-breadth-limit*)
  841.               (no-highlights? pair))
  842.          (make-singleton-list-node "..."))
  843.         ((null? (cdr pair))
  844.          (make-singleton-list-node
  845.           (let ((half-pointer/queue
  846.              (advance
  847.               (car half-pointer/queue)
  848.               (update-queue (cdr half-pointer/queue) '(CAR)))))
  849.             (if (eq? (car half-pointer/queue) (car pair))
  850.             (circularity-string (cdr half-pointer/queue))
  851.             (numerical-walk-terminating
  852.              (car pair) half-pointer/queue list-depth)))))
  853.         (else
  854.          (make-list-node
  855.           (let ((half-pointer/queue
  856.              (advance
  857.               (car half-pointer/queue)
  858.               (update-queue (cdr half-pointer/queue) '(CAR)))))
  859.             (if (eq? (car half-pointer/queue) (car pair))
  860.             (circularity-string (cdr half-pointer/queue))
  861.             (numerical-walk-terminating
  862.              (car pair) half-pointer/queue list-depth)))
  863.           (let ((list-breadth (+ list-breadth 1)))
  864.             (if
  865.              (and (pair? (cdr pair))
  866.               (not (unparse-list/unparser (cdr pair))))
  867.              (let ((half-pointer/queue
  868.                 (advance
  869.                  (car half-pointer/queue)
  870.                  (update-queue (cdr half-pointer/queue) '(CDR)))))
  871.                (if (eq? (car half-pointer/queue) (cdr pair))
  872.                (make-singleton-list-node
  873.                 (string-append
  874.                  ". "
  875.                  (circularity-string (cdr half-pointer/queue))))
  876.                (loop (cdr pair) list-breadth half-pointer/queue)))
  877.              (make-list-node
  878.               "."
  879.               (make-singleton-list-node
  880.                (if
  881.             (and *unparser-list-breadth-limit*
  882.                  (>= list-breadth
  883.                  *unparser-list-breadth-limit*)
  884.                  (no-highlights? pair))
  885.             "..."
  886.             (let ((half-pointer/queue
  887.                    (advance
  888.                 (car half-pointer/queue)
  889.                 (update-queue
  890.                  (cdr half-pointer/queue) '(CDR)))))
  891.               (if (eq? (car half-pointer/queue) (cdr pair))
  892.                   (circularity-string (cdr half-pointer/queue))
  893.                   (numerical-walk-terminating
  894.                    (cdr pair)
  895.                    half-pointer/queue list-depth)))))))))))))))
  896.  
  897. (define (walk-vector-terminating pair half-pointer/queue list-depth)
  898.   (if (and *unparser-list-depth-limit*
  899.        (>= list-depth *unparser-list-depth-limit*)
  900.        (no-highlights? pair))
  901.       "..."
  902.       (let ((list-depth (+ list-depth 1)))
  903.     (let loop ((pair pair) (list-breadth 0))
  904.       (cond ((and *unparser-list-breadth-limit*
  905.               (>= list-breadth *unparser-list-breadth-limit*)
  906.               (no-highlights? pair))
  907.          (make-singleton-list-node "..."))
  908.         ((null? (cdr pair))
  909.          (make-singleton-list-node
  910.           (let ((half-pointer/queue
  911.              (advance
  912.               (car half-pointer/queue)
  913.               (update-queue
  914.                (cdr half-pointer/queue) (list list-breadth)))))
  915.             (if (eq? (car half-pointer/queue) (car pair))
  916.             (circularity-string (cdr half-pointer/queue))
  917.             (numerical-walk-terminating
  918.              (car pair) half-pointer/queue list-depth)))))
  919.         (else
  920.          (make-list-node
  921.           (let ((half-pointer/queue
  922.              (advance (car half-pointer/queue)
  923.                   (update-queue (cdr half-pointer/queue)
  924.                         (list list-breadth)))))
  925.             (if (eq? (car half-pointer/queue) (car pair))
  926.             (circularity-string (cdr half-pointer/queue))
  927.             (numerical-walk-terminating
  928.              (car pair) half-pointer/queue list-depth)))
  929.           (let ((list-breadth (+ list-breadth 1)))
  930.             (if (not (unparse-list/unparser (cdr pair)))
  931.             (loop (cdr pair) list-breadth)
  932.             (make-list-node
  933.              "."
  934.              (make-singleton-list-node
  935.               (if (and *unparser-list-breadth-limit*
  936.                    (>= list-breadth
  937.                        *unparser-list-breadth-limit*)
  938.                    (no-highlights? pair))
  939.                   "..."
  940.                   (numerical-walk-terminating
  941.                    (cdr pair)
  942.                    half-pointer/queue list-depth)))))))))))))
  943.  
  944. ;;;; These procedures allow the walkers to interact with the queue.
  945.  
  946. (define cocked-object)
  947.  
  948. (define (advance half-object queue)
  949.   (cond ((vector? half-object)
  950.      (cons (cons cocked-object half-object) queue))
  951.     ((not (pair? half-object))
  952.      (cons half-object queue))
  953.     ((eq? (car half-object) cocked-object)
  954.      (cons (let ((directive (queue-car queue)))
  955.          (cond ((>= directive 0)
  956.             (vector-ref (cdr half-object) directive))
  957.                ((= directive -1)
  958.             (cadr half-object))
  959.                (else
  960.             (cddr half-object))))
  961.            (queue-cdr queue)))
  962.     (else
  963.      (cons (cons cocked-object half-object) queue))))
  964.  
  965. (define (update-queue queue command-list)
  966.   (define (uq-iter queue command-list)
  967.     (cond ((null? command-list) queue)
  968.       ((eq? (car command-list) 'CAR)
  969.        (uq-iter (add-car queue) (cdr command-list)))
  970.       ((eq? (car command-list) 'CDR)
  971.        (uq-iter (add-cdr queue) (cdr command-list)))
  972.       (else
  973.        (uq-iter (add-vector-ref (car command-list) queue)
  974.             (cdr command-list)))))
  975.   (uq-iter queue command-list))
  976.  
  977. (define (add-car queue)
  978.   (queue-cons queue -1))
  979.  
  980. (define (add-cdr queue)
  981.   (queue-cons queue -2))
  982.  
  983. (define (add-vector-ref n queue)
  984.   (queue-cons queue n))
  985.  
  986.  
  987. ;;;; The Queue Abstraction.  Queues are data structures allowing fifo
  988. ;;;  access without mutation.  The following procedures implement them.
  989.  
  990. (define-structure (queue
  991.            (conc-name queue/)
  992.            (constructor
  993.             make-queue
  994.             (#!optional cons-cell past-cdrs)))
  995.   (cons-cell (let* ((new-vector (make-fluid-vector))
  996.             (pointer (cons 0 new-vector)))
  997.            (cons pointer pointer)))
  998.   (past-cdrs 0))
  999.  
  1000. ;;; Fluid Vectors.
  1001. ;;  Queues are built on a subabstraction, "fluid-vectors," which
  1002. ;;  are actually nested vectors of a default length.
  1003.  
  1004. (define default-fluid-vector-length 10)
  1005. (define virtual-fluid-vector-length (-1+ default-fluid-vector-length))
  1006.  
  1007. (define (fluid-vector-extend fluid-vector)
  1008.   (define new-fluid-vector (make-fluid-vector))
  1009.   (vector-set! fluid-vector virtual-fluid-vector-length new-fluid-vector)
  1010.   new-fluid-vector)
  1011.  
  1012. (define (fluid-vector-set! fluid-vector index object)
  1013.   (define tail (vector-ref fluid-vector virtual-fluid-vector-length))
  1014.   (if (< index virtual-fluid-vector-length)
  1015.       (vector-set! fluid-vector index object)
  1016.       (fluid-vector-set! tail (- index virtual-fluid-vector-length) object)))
  1017.  
  1018. (define (make-fluid-vector)
  1019.   (make-vector default-fluid-vector-length #f))
  1020.  
  1021. ;;; The actual queue constructors/extractors
  1022.  
  1023. (define (queue-cons queue object)
  1024.   (let* ((old-cell (queue/cons-cell queue))
  1025.      (head (car old-cell))
  1026.      (tail (cdr old-cell)))
  1027.     (if (eq? head tail)
  1028.     (begin
  1029.       (fluid-vector-set! (cdr tail) 0 object)
  1030.       (make-queue (cons head (cons 1 (cdr tail))) (queue/past-cdrs queue)))
  1031.     (begin
  1032.       (fluid-vector-set! (cdr tail) (car tail) object)
  1033.       (make-queue (cons
  1034.                head
  1035.                (if (= (car tail) (-1+ virtual-fluid-vector-length))
  1036.                 (cons 0 (fluid-vector-extend (cdr tail)))
  1037.                 (cons (1+ (car tail)) (cdr tail))))
  1038.               (queue/past-cdrs queue))))))
  1039.  
  1040. (define (queue-car queue)
  1041.   (define head (car (queue/cons-cell queue)))
  1042.   (vector-ref (cdr head) (car head)))
  1043.  
  1044. (define (queue-cdr queue)
  1045.   (define head (car (queue/cons-cell queue)))
  1046.   (define tail (cdr (queue/cons-cell queue)))
  1047.   (make-queue
  1048.    (cons
  1049.     (if (= (car head) (-1+ virtual-fluid-vector-length))
  1050.     (cons 0 (vector-ref (cdr head) virtual-fluid-vector-length))
  1051.     (cons (1+ (car head)) (cdr head)))
  1052.     tail)
  1053.    (if (= (queue-car queue) -2)
  1054.        (1+ (queue/past-cdrs queue))
  1055.        0)))
  1056.  
  1057. ;;; Auxilary queue handlers.
  1058.  
  1059. (define (null-queue? queue)
  1060.   (define cell (queue/cons-cell queue))
  1061.   (eq? (car cell) (cdr cell)))
  1062.  
  1063. (define (queue-depth queue)
  1064.   (define (flatten starting-vector starting-n ending-vector ending-n)
  1065.     (if (eq? starting-vector ending-vector)
  1066.     (vector->list (subvector starting-vector starting-n ending-n))
  1067.     (append
  1068.      (vector->list
  1069.       (subvector starting-vector starting-n virtual-fluid-vector-length))
  1070.      (flatten
  1071.       (vector-ref starting-vector virtual-fluid-vector-length) 0
  1072.       ending-vector ending-n))))
  1073.   (define (proc-list-iter list code-cache)
  1074.     (cond ((null? list) (if (eq? code-cache -2) 1 0))
  1075.       ((>= (car list) 0)
  1076.        (+ (if (eq? code-cache -2) 2 1)
  1077.           (proc-list-iter (cdr list) (car list))))
  1078.       ((= (car list) -1)
  1079.        (1+ (proc-list-iter (cdr list) (car list))))
  1080.       (else
  1081.        (proc-list-iter (cdr list) (car list)))))
  1082.   (let* ((cell (queue/cons-cell queue))
  1083.      (head (car cell))
  1084.      (tail (cdr cell))
  1085.      (operating-list
  1086.       (flatten (cdr head) (car head) (cdr tail) (car tail))))
  1087.     (proc-list-iter operating-list #f)))
  1088.  
  1089.  
  1090. ;;; This procedure creates the circularity object which is printed
  1091. ;;; within circular structures.
  1092.  
  1093. (define (circularity-string queue)
  1094.   (let ((depth (queue-depth queue))
  1095.     (cdrs (queue/past-cdrs queue)))
  1096.     (string-append
  1097.      (cond ((= depth 1) "#[circularity (current parenthetical level")
  1098.        ((= depth 2) "#[circularity (up 1 parenthetical level")
  1099.        (else
  1100.         (string-append "#[circularity (up "
  1101.                (number->string (-1+ depth))
  1102.                " parenthetical levels")))
  1103.      (cond ((= cdrs 0) ")]")
  1104.        ((= cdrs 1) ", downstream 1 cdr.)]")
  1105.        (else
  1106.         (string-append ", downstream "
  1107.                (number->string cdrs) " cdrs.)]"))))))
  1108.  
  1109.  
  1110. ;;;; Node Model
  1111. ;;;  Carefully crafted to use the least amount of memory, while at the
  1112. ;;;  same time being as fast as possible.  The only concession to
  1113. ;;;  space was in the implementation of atomic nodes, in which it was
  1114. ;;;  decided that the extra space needed to cache the size of a string
  1115. ;;;  or the print-name of a symbol wasn't worth the speed that would
  1116. ;;;  be gained by keeping it around.
  1117.  
  1118. (define-integrable (symbol-length symbol)
  1119.   (string-length (symbol-name symbol)))
  1120.  
  1121. (define-integrable (*unparse-symbol symbol)
  1122.   (*unparse-string (symbol-name symbol)))
  1123.  
  1124. (define-structure (prefix-node
  1125.            (conc-name prefix-node-)
  1126.            (constructor %make-prefix-node))
  1127.   (size #f read-only #t)
  1128.   (prefix #f read-only #t)
  1129.   (subnode #f read-only #t))
  1130.  
  1131. (define (make-prefix-node prefix subnode)
  1132.   (cond ((string? subnode)
  1133.      (string-append prefix subnode))
  1134.     ((prefix-node? subnode)
  1135.      (make-prefix-node (string-append prefix (prefix-node-prefix subnode))
  1136.                (prefix-node-subnode subnode)))
  1137.     (else
  1138.      (%make-prefix-node (+ (string-length prefix) (node-size subnode))
  1139.                 prefix
  1140.                 subnode))))
  1141.  
  1142.  
  1143. (define (make-list-node car-node cdr-node)
  1144.   (cons (+ 1 (node-size car-node) (list-node-size cdr-node)) ;+1 space.
  1145.     (cons car-node (node-subnodes cdr-node))))
  1146.  
  1147. (define (make-singleton-list-node car-node)
  1148.   (cons (+ 2 (node-size car-node))            ;+1 each parenthesis.
  1149.     (list car-node)))
  1150.  
  1151. (define-integrable (list-node? object)
  1152.   (pair? object))
  1153.  
  1154. (define-integrable (list-node-size node)
  1155.   (car node))
  1156.  
  1157. (define-integrable (node-subnodes node)
  1158.   (cdr node))
  1159.  
  1160. (define (node-size node)
  1161.   (cond ((list-node? node) (list-node-size node))
  1162.     ((symbol? node) (symbol-length node))
  1163.     ((prefix-node? node) (prefix-node-size node))
  1164.     ((highlighted-node? node)
  1165.      (highlighted-node/size node))
  1166.     (else (string-length node))))
  1167.  
  1168. (define-structure (highlighted-node
  1169.            (conc-name highlighted-node/)
  1170.            (constructor make-highlighted-node))
  1171.   (size #f read-only #t)
  1172.   (highlight #f read-only #t)
  1173.   (subnode #f read-only #t))
  1174.  
  1175. (define (unhighlight node)
  1176.   (if (highlighted-node? node)
  1177.       (unhighlight (highlighted-node/subnode node))
  1178.       node))