264 lines
8.8 KiB
Scheme
264 lines
8.8 KiB
Scheme
|
;;;; fmt-pretty.scm -- pretty printing format combinator
|
||
|
;;
|
||
|
;; Copyright (c) 2006-2007 Alex Shinn. All rights reserved.
|
||
|
;; BSD-style license: http://synthcode.com/license.txt
|
||
|
|
||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
;;; additional settings
|
||
|
|
||
|
(define (fmt-shares st) (fmt-ref st 'shares))
|
||
|
(define (fmt-set-shares! st x) (fmt-set! st 'shares x))
|
||
|
(define (fmt-copy-shares st)
|
||
|
(fmt-set-shares! (copy-fmt-state st) (copy-shares (fmt-shares st))))
|
||
|
|
||
|
(define (copy-shares shares)
|
||
|
(let ((tab (make-eq?-table)))
|
||
|
(hash-table-walk
|
||
|
(car shares)
|
||
|
(lambda (obj x) (eq?-table-set! tab obj (cons (car x) (cdr x)))))
|
||
|
(cons tab (cdr shares))))
|
||
|
|
||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
;;; utilities
|
||
|
|
||
|
(define (fmt-shared-write obj proc)
|
||
|
(lambda (st)
|
||
|
(let* ((shares (fmt-shares st))
|
||
|
(cell (and shares (eq?-table-ref (car shares) obj))))
|
||
|
(if (pair? cell)
|
||
|
(cond
|
||
|
((cdr cell)
|
||
|
((fmt-writer st) (gen-shared-ref (car cell) "#") st))
|
||
|
(else
|
||
|
(set-car! cell (cdr shares))
|
||
|
(set-cdr! cell #t)
|
||
|
(set-cdr! shares (+ (cdr shares) 1))
|
||
|
(proc ((fmt-writer st) (gen-shared-ref (car cell) "=") st))))
|
||
|
(proc st)))))
|
||
|
|
||
|
(define (fmt-join/shares fmt ls . o)
|
||
|
(let ((sep (dsp (if (pair? o) (car o) " "))))
|
||
|
(lambda (st)
|
||
|
(if (null? ls)
|
||
|
st
|
||
|
(let* ((shares (fmt-shares st))
|
||
|
(tab (car shares))
|
||
|
(output (fmt-writer st)))
|
||
|
(let lp ((ls ls) (st st))
|
||
|
(let ((st ((fmt (car ls)) st))
|
||
|
(rest (cdr ls)))
|
||
|
(cond
|
||
|
((null? rest) st)
|
||
|
((pair? rest)
|
||
|
(call-with-shared-ref/cdr rest st shares
|
||
|
(lambda (st) (lp rest st))
|
||
|
sep))
|
||
|
(else ((fmt rest) (output ". " (sep st))))))))))))
|
||
|
|
||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
;;; pretty printing
|
||
|
|
||
|
(define (non-app? x)
|
||
|
(if (pair? x)
|
||
|
(or (not (or (null? (cdr x)) (pair? (cdr x))))
|
||
|
(non-app? (car x)))
|
||
|
(not (symbol? x))))
|
||
|
|
||
|
(define syntax-abbrevs
|
||
|
'((quote . "'") (quasiquote . "`") (unquote . ",") (unquote-splicing . ",@")
|
||
|
))
|
||
|
|
||
|
(define (pp-let ls)
|
||
|
(if (and (pair? (cdr ls)) (symbol? (cadr ls)))
|
||
|
(pp-with-indent 2 ls)
|
||
|
(pp-with-indent 1 ls)))
|
||
|
|
||
|
(define indent-rules
|
||
|
`((lambda . 1) (define . 1)
|
||
|
(let . ,pp-let) (loop . ,pp-let)
|
||
|
(let* . 1) (letrec . 1) (letrec* . 1) (and-let* . 1) (let1 . 2)
|
||
|
(let-values . 1) (let*-values . 1) (receive . 2) (parameterize . 1)
|
||
|
(let-syntax . 1) (letrec-syntax . 1) (syntax-rules . 1) (syntax-case . 2)
|
||
|
(match . 1) (match-let . 1) (match-let* . 1)
|
||
|
(if . 3) (when . 1) (unless . 1) (case . 1) (while . 1) (until . 1)
|
||
|
(do . 2) (dotimes . 1) (dolist . 1) (test . 1)
|
||
|
(condition-case . 1) (guard . 1) (rec . 1)
|
||
|
(call-with-current-continuation . 0)
|
||
|
))
|
||
|
|
||
|
(define indent-prefix-rules
|
||
|
`(("with-" . -1) ("call-with-" . -1) ("define-" . 1))
|
||
|
)
|
||
|
|
||
|
(define indent-suffix-rules
|
||
|
`(("-case" . 1))
|
||
|
)
|
||
|
|
||
|
(define (pp-indentation form)
|
||
|
(let ((indent
|
||
|
(cond
|
||
|
((assq (car form) indent-rules) => cdr)
|
||
|
((and (symbol? (car form))
|
||
|
(let ((str (symbol->string (car form))))
|
||
|
(or (find (lambda (rx) (string-prefix? (car rx) str))
|
||
|
indent-prefix-rules)
|
||
|
(find (lambda (rx) (string-suffix? (car rx) str))
|
||
|
indent-suffix-rules))))
|
||
|
=> cdr)
|
||
|
(else #f))))
|
||
|
(if (and (number? indent) (negative? indent))
|
||
|
(max 0 (- (+ (length+ form) indent) 1))
|
||
|
indent)))
|
||
|
|
||
|
(define (pp-with-indent indent-rule ls)
|
||
|
(lambda (st)
|
||
|
(let* ((col1 (fmt-col st))
|
||
|
(st ((cat "(" (pp-object (car ls))) st))
|
||
|
(col2 (fmt-col st))
|
||
|
(fixed (take* (cdr ls) (or indent-rule 1)))
|
||
|
(tail (drop* (cdr ls) (or indent-rule 1)))
|
||
|
(st2 (fmt-copy-shares st))
|
||
|
(first-line
|
||
|
((fmt-to-string (cat " " (fmt-join/shares pp-flat fixed " "))) st2))
|
||
|
(default
|
||
|
(let ((sep (make-nl-space (+ col1 1))))
|
||
|
(cat sep (fmt-join/shares pp-object (cdr ls) sep) ")"))))
|
||
|
(cond
|
||
|
((< (+ col2 (string-length first-line)) (fmt-width st2))
|
||
|
;; fixed values on first line
|
||
|
(let ((sep (make-nl-space
|
||
|
(if indent-rule (+ col1 2) (+ col2 1)))))
|
||
|
((cat first-line
|
||
|
(cond
|
||
|
((not (or (null? tail) (pair? tail)))
|
||
|
(cat ". " (pp-object tail)))
|
||
|
((> (length+ (cdr ls)) (or indent-rule 1))
|
||
|
(cat sep (fmt-join/shares pp-object tail sep)))
|
||
|
(else
|
||
|
fmt-null))
|
||
|
")")
|
||
|
st2)))
|
||
|
(indent-rule ;;(and indent-rule (not (pair? (car ls))))
|
||
|
;; fixed values lined up, body indented two spaces
|
||
|
((fmt-try-fit
|
||
|
(lambda (st)
|
||
|
((cat
|
||
|
" "
|
||
|
(fmt-join/shares pp-object fixed (make-nl-space (+ col2 1)))
|
||
|
(if (pair? tail)
|
||
|
(let ((sep (make-nl-space (+ col1 2))))
|
||
|
(cat sep (fmt-join/shares pp-object tail sep)))
|
||
|
"")
|
||
|
")")
|
||
|
(fmt-copy-shares st)))
|
||
|
default)
|
||
|
st))
|
||
|
(else
|
||
|
;; all on separate lines
|
||
|
(default st))))))
|
||
|
|
||
|
(define (pp-app ls)
|
||
|
(let ((indent-rule (pp-indentation ls)))
|
||
|
(if (procedure? indent-rule)
|
||
|
(indent-rule ls)
|
||
|
(pp-with-indent indent-rule ls))))
|
||
|
|
||
|
;; the elements may be shared, just checking the top level list
|
||
|
;; structure
|
||
|
(define (proper-non-shared-list? ls shares)
|
||
|
(let ((tab (car shares)))
|
||
|
(let lp ((ls ls))
|
||
|
(or (null? ls)
|
||
|
(and (pair? ls)
|
||
|
(not (eq?-table-ref tab ls))
|
||
|
(lp (cdr ls)))))))
|
||
|
|
||
|
(define (pp-flat x)
|
||
|
(cond
|
||
|
((pair? x)
|
||
|
(fmt-shared-write
|
||
|
x
|
||
|
(cond
|
||
|
((and (pair? (cdr x)) (null? (cddr x))
|
||
|
(assq (car x) syntax-abbrevs))
|
||
|
=> (lambda (abbrev)
|
||
|
(cat (cdr abbrev) (pp-flat (cadr x)))))
|
||
|
(else
|
||
|
(cat "(" (fmt-join/shares pp-flat x " ") ")")))))
|
||
|
((vector? x)
|
||
|
(fmt-shared-write
|
||
|
x
|
||
|
(cat "#(" (fmt-join/shares pp-flat (vector->list x) " ") ")")))
|
||
|
(else
|
||
|
(lambda (st) ((write-with-shares x (fmt-shares st)) st)))))
|
||
|
|
||
|
(define (pp-pair ls)
|
||
|
(fmt-shared-write
|
||
|
ls
|
||
|
(cond
|
||
|
;; one element list, no lines to break
|
||
|
((null? (cdr ls))
|
||
|
(cat "(" (pp-object (car ls)) ")"))
|
||
|
;; quote or other abbrev
|
||
|
((and (pair? (cdr ls)) (null? (cddr ls))
|
||
|
(assq (car ls) syntax-abbrevs))
|
||
|
=> (lambda (abbrev)
|
||
|
(cat (cdr abbrev) (pp-object (cadr ls)))))
|
||
|
(else
|
||
|
(fmt-try-fit
|
||
|
(lambda (st) ((pp-flat ls) (fmt-copy-shares st)))
|
||
|
(lambda (st)
|
||
|
(if (and (non-app? ls)
|
||
|
(proper-non-shared-list? ls (fmt-shares st)))
|
||
|
((pp-data-list ls) st)
|
||
|
((pp-app ls) st))))))))
|
||
|
|
||
|
(define (pp-data-list ls)
|
||
|
(lambda (st)
|
||
|
(let* ((output (fmt-writer st))
|
||
|
(st (output "(" st))
|
||
|
(col (fmt-col st))
|
||
|
(width (- (fmt-width st) col))
|
||
|
(st2 (fmt-copy-shares st)))
|
||
|
(cond
|
||
|
((and (pair? (cdr ls)) (pair? (cddr ls)) (pair? (cdddr ls))
|
||
|
((fits-in-columns ls pp-flat width) st2))
|
||
|
=> (lambda (ls)
|
||
|
;; at least four elements which can be broken into columns
|
||
|
(let* ((prefix (make-nl-space (+ col 1)))
|
||
|
(widest (+ 1 (car ls)))
|
||
|
(columns (quotient width widest))) ; always >= 2
|
||
|
(let lp ((ls (cdr ls)) (st st2) (i 1))
|
||
|
(cond
|
||
|
((null? ls)
|
||
|
(output ")" st))
|
||
|
((null? (cdr ls))
|
||
|
(output ")" (output (car ls) st)))
|
||
|
(else
|
||
|
(let ((st (output (car ls) st)))
|
||
|
(if (>= i columns)
|
||
|
(lp (cdr ls) (output prefix st) 1)
|
||
|
(let* ((pad (- widest (string-length (car ls))))
|
||
|
(st (output (make-space pad) st)))
|
||
|
(lp (cdr ls) st (+ i 1)))))))))))
|
||
|
(else
|
||
|
;; no room, print one per line
|
||
|
((cat (fmt-join pp-object ls (make-nl-space col)) ")") st))))))
|
||
|
|
||
|
(define (pp-vector vec)
|
||
|
(fmt-shared-write vec (cat "#" (pp-data-list (vector->list vec)))))
|
||
|
|
||
|
(define (pp-object obj)
|
||
|
(cond
|
||
|
((pair? obj) (pp-pair obj))
|
||
|
((vector? obj) (pp-vector obj))
|
||
|
(else (lambda (st) ((write-with-shares obj (fmt-shares st)) st)))))
|
||
|
|
||
|
(define (pretty obj)
|
||
|
(fmt-bind 'shares (cons (make-shared-ref-table obj) 0)
|
||
|
(cat (pp-object obj) fl)))
|
||
|
|
||
|
(define (pretty/unshared obj)
|
||
|
(fmt-bind 'shares (cons (make-eq?-table) 0) (cat (pp-object obj) fl)))
|
||
|
|