fold.scm 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. ;;;; (sxml fold) -- transformation of sxml via fold operations
  2. ;;;;
  3. ;;;; Copyright (C) 2009, 2010 Free Software Foundation, Inc.
  4. ;;;; Written 2007 by Andy Wingo <wingo at pobox dot com>.
  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. ;;; Commentary:
  21. ;;
  22. ;; @code{(sxml fold)} defines a number of variants of the @dfn{fold}
  23. ;; algorithm for use in transforming SXML trees. Additionally it defines
  24. ;; the layout operator, @code{fold-layout}, which might be described as
  25. ;; a context-passing variant of SSAX's @code{pre-post-order}.
  26. ;;
  27. ;;; Code:
  28. (define-module (sxml fold)
  29. #:use-module (srfi srfi-1)
  30. #:export (foldt
  31. foldts
  32. foldts*
  33. fold-values
  34. foldts*-values
  35. fold-layout))
  36. (define (atom? x)
  37. (not (pair? x)))
  38. (define (foldt fup fhere tree)
  39. "The standard multithreaded tree fold.
  40. @var{fup} is of type [a] -> a. @var{fhere} is of type object -> a.
  41. "
  42. (if (atom? tree)
  43. (fhere tree)
  44. (fup (map (lambda (kid)
  45. (foldt fup fhere kid))
  46. tree))))
  47. (define (foldts fdown fup fhere seed tree)
  48. "The single-threaded tree fold originally defined in SSAX.
  49. @xref{sxml ssax,,(sxml ssax)}, for more information."
  50. (if (atom? tree)
  51. (fhere seed tree)
  52. (fup seed
  53. (fold (lambda (kid kseed)
  54. (foldts fdown fup fhere kseed kid))
  55. (fdown seed tree)
  56. tree)
  57. tree)))
  58. (define (foldts* fdown fup fhere seed tree)
  59. "A variant of @ref{sxml fold foldts,,foldts} that allows pre-order
  60. tree rewrites. Originally defined in Andy Wingo's 2007 paper,
  61. @emph{Applications of fold to XML transformation}."
  62. (if (atom? tree)
  63. (fhere seed tree)
  64. (call-with-values
  65. (lambda () (fdown seed tree))
  66. (lambda (kseed tree)
  67. (fup seed
  68. (fold (lambda (kid kseed)
  69. (foldts* fdown fup fhere
  70. kseed kid))
  71. kseed
  72. tree)
  73. tree)))))
  74. (define (fold-values proc list . seeds)
  75. "A variant of @ref{SRFI-1 Fold and Map, fold} that allows multi-valued
  76. seeds. Note that the order of the arguments differs from that of
  77. @code{fold}."
  78. (if (null? list)
  79. (apply values seeds)
  80. (call-with-values
  81. (lambda () (apply proc (car list) seeds))
  82. (lambda seeds
  83. (apply fold-values proc (cdr list) seeds)))))
  84. (define (foldts*-values fdown fup fhere tree . seeds)
  85. "A variant of @ref{sxml fold foldts*,,foldts*} that allows
  86. multi-valued seeds. Originally defined in Andy Wingo's 2007 paper,
  87. @emph{Applications of fold to XML transformation}."
  88. (if (atom? tree)
  89. (apply fhere tree seeds)
  90. (call-with-values
  91. (lambda () (apply fdown tree seeds))
  92. (lambda (tree . kseeds)
  93. (call-with-values
  94. (lambda ()
  95. (apply fold-values
  96. (lambda (tree . seeds)
  97. (apply foldts*-values
  98. fdown fup fhere tree seeds))
  99. tree kseeds))
  100. (lambda kseeds
  101. (apply fup tree (append seeds kseeds))))))))
  102. (define (assq-ref alist key default)
  103. (cond ((assq key alist) => cdr)
  104. (else default)))
  105. (define (fold-layout tree bindings params layout stylesheet)
  106. "A traversal combinator in the spirit of SSAX's @ref{sxml transform
  107. pre-post-order,,pre-post-order}.
  108. @code{fold-layout} was originally presented in Andy Wingo's 2007 paper,
  109. @emph{Applications of fold to XML transformation}.
  110. @example
  111. bindings := (<binding>...)
  112. binding := (<tag> <bandler-pair>...)
  113. | (*default* . <post-handler>)
  114. | (*text* . <text-handler>)
  115. tag := <symbol>
  116. handler-pair := (pre-layout . <pre-layout-handler>)
  117. | (post . <post-handler>)
  118. | (bindings . <bindings>)
  119. | (pre . <pre-handler>)
  120. | (macro . <macro-handler>)
  121. @end example
  122. @table @var
  123. @item pre-layout-handler
  124. A function of three arguments:
  125. @table @var
  126. @item kids
  127. the kids of the current node, before traversal
  128. @item params
  129. the params of the current node
  130. @item layout
  131. the layout coming into this node
  132. @end table
  133. @var{pre-layout-handler} is expected to use this information to return a
  134. layout to pass to the kids. The default implementation returns the
  135. layout given in the arguments.
  136. @item post-handler
  137. A function of five arguments:
  138. @table @var
  139. @item tag
  140. the current tag being processed
  141. @item params
  142. the params of the current node
  143. @item layout
  144. the layout coming into the current node, before any kids were processed
  145. @item klayout
  146. the layout after processing all of the children
  147. @item kids
  148. the already-processed child nodes
  149. @end table
  150. @var{post-handler} should return two values, the layout to pass to the
  151. next node and the final tree.
  152. @item text-handler
  153. @var{text-handler} is a function of three arguments:
  154. @table @var
  155. @item text
  156. the string
  157. @item params
  158. the current params
  159. @item layout
  160. the current layout
  161. @end table
  162. @var{text-handler} should return two values, the layout to pass to the
  163. next node and the value to which the string should transform.
  164. @end table
  165. "
  166. (define (err . args)
  167. (error "no binding available" args))
  168. (define (fdown tree bindings pcont params layout ret)
  169. (define (fdown-helper new-bindings new-layout cont)
  170. (let ((cont-with-tag (lambda args
  171. (apply cont (car tree) args)))
  172. (bindings (if new-bindings
  173. (append new-bindings bindings)
  174. bindings))
  175. (style-params (assq-ref stylesheet (car tree) '())))
  176. (cond
  177. ((null? (cdr tree))
  178. (values
  179. '() bindings cont-with-tag (cons style-params params) new-layout '()))
  180. ((and (pair? (cadr tree)) (eq? (caadr tree) '@))
  181. (let ((params (cons (append (cdadr tree) style-params) params)))
  182. (values
  183. (cddr tree) bindings cont-with-tag params new-layout '())))
  184. (else
  185. (values
  186. (cdr tree) bindings cont-with-tag (cons style-params params) new-layout '())))))
  187. (define (no-bindings)
  188. (fdown-helper #f layout (assq-ref bindings '*default* err)))
  189. (define (macro macro-handler)
  190. (fdown (apply macro-handler tree)
  191. bindings pcont params layout ret))
  192. (define (pre pre-handler)
  193. (values '() bindings
  194. (lambda (params layout old-layout kids)
  195. (values layout (reverse kids)))
  196. params layout (apply pre-handler tree)))
  197. (define (have-bindings tag-bindings)
  198. (fdown-helper
  199. (assq-ref tag-bindings 'bindings #f)
  200. ((assq-ref tag-bindings 'pre-layout
  201. (lambda (tag params layout)
  202. layout))
  203. tree params layout)
  204. (assq-ref tag-bindings 'post
  205. (assq-ref bindings '*default* err))))
  206. (let ((tag-bindings (assq-ref bindings (car tree) #f)))
  207. (cond
  208. ((not tag-bindings) (no-bindings))
  209. ((assq-ref tag-bindings 'macro #f) => macro)
  210. ((assq-ref tag-bindings 'pre #f) => pre)
  211. (else (have-bindings tag-bindings)))))
  212. (define (fup tree bindings cont params layout ret
  213. kbindings kcont kparams klayout kret)
  214. (call-with-values
  215. (lambda ()
  216. (kcont kparams layout klayout (reverse kret)))
  217. (lambda (klayout kret)
  218. (values bindings cont params klayout (cons kret ret)))))
  219. (define (fhere tree bindings cont params layout ret)
  220. (call-with-values
  221. (lambda ()
  222. ((assq-ref bindings '*text* err) tree params layout))
  223. (lambda (tlayout tret)
  224. (values bindings cont params tlayout (cons tret ret)))))
  225. (call-with-values
  226. (lambda ()
  227. (foldts*-values
  228. fdown fup fhere tree bindings #f (cons params '()) layout '()))
  229. (lambda (bindings cont params layout ret)
  230. (values (car ret) layout))))