summaryrefslogtreecommitdiff
path: root/module/sxml/apply-templates.scm
diff options
context:
space:
mode:
Diffstat (limited to 'module/sxml/apply-templates.scm')
-rw-r--r--module/sxml/apply-templates.scm102
1 files changed, 102 insertions, 0 deletions
diff --git a/module/sxml/apply-templates.scm b/module/sxml/apply-templates.scm
new file mode 100644
index 000000000..0ee27477c
--- /dev/null
+++ b/module/sxml/apply-templates.scm
@@ -0,0 +1,102 @@
+;;;; (sxml apply-templates) -- xslt-like transformation for sxml
+;;;;
+;;;; Copyright (C) 2009 Free Software Foundation, Inc.
+;;;; Copyright 2004 by Andy Wingo <wingo at pobox dot com>.
+;;;; Written 2003 by Oleg Kiselyov <oleg at pobox dot com> as apply-templates.scm.
+;;;;
+;;;; This library is free software; you can redistribute it and/or
+;;;; modify it under the terms of the GNU Lesser General Public
+;;;; License as published by the Free Software Foundation; either
+;;;; version 3 of the License, or (at your option) any later version.
+;;;;
+;;;; This library is distributed in the hope that it will be useful,
+;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;;;; Lesser General Public License for more details.
+;;;;
+;;;; You should have received a copy of the GNU Lesser General Public
+;;;; License along with this library; if not, write to the Free Software
+;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+;;;;
+
+;;; Commentary:
+;;
+;; Pre-order traversal of a tree and creation of a new tree:
+;;
+;;@smallexample
+;; apply-templates:: tree x <templates> -> <new-tree>
+;;@end smallexample
+;; where
+;;@smallexample
+;; <templates> ::= (<template> ...)
+;; <template> ::= (<node-test> <node-test> ... <node-test> . <handler>)
+;; <node-test> ::= an argument to node-typeof? above
+;; <handler> ::= <tree> -> <new-tree>
+;;@end smallexample
+;;
+;; This procedure does a @emph{normal}, pre-order traversal of an SXML
+;; tree. It walks the tree, checking at each node against the list of
+;; matching templates.
+;;
+;; If the match is found (which must be unique, i.e., unambiguous), the
+;; corresponding handler is invoked and given the current node as an
+;; argument. The result from the handler, which must be a @code{<tree>},
+;; takes place of the current node in the resulting tree.
+;;
+;; The name of the function is not accidental: it resembles rather
+;; closely an @code{apply-templates} function of XSLT.
+;;
+;;; Code:
+
+(define-module (sxml apply-templates)
+ #:use-module (sxml ssax)
+ #:use-module ((sxml xpath) :hide (filter))
+
+ #:export (apply-templates))
+
+(define (apply-templates tree templates)
+
+ ; Filter the list of templates. If a template does not
+ ; contradict the given node (that is, its head matches
+ ; the type of the node), chop off the head and keep the
+ ; rest as the result. All contradicting templates are removed.
+ (define (filter-templates node templates)
+ (cond
+ ((null? templates) templates)
+ ((not (pair? (car templates))) ; A good template must be a list
+ (filter-templates node (cdr templates)))
+ (((node-typeof? (caar templates)) node)
+ (cons (cdar templates) (filter-templates node (cdr templates))))
+ (else
+ (filter-templates node (cdr templates)))))
+
+ ; Here <templates> ::= [<template> | <handler>]
+ ; If there is a <handler> in the above list, it must
+ ; be only one. If found, return it; otherwise, return #f
+ (define (find-handler templates)
+ (and (pair? templates)
+ (cond
+ ((procedure? (car templates))
+ (if (find-handler (cdr templates))
+ (error "ambiguous template match"))
+ (car templates))
+ (else (find-handler (cdr templates))))))
+
+ (let loop ((tree tree) (active-templates '()))
+ ;(cout "active-templates: " active-templates nl "tree: " tree nl)
+ (if (nodeset? tree)
+ (map-union (lambda (a-tree) (loop a-tree active-templates)) tree)
+ (let ((still-active-templates
+ (append
+ (filter-templates tree active-templates)
+ (filter-templates tree templates))))
+ (cond
+ ;((null? still-active-templates) '())
+ ((find-handler still-active-templates) =>
+ (lambda (handler) (handler tree)))
+ ((not (pair? tree)) '())
+ (else
+ (loop (cdr tree) still-active-templates)))))))
+
+;;; arch-tag: 88cd87de-8825-4ab3-9721-cf99694fb787
+;;; templates.scm ends here