transform.scm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. ;;;; (sxml transform) -- pre- and post-order sxml transformation
  2. ;;;;
  3. ;;;; Copyright (C) 2009 Free Software Foundation, Inc.
  4. ;;;; Modified 2004 by Andy Wingo <wingo at pobox dot com>.
  5. ;;;; Written 2003 by Oleg Kiselyov <oleg at pobox dot com> as SXML-tree-trans.scm.
  6. ;;;;
  7. ;;;; This library is free software; you can redistribute it and/or
  8. ;;;; modify it under the terms of the GNU Lesser General Public
  9. ;;;; License as published by the Free Software Foundation; either
  10. ;;;; version 3 of the License, or (at your option) any later version.
  11. ;;;;
  12. ;;;; This library is distributed in the hope that it will be useful,
  13. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. ;;;; Lesser General Public License for more details.
  16. ;;;;
  17. ;;;; You should have received a copy of the GNU Lesser General Public
  18. ;;;; License along with this library; if not, write to the Free Software
  19. ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. ;;;;
  21. ;;; Commentary:
  22. ;;
  23. ;;@heading SXML expression tree transformers
  24. ;
  25. ;@subheading Pre-Post-order traversal of a tree and creation of a new tree
  26. ;@smallexample
  27. ;pre-post-order:: <tree> x <bindings> -> <new-tree>
  28. ;@end smallexample
  29. ; where
  30. ;@smallexample
  31. ; <bindings> ::= (<binding> ...)
  32. ; <binding> ::= (<trigger-symbol> *preorder* . <handler>) |
  33. ; (<trigger-symbol> *macro* . <handler>) |
  34. ; (<trigger-symbol> <new-bindings> . <handler>) |
  35. ; (<trigger-symbol> . <handler>)
  36. ; <trigger-symbol> ::= XMLname | *text* | *default*
  37. ; <handler> :: <trigger-symbol> x [<tree>] -> <new-tree>
  38. ;@end smallexample
  39. ;
  40. ; The pre-post-order function visits the nodes and nodelists
  41. ; pre-post-order (depth-first). For each @code{<Node>} of the form
  42. ; @code{(@var{name} <Node> ...)}, it looks up an association with the
  43. ; given @var{name} among its @var{<bindings>}. If failed,
  44. ; @code{pre-post-order} tries to locate a @code{*default*} binding. It's
  45. ; an error if the latter attempt fails as well. Having found a binding,
  46. ; the @code{pre-post-order} function first checks to see if the binding
  47. ; is of the form
  48. ;@smallexample
  49. ; (<trigger-symbol> *preorder* . <handler>)
  50. ;@end smallexample
  51. ;
  52. ; If it is, the handler is 'applied' to the current node. Otherwise, the
  53. ; pre-post-order function first calls itself recursively for each child
  54. ; of the current node, with @var{<new-bindings>} prepended to the
  55. ; @var{<bindings>} in effect. The result of these calls is passed to the
  56. ; @var{<handler>} (along with the head of the current @var{<Node>}). To
  57. ; be more precise, the handler is _applied_ to the head of the current
  58. ; node and its processed children. The result of the handler, which
  59. ; should also be a @code{<tree>}, replaces the current @var{<Node>}. If
  60. ; the current @var{<Node>} is a text string or other atom, a special
  61. ; binding with a symbol @code{*text*} is looked up.
  62. ;
  63. ; A binding can also be of a form
  64. ;@smallexample
  65. ; (<trigger-symbol> *macro* . <handler>)
  66. ;@end smallexample
  67. ; This is equivalent to @code{*preorder*} described above. However, the
  68. ; result is re-processed again, with the current stylesheet.
  69. ;;
  70. ;;; Code:
  71. (define-module (sxml transform)
  72. #:export (SRV:send-reply
  73. foldts
  74. post-order
  75. pre-post-order
  76. replace-range))
  77. ;; Upstream version:
  78. ; $Id: SXML-tree-trans.scm,v 1.8 2003/04/24 19:39:53 oleg Exp oleg $
  79. ; Like let* but allowing for multiple-value bindings
  80. (define-macro (let*-values bindings . body)
  81. (if (null? bindings) (cons 'begin body)
  82. (apply
  83. (lambda (vars initializer)
  84. (let ((cont
  85. (cons 'let*-values
  86. (cons (cdr bindings) body))))
  87. (cond
  88. ((not (pair? vars)) ; regular let case, a single var
  89. `(let ((,vars ,initializer)) ,cont))
  90. ((null? (cdr vars)) ; single var, see the prev case
  91. `(let ((,(car vars) ,initializer)) ,cont))
  92. (else ; the most generic case
  93. `(call-with-values (lambda () ,initializer)
  94. (lambda ,vars ,cont))))))
  95. (car bindings))))
  96. (define (SRV:send-reply . fragments)
  97. "Output the @var{fragments} to the current output port.
  98. The fragments are a list of strings, characters, numbers, thunks,
  99. @code{#f}, @code{#t} -- and other fragments. The function traverses the
  100. tree depth-first, writes out strings and characters, executes thunks,
  101. and ignores @code{#f} and @code{'()}. The function returns @code{#t} if
  102. anything was written at all; otherwise the result is @code{#f} If
  103. @code{#t} occurs among the fragments, it is not written out but causes
  104. the result of @code{SRV:send-reply} to be @code{#t}."
  105. (let loop ((fragments fragments) (result #f))
  106. (cond
  107. ((null? fragments) result)
  108. ((not (car fragments)) (loop (cdr fragments) result))
  109. ((null? (car fragments)) (loop (cdr fragments) result))
  110. ((eq? #t (car fragments)) (loop (cdr fragments) #t))
  111. ((pair? (car fragments))
  112. (loop (cdr fragments) (loop (car fragments) result)))
  113. ((procedure? (car fragments))
  114. ((car fragments))
  115. (loop (cdr fragments) #t))
  116. (else
  117. (display (car fragments))
  118. (loop (cdr fragments) #t)))))
  119. ;------------------------------------------------------------------------
  120. ; Traversal of an SXML tree or a grove:
  121. ; a <Node> or a <Nodelist>
  122. ;
  123. ; A <Node> and a <Nodelist> are mutually-recursive datatypes that
  124. ; underlie the SXML tree:
  125. ; <Node> ::= (name . <Nodelist>) | "text string"
  126. ; An (ordered) set of nodes is just a list of the constituent nodes:
  127. ; <Nodelist> ::= (<Node> ...)
  128. ; Nodelists, and Nodes other than text strings are both lists. A
  129. ; <Nodelist> however is either an empty list, or a list whose head is
  130. ; not a symbol (an atom in general). A symbol at the head of a node is
  131. ; either an XML name (in which case it's a tag of an XML element), or
  132. ; an administrative name such as '@'.
  133. ; See SXPath.scm and SSAX.scm for more information on SXML.
  134. ;; see the commentary for docs
  135. (define (pre-post-order tree bindings)
  136. (let* ((default-binding (assq '*default* bindings))
  137. (text-binding (or (assq '*text* bindings) default-binding))
  138. (text-handler ; Cache default and text bindings
  139. (and text-binding
  140. (if (procedure? (cdr text-binding))
  141. (cdr text-binding) (cddr text-binding)))))
  142. (let loop ((tree tree))
  143. (cond
  144. ((null? tree) '())
  145. ((not (pair? tree))
  146. (let ((trigger '*text*))
  147. (if text-handler (text-handler trigger tree)
  148. (error "Unknown binding for " trigger " and no default"))))
  149. ((not (symbol? (car tree))) (map loop tree)) ; tree is a nodelist
  150. (else ; tree is an SXML node
  151. (let* ((trigger (car tree))
  152. (binding (or (assq trigger bindings) default-binding)))
  153. (cond
  154. ((not binding)
  155. (error "Unknown binding for " trigger " and no default"))
  156. ((not (pair? (cdr binding))) ; must be a procedure: handler
  157. (apply (cdr binding) trigger (map loop (cdr tree))))
  158. ((eq? '*preorder* (cadr binding))
  159. (apply (cddr binding) tree))
  160. ((eq? '*macro* (cadr binding))
  161. (loop (apply (cddr binding) tree)))
  162. (else ; (cadr binding) is a local binding
  163. (apply (cddr binding) trigger
  164. (pre-post-order (cdr tree) (append (cadr binding) bindings)))
  165. ))))))))
  166. ; post-order is a strict subset of pre-post-order without *preorder*
  167. ; (let alone *macro*) traversals.
  168. ; Now pre-post-order is actually faster than the old post-order.
  169. ; The function post-order is deprecated and is aliased below for
  170. ; backward compatibility.
  171. (define post-order pre-post-order)
  172. ;------------------------------------------------------------------------
  173. ; Extended tree fold
  174. ; tree = atom | (node-name tree ...)
  175. ;
  176. ; foldts fdown fup fhere seed (Leaf str) = fhere seed str
  177. ; foldts fdown fup fhere seed (Nd kids) =
  178. ; fup seed $ foldl (foldts fdown fup fhere) (fdown seed) kids
  179. ; procedure fhere: seed -> atom -> seed
  180. ; procedure fdown: seed -> node -> seed
  181. ; procedure fup: parent-seed -> last-kid-seed -> node -> seed
  182. ; foldts returns the final seed
  183. (define (foldts fdown fup fhere seed tree)
  184. (cond
  185. ((null? tree) seed)
  186. ((not (pair? tree)) ; An atom
  187. (fhere seed tree))
  188. (else
  189. (let loop ((kid-seed (fdown seed tree)) (kids (cdr tree)))
  190. (if (null? kids)
  191. (fup seed kid-seed tree)
  192. (loop (foldts fdown fup fhere kid-seed (car kids))
  193. (cdr kids)))))))
  194. ;------------------------------------------------------------------------
  195. ; Traverse a forest depth-first and cut/replace ranges of nodes.
  196. ;
  197. ; The nodes that define a range don't have to have the same immediate
  198. ; parent, don't have to be on the same level, and the end node of a
  199. ; range doesn't even have to exist. A replace-range procedure removes
  200. ; nodes from the beginning node of the range up to (but not including)
  201. ; the end node of the range. In addition, the beginning node of the
  202. ; range can be replaced by a node or a list of nodes. The range of
  203. ; nodes is cut while depth-first traversing the forest. If all
  204. ; branches of the node are cut a node is cut as well. The procedure
  205. ; can cut several non-overlapping ranges from a forest.
  206. ; replace-range:: BEG-PRED x END-PRED x FOREST -> FOREST
  207. ; where
  208. ; type FOREST = (NODE ...)
  209. ; type NODE = Atom | (Name . FOREST) | FOREST
  210. ;
  211. ; The range of nodes is specified by two predicates, beg-pred and end-pred.
  212. ; beg-pred:: NODE -> #f | FOREST
  213. ; end-pred:: NODE -> #f | FOREST
  214. ; The beg-pred predicate decides on the beginning of the range. The node
  215. ; for which the predicate yields non-#f marks the beginning of the range
  216. ; The non-#f value of the predicate replaces the node. The value can be a
  217. ; list of nodes. The replace-range procedure then traverses the tree and skips
  218. ; all the nodes, until the end-pred yields non-#f. The value of the end-pred
  219. ; replaces the end-range node. The new end node and its brothers will be
  220. ; re-scanned.
  221. ; The predicates are evaluated pre-order. We do not descend into a node that
  222. ; is marked as the beginning of the range.
  223. (define (replace-range beg-pred end-pred forest)
  224. ; loop forest keep? new-forest
  225. ; forest is the forest to traverse
  226. ; new-forest accumulates the nodes we will keep, in the reverse
  227. ; order
  228. ; If keep? is #t, keep the curr node if atomic. If the node is not atomic,
  229. ; traverse its children and keep those that are not in the skip range.
  230. ; If keep? is #f, skip the current node if atomic. Otherwise,
  231. ; traverse its children. If all children are skipped, skip the node
  232. ; as well.
  233. (define (loop forest keep? new-forest)
  234. (if (null? forest) (values (reverse new-forest) keep?)
  235. (let ((node (car forest)))
  236. (if keep?
  237. (cond ; accumulate mode
  238. ((beg-pred node) => ; see if the node starts the skip range
  239. (lambda (repl-branches) ; if so, skip/replace the node
  240. (loop (cdr forest) #f
  241. (append (reverse repl-branches) new-forest))))
  242. ((not (pair? node)) ; it's an atom, keep it
  243. (loop (cdr forest) keep? (cons node new-forest)))
  244. (else
  245. (let*-values
  246. (((node?) (symbol? (car node))) ; or is it a nodelist?
  247. ((new-kids keep?) ; traverse its children
  248. (loop (if node? (cdr node) node) #t '())))
  249. (loop (cdr forest) keep?
  250. (cons
  251. (if node? (cons (car node) new-kids) new-kids)
  252. new-forest)))))
  253. ; skip mode
  254. (cond
  255. ((end-pred node) => ; end the skip range
  256. (lambda (repl-branches) ; repl-branches will be re-scanned
  257. (loop (append repl-branches (cdr forest)) #t
  258. new-forest)))
  259. ((not (pair? node)) ; it's an atom, skip it
  260. (loop (cdr forest) keep? new-forest))
  261. (else
  262. (let*-values
  263. (((node?) (symbol? (car node))) ; or is it a nodelist?
  264. ((new-kids keep?) ; traverse its children
  265. (loop (if node? (cdr node) node) #f '())))
  266. (loop (cdr forest) keep?
  267. (if (or keep? (pair? new-kids))
  268. (cons
  269. (if node? (cons (car node) new-kids) new-kids)
  270. new-forest)
  271. new-forest) ; if all kids are skipped
  272. )))))))) ; skip the node too
  273. (let*-values (((new-forest keep?) (loop forest #t '())))
  274. new-forest))
  275. ;;; arch-tag: 6c814f4b-38f7-42c1-b8ef-ce3447edefc7
  276. ;;; transform.scm ends here