pretty-print.scm 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. ;;;; -*- coding: utf-8; mode: scheme -*-
  2. ;;;;
  3. ;;;; Copyright (C) 2001, 2004, 2006, 2009, 2010,
  4. ;;;; 2012, 2013, 2014, 2023 Free Software Foundation, Inc.
  5. ;;;;
  6. ;;;; This library is free software; you can redistribute it and/or
  7. ;;;; modify it under the terms of the GNU Lesser General Public
  8. ;;;; License as published by the Free Software Foundation; either
  9. ;;;; version 3 of the License, or (at your option) any later version.
  10. ;;;;
  11. ;;;; This library is distributed in the hope that it will be useful,
  12. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. ;;;; Lesser General Public License for more details.
  15. ;;;;
  16. ;;;; You should have received a copy of the GNU Lesser General Public
  17. ;;;; License along with this library; if not, write to the Free Software
  18. ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  19. ;;;;
  20. (define-module (ice-9 pretty-print)
  21. #:use-module (ice-9 match)
  22. #:use-module (srfi srfi-1)
  23. #:use-module (rnrs bytevectors)
  24. #:use-module (ice-9 soft-ports)
  25. #:use-module (ice-9 textual-ports)
  26. #:export (pretty-print
  27. truncated-print))
  28. (define* (call-with-truncating-output-string proc success failure #:key
  29. (initial-column 0)
  30. (max-column 79)
  31. (allow-newline? #f))
  32. (define length 0)
  33. (define strs '())
  34. (define tag (make-prompt-tag))
  35. (define (write-string str)
  36. (set! length (+ length (string-length str)))
  37. (set! strs (cons str strs))
  38. (when (or (< (- max-column initial-column) length)
  39. (and (not allow-newline?)
  40. (not (zero? (port-line port)))))
  41. (abort-to-prompt tag)))
  42. (define port
  43. (make-soft-port #:id "truncating-output-port"
  44. #:write-string write-string))
  45. (call-with-prompt
  46. tag
  47. (lambda ()
  48. (proc port)
  49. (close port)
  50. (success (string-concatenate-reverse strs)))
  51. (lambda (_)
  52. (failure (string-concatenate-reverse strs)))))
  53. ;; Parts of pretty-print derived from "genwrite.scm", from SLIB.
  54. ;; Copyright (c) 1991, Marc Feeley
  55. ;; Author: Marc Feeley (feeley@iro.umontreal.ca)
  56. ;; Distribution restrictions: none
  57. (define* (pretty-print obj #:optional port*
  58. #:key
  59. (port (or port* (current-output-port)))
  60. (width 79)
  61. (max-expr-width 50)
  62. (display? #f)
  63. (per-line-prefix ""))
  64. "Pretty-print OBJ on PORT, which is a keyword argument defaulting to
  65. the current output port. Formatting can be controlled by a number of
  66. keyword arguments: Each line in the output is preceded by the string
  67. PER-LINE-PREFIX, which is empty by default. The output lines will be
  68. at most WIDTH characters wide; the default is 79. If DISPLAY? is
  69. true, display rather than write representation will be used.
  70. Instead of with a keyword argument, you can also specify the output
  71. port directly after OBJ, like (pretty-print OBJ PORT)."
  72. (define (wr obj port)
  73. (define (wr-read-macro prefix x)
  74. (put-string port prefix)
  75. (wr x port))
  76. (match obj
  77. (('quote x) (wr-read-macro "'" x))
  78. (('quasiquote x) (wr-read-macro "`" x))
  79. (('unquote x) (wr-read-macro "," x))
  80. (('unquote-splicing x) (wr-read-macro ",@" x))
  81. ((head . (rest ...))
  82. ;; A proper list: do our own list printing so as to catch read
  83. ;; macros that appear in the middle of the list.
  84. (put-string port "(")
  85. (wr head port)
  86. (for-each (lambda (x)
  87. (put-string port " ")
  88. (wr x port))
  89. rest)
  90. (put-string port ")"))
  91. (_
  92. ((if display? display write) obj port))))
  93. ; define formatting style (change these to suit your style)
  94. (define indent-general 2)
  95. (define max-call-head-width 5)
  96. (define (spaces n)
  97. (when (< 0 n)
  98. (put-string port " " 0 (min 8 n))
  99. (when (< 8 n)
  100. (spaces (- n 8)))))
  101. (define (indent to)
  102. (let ((col (port-column port)))
  103. (cond
  104. ((< to col)
  105. (put-string port "\n")
  106. (put-string port per-line-prefix)
  107. (spaces (- to (string-length per-line-prefix))))
  108. (else
  109. (spaces (- to col))))))
  110. (define (pr obj pp-pair)
  111. (match obj
  112. ((? vector?)
  113. (put-string port "#")
  114. (pr (vector->list obj) pp-pair))
  115. ((not (? pair?))
  116. (wr obj port))
  117. (('quote x) (put-string port "'") (pr x pp-pair))
  118. (('quasiquote x) (put-string port "`") (pr x pp-pair))
  119. (('unquote x) (put-string port ",") (pr x pp-pair))
  120. (('unquote-splicing x) (put-string port ",@") (pr x pp-pair))
  121. (_
  122. ;; A pair (and possibly a list). May have to split on multiple
  123. ;; lines.
  124. (call-with-truncating-output-string
  125. (lambda (port) (wr obj port))
  126. (lambda (full-str) (put-string port full-str))
  127. (lambda (partial-str) (pp-pair obj))
  128. #:initial-column (port-column port)
  129. #:max-column (- width (string-length per-line-prefix))
  130. #:allow-newline? #f))))
  131. (define (pp-expr expr)
  132. (match expr
  133. (((or 'quote 'quasiquote 'unquote 'unquote-splicing) _)
  134. (pp-quote expr))
  135. (('lambda _ _ . _) (pp-lambda expr))
  136. (('lambda* _ _ . _) (pp-lambda expr))
  137. (('let (? symbol?) _ _ . _) (pp-named-let expr))
  138. (('let _ _ . _) (pp-let expr))
  139. (('let* _ _ . _) (pp-let expr))
  140. (('letrec _ _ . _) (pp-let expr))
  141. (('letrec* _ _ . _) (pp-let expr))
  142. (('let-syntax _ _ . _) (pp-let expr))
  143. (('letrec-syntax _ _ . _) (pp-let expr))
  144. (('define _ _ . _) (pp-define expr))
  145. (('define* _ _ . _) (pp-define expr))
  146. (('define-public _ _ . _) (pp-define expr))
  147. (('define-syntax _ _ . _) (pp-define expr))
  148. (('if _ _ . (or () (_))) (pp-if expr))
  149. (('cond . _) (pp-cond expr))
  150. (('case _ . _) (pp-case expr))
  151. (('begin . _) (pp-begin expr))
  152. (('do _ _ . _) (pp-do expr))
  153. (('syntax-rules _ . _) (pp-syntax-rules expr))
  154. (('syntax-case _ _ . _) (pp-syntax-case expr))
  155. (((? symbol? head) . _)
  156. (if (< max-call-head-width (string-length (symbol->string head)))
  157. (pp-list expr pp-expr)
  158. (pp-call expr pp-expr)))
  159. (_ (pp-list expr pp-expr))))
  160. (define (pp0 head body)
  161. (let ((body-col (+ (port-column port) indent-general)))
  162. (put-string port "(")
  163. (wr head port)
  164. (pp-down body body-col pp-expr)))
  165. (define (pp1 head param0 body pp-param0)
  166. (let ((body-col (+ (port-column port) indent-general)))
  167. (put-string port "(")
  168. (wr head port)
  169. (put-string port " ")
  170. (pr param0 pp-param0)
  171. (pp-down body body-col pp-expr)))
  172. (define (pp2 head param0 param1 body pp-param0 pp-param1)
  173. (let ((body-col (+ (port-column port) indent-general)))
  174. (put-string port "(")
  175. (wr head port)
  176. (put-string port " ")
  177. (pr param0 pp-param0)
  178. (put-string port " ")
  179. (pr param1 pp-param1)
  180. (pp-down body body-col pp-expr)))
  181. (define (pp-quote expr)
  182. (match obj
  183. ((head x)
  184. (put-string port
  185. (match x
  186. ('quote "'")
  187. ('quasiquote "`")
  188. ('unquote ",")
  189. ('unquote-splicing ",@")))
  190. (pr x pp-expr))))
  191. (define (pp-lambda expr)
  192. (match expr
  193. ((head args . body)
  194. (pp1 head args body pp-expr-list))))
  195. (define (pp-let expr)
  196. (match expr
  197. ((head bindings . body)
  198. (pp1 head bindings body pp-expr-list))))
  199. (define (pp-named-let expr)
  200. (match expr
  201. ((head name bindings . body)
  202. (pp2 head name bindings body pp-expr pp-expr-list))))
  203. (define (pp-define expr)
  204. (match expr
  205. ((head args . body)
  206. (pp1 head args body pp-expr-list))))
  207. (define (pp-if expr)
  208. (match expr
  209. ((head test . body)
  210. ;; "if" indent is 4.
  211. (put-string port "(")
  212. (wr head port)
  213. (put-string port " ")
  214. (let ((body-col (port-column port)))
  215. (pr test pp-expr)
  216. (pp-down body body-col pp-expr)))))
  217. (define (pp-cond expr)
  218. (match expr
  219. ((head . clauses)
  220. (pp0 head clauses))))
  221. (define (pp-case expr)
  222. (match expr
  223. ((head x . clauses)
  224. (pp1 head x clauses pp-expr))))
  225. (define (pp-begin expr)
  226. (match expr
  227. ((head . body) (pp0 head body))))
  228. (define (pp-do expr)
  229. (match expr
  230. ((head bindings exit . body)
  231. (pp2 head bindings exit body pp-expr-list pp-expr-list))))
  232. (define (pp-syntax-rules expr)
  233. (match expr
  234. ((head literals . clauses)
  235. (pp1 head literals clauses pp-expr-list))))
  236. (define (pp-syntax-case expr)
  237. (match expr
  238. ((head stx literals . clauses)
  239. (pp2 head stx literals clauses pp-expr pp-expr-list))))
  240. ; (head item1
  241. ; item2
  242. ; item3)
  243. (define (pp-call expr pp-item)
  244. (match expr
  245. ((head . tail)
  246. (put-string port "(")
  247. (wr head port)
  248. (pp-down tail (+ (port-column port) 1) pp-item))))
  249. ; (item1
  250. ; item2
  251. ; item3)
  252. (define (pp-list l pp-item)
  253. (put-string port "(")
  254. (pp-down l (port-column port) pp-item))
  255. (define (pp-down l item-indent pp-item)
  256. (let loop ((l l))
  257. (match l
  258. (() (put-string port ")"))
  259. ((head . tail)
  260. (indent item-indent)
  261. (pr head pp-item)
  262. (loop tail))
  263. (improper-tail
  264. (indent item-indent)
  265. (put-string port ".")
  266. (indent item-indent)
  267. (pr improper-tail pp-item)
  268. (put-string port ")")))))
  269. (define (pp-expr-list l)
  270. (pp-list l pp-expr))
  271. (put-string port per-line-prefix)
  272. (pr obj pp-expr)
  273. (newline port)
  274. ;; Return `unspecified'
  275. (if #f #f))
  276. (define* (truncated-print x #:optional port*
  277. #:key
  278. (port (or port* (current-output-port)))
  279. (width 79)
  280. (display? #f)
  281. (breadth-first? #f))
  282. "Print @var{x}, truncating the output, if necessary, to make it fit
  283. into @var{width} characters. By default, @var{x} will be printed using
  284. @code{write}, though that behavior can be overriden via the
  285. @var{display?} keyword argument.
  286. The default behaviour is to print depth-first, meaning that the entire
  287. remaining width will be available to each sub-expression of @var{x} --
  288. e.g., if @var{x} is a vector, each member of @var{x}. One can attempt to
  289. \"ration\" the available width, trying to allocate it equally to each
  290. sub-expression, via the @var{breadth-first?} keyword argument."
  291. (define ellipsis
  292. ;; Choose between `HORIZONTAL ELLIPSIS' (U+2026) and three dots, depending
  293. ;; on the encoding of PORT.
  294. (let ((e "…"))
  295. (catch 'encoding-error
  296. (lambda ()
  297. (with-fluids ((%default-port-conversion-strategy 'error))
  298. (call-with-output-string
  299. (lambda (p)
  300. (set-port-encoding! p (port-encoding port))
  301. (display e p)))))
  302. (lambda (key . args)
  303. "..."))))
  304. (let ((ellipsis-width (string-length ellipsis)))
  305. (define* (print-sequence x width len ref next #:key inner?)
  306. (let lp ((x x)
  307. (width width)
  308. (i 0))
  309. (if (> i 0)
  310. (display #\space))
  311. (cond
  312. ((= i len)) ; catches 0-length case
  313. ((and (= i (1- len)) (or (zero? i) (> width 1)))
  314. (print (ref x i) (if (zero? i) width (1- width)) #:inner? inner?))
  315. ((<= width (+ 1 ellipsis-width))
  316. (display ellipsis))
  317. (else
  318. (let ((str (with-output-to-string
  319. (lambda ()
  320. (print (ref x i)
  321. (if breadth-first?
  322. (max 1
  323. (1- (floor (/ width (- len i)))))
  324. (- width (+ 1 ellipsis-width)))
  325. #:inner? inner?)))))
  326. (display str)
  327. (lp (next x) (- width 1 (string-length str)) (1+ i)))))))
  328. (define (print-tree x width)
  329. ;; width is >= the width of # . #, which is 5
  330. (let lp ((x x)
  331. (width width))
  332. (cond
  333. ((or (not (pair? x)) (<= width 4))
  334. (display ". ")
  335. (print x (- width 2)))
  336. (else
  337. ;; width >= 5
  338. (let ((str (with-output-to-string
  339. (lambda ()
  340. (print (car x)
  341. (if breadth-first?
  342. (floor (/ (- width 3) 2))
  343. (- width 4)))))))
  344. (display str)
  345. (display " ")
  346. (lp (cdr x) (- width 1 (string-length str))))))))
  347. (define (truncate-string str width)
  348. (unless (< width (string-length str))
  349. (error "precondition failed"))
  350. (or (or-map (match-lambda
  351. ((prefix . suffix)
  352. (and (string-prefix? prefix str)
  353. (<= (+ (string-length prefix)
  354. (string-length suffix)
  355. ellipsis-width)
  356. width)
  357. (format #f "~a~a~a"
  358. (substring str 0
  359. (- width (string-length suffix)
  360. ellipsis-width))
  361. ellipsis
  362. suffix))))
  363. '(("#<" . ">")
  364. ("#(" . ")")
  365. ("(" . ")")
  366. ("\"" . "\"")))
  367. "#"))
  368. (define* (print x width #:key inner?)
  369. (cond
  370. ((<= width 0)
  371. (error "expected a positive width" width))
  372. ((list? x)
  373. (cond
  374. ((>= width (+ 2 ellipsis-width))
  375. (display "(")
  376. (print-sequence x (- width 2) (length x)
  377. (lambda (x i) (car x)) cdr)
  378. (display ")"))
  379. (else
  380. (display "#"))))
  381. ((vector? x)
  382. (cond
  383. ((>= width (+ 3 ellipsis-width))
  384. (display "#(")
  385. (print-sequence x (- width 3) (vector-length x)
  386. vector-ref identity)
  387. (display ")"))
  388. (else
  389. (display "#"))))
  390. ((bytevector? x)
  391. (cond
  392. ((>= width 9)
  393. (format #t "#~a(" (array-type x))
  394. (print-sequence x (- width 6) (array-length x)
  395. array-ref identity)
  396. (display ")"))
  397. (else
  398. (display "#"))))
  399. ((bitvector? x)
  400. (cond
  401. ((>= width (+ 2 (array-length x)))
  402. (format #t "~a" x))
  403. ;; the truncated bitvector would print as #1b(...), so we print by hand.
  404. ((>= width (+ 2 ellipsis-width))
  405. (format #t "#*")
  406. (array-for-each (lambda (xi) (display (if xi "1" "0")))
  407. (make-shared-array x list (- width 2 ellipsis-width)))
  408. (display ellipsis))
  409. (else
  410. (display "#"))))
  411. ((and (array? x) (not (string? x)))
  412. (let* ((type (array-type x))
  413. (prefix
  414. (if inner?
  415. ""
  416. (call-with-output-string
  417. (lambda (s) ((@@ (ice-9 arrays) array-print-prefix) x s)))))
  418. (width-prefix (string-length prefix)))
  419. (cond
  420. ((>= width (+ 2 width-prefix ellipsis-width))
  421. (format #t "~a(" prefix)
  422. (if (zero? (array-rank x))
  423. (print (array-ref x) (- width width-prefix 2))
  424. (print-sequence x (- width width-prefix 2) (array-length x)
  425. (let ((base (caar (array-shape x))))
  426. (lambda (x i) (array-cell-ref x (+ base i))))
  427. identity
  428. #:inner? (< 1 (array-rank x))))
  429. (display ")"))
  430. (else
  431. (display "#")))))
  432. ((pair? x)
  433. (cond
  434. ((>= width (+ 4 ellipsis-width))
  435. (display "(")
  436. (print-tree x (- width 2))
  437. (display ")"))
  438. (else
  439. (display "#"))))
  440. (else
  441. (call-with-truncating-output-string
  442. (lambda (port)
  443. (if display? (display x port) (write x port)))
  444. (lambda (full-str)
  445. (display full-str))
  446. (lambda (partial-str)
  447. (display (truncate-string partial-str width)))
  448. #:max-column width
  449. #:allow-newline? #f))))
  450. (with-output-to-port port
  451. (lambda ()
  452. (print x width)))))