On this page:
sxml:  modify
pre-post-order

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
Returns a procedure that applies the given updaters to an SXML document, producing a new SXML document. Each updater has the following form:

  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.

Examples:
> (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)
Traverses tree, applying the transformation rules specified by bindings to each node.

  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.

Examples:
> (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")))))