1 ;;; -*- show-trailing-whitespace: t; indent-tabs-mode: nil -*-
3 ;;; Copyright (c) 2007,2008 David Lichteblau, Ivan Shvedunov.
4 ;;; All rights reserved.
6 ;;; Redistribution and use in source and binary forms, with or without
7 ;;; modification, are permitted provided that the following conditions
10 ;;; * Redistributions of source code must retain the above copyright
11 ;;; notice, this list of conditions and the following disclaimer.
13 ;;; * Redistributions in binary form must reproduce the above
14 ;;; copyright notice, this list of conditions and the following
15 ;;; disclaimer in the documentation and/or other materials
16 ;;; provided with the distribution.
18 ;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
19 ;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 ;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 ;;; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22 ;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 ;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
24 ;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 ;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 ;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 ;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 ;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 (in-package :xuriella
)
33 (declaim (optimize (debug 2)))
38 (defmacro define-instruction
(name (args-var env-var
) &body body
)
39 `(setf (get ',name
'xslt-instruction
)
40 (lambda (,args-var
,env-var
)
41 (declare (ignorable ,env-var
))
44 (define-instruction if
(args env
)
45 (destructuring-bind (test then
&optional else
) args
46 (let ((test-thunk (compile-xpath test env
))
47 (then-thunk (compile-instruction then env
))
48 (else-thunk (when else
(compile-instruction else env
))))
51 ((xpath:boolean-value
(funcall test-thunk ctx
))
52 (funcall then-thunk ctx
))
54 (funcall else-thunk ctx
)))))))
56 (define-instruction when
(args env
)
57 (destructuring-bind (test &rest body
) args
58 (compile-instruction `(if ,test
(progn ,@body
)) env
)))
60 (define-instruction unless
(args env
)
61 (destructuring-bind (test &rest body
) args
62 (compile-instruction `(if (:not
,test
) (progn ,@body
)) env
)))
64 (define-instruction cond
(args env
)
66 (destructuring-bind ((test &body body
) &rest clauses
) args
67 (compile-instruction (if (eq test t
)
75 (define-instruction progn
(args env
)
77 (let ((first-thunk (compile-instruction (first args
) env
))
78 (rest-thunk (compile-instruction `(progn ,@(rest args
)) env
)))
80 (funcall first-thunk ctx
)
81 (funcall rest-thunk ctx
)))
84 (defun decode-qname/runtime
(qname namespaces attributep
)
86 (multiple-value-bind (prefix local-name
)
89 (if (or prefix
(not attributep
))
90 (cdr (assoc prefix namespaces
:test
'equal
))
93 (cxml:well-formedness-violation
()
94 (xslt-error "not a qname: ~A" qname
))))
96 (define-instruction xsl
:element
(args env
)
97 (destructuring-bind ((name &key namespace use-attribute-sets
)
100 (declare (ignore use-attribute-sets
)) ;fixme
101 (multiple-value-bind (name-thunk constant-name-p
)
102 (compile-avt name env
)
103 (multiple-value-bind (ns-thunk constant-ns-p
)
105 (compile-avt namespace env
)
107 (let ((body-thunk (compile-instruction `(progn ,@body
) env
)))
108 (if (and constant-name-p constant-ns-p
)
109 (compile-element/constant-name name namespace env body-thunk
)
110 (compile-element/runtime name-thunk ns-thunk body-thunk
)))))))
112 (defun compile-element/constant-name
(qname namespace env body-thunk
)
113 ;; the simple case: compile-time decoding of the QName
114 (multiple-value-bind (local-name uri prefix
)
115 (decode-qname qname env nil
)
117 (setf uri namespace
))
119 (with-element (local-name uri
:suggested-prefix prefix
)
120 (funcall body-thunk ctx
)))))
122 (defun compile-element/runtime
(name-thunk ns-thunk body-thunk
)
123 ;; run-time decoding of the QName, but using the same namespaces
124 ;; that would have been known at compilation time.
125 (let ((namespaces *namespaces
*))
127 (let ((qname (funcall name-thunk ctx
)))
128 (multiple-value-bind (local-name uri prefix
)
129 (decode-qname/runtime qname namespaces nil
)
131 (setf uri
(funcall ns-thunk ctx
)))
134 (with-element (local-name uri
:suggested-prefix prefix
)
135 (funcall body-thunk ctx
)))))))
137 (define-instruction xsl
:use-attribute-sets
(args env
)
138 (destructuring-bind (str) args
139 (let ((sets (mapcar (lambda (qname)
140 (multiple-value-list (decode-qname qname env nil
)))
143 (loop for
(local-name uri nil
) in sets do
144 (dolist (thunk (find-attribute-set local-name uri
))
145 (funcall thunk ctx
)))))))
147 (define-instruction xsl
:attribute
(args env
)
148 (destructuring-bind ((name &key namespace
) &body body
) args
150 (xslt-error "xsl:attribute: name not specified"))
151 (multiple-value-bind (name-thunk constant-name-p
)
152 (compile-avt name env
)
153 (multiple-value-bind (ns-thunk constant-ns-p
)
155 (compile-avt namespace env
)
157 (let ((value-thunk (compile-instruction `(progn ,@body
) env
)))
158 (if (and constant-name-p constant-ns-p
)
159 (compile-attribute/constant-name name namespace env value-thunk
)
160 (compile-attribute/runtime name-thunk ns-thunk value-thunk
)))))))
162 (defun compile-attribute/constant-name
(qname namespace env value-thunk
)
163 ;; the simple case: compile-time decoding of the QName
164 (multiple-value-bind (local-name uri prefix
)
165 (decode-qname qname env nil
)
167 (setf uri namespace
))
169 (write-attribute local-name
171 (with-toplevel-text-output-sink (s)
173 (funcall value-thunk ctx
)))
174 :suggested-prefix prefix
))))
176 (defun compile-attribute/runtime
(name-thunk ns-thunk value-thunk
)
177 ;; run-time decoding of the QName, but using the same namespaces
178 ;; that would have been known at compilation time.
179 (let ((namespaces *namespaces
*))
181 (let ((qname (funcall name-thunk ctx
)))
182 (multiple-value-bind (local-name uri prefix
)
183 (decode-qname/runtime qname namespaces nil
)
185 (setf uri
(funcall ns-thunk ctx
)))
186 (write-attribute local-name
188 (with-toplevel-text-output-sink (s)
190 (funcall value-thunk ctx
)))
191 :suggested-prefix prefix
))))))
193 (defun remove-excluded-namespaces
194 (namespaces &optional
(excluded-uris *excluded-namespaces
*))
195 (let ((koerbchen '())
198 for cons in namespaces
199 for
(prefix . uri
) = cons
202 ((find prefix kroepfchen
:test
#'equal
))
203 ((find uri excluded-uris
:test
#'equal
)
204 (push prefix kroepfchen
))
206 (push cons koerbchen
))))
209 (define-instruction xsl
:literal-element
(args env
)
211 ((local-name &optional
(uri "") suggested-prefix
) &body body
)
213 (let ((body-thunk (compile-instruction `(progn ,@body
) env
))
214 (namespaces (remove-excluded-namespaces *namespaces
*)))
216 (with-element (local-name uri
217 :suggested-prefix suggested-prefix
218 :extra-namespaces namespaces
)
219 (funcall body-thunk ctx
))))))
221 (define-instruction xsl
:literal-attribute
(args env
)
222 (destructuring-bind ((local-name &optional uri suggested-prefix
) value
) args
223 (let ((value-thunk (compile-avt value env
)))
225 (write-attribute local-name
227 (funcall value-thunk ctx
)
228 :suggested-prefix suggested-prefix
)))))
230 (define-instruction xsl
:text
(args env
)
231 (destructuring-bind (str) args
233 (declare (ignore ctx
))
236 (define-instruction xsl
:processing-instruction
(args env
)
237 (destructuring-bind (name &rest body
) args
238 (let ((name-thunk (compile-avt name env
))
239 (value-thunk (compile-instruction `(progn ,@body
) env
)))
241 (write-processing-instruction
242 (funcall name-thunk ctx
)
243 (with-toplevel-text-output-sink (s)
245 (funcall value-thunk ctx
))))))))
247 (define-instruction xsl
:comment
(args env
)
248 (let ((value-thunk (compile-instruction `(progn ,@args
) env
)))
250 (write-comment (with-toplevel-text-output-sink (s)
252 (funcall value-thunk ctx
)))))))
254 (define-instruction xsl
:value-of
(args env
)
255 (destructuring-bind (xpath) args
256 (let ((thunk (compile-xpath xpath env
)))
259 (write-text (xpath:string-value
(funcall thunk ctx
))))
260 "value-of ~s = ~s" xpath
:result
))))
262 (define-instruction xsl
:unescaped-value-of
(args env
)
263 (destructuring-bind (xpath) args
264 (let ((thunk (compile-xpath xpath env
)))
266 (write-unescaped (xpath:string-value
(funcall thunk ctx
)))))))
268 (define-instruction xsl
:copy-of
(args env
)
269 (destructuring-bind (xpath) args
270 (let ((thunk (compile-xpath xpath env
))
271 ;; FIXME: what was this for? --david
272 #+(or) (v (intern-variable "varName" "")))
275 (let ((result (funcall thunk ctx
)))
277 (xpath:node-set
;; FIXME: variables can contain node sets w/fragments inside. Maybe just fragments would do?
278 (xpath:map-node-set
#'copy-into-result
(xpath:sort-node-set result
)))
279 (result-tree-fragment
280 (copy-into-result result
))
282 (write-text (xpath:string-value result
))))))
283 "copy-of ~s" xpath
))))
285 (defun copy-into-result (node)
287 ((result-tree-fragment-p node
)
288 (stp:do-children
(child (result-tree-fragment-node node
))
289 (copy-into-result child
)))
290 ((xpath-protocol:node-type-p node
:element
)
291 (with-element ((xpath-protocol:local-name node
)
292 (xpath-protocol:namespace-uri node
)
293 :suggested-prefix
(xpath-protocol:namespace-prefix node
)
294 :extra-namespaces
(namespaces-as-alist node
))
295 (map-pipe-eagerly #'copy-into-result
296 (xpath-protocol:attribute-pipe node
))
297 (map-pipe-eagerly #'copy-into-result
298 (xpath-protocol:child-pipe node
))))
299 ((xpath-protocol:node-type-p node
:document
)
300 (map-pipe-eagerly #'copy-into-result
301 (xpath-protocol:child-pipe node
)))
303 (copy-leaf-node node
))))
305 (defun make-sorter (spec env
)
306 (destructuring-bind (&key select lang data-type order case-order
)
308 ;; FIXME: implement case-order
309 (declare (ignore lang case-order
))
310 (let ((select-thunk (compile-xpath (or select
".") env
))
311 (numberp (equal data-type
"number"))
312 (f (if (equal order
"descending") -
1 1)))
314 (let ((i (xpath:string-value
315 (funcall select-thunk
(xpath:make-context a
))))
316 (j (xpath:string-value
317 (funcall select-thunk
(xpath:make-context b
)))))
320 (let ((n-a (xpath:number-value i
))
321 (n-b (xpath:number-value j
)))
322 (cond ((and (xpath::nan-p a
)
323 (not (xpath::nan-p b
)))
325 ((and (not (xpath::nan-p a
))
328 ((xpath::compare-numbers
'< n-a n-b
) -
1)
329 ((xpath::compare-numbers
'> n-a n-b
) 1)
336 (defun compose-sorters (sorters)
338 (let ((this (car sorters
))
339 (next (compose-sorters (rest sorters
))))
341 (let ((d (funcall this a b
)))
347 (defun make-sort-predicate (decls env
)
350 (mapcar (lambda (x) (make-sorter x env
)) decls
))))
352 (minusp (funcall sorter a b
)))))
354 (define-instruction xsl
:for-each
(args env
)
355 (destructuring-bind (select &optional decls
&rest body
) args
356 (unless (and (consp decls
)
357 (eq (car decls
) 'declare
))
360 (let ((select-thunk (compile-xpath select env
))
361 (body-thunk (compile-instruction `(progn ,@body
) env
))
364 (make-sort-predicate (cdr decls
) env
))))
366 (let ((selected (funcall select-thunk ctx
)))
367 (unless (xpath:node-set-p selected
)
368 (xslt-error "for-each select expression should yield a node-set"))
369 (let ((nodes (xpath::force
370 (xpath::sorted-pipe-of selected
))))
372 (setf nodes
(sort (copy-list nodes
) sort-predicate
)))
374 with n
= (length nodes
)
379 (xpath:make-context node
(lambda () n
) i
)))))))))
381 (define-instruction xsl
:with-namespaces
(args env
)
382 (destructuring-bind ((&rest forms
) &rest body
) args
383 (let ((*namespaces
* *namespaces
*))
385 (destructuring-bind (prefix uri
) form
386 (push (cons prefix uri
) *namespaces
*)))
387 (compile-instruction `(progn ,@body
) env
))))
389 (define-instruction xsl
:with-excluded-namespaces
(args env
)
390 (destructuring-bind ((&rest uris
) &rest body
) args
391 (let ((*excluded-namespaces
* (append uris
*excluded-namespaces
*)))
392 (compile-instruction `(progn ,@body
) env
))))
394 (define-instruction xsl
:with-extension-namespaces
(args env
)
395 (destructuring-bind ((&rest uris
) &rest body
) args
396 (let ((*extension-namespaces
* (append uris
*extension-namespaces
*)))
397 (compile-instruction `(progn ,@body
) env
))))
399 ;; XSLT disallows multiple definitions of the same variable within a
400 ;; template. Local variables can shadow global variables though.
401 ;; Since our LET syntax makes it natural to shadow local variables the
402 ;; Lisp way, we check for duplicate variables only where instructed to
403 ;; by the XML syntax parser using WITH-DUPLICATES-CHECK:
404 (defvar *template-variables
* nil
)
406 (define-instruction xsl
:with-duplicates-check
(args env
)
407 (let ((*template-variables
* *template-variables
*))
408 (destructuring-bind ((&rest qnames
) &rest body
) args
409 (dolist (qname qnames
)
410 (multiple-value-bind (local-name uri
)
411 (decode-qname qname env nil
)
412 (let ((key (cons local-name uri
)))
413 (when (find key
*template-variables
* :test
#'equal
)
414 (xslt-error "duplicate variable: ~A, ~A" local-name uri
))
415 (push key
*template-variables
*))))
416 (compile-instruction `(progn ,@body
) env
))))
418 (define-instruction xsl
:with-base-uri
(args env
)
419 (destructuring-bind (uri &rest body
) args
420 (let ((*instruction-base-uri
* uri
))
421 (compile-instruction `(progn ,@body
) env
))))
423 (defstruct (result-tree-fragment
424 (:constructor make-result-tree-fragment
(node)))
427 (define-default-method xpath-protocol
:node-p
428 ((node result-tree-fragment
))
431 (define-default-method xpath-protocol
:node-text
432 ((node result-tree-fragment
))
433 (xpath-protocol:node-text
(result-tree-fragment-node node
)))
435 (defun apply-to-result-tree-fragment (ctx thunk
)
437 (with-xml-output (stp:make-builder
)
438 (with-element ("fragment" "")
439 (funcall thunk ctx
)))))
440 (make-result-tree-fragment (stp:document-element document
))))
442 (define-instruction let
(args env
)
443 (destructuring-bind ((&rest forms
) &rest body
) args
444 (let* ((old-top (length *lexical-variable-declarations
*))
445 (vars-and-names (compile-var-bindings/nointern forms env
))
447 (loop for
((local-name . uri
) thunk
) in vars-and-names
449 (list (push-variable local-name
451 *lexical-variable-declarations
*)
453 (let ((thunk (compile-instruction `(progn ,@body
) env
)))
454 (fill *lexical-variable-declarations
* nil
:start old-top
)
456 (loop for
(index var-thunk
) in vars-and-positions
457 do
(setf (lexical-variable-value index
)
458 (funcall var-thunk ctx
)))
459 (funcall thunk ctx
))))))
461 (define-instruction let
* (args env
)
462 (destructuring-bind ((&rest forms
) &rest body
) args
464 (compile-instruction `(let (,(car forms
))
465 (let* (,@(cdr forms
))
468 (compile-instruction `(progn ,@body
) env
))))
470 (define-instruction xsl
:message
(args env
)
471 (compile-message #'warn args env
))
473 (define-instruction xsl
:terminate
(args env
)
474 (compile-message #'error args env
))
476 (defun namespaces-as-alist (element)
477 (let ((namespaces '()))
478 (do-pipe (ns (xpath-protocol:namespace-pipe element
))
479 (push (cons (xpath-protocol:local-name ns
)
480 (xpath-protocol:namespace-uri ns
))
484 (define-instruction xsl
:copy
(args env
)
485 (let ((body (compile-instruction `(progn ,@args
) env
)))
487 (let ((node (xpath:context-node ctx
)))
489 ((xpath-protocol:node-type-p node
:element
)
491 ((xpath-protocol:local-name node
)
492 (xpath-protocol:namespace-uri node
)
493 :suggested-prefix
(xpath-protocol:namespace-prefix node
)
494 :extra-namespaces
(namespaces-as-alist node
))
496 ((xpath-protocol:node-type-p node
:document
)
499 (copy-leaf-node node
)))))))
501 (defun copy-leaf-node (node)
503 ((xpath-protocol:node-type-p node
:text
)
504 (write-text (xpath-protocol:node-text node
)))
505 ((xpath-protocol:node-type-p node
:comment
)
506 (write-comment (xpath-protocol:node-text node
)))
507 ((xpath-protocol:node-type-p node
:processing-instruction
)
508 (write-processing-instruction
509 (xpath-protocol:processing-instruction-target node
)
510 (xpath-protocol:node-text node
)))
511 ((xpath-protocol:node-type-p node
:attribute
)
513 (xpath-protocol:local-name node
)
514 (xpath-protocol:namespace-uri node
)
515 (xpath-protocol:node-text node
)
516 :suggested-prefix
(xpath-protocol:namespace-prefix node
)))
517 ((xpath-protocol:node-type-p node
:namespace
)
518 (write-extra-namespace
519 (xpath-protocol:local-name node
)
520 (xpath-protocol:namespace-uri node
)))
522 (error "don't know how to copy node ~A" node
))))
524 (defun compile-message (fn args env
)
525 (let ((thunk (compile-instruction `(progn ,@args
) env
)))
528 (with-xml-output (cxml:make-string-sink
)
529 (funcall thunk ctx
))))))
531 (define-instruction xsl
:apply-templates
(args env
)
532 (destructuring-bind ((&key select mode
) &rest param-binding-specs
) args
534 (when (and (consp (car param-binding-specs
))
535 (eq (caar param-binding-specs
) 'declare
))
536 (cdr (pop param-binding-specs
))))
538 (compile-xpath (or select
"child::node()") env
))
540 (compile-var-bindings param-binding-specs env
))
543 (make-sort-predicate decls env
))))
544 (multiple-value-bind (mode-local-name mode-uri
)
545 (and mode
(decode-qname mode env nil
))
547 (let ((*mode
* (if mode
548 (or (find-mode *stylesheet
*
553 (apply-templates/list
555 (xpath::sorted-pipe-of
(funcall select-thunk ctx
)))
556 (loop for
(name nil value-thunk
) in param-bindings
557 collect
(list name
(funcall value-thunk ctx
)))
558 sort-predicate
)))))))
560 (define-instruction xsl
:apply-imports
(args env
)
561 (declare (ignore args env
))
563 (declare (ignore ctx
))
564 (funcall *apply-imports
*)))
566 (define-instruction xsl
:call-template
(args env
)
567 (destructuring-bind (name &rest param-binding-specs
) args
568 (let ((param-bindings
569 (compile-var-bindings param-binding-specs env
)))
570 (multiple-value-bind (local-name uri
)
571 (decode-qname name env nil
)
572 (setf name
(cons local-name uri
)))
574 (call-template ctx name
575 (loop for
(name nil value-thunk
) in param-bindings
576 collect
(list name
(funcall value-thunk ctx
))))))))
578 ;; fixme: incompatible with XSLT 2.0
579 (define-instruction xsl
:document
(args env
)
580 (destructuring-bind ((href &key method indent doctype-public doctype-system
)
583 (declare (ignore doctype-public doctype-system
)) ;fixme
584 (let ((thunk (compile-instruction `(progn ,@body
) env
))
585 (href-thunk (compile-avt href env
)))
589 (puri:merge-uris
(funcall href-thunk ctx
)
590 (xpath-protocol:base-uri
591 (xpath:context-node ctx
))))))
592 (ensure-directories-exist pathname
) ;really?
593 (invoke-with-output-sink
596 (make-output-specification :method
(or method
"XML") :indent indent
)
599 (defun compile-instruction (form env
)
601 (funcall (or (get (car form
) 'xslt-instruction
)
602 (error "undefined instruction: ~A" (car form
)))
605 "instruction ~s" (car form
)))
607 ;;: WTF: "A right curly brace inside a Literal in an expression is not
608 ;;; recognized as terminating the expression."
610 ;;; Da hilft nur tagbody.
611 (defun parse-attribute-value-template (template-string)
612 (with-input-from-string (input template-string
)
613 (let ((ordinary (make-string-output-stream))
614 (xpath (make-string-output-stream))
616 (c (read-char input nil
:eof
)))
618 (let ((o (get-output-stream-string ordinary
)))
619 (when (plusp (length o
))
620 (push (list :data o
) tokens
)))
621 (let ((x (get-output-stream-string xpath
)))
622 (when (plusp (length x
))
623 (push (list :xpath x
) tokens
))))
625 (write-char c ordinary
))
627 (write-char c xpath
)))
628 (macrolet ((goto (target)
630 (setf c
(read-char input nil
:eof
))
651 (goto in-single-quote
))
653 (xslt-error "unexpected end of avt")))
662 (goto in-single-quote
))
665 (goto in-double-quote
))
667 (goto seen-closing-
}))
669 (xslt-error "unexpected end of avt")))
679 (xslt-error "unexpected end of avt")))
681 (goto in-single-quote
)
689 (xslt-error "unexpected end of avt")))
691 (goto in-double-quote
)
712 (xslt-error "unexpected closing brace in avt")
718 (defun compile-avt (template-string env
)
724 (constantly (second x
)))
727 (compile-xpath (second x
) env
))))
728 (parse-attribute-value-template template-string
))))
729 (values (lambda (ctx)
730 (with-output-to-string (s)
732 (write-string (xpath:string-value
(funcall fn ctx
)) s
))))
736 ;;;; Indentation for slime
738 (defmacro define-indentation
(name (&rest args
))
739 (labels ((collect-variables (list)
745 (collect-variables sub
))
747 (if (eql (mismatch "&" (symbol-name sub
)) 1)
750 `(defmacro ,name
(,@args
)
751 (declare (ignorable ,@(collect-variables args
)))
752 (error "XSL indentation helper ~A used literally in lisp code"
755 (define-indentation xsl
:element
756 ((name &key namespace use-attribute-sets
) &body body
))
757 (define-indentation xsl
:literal-element
((name &optional uri
) &body body
))
758 (define-indentation xsl
:attribute
((name &key namespace
) &body body
))
759 (define-indentation xsl
:literal-attribute
((name &optional uri
) &body body
))
760 (define-indentation xsl
:text
(str))
761 (define-indentation xsl
:processing-instruction
(name &body body
))
762 (define-indentation xsl
:comment
(&body body
))
763 (define-indentation xsl
:value-of
(xpath))
764 (define-indentation xsl
:unescaped-value-of
(xpath))
765 (define-indentation xsl
:for-each
(select &body decls-and-body
))
766 (define-indentation xsl
:message
(&body body
))
767 (define-indentation xsl
:terminate
(&body body
))
768 (define-indentation xsl
:apply-templates
((&key select mode
) &body decls-and-body
))
769 (define-indentation xsl
:call-template
(name &rest parameters
))
770 (define-indentation xsl
:copy-of
(xpath))
774 (defun test-instruction (form document
)
775 (let ((thunk (compile-instruction form
(make-instance 'lexical-environment
)))
776 (root (cxml:parse document
(stp:make-builder
))))
777 (with-xml-output (cxml:make-string-sink
)
778 (funcall thunk
(xpath:make-context root
)))))