pp.scm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. ; Part of Scheme 48 1.9. See file COPYING for notices and license.
  2. ; Authors: John Ramsdell, Richard Kelsey, Jonathan Rees
  3. ;;;; A pretty-printer
  4. ; This isn't exactly in the spirit of the rest of the Scheme 48
  5. ; system. It's too hairy, and it has unexploited internal generality.
  6. ; It really ought to be rewritten. In addition, it seems to be buggy
  7. ; -- it sometimes prints unnecessarily wide lines. Usually it's
  8. ; better than no pretty printer at all, so we tolerate it.
  9. ; From: ramsdell@linus.mitre.org
  10. ; Date: Wed, 12 Sep 1990 05:14:49 PDT
  11. ;
  12. ; As you noted in your comments, pp.scm is not a straight forward
  13. ; program. You could add some comments that would greatly ease the task
  14. ; of figuring out what his going on. In particular, you should describe
  15. ; the interface of various objects---most importantly the interface of a
  16. ; formatter. You might also add some description as to what protocol
  17. ; they are to follow.
  18. ; Other things to implement some day:
  19. ; - LET, LET*, LETREC binding lists should be printed vertically if longer
  20. ; than about 30 characters
  21. ; - COND clauses should all be printed vertically if the COND is vertical
  22. ; - Add an option to lowercase or uppercase symbols and named characters.
  23. ; - Parameters controlling behavior of printer should be passed around
  24. ; - Do something about choosing between #f and ()
  25. ; - Insert line breaks intelligently following head of symbol-headed list,
  26. ; when necessary
  27. ; - Some equivalents of *print-level*, *print-length*, *print-circle*.
  28. ; Possible strategies:
  29. ; (foo x y z) Horizontal = infinity sticky
  30. ; (foo x y One sticky + one + body (e.g. named LET)
  31. ; z
  32. ; w)
  33. ; (foo x One + body
  34. ; y
  35. ; z)
  36. ; (foo x Two + body
  37. ; y
  38. ; z)
  39. ; (foo x Big ell = infinity + body (combination)
  40. ; y
  41. ; z)
  42. ; (foo Little ell, zero + body (combination)
  43. ; x
  44. ; y)
  45. ; (foo Vertical
  46. ; x
  47. ; y)
  48. ;
  49. ; Available height/width tradeoffs:
  50. ; Combination:
  51. ; Horizontal, big ell, or little ell.
  52. ; Special form:
  53. ; Horizontal, or M sticky + N + body.
  54. ; Random (e.g. vector, improper list, non-symbol-headed list):
  55. ; Horizontal, or vertical. (Never zero plus body.)
  56. (define (p x . port-option)
  57. (let ((port (if (pair? port-option) (car port-option)
  58. (current-output-port))))
  59. (pretty-print x port 0)
  60. (newline port)))
  61. (define *line-width* 80)
  62. (define *single-line-special-form-limit* 30)
  63. ; Stream primitives
  64. (define head car)
  65. (define (tail s) (force (cdr s)))
  66. (define (map-stream proc stream)
  67. (cons (proc (head stream))
  68. (delay (map-stream proc (tail stream)))))
  69. (define (stream-ref stream n)
  70. (if (= n 0)
  71. (head stream)
  72. (stream-ref (tail stream) (- n 1))))
  73. ; Printer
  74. (define (pretty-print obj port pos)
  75. (let ((node (pp-prescan obj 0)))
  76. ; (if (> (column-of (node-dimensions node)) *line-width*)
  77. ; ;; Eventually add a pass to change format of selected combinations
  78. ; ;; from big-ell to little-ell.
  79. ; (begin (display ";** too wide - ")
  80. ; (write (node-dimensions node))
  81. ; (newline)))
  82. (print-node node port pos)))
  83. (define make-node list)
  84. (define (node-dimensions node)
  85. ((car node)))
  86. (define (node-pass-2 node pos)
  87. ((cadr node) pos))
  88. (define (print-node node port pos)
  89. ((caddr node) port pos))
  90. (define (pp-prescan obj hang)
  91. (cond ((symbol? obj)
  92. (make-leaf (string-length (symbol->string obj))
  93. obj hang))
  94. ((number? obj)
  95. (make-leaf (string-length (number->string obj))
  96. obj hang))
  97. ((boolean? obj)
  98. (make-leaf 2 obj hang))
  99. ((string? obj)
  100. ;;++ Should count number of backslashes and quotes
  101. (make-leaf (+ (string-length obj) 2) obj hang))
  102. ((char? obj)
  103. (make-leaf (case obj
  104. ((#\space) 7)
  105. ((#\newline) 9)
  106. (else 3))
  107. obj hang))
  108. ((pair? obj)
  109. (pp-prescan-pair obj hang))
  110. ((vector? obj)
  111. (pp-prescan-vector obj hang))
  112. (else
  113. (pp-prescan-random obj hang))))
  114. (define (make-leaf width obj hang)
  115. (let ((width (+ width hang)))
  116. (make-node (lambda () width)
  117. (lambda (pos)
  118. (+ pos width))
  119. (lambda (port pos)
  120. (write obj port)
  121. (do ((i 0 (+ i 1)))
  122. ((>= i hang) (+ pos width))
  123. (write-char #\) port))))))
  124. (define (make-prefix-node string node)
  125. (let ((len (string-length string)))
  126. (make-node (lambda ()
  127. (+ (node-dimensions node) len))
  128. (lambda (pos)
  129. (node-pass-2 node (+ pos len)))
  130. (lambda (port pos)
  131. (display string port)
  132. (print-node node port (+ pos len))))))
  133. (define (pp-prescan-vector obj hang)
  134. (if (= (vector-length obj) 0)
  135. (make-leaf 3 obj hang)
  136. (make-prefix-node "#" (pp-prescan-list (vector->list obj) #t hang))))
  137. ; Improve later.
  138. (define (pp-prescan-random obj hang)
  139. (let ((l (disclose obj)))
  140. (if (list? l)
  141. (make-prefix-node "#." (pp-prescan-list l #t hang))
  142. (make-leaf 25 obj hang)))) ;Very random number
  143. (define (pp-prescan-pair obj hang)
  144. (cond ((read-macro-inverse obj)
  145. =>
  146. (lambda (inverse)
  147. (make-prefix-node inverse (pp-prescan (cadr obj) hang))))
  148. (else
  149. (pp-prescan-list obj #f hang))))
  150. (define (pp-prescan-list obj random? hang)
  151. (let loop ((l obj) (z '()))
  152. (if (pair? (cdr l))
  153. (loop (cdr l)
  154. (cons (pp-prescan (car l) 0) z))
  155. (make-list-node
  156. (reverse
  157. (if (null? (cdr l))
  158. (cons (pp-prescan (car l) (+ hang 1)) z)
  159. (cons (make-prefix-node ". " (pp-prescan (cdr l) (+ hang 1)))
  160. (cons (pp-prescan (car l) 0) z))))
  161. obj
  162. (or random? (not (null? (cdr l))))))))
  163. ; Is it sufficient to tell parent node:
  164. ; At a cost of X line breaks, I can make myself narrower by Y columns. ?
  165. ; Then how do we decide whether we narrow ourselves or some of our children?
  166. (define (make-list-node node-list obj random?)
  167. (let* ((random? (or random?
  168. ;; Heuristic for things like do, cond, let, ...
  169. (not (symbol? (car obj)))
  170. (eq? (car obj) 'else)))
  171. (probe (if (not random?)
  172. (indentation-for (car obj))
  173. #f))
  174. (format horizontal-format)
  175. (dimensions (compute-dimensions node-list format))
  176. (go-non-horizontal
  177. (lambda (col)
  178. (set! format
  179. (cond (random? vertical-format)
  180. (probe (probe obj))
  181. (else big-ell-format)))
  182. (let* ((start-col (+ col 1))
  183. (col (node-pass-2 (car node-list) start-col))
  184. (final-col
  185. (format (cdr node-list)
  186. (lambda (node col target-col)
  187. (node-pass-2 node target-col))
  188. start-col
  189. (+ col 1)
  190. col)))
  191. (set! dimensions (compute-dimensions node-list format))
  192. final-col))))
  193. (if (> dimensions
  194. (if probe
  195. *single-line-special-form-limit*
  196. *line-width*))
  197. (go-non-horizontal 0))
  198. (make-node (lambda () dimensions)
  199. (lambda (col) ;Pass 2: if necessary, go non-horizontal
  200. (let ((defacto (+ col (column-of dimensions))))
  201. (if (> defacto *line-width*)
  202. (go-non-horizontal col)
  203. defacto)))
  204. (lambda (port pos)
  205. (write-char #\( port)
  206. (let* ((pos (+ pos 1))
  207. (start-col (column-of pos))
  208. (pos (print-node (car node-list) port pos)))
  209. (format (cdr node-list)
  210. (lambda (node pos target-col)
  211. (let ((pos (go-to-column target-col
  212. port pos)))
  213. (print-node node port pos)))
  214. start-col
  215. (+ (column-of pos) 1)
  216. pos))))))
  217. (define (compute-dimensions node-list format)
  218. (let* ((start-col 1) ;open paren
  219. (pos (+ (make-position start-col 0)
  220. (node-dimensions (car node-list)))))
  221. (format (cdr node-list)
  222. (lambda (node pos target-col)
  223. (let* ((dims (node-dimensions node))
  224. (lines (+ (line-of pos) (line-of dims)))
  225. (width (+ target-col (column-of dims))))
  226. (if (>= (column-of pos) target-col)
  227. ;; Line break required
  228. (make-position
  229. (max (column-of pos) width)
  230. (+ lines 1))
  231. (make-position width lines))))
  232. start-col
  233. (+ (column-of pos) 1) ;first-col
  234. pos)))
  235. ; Three positions are significant
  236. ; (foo baz ...)
  237. ; ^ ^ ^
  238. ; | | +--- (column-of pos)
  239. ; | +------ first-col
  240. ; +---------- start-col
  241. ; Separators
  242. (define on-same-line
  243. (lambda (start-col first-col pos)
  244. start-col first-col ;ignored
  245. (+ (column-of pos) 1)))
  246. (define indent-under-first
  247. (lambda (start-col first-col pos)
  248. start-col ;ignored
  249. first-col))
  250. (define indent-for-body
  251. (lambda (start-col first-col pos)
  252. first-col ;ignored
  253. (+ start-col 1)))
  254. (define indent-under-head
  255. (lambda (start-col first-col pos)
  256. first-col ;ignored
  257. start-col))
  258. ; Format constructors
  259. (define (once separator format)
  260. (lambda (tail proc start-col first-col pos)
  261. (if (null? tail)
  262. pos
  263. (let ((target-col (separator start-col first-col pos)))
  264. (format (cdr tail)
  265. proc
  266. start-col
  267. first-col
  268. (proc (car tail) pos target-col))))))
  269. (define (indefinitely separator)
  270. (letrec ((self (once separator ;eta
  271. (lambda (tail proc start-col first-col pos)
  272. (self tail proc start-col first-col pos)))))
  273. self))
  274. (define (repeatedly separator count format)
  275. (do ((i 0 (+ i 1))
  276. (format format
  277. (once separator format)))
  278. ((>= i count) format)))
  279. ; Particular formats
  280. (define vertical-format
  281. (indefinitely indent-under-head))
  282. (define horizontal-format
  283. (indefinitely on-same-line))
  284. (define big-ell-format
  285. (indefinitely indent-under-first))
  286. (define little-ell-format
  287. (indefinitely indent-for-body))
  288. (define format-for-named-let
  289. (repeatedly on-same-line 2 (indefinitely indent-for-body)))
  290. (define hook-formats
  291. (letrec ((stream (cons little-ell-format
  292. (delay (map-stream (lambda (format)
  293. (once indent-under-first format))
  294. stream)))))
  295. stream))
  296. ; Hooks for special forms.
  297. ; A hook maps an expression to a format.
  298. (define (compute-let-indentation exp)
  299. (if (and (not (null? (cdr exp)))
  300. (symbol? (cadr exp)))
  301. format-for-named-let
  302. (stream-ref hook-formats 1)))
  303. (define hook
  304. (let ((hooks (map-stream (lambda (format)
  305. (lambda (exp) exp ;ignored
  306. format))
  307. hook-formats)))
  308. (lambda (n)
  309. (stream-ref hooks n))))
  310. ; Table of indent hooks.
  311. (define indentations (make-table))
  312. (define (indentation-for name)
  313. (table-ref indentations name))
  314. (define (define-indentation name n)
  315. (table-set! indentations
  316. name
  317. (if (integer? n) (hook n) n)))
  318. ; Indent hooks for Revised^n Scheme.
  319. (for-each (lambda (name)
  320. (define-indentation name 1))
  321. '(lambda define define-syntax let* letrec let-syntax letrec-syntax
  322. case call-with-values call-with-input-file
  323. call-with-output-file with-input-from-file
  324. with-output-to-file syntax-rules))
  325. (define-indentation 'do 2)
  326. (define-indentation 'call-with-current-continuation 0)
  327. (define-indentation 'let compute-let-indentation)
  328. ; Kludge to force vertical printing (do AND and OR as well?)
  329. (define-indentation 'if (lambda (exp) big-ell-format))
  330. (define-indentation 'cond (lambda (exp) big-ell-format))
  331. ; Other auxiliaries
  332. (define (go-to-column target-col port pos) ;=> pos
  333. ;; Writes at least one space or newline
  334. (let* ((column (column-of pos))
  335. (line (if (>= column target-col)
  336. (+ (line-of pos) 1)
  337. (line-of pos))))
  338. (do ((column (if (>= column target-col)
  339. (begin (newline port) 0)
  340. column)
  341. (+ column 1)))
  342. ((>= column target-col)
  343. (make-position column line))
  344. (write-char #\space port))))
  345. (define (make-position column line)
  346. (+ column (* line 1000)))
  347. (define (column-of pos)
  348. (remainder pos 1000))
  349. (define (line-of pos)
  350. (quotient pos 1000))
  351. (define (read-macro-inverse x)
  352. (cond ((and (pair? x)
  353. (pair? (cdr x))
  354. (null? (cddr x)))
  355. (case (car x)
  356. ((quote) "'")
  357. ((quasiquote) "`")
  358. ((unquote) ",")
  359. ((unquote-splicing) ",@")
  360. (else #f)))
  361. (else #f)))
  362. ; For the command processor:
  363. ;(define-command 'p "<exp>" "pretty-print" '(expression)
  364. ; (p (eval expression (user-package)) (command-output)))