home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-07-26 | 9.3 KB | 220 lines | [TEXT/gamI] |
- ; Graphical display of trees (on a text oriented output device)
- ;
- ; try: (tree-display '((the dog) ate (the cat)))
-
- (define (tree-display tree . optional)
-
- ; how many space characters between branches of tree
- (define tree-spacing 1)
-
- ; print tree with all leaves at bottom level?
- (define leaves-at-bottom? #f)
-
- ; define what a tree is (leaf & internal node) and how to get its components
- (define (leaf? tree) (not (pair? tree)))
- (define (leaf-name tree) (string->symbol "."))
- (define (leaf-info tree) tree)
- (define (int-node-name tree) (string->symbol "."))
- (define (int-node-children tree) (list (car tree) (cdr tree)))
-
- (define (make-augm-leaf width root name info)
- (vector 'leaf width root name info))
-
- (define (make-augm-pad width)
- (vector 'pad width))
-
- (define (make-augm-int-node width root name lpad rpad children)
- (vector #f width root name lpad rpad children))
-
- (define (augm-tree-int-node? x) (not (vector-ref x 0)))
- (define (augm-tree-pad? x) (eq? (vector-ref x 0) 'pad))
- (define (augm-tree-width x) (vector-ref x 1))
- (define (augm-tree-root x) (vector-ref x 2))
- (define (augm-tree-name x) (vector-ref x 3))
- (define (augm-leaf-info x) (vector-ref x 4))
- (define (augm-int-node-lpad x) (vector-ref x 4))
- (define (augm-int-node-rpad x) (vector-ref x 5))
- (define (augm-int-node-children x) (vector-ref x 6))
-
- (define (pad width l)
- (if (> width 0)
- (cons (make-augm-pad width) l)
- l))
-
- (define (field-width x) ; return number of chars in the written repr of `x'
- (cond ((boolean? x) 2)
- ((symbol? x) (string-length (symbol->string x)))
- ((char? x) (case x ((#\space) 7) ((#\newline) 9) (else 3)))
- ((number? x) (string-length (number->string x)))
- ((vector? x) (+ (field-width (vector->list x)) 1))
- ((null? x) 2)
- ((pair? x) (let loop ((l (cdr x)) (w (+ (field-width (car x)) 2)))
- (cond ((null? l)
- w)
- ((pair? l)
- (loop (cdr l) (+ w (field-width (car l)) 1)))
- (else
- (+ w (field-width l) 3)))))
- ((string? x) (let loop ((i (- (string-length x) 1)) (w 2))
- (if (>= i 0)
- (let ((c (string-ref x i)))
- (loop (- i 1)
- (+ w (case c ((#\\ #\") 2) (else 1)))))
- w)))
- (else 0)))
-
- (define (augment-tree tree)
- (if (leaf? tree)
-
- (let* ((name (leaf-name tree))
- (info (leaf-info tree))
- (name-width (field-width name))
- (info-width (field-width info))
- (tree-width (max name-width info-width)))
- (make-augm-leaf tree-width (quotient tree-width 2) name info))
-
- (let* ((children (map augment-tree (int-node-children tree)))
- (name (int-node-name tree))
- (name-width (field-width name))
- (name-left (quotient name-width 2))
- (name-right (- name-width name-left)))
- (if (null? children)
- (make-augm-int-node name-width name-left name 0 0 '())
- (let* ((first-child (car children))
- (last-child (list-ref children (- (length children) 1)))
- (width
- (+ (* (- (length children) 1) tree-spacing)
- (apply + (map augm-tree-width children))))
- (left
- (quotient (+ (- width (augm-tree-width last-child))
- (+ (augm-tree-root first-child)
- (augm-tree-root last-child)))
- 2))
- (right
- (- width left))
- (max-left
- (max name-left left))
- (max-right
- (max name-right right)))
- (make-augm-int-node (+ max-left max-right) max-left name
- (- max-left left) (- max-right right)
- children))))))
-
- (define (any-int-nodes? trees)
- (if (null? trees)
- #f
- (or (augm-tree-int-node? (car trees))
- (any-int-nodes? (cdr trees)))))
-
- (define (all-done? trees)
- (if (null? trees)
- #t
- (and (augm-tree-pad? (car trees))
- (all-done? (cdr trees)))))
-
- (define (seq c n port)
- (if (> n 0)
- (begin
- (write-char c port)
- (seq c (- n 1) port))))
-
- (define (print-trees trees port)
- (if (not (all-done? trees))
- (let ((delay-leaves? (and leaves-at-bottom? (any-int-nodes? trees))))
-
- (let loop1 ((l trees))
- (if (pair? l)
- (let* ((tree (car l))
- (tree-width (augm-tree-width tree)))
- (if (augm-tree-pad? tree)
- (begin
- (seq #\space tree-width port)
- (loop1 (cdr l)))
- (let* ((root (augm-tree-root tree))
- (name (augm-tree-name tree))
- (name-width (field-width name))
- (name-left (quotient name-width 2))
- (name-right (- name-width name-left)))
- (if (or (not delay-leaves?) (augm-tree-int-node? tree))
- (begin
- (seq #\space (- root name-left) port)
- (write name port)
- (seq #\space (- tree-width root name-right) port)
- (loop1 (cdr l)))
- (begin
- (seq #\space root port)
- (write-char #\. port)
- (seq #\space (- tree-width root 1) port)
- (loop1 (cdr l)))))))))
-
- (newline port)
-
- (let loop2 ((l trees) (new-trees '()))
- (if (pair? l)
- (let* ((tree (car l))
- (tree-width (augm-tree-width tree)))
- (if (augm-tree-pad? tree)
- (begin
- (seq #\space tree-width port)
- (loop2 (cdr l) (append new-trees (list tree))))
- (let* ((root (augm-tree-root tree))
- (name (augm-tree-name tree))
- (name-width (field-width name))
- (name-left (quotient name-width 2))
- (name-right (- name-width name-left)))
- (if (augm-tree-int-node? tree)
- (let ((children (augm-int-node-children tree)))
- (if (null? children)
- (begin
- (seq #\space (- root name-left) port)
- (write name port)
- (seq #\space (- tree-width root name-right) port)
- (loop2 (cdr l)
- (append new-trees (pad tree-width '()))))
- (let* ((child1 (car children))
- (root1 (augm-tree-root child1))
- (width1 (augm-tree-width child1))
- (lpad (augm-int-node-lpad tree))
- (rpad (augm-int-node-rpad tree)))
- (seq #\space (+ lpad root1) port)
- (write-char #\. port)
- (let loop3 ((l1 (cdr children))
- (l2 (cons child1 (pad lpad '())))
- (right (- width1 (+ root1 1))))
- (if (pair? l1)
- (let* ((child (car l1))
- (root (augm-tree-root child))
- (width (augm-tree-width child)))
- (seq #\- (+ root tree-spacing right) port)
- (write-char #\. port)
- (loop3 (cdr l1)
- (cons child (pad tree-spacing l2))
- (- width (+ root 1))))
- (begin
- (seq #\space (+ right rpad) port)
- (loop2 (cdr l)
- (append new-trees
- (reverse (pad rpad l2))))))))))
- (if delay-leaves?
- (begin
- (seq #\space root port)
- (write-char #\. port)
- (seq #\space (- tree-width root 1) port)
- (loop2 (cdr l) (append new-trees (list tree))))
- (let* ((info (augm-leaf-info tree))
- (info-width (field-width info))
- (info-left (quotient info-width 2))
- (info-right (- info-width info-left)))
- (seq #\space (- root info-left) port)
- (write info port)
- (seq #\space (- tree-width root info-right) port)
- (loop2 (cdr l)
- (append new-trees (pad tree-width '())))))))))
-
- (begin
- (newline port)
- (print-trees new-trees port)))))))
-
- (print-trees (list (augment-tree tree))
- (if (null? optional) (current-output-port) (car optional))))
-