5 SXML Transformation (SXSLT)
This library provides a means to transform XML elements. It is the descendant of the system described by Kiselyov and Krishnamurthi in their 2003 PADL paper, "SXSLT: Manipulation Language for SXML".
procedure
(sxml:modify updater ...) → (-> node node)
updater : update-spec
update-spec | = | (list xpath-location-path action action-param ...) | ||
action | = | 'delete | ||
| | 'delete-undeep | |||
| | 'insert-into | |||
| | 'insert-following | |||
| | 'insert-preceding | |||
| | 'replace | |||
| | 'move-into | |||
| | 'move-following | |||
| | 'move-preceding | |||
| | handler-proc |
The xpath-location-path describes the nodes to be transformed (see also sxpath). The xpath-location-path is interpreted with respect to some base node. If the location path is absolute, the base node is the root of the document being transformed. If the location path is relative, the base node is the node selected by the previous updater.
Note! sxml:modify depends on the uniqueness of all subtrees; if an eq? subtree occurs more than once, 'delete may fail to delete all instances, for example.
The following combinations of actions and action-params are supported:
'delete Deletes the selected nodes.
'delete-undeep Deletes the selected nodes, but keeps all of their contents, which thus move one level upwards in the document tree.
'insert-into new-node ... Inserts the new-nodes as the last children of the selected nodes.
'insert-following new-node ... Inserts the new-nodes after the selected nodes.
'insert-preceding new-node ... Inserts the new-nodes before the selected nodes.
'replace new-node ... Replaces the selected nodes with the new-nodes.
'rename new-tag Renames the selected nodes, replacing its element tag with new-tag.
'move-into new-location-path Moves the selected nodes to a new location. The selected nodes become the last children of the nodes selected by new-location-path.
'move-following new-location-path Moves the selected nodes to a new location. The selected nodes are placed immediately after the nodes selected by new-location-path.
'move-preceding new-location-path Moves the selected nodes to a new location. The selected nodes are placed immediately before the nodes selected by new-location-path.
handler-proc Applies handler-proc to three arguments: the selected node, a context (?), and the base node. The procedure must return a node or nodeset, which replaces the selected node.
> (define sample-doc `(*TOP* (html (title "the title") (body (p "paragraph 1") (p "paragraph 2")))))
> ((sxml:modify (list "//title" 'delete)) sample-doc) '(*TOP* (html (body (p "paragraph 1") (p "paragraph 2"))))
> ((sxml:modify (list "//body" 'delete-undeep)) sample-doc) '(*TOP* (html (title "the title") (p "paragraph 1") (p "paragraph 2")))
> ((sxml:modify (list "//body" 'rename 'table) (list "p" (lambda (node ctx root) `(tr (td ,node))))) sample-doc)
'(*TOP*
(html
(title "the title")
(table (tr (td (p "paragraph 1"))) (tr (td (p "paragraph 2"))))))
procedure
(pre-post-order tree bindings) → node
tree : node bindings : (listof binding)
binding | = | (list* trigger-symbol '*preorder* . handler-proc) | ||
| | (list* trigger-symbol '*macro* . handler-proc) | |||
| | (list* trigger-symbol new-bindings . handler-proc) | |||
| | (list* trigger-symbol . handler-proc) | |||
trigger-symbol | = | XMLname | ||
| | '*text* | |||
| | '*default* |
The pre-post-order function visits the nodes and nodelists pre-post-order (depth-first). For each node of the form (name node ...) it looks up an association with the given name among its bindings. If it fails, pre-post-order tries to locate a default binding. It’s an error if the latter attempt fails as well.
The following types of binding are supported:
(list* trigger-symbol '*preorder* . handler-proc) The handler-proc is applied to each node matching trigger-symbol without first processing the node’s contents. The result is not traversed by pre-post-order.
(list* trigger-symbol '*macro* . handler-proc) This is equivalent to '*preorder* described above. However, the result is re-processed again, with the current stylesheet.
(list* trigger-symbol new-bindings . handler-proc)
(list* trigger-symbol . handler-proc) The handler-proc is applied to each node matching trigger-symbol, but only after the node’s contents have been recursively processed, using the additional new-bindings, if present.
To be more precise, the handler is applied to the head of the current node and its processed children. The result of the handler, which should also be a tree, replaces the current node. If the current node is a text string or other atom, a special binding with a symbol *text* is looked up.
> (define sample-doc `(*TOP* (html (title "the title") (body (p "paragraph 1") (p "paragraph 2")))))
> (define italicizer `((p . ,(lambda (tag . content) (cons tag (cons "PARAGRAPH BEGINS: " content)))) (*text* . ,(lambda (tag content) `(i ,content))) (*default* . ,(lambda args args))))
> (pre-post-order sample-doc italicizer)
'(*TOP*
(html
(title (i "the title"))
(body
(p "PARAGRAPH BEGINS: " (i "paragraph 1"))
(p "PARAGRAPH BEGINS: " (i "paragraph 2")))))