Re: chains of SXML transformations?
[email protected] Tue, 21 Oct 2003 14:09:15 -0700 (PDT)
| Newsgroups | gmane.lisp.scheme.ssax-sxml |
|---|---|
| Message-ID | <[email protected]> |
Hello!
> My original idea was to operate chains of transformations on a SXML source.
> SXML --transform--> SXML --transform-->SXML ...
> It's only the last transformation step that would produce XML.
> I.e. pre-post-order handlers are in each step, whereas SRV:send-reply
> is only used at the final step.
> The code works fine so far.
> E.g.
> SXML --transforms1-->"SXML"1
> --transforms2-->"SXML"2
> --XML-transforms-->XML
> The intermediate data is not pure SXML as it now appears, because it
> contains too many nesting levels. These reflect the transformation handlers.
> ...
> (my-test '(Data (repeat 3 (random-Header 3))))
> ->
> (Data
> ((Header VOTj)
> (Header 0qel)
> (Header bA97)))
> See, the output is not strict SXML anymore.
> Nevertheless, I can still feed that output into another pre-post-order stage.
The problem can be rectified, by changing pre-post-order as shown in
the appendix. The only change is replacing the two occurrences of
'map' (there are only two such occurrences) with map-node-concat. That
seems to give the desired result, at least for your
example. Justification for the change: a pre-post-order handler always
receives a node (or a list of node's children). The handler can return
either a node, or a nodelist. Now, if the handler returns a nodelist,
we _splice_ it in in the result tree. This operation seems to make sure
that each node of a tree is an SXML node.
For a pure SXML-to-XML conversion, the splicing-in seems to be an
overkill. Therefore, it may make sense to keep both versions of
pre-post-order. Personally I have no problem with proliferation of
pre-post-order-like functions. I believe that it is the data
structure/protocols that should be standardized and parsimonious. Each
user may write processing code in its own way. Of course some of the
processing code turns out more general than the other, and can be
shared. Nevertheless, it's the common data structure, the common
format that guarantees interoperability -- rather than the common
library. Code should be tailored (or even automatically generated) to
suit circumstances.
Appendix. The full code with the example. It runs against the current
CVS, on Bigloo.
(define (make-list count elem)
(if (not (positive? count)) '()
(cons elem (make-list (-- count) elem))))
; Map fn to lst
; The can return either an SXML node, or a nodelist.
; In the latter case, the list is _spliced_ in.
(define (map-node-concat fn lst)
(if (null? lst) '()
(let ((result (fn (car lst))))
(if (and (pair? result) (not (symbol? (car result))))
; it's a node-list
(append result (map-node-concat fn (cdr lst)))
(cons result (map-node-concat fn (cdr lst)))))))
(define (pre-post-order tree bindings)
(let* ((default-binding (assq '*default* bindings))
(text-binding (or (assq '*text* bindings) default-binding))
(text-handler ; Cache default and text bindings
(and text-binding
(if (procedure? (cdr text-binding))
(cdr text-binding) (cddr text-binding)))))
(let loop ((tree tree))
(cond
((null? tree) '())
((not (pair? tree))
(let ((trigger '*text*))
(if text-handler (text-handler trigger tree)
(error "Unknown binding for " trigger " and no default"))))
((not (symbol? (car tree))) (map-node-concat loop tree)) ; tree is a nodelist
(else ; tree is an SXML node
(let* ((trigger (car tree))
(binding (or (assq trigger bindings) default-binding)))
(cond
((not binding)
(error "Unknown binding for " trigger " and no default"))
((not (pair? (cdr binding))) ; must be a procedure: handler
(apply (cdr binding) trigger (map-node-concat loop (cdr tree))))
((eq? '*preorder* (cadr binding))
(apply (cddr binding) tree))
((eq? '*macro* (cadr binding))
(loop (apply (cddr binding) tree)))
(else ; (cadr binding) is a local binding
(apply (cddr binding) trigger
(pre-post-order (cdr tree) (append (cadr binding) bindings)))
))))))))
(define (transform1 sxml)
(pre-post-order sxml
`((repeat *macro*
. ,(lambda (tag count . elems)
;;(require 'srfi-1) make-list from SRFI-1
(apply make-list count elems)))
(random-Header *preorder*
. ,(lambda (tag elems)
`(Header ,(gensym))))
(*text* . ,(lambda (trigger x) x))
(*default* . ,(lambda x x)))))
(pp
(transform1 '(Data (repeat 3 (random-Header 3)))))
(newline)
-------------------------------------------------------
This SF.net email is sponsored by OSDN developer relations
Here's your chance to show off your extensive product knowledge
We want to know what you know. Tell us and you have a chance to win $100
http://www.zoomerang.com/survey.zgi?HRPT1X3RYQNC5V4MLNSV3E54