print.scm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. ; -*- mode: scheme; coding: utf-8 -*-
  2. ; (c) Daniel Llorens - 2017-2023
  3. ; This library is free software; you can redistribute it and/or modify it under
  4. ; the terms of the GNU General Public License as published by the Free
  5. ; Software Foundation; either version 3 of the License, or (at your option) any
  6. ; later version.
  7. ;;; Commentary:
  8. ;; Printer for ra objects. They start with #% instead of #, otherwise the syntax
  9. ;; is the same as for regular Guile arrays. Loading this module installs the
  10. ;; printer. This module also provides a pretty-printer (ra-format).
  11. ;;; Code:
  12. (define-module (newra print)
  13. #:export (ra-print-prefix ra-print ra-format
  14. *ra-print* *ra-parenthesized-rank-zero*))
  15. (import (rnrs io ports) (rnrs base) (srfi srfi-1) (srfi srfi-4 gnu) (srfi srfi-26) (srfi srfi-71)
  16. (ice-9 match) (ice-9 control)
  17. (newra base) (newra map) (newra cat) (newra from) (newra lib) (newra reshape))
  18. (define *ra-print*
  19. (make-parameter #f
  20. (lambda (x) (match x
  21. ((or 'box 'box1 'box2 'default #f (? procedure?)) x)
  22. (x (throw 'bad-argument-to-*ra-print* x))))))
  23. (define *ra-parenthesized-rank-zero*
  24. (make-parameter #t))
  25. ; FIXME still need to extend (truncated-print).
  26. (define* (ra-print-prefix ra port #:key (dims? #t))
  27. (display #\# port)
  28. (display #\% port)
  29. (display (ra-rank ra) port)
  30. (let ((type (ra-type ra)))
  31. (unless (eq? #t type)
  32. (display type port)))
  33. (vector-for-each
  34. (lambda (dim)
  35. (let ((lo (dim-lo dim)))
  36. (unless (or (not lo) (zero? lo))
  37. (display #\@ port)
  38. (display (or lo 'f) port)))
  39. (when dims?
  40. (display #\: port)
  41. (display (match (dim-len dim)
  42. ; print len of dead axes with 'd and of infinite axes with 'f.
  43. (#f (if (zero? (dim-step dim)) 'd 'f))
  44. (len len))
  45. port)))
  46. (ra-dims ra)))
  47. (define* (ra-print ra port #:key (dims? #t))
  48. (ra-print-prefix ra port #:dims? dims?)
  49. (let ((base (ra-offset (ra-zero ra) (ra-dims ra)))
  50. (ref (cute (ra-vref ra) (ra-root ra) <>))
  51. (rank (ra-rank ra)))
  52. ; special case
  53. (if (zero? rank)
  54. (if (*ra-parenthesized-rank-zero*)
  55. (begin
  56. (display #\( port)
  57. (write (ref base) port)
  58. (display #\) port))
  59. (begin
  60. (display #\space port)
  61. (write (ref base) port)))
  62. (let loop ((k 0) (b base))
  63. (let* ((dim (vector-ref (ra-dims ra) k))
  64. (i (dim-step dim))
  65. (lo (dim-lo dim))
  66. ; print dead axes as if of size 1. Infinite arrays aren't printed (FIXME?)
  67. (len (or (dim-len dim) (if (zero? i) 1 #f))))
  68. (when len
  69. (let ((hi (+ (or lo 0) len -1)))
  70. (display #\( port)
  71. (cond
  72. ((= (- rank 1) k)
  73. (do ((j (or lo 0) (+ 1 j)) (b b (+ b i)))
  74. ((> j hi))
  75. (write (ref b) port)
  76. (when (< j hi)
  77. (display #\space port))))
  78. (else
  79. (do ((j (or lo 0) (+ 1 j)) (b b (+ b i)))
  80. ((> j hi))
  81. (loop (+ k 1) b)
  82. (when (< j hi)
  83. (display #\space port)))))
  84. (display #\) port))))))))
  85. (define* (sc-print sc #:optional (o #t))
  86. (let ((o (match o
  87. (#t (current-output-port))
  88. (#f (throw 'bad-output-spec))
  89. (o o))))
  90. (ra-slice-for-each 1
  91. (lambda (line)
  92. (ra-for-each (cut display <> o) line)
  93. (newline o))
  94. sc)))
  95. (define arts (make-ra-root (vector "┆╌┌┐└┘ " "│─┌┐└┘├┤┬┴┼" "║═╔╗╚╝╠╣╦╩╬" "┃━┏┓┗┛┣┫┳┻╋"
  96. "░░░░░░░░░░░" "▒▒▒▒▒▒▒▒▒▒▒" "▓▓▓▓▓▓▓▓▓▓▓" "████████████")))
  97. (define (vector-any pred? v)
  98. (let/ec exit
  99. (vector-for-each (lambda (e) (and=> (pred? e) exit)) v)
  100. #f))
  101. ; FIXME if a cell prints as nothing (e.g. "" with compact >0) then it shouldn't take up vertical space.
  102. ; FIXME compact >0 rank <=2 should avoid borders at all.
  103. (define* (ra-format ra #:optional (port #t) #:key (fmt "~a") (prefix? #t) (compact 0))
  104. (define prefix (and prefix? (call-with-output-string (cut ra-print-prefix ra <>))))
  105. (let ((ra (if (vector-any (lambda (d)
  106. (and (not (dim-len d))
  107. (not (zero? (dim-step d)))))
  108. (ra-dims ra))
  109. ; for arrays with infinite axes, print just the prefix.
  110. (make-ra #f 0)
  111. ; for arrays with dead axes, print them as if the len was 1, but preserve the prefix.
  112. (ra-singletonize ra))))
  113. (define tostring (if (string? fmt) (cut format #f fmt <>) fmt))
  114. ; size the cells
  115. (define s (ra-map! (apply make-ra #f (ra-dimensions ra))
  116. (lambda (x)
  117. (if (ra? x)
  118. (ra-format x #f #:fmt fmt #:prefix? prefix? #:compact compact)
  119. (ra-tile (make-ra-root (tostring x)) 0 1)))
  120. ra))
  121. ; vertical axes go in dimv, horizontal axes in dimh
  122. (define-values (dimv dimh)
  123. (let* ((q r (euclidean/ (ra-rank s) 2))
  124. (a (ra-iota (+ q r) 0 2))
  125. (b (ra-iota q 1 2)))
  126. (if (zero? r)
  127. (values a b)
  128. (values b a))))
  129. (define extrav (> (ra-len dimv) (if (< compact 2) 0 1)))
  130. (define extrah (> (ra-len dimh) (if (< compact 2) 0 1)))
  131. (define (lengths dimv dimh k compact)
  132. (let* ((sq (apply ra-untranspose s (ra->list (ra-cat #f 0 dimh dimv))))
  133. (l (apply make-ra 0 (drop (ra-dimensions sq) (ra-len dimh))))
  134. (inner-compact? (if (zero? compact)
  135. (= (ra-len dimv) 0)
  136. (>= (ra-len dimv) 0)))
  137. (border (if inner-compact? 0 1)))
  138. (ra-slice-for-each-in-order (ra-len dimh)
  139. (lambda (w) (ra-map! l (lambda (l w) (max l (+ border (ra-len w k)))) l w))
  140. sq)
  141. ; FIXME handle border entirely here
  142. (when (and inner-compact? (match k (0 extrav) (1 extrah)))
  143. (let ((ll (ra-from l (dots) ((match k (0 dim-hi) (1 dim-lo)) (vector-ref (ra-dims l) (- (ra-rank l) 1))))))
  144. (ra-map! ll (cut + <> 1) ll)))
  145. l))
  146. (define lv (lengths dimv dimh 0 (match compact (0 0) (1 1) (2 2))))
  147. (define lh (lengths dimh dimv 1 (match compact (0 0) (1 0) (2 2))))
  148. (define tv (ra-fold + (if extrav 0 -1) lv))
  149. (define th (ra-fold + (if extrah 0 -1) lh))
  150. ; compute positions for grid and cells
  151. (define (scan! a) (let ((s 0)) (ra-map-in-order! a (lambda (c) (let ((d s)) (set! s (+ s c)) d)) a)))
  152. (define (scan-0 a) (scan! (ra-copy a)))
  153. (define (scan-1 a) (scan! (ra-cat #f 0 a (make-ra 0))))
  154. (define (marks l k)
  155. (and (>= k 0)
  156. (let ((m (apply make-ra 0 (take (ra-dimensions l) (+ k 1)))))
  157. (ra-slice-for-each (+ k 1) (lambda (l m) (set! (m) (ra-fold + 0 l)) m) l m)
  158. (scan-1 (ra-ravel m)))))
  159. ; make screen, adding line for prefix if necessary
  160. (define prefix-lines (if (and prefix (not extrav)) 1 0))
  161. (define scc (make-typed-ra 'a #\space
  162. (+ 1 tv prefix-lines)
  163. (max (if prefix (string-length prefix) 0) (+ 1 th))))
  164. (define sc (ra-from scc (ra-iota (- (ra-len scc) prefix-lines) prefix-lines)))
  165. (define (char k n) (string-ref (ra-ref arts (+ (if (positive? compact) 0 1) k)) n))
  166. (define (line-0 sc k range at) (ra-amend! sc (char k 0) range at))
  167. (define (line-1 sc k range at) (ra-amend! sc (char k 1) at range))
  168. (cond
  169. ((zero? (ra-rank ra))
  170. (let ((s (s))) (ra-copy! (ra-clip sc s) s))) ; align left
  171. ((zero? (ra-size ra)) #f)
  172. (else
  173. ; print grid
  174. (let loop ((k (max 0 (- compact 1))))
  175. (let* ((m0 (marks lv (- (ra-len dimv) 1 k)))
  176. (m1 (marks lh (- (ra-len dimh) 1 k)))
  177. (>m0< (and m0 (ra-from m0 (ra-iota (- (ra-len m0) 2) 1))))
  178. (>m1< (and m1 (ra-from m1 (ra-iota (- (ra-len m1) 2) 1)))))
  179. (cond ((and m0 m1)
  180. ; horiz + vert
  181. (if (and (positive? compact) (zero? k))
  182. (begin
  183. (line-1 sc k (ra-iota (+ 1 th) 0) (ra-ref m0 0))
  184. (line-1 sc k (ra-iota (+ 1 th) 0) (ra-ref m0 (- (ra-len m0) 1)))
  185. (line-0 sc k (ra-iota (+ 1 tv) 0) (ra-ref m1 0))
  186. (line-0 sc k (ra-iota (+ 1 tv) 0) (ra-ref m1 (- (ra-len m1) 1))))
  187. (begin
  188. (ra-for-each (lambda (m0) (line-1 sc k (ra-iota (+ 1 th) 0) m0)) m0)
  189. (ra-for-each (lambda (m1) (line-0 sc k (ra-iota (+ 1 tv) 0) m1)) m1)))
  190. ; inner crosses
  191. (if (positive? compact)
  192. (when (> k 0)
  193. (ra-for-each (lambda (m0 m1) (ra-set! sc (char k 10) m0 m1))
  194. >m0< (ra-transpose >m1< 1)))
  195. (ra-for-each (lambda (m0 m1) (ra-set! sc (char k 10) m0 m1))
  196. >m0< (ra-transpose >m1< 1)))
  197. ; edge crosses
  198. (unless (and (positive? compact) (zero? k))
  199. (ra-for-each (lambda (m0)
  200. (ra-set! sc (char k 6) m0 0)
  201. (ra-set! sc (char k 7) m0 th))
  202. >m0<)
  203. (ra-for-each (lambda (m1)
  204. (ra-set! sc (char k 8) 0 m1)
  205. (ra-set! sc (char k 9) tv m1))
  206. >m1<))
  207. ; corners
  208. (ra-set! sc (char k 2) 0 0)
  209. (ra-set! sc (char k 3) 0 th)
  210. (ra-set! sc (char k 4) tv 0)
  211. (ra-set! sc (char k 5) tv th)
  212. (loop (+ k 1)))
  213. (m1
  214. (if (and (positive? compact) (zero? k))
  215. (begin
  216. (line-0 sc k (ra-iota (+ tv 1) 0) 0)
  217. (line-0 sc k (ra-iota (+ tv 1) 0) (ra-ref m1 (- (ra-len m1) 1))))
  218. (ra-for-each (lambda (m1) (line-0 sc k (ra-iota (+ tv 1) 0) m1)) m1)))
  219. (else #f))))
  220. ; print cells
  221. (ra-for-each
  222. (lambda (sq ov lv oh lh)
  223. (ra-copy! (ra-from sc
  224. (ra-iota (ra-len sq 0) (+ ov (if extrav 1 0)))
  225. (ra-iota (ra-len sq 1) (+ oh lh (- (ra-len sq 1))))) ; align right
  226. sq))
  227. (apply ra-untranspose s (ra->list (ra-cat #f 0 dimv dimh)))
  228. (apply ra-reshape (scan-0 (ra-ravel lv)) 0 (ra-dimensions lv))
  229. lv
  230. (ra-transpose (apply ra-reshape (scan-0 (ra-ravel lh)) 0 (ra-dimensions lh)) (ra-rank lv))
  231. (ra-transpose lh (ra-rank lv)))))
  232. ; print prefix
  233. (when prefix
  234. (ra-amend! scc (make-ra-root prefix) 0 (ra-iota (string-length prefix))))
  235. (if port
  236. (sc-print scc port)
  237. scc)))
  238. (struct-set! (@ (newra base) <ra-vtable>) vtable-index-printer
  239. (lambda (ra o)
  240. (match (*ra-print*)
  241. ('box (newline o) (ra-format ra o))
  242. ('box1 (newline o) (ra-format ra o #:compact 1))
  243. ('box2 (newline o) (ra-format ra o #:compact 2))
  244. ((or 'default #f) (ra-print ra o))
  245. (f (f ra o)))))