util.scm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. ;;; nyacc/util.scm
  2. ;; Copyright (C) 2014-2017 Matthew R. Wette
  3. ;;
  4. ;; This library is free software; you can redistribute it and/or
  5. ;; modify it under the terms of the GNU Lesser General Public
  6. ;; License as published by the Free Software Foundation; either
  7. ;; version 3 of the License, or (at your option) any later version.
  8. ;;
  9. ;; This library is distributed in the hope that it will be useful,
  10. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. ;; Lesser General Public License for more details.
  13. ;;
  14. ;; You should have received a copy of the GNU Lesser General Public License
  15. ;; along with this library; if not, see <http://www.gnu.org/licenses/>
  16. ;;; Code:
  17. (define-module (nyacc util)
  18. #:export (
  19. fmtstr fmtout fmterr fmt
  20. wrap-action
  21. obj->str
  22. fixpoint prune-assoc
  23. map-attr->vector
  24. x-flip x-comb
  25. write-vec
  26. ugly-print OLD-ugly-print
  27. tzort)
  28. #:use-module ((srfi srfi-43) #:select (vector-fold)))
  29. (cond-expand
  30. (mes)
  31. (guile-2)
  32. (guile
  33. (use-modules (ice-9 optargs))
  34. (use-modules (nyacc compat18)))
  35. (else))
  36. (define (fmtstr fmt . args)
  37. (apply simple-format #f fmt args))
  38. (define (fmtout fmt . args)
  39. (apply simple-format (current-output-port) fmt args))
  40. (define (fmterr fmt . args)
  41. (apply simple-format (current-error-port) fmt args))
  42. (define fmt simple-format)
  43. ;; @deffn {Procedure} make-arg-list N => '($N $Nm1 $Nm2 ... $1 . $rest)
  44. ;; This is a helper for @code{mkact}.
  45. ;; @end deffn
  46. (define (make-arg-list n)
  47. (let ((mkarg
  48. (lambda (i) (string->symbol (string-append "$" (number->string i))))))
  49. (let iter ((r '(. $rest)) (i 1))
  50. (if (> i n) r (iter (cons (mkarg i) r) (1+ i))))))
  51. ;; @deffn {Procedure} wrap-action (n . guts) => quoted procedure
  52. ;; Wrap user-specified action (body, as a quoted list of expressions) with
  53. ;; n arguments to generate a quoted lambda. That is,
  54. ;; @example
  55. ;; `(lambda ($n ... $2 $1 . $rest) ,@guts)
  56. ;; @end example
  57. ;; The rationale for the arglist format is that we @code{apply} this
  58. ;; lambda to the the semantic stack.
  59. (define (wrap-action actn)
  60. (cons* 'lambda (make-arg-list (car actn)) (cdr actn)))
  61. ;; @deffn obj->str object => string
  62. ;; Convert terminal (symbol, string, character) to string.
  63. ;; This is like @code{write} but will prefix symbols with @code{'}.
  64. (define (obj->str obj)
  65. (cond ((string? obj) (simple-format #f "~S" obj))
  66. ((symbol? obj) (string-append "'" (symbol->string obj)))
  67. ((char? obj) (simple-format #f "~S" obj))))
  68. ;; @deffn prune-assoc al
  69. ;; Prune obsolete entries from an a-list. This is order n^2.
  70. (define (prune-assoc al)
  71. (let iter ((al1 '()) (al0 al))
  72. (if (null? al0) al1
  73. (iter (if (assoc (caar al0) al1) al1 (cons (car al0) al1)) (cdr al0)))))
  74. ;; @deffn {Procedure} fixpoint proc seed
  75. ;; This generates the fixpoint for @var{proc} applied to @var{seed},
  76. ;; a list. The procedure @code{proc} takes as arguments an element from
  77. ;; the list and the entire list. Updates should be cons'd onto the front
  78. ;; of the list.
  79. ;;
  80. ;; The routine works by setting prev to the empty list and next, curr and
  81. ;; item to the seed. The item reference is propagated through the current
  82. ;; list until it reaches prev. The calls to proc will update @code{next}.
  83. ;; @example
  84. ;; next-> +---+
  85. ;; | |
  86. ;; curr-> +---+
  87. ;; | |
  88. ;; item-> | |
  89. ;; | |
  90. ;; prev-> +---+
  91. ;; | |
  92. ;; +---+
  93. ;; @end example
  94. ;; @end deffn
  95. (define (fixpoint proc seed)
  96. (let iter ((prev '()) (item seed) (curr seed) (next seed))
  97. (cond
  98. ((not (eqv? item prev))
  99. (iter prev (cdr item) curr (proc (car item) next)))
  100. ((not (eqv? next curr))
  101. (iter curr next next next))
  102. (else
  103. curr))))
  104. ;; @deffn vector-fixpoint proc vec => vec
  105. ;; (proc vec) => chg (boolean)
  106. ;; Not used yet (in step3).
  107. (define (vector-fixpoint proc vec)
  108. (let iter ((chg #t))
  109. (if chg (proc vec) vec)))
  110. ;; @deffn map-attr->vector list-of-alists key => vector
  111. ;; map list of attribute lists to vector of attr
  112. ;; @example
  113. ;; (map-attr->vector '(((a . 1) ...) ((a . 2) ...) ...) => #(1 2 ...)
  114. ;; @end example
  115. (define (map-attr->vector al-l key)
  116. (list->vector (map (lambda (al) (assq-ref al key)) al-l)))
  117. ;; @deffn flip al => a-list
  118. ;; change (a 1 2 3) to ((1 . a) (2 . a) (3 . a))
  119. (define (x-flip al)
  120. (let iter ((result '()) (tail (cdr al)))
  121. (if (null? tail) result
  122. (iter (acons (car tail) (car al) result) (cdr tail)))))
  123. ;; @deffn x-comb (a1 a2 a3) (b1 b2 b3) => (a1 b1) (a1 b2) ...
  124. ;; The implementation needs work.
  125. (define (x-comb a b)
  126. (let iter ((res '()) (al a) (bl b))
  127. (cond
  128. ((null? al) res)
  129. ((pair? bl) (iter (acons (car al) (car bl) res) al (cdr bl)))
  130. ((pair? al) (iter res (cdr al) b)))))
  131. (define (write-vec port vec)
  132. (let* ((nv (vector-length vec)))
  133. (fmt port " #(")
  134. (let iter ((col 4) (ix 0))
  135. (if (eq? ix nv) #f
  136. (let* ((item (vector-ref vec ix))
  137. (stng (fmt #f "~S " item))
  138. (leng (string-length stng)))
  139. (cond
  140. ((> (+ col leng) 78)
  141. (fmt port "\n ~A" stng)
  142. (iter (+ 4 leng) (1+ ix)))
  143. (else
  144. (fmt port "~A" stng)
  145. (iter (+ col leng) (1+ ix)))))))
  146. (fmt port ")")))
  147. ;; @deffn {Procedure} OLD-ugly-print sexp [port] [#:indent 4] [#:extent 78]
  148. ;; This will print in compact form which shows no structure.
  149. ;; @end deffn
  150. (define* (OLD-ugly-print sexp #:optional port #:key (indent 4) (extent 78))
  151. (define (obj->str obj)
  152. (simple-format #f "~S" obj))
  153. ;; @deffn {Procedure} make-strout indent extent port
  154. ;; This will generate a procedure of signature @code{(proc col str)} which
  155. ;; takes a column and string, prints the string and returns updated column.
  156. ;; @end deffn
  157. (define (make-strout ind ext port)
  158. (let ((leader (make-string ind #\space)))
  159. (lambda (col str)
  160. (let* ((len (string-length str)))
  161. (cond
  162. ((> (+ col len) ext)
  163. (newline port)
  164. (display leader port)
  165. (unless (string-every #\space str) (display str port))
  166. (+ ind len))
  167. (else
  168. (display str port)
  169. (+ col len)))))))
  170. (letrec* ((out-p (or port (current-output-port)))
  171. (leader (make-string 2 #\space))
  172. (strout (make-strout indent extent out-p))
  173. (iter1
  174. (lambda (col sx)
  175. (cond
  176. ((pair? sx) (strout (iter2 (strout col "(") sx) ")"))
  177. ((vector? sx)
  178. (strout
  179. (vector-fold
  180. (lambda (ix col elt)
  181. (iter1 (if (zero? ix) col (strout col " ")) elt))
  182. (strout col "#(") sx) ")"))
  183. ((null? sx) (strout col "'()"))
  184. (else (strout col (obj->str sx))))))
  185. (iter2
  186. (lambda (col sx)
  187. (cond
  188. ((pair? sx)
  189. (if (null? (cdr sx))
  190. (iter2 (iter1 col (car sx)) (cdr sx))
  191. (iter2 (strout (iter1 col (car sx)) " ") (cdr sx))))
  192. ((null? sx) col)
  193. (else (strout (strout col ". ") (obj->str sx))))))
  194. )
  195. ;;(simple-format out-p leader)
  196. (iter1 (if (pair? sexp) (strout indent "'") indent) sexp)
  197. ;;(iter1 indent sexp)
  198. ;;(newline out-p)
  199. ))
  200. ;; @deffn {Procedure} ugly-print sexp [port] [options]
  201. ;; This will print in compact form which shows no structure. The optional
  202. ;; keyword argument @var{#:pre-line-prefix} prints the provided string
  203. ;; at the start of each continued line. The default is four spaces.
  204. ;; @end deffn
  205. (define* (ugly-print sexp #:optional (port (current-output-port))
  206. #:key (per-line-prefix "") (width 79) trim-ends)
  207. (define plplen (string-length per-line-prefix))
  208. (define obj->str object->string)
  209. ;; @deffn {Procedure} strout column string-or-number
  210. ;; Nominally takes a column and string, prints the string and returns updated
  211. ;; column. If passed a number instead of string guarantee that many chars.
  212. ;; @end deffn
  213. (define (strout col str)
  214. (cond
  215. ((number? str)
  216. (if (>= (+ col str) width) (strout col "\n") col))
  217. ((string=? "\n" str)
  218. (newline port)
  219. (display per-line-prefix port)
  220. (display " " port)
  221. (1+ plplen))
  222. ((and (string=? str ")") (= width col))
  223. (display str port)
  224. (1+ col))
  225. ((>= (+ col (string-length str)) width)
  226. (cond
  227. ((string-every #\space str) (strout col "\n"))
  228. (else (strout (strout col "\n") str))))
  229. (else
  230. (display str port)
  231. (+ col (string-length str)))))
  232. (letrec*
  233. ((iter1
  234. (lambda (col sx)
  235. (cond
  236. ((pair? sx)
  237. ;;(fmterr "[car sx=~S]" (car sx))
  238. (case (car sx)
  239. ((quote) (iter2 (strout (strout col 3) "'") (cdr sx)))
  240. ((quasiquote) (iter2 (strout (strout col 3) "`") (cdr sx)))
  241. ((unquote) (iter2 (strout (strout col 2) ",") (cdr sx)))
  242. ((unquote-splicing) (iter2 (strout (strout col 3) ",@") (cdr sx)))
  243. ;;(else (strout (iter2 (strout col "(") sx) ")"))))
  244. ;; (strout col 8) is kludge to prevent lone `(' at end of line
  245. (else (strout (iter2 (strout (strout col 8) "(") sx) ")"))))
  246. ((vector? sx)
  247. (strout
  248. (vector-fold
  249. (lambda (ix col elt)
  250. (iter1 (if (zero? ix) col (strout col " ")) elt))
  251. (strout col "#(") sx) ")"))
  252. ;;((null? sx) (strout col "'()"))
  253. ((null? sx) (strout col "()"))
  254. (else (strout col (obj->str sx))))))
  255. (iter2
  256. (lambda (col sx)
  257. (cond
  258. ((pair? sx)
  259. (if (null? (cdr sx))
  260. (iter2 (iter1 col (car sx)) (cdr sx))
  261. (iter2 (strout (iter1 col (car sx)) " ") (cdr sx))))
  262. ((null? sx) col)
  263. (else (strout (strout col ". ") (obj->str sx)))))))
  264. (if (not trim-ends) (strout 0 per-line-prefix))
  265. (iter1 plplen sexp)
  266. (if (not trim-ends) (newline port))
  267. (if #f #f)))
  268. ;; stuff
  269. ;; @deffn {Procedure} depth-first-search graph => (values ht gv tv xl)
  270. ;; The argument @var{gfraph} is a list of verticies and adjacency nodes:
  271. ;; @example
  272. ;; graph => ((1 2 3 4) (2 6 7) ...)
  273. ;; @end example
  274. ;; @noindent
  275. ;; @table @var
  276. ;; @item ht
  277. ;; hash of vertex to index
  278. ;; @item gv
  279. ;; vector of index to vertex
  280. ;; @item tv
  281. ;; vector of (d . f)
  282. ;; @end table
  283. ;; ref: Algorithms, p 478
  284. ;; @end deffn
  285. (define (depth-first-search graph)
  286. (let* ((n (length graph))
  287. (ht (make-hash-table n)) ; vertex -> index
  288. (gv (make-vector n)) ; index -> vertex
  289. (tv (make-vector n #f)) ; index -> times
  290. (pv (make-vector n #f)) ; index -> predecessor :unused
  291. (xl '()))
  292. (letrec
  293. ((next-t (let ((t 0)) (lambda () (set! t (+ 1 t)) t)))
  294. (visit (lambda (k)
  295. (vector-set! tv k (cons (next-t) #f))
  296. (let iter ((l (cdr (vector-ref gv k))))
  297. (if (not (null? l))
  298. (let ((ix (hashq-ref ht (car l))))
  299. (unless (vector-ref tv ix)
  300. (fmtout "set-pv! ~a ~a" ix k)
  301. (vector-set! pv ix k)
  302. (visit ix))
  303. (iter (cdr l)))))
  304. (set! xl (cons k xl))
  305. (set-cdr! (vector-ref tv k) (next-t))
  306. ))
  307. )
  308. ;; Set up hash of vertex to index.
  309. (do ((i 0 (+ i 1)) (l graph (cdr l))) ((= i n))
  310. (vector-set! gv i (car l)) ; (vector-ref gv i) = (list-ref graph i)
  311. (hashq-set! ht (caar l) i)) ; (hash-ref ht (list-ref graph i)) = i
  312. ;; Run through vertices.
  313. (do ((i 0 (+ 1 i))) ((= i n))
  314. (unless (vector-ref tv i) (visit i)))
  315. (values ht gv tv xl))))
  316. ;; @deffn tzort dag
  317. ;; Given DAG return order of nodes. The DAG is provided as list of:
  318. ;; (<node> <priors>)
  319. ;; ref: D.E.Knuth - The Art of C.P., Vol I, Sec 2.2.3
  320. (define (tzort dag)
  321. (let* ((n (length dag))
  322. (ht (make-hash-table n)) ; node -> ix
  323. (nv (make-vector n #f)) ; ix -> (node . adj-list)
  324. (cv (make-vector n 0)) ; ix -> count
  325. (incr (lambda (ix) (vector-set! cv ix (+ (vector-ref cv ix) 1))))
  326. (decr (lambda (ix) (vector-set! cv ix (- (vector-ref cv ix) 1)))))
  327. ;; Set up ht and nv.
  328. (do ((i 0 (+ i 1)) (l dag (cdr l))) ((= n i))
  329. (vector-set! nv i (car l))
  330. (hashq-set! ht (caar l) i))
  331. ;; set up cv
  332. (do ((i 0 (+ i 1))) ((= n i))
  333. (for-each (lambda (n) (incr (hashq-ref ht n)))
  334. (cdr (vector-ref nv i))))
  335. ;; Iterate through nodes until cv all zero.
  336. (let iter1 ((ol '()) (uh '()) ; ordered list, unordered head
  337. (ut (let r ((l '()) (x 0)) ; unordered tail
  338. (if (= x n) l (r (cons x l) (+ x 1))))))
  339. (cond
  340. ((null? ut)
  341. (if (null? uh)
  342. (reverse (map (lambda (e) (car (vector-ref nv e))) ol))
  343. (iter1 ol '() uh)))
  344. (else
  345. (let* ((ix (car ut)))
  346. (if (zero? (vector-ref cv ix))
  347. (iter1
  348. (let iter2 ((l (cdr (vector-ref nv ix))))
  349. (if (null? l) (cons ix ol)
  350. (begin
  351. (decr (hashq-ref ht (car l)))
  352. (iter2 (cdr l)))))
  353. uh
  354. (cdr ut))
  355. (iter1 ol (cons ix uh) (cdr ut)))))))))
  356. ;;; --- last line ---