more xsl:number
[xuriella.git] / instructions.lisp
blobd2c154a5c3214fee5951aaf18f299f2a708171f6
1 ;;; -*- show-trailing-whitespace: t; indent-tabs: 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
8 ;;; are met:
9 ;;;
10 ;;; * Redistributions of source code must retain the above copyright
11 ;;; notice, this list of conditions and the following disclaimer.
12 ;;;
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.
17 ;;;
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)
32 #+sbcl
33 (declaim (optimize (debug 2)))
36 ;;;; Instructions
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))
42 ,@body)))
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))))
49 (lambda (ctx)
50 (cond
51 ((xpath:boolean-value (funcall test-thunk ctx))
52 (funcall then-thunk ctx))
53 (else-thunk
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)
65 (if args
66 (destructuring-bind ((test &body body) &rest clauses) args
67 (compile-instruction (if (eq test t)
68 `(progn ,@body)
69 `(if ,test
70 (progn ,@body)
71 (cond ,@clauses)))
72 env))
73 (constantly nil)))
75 (define-instruction progn (args env)
76 (if args
77 (let ((first-thunk (compile-instruction (first args) env))
78 (rest-thunk (compile-instruction `(progn ,@(rest args)) env)))
79 (lambda (ctx)
80 (funcall first-thunk ctx)
81 (funcall rest-thunk ctx)))
82 (constantly nil)))
84 (defun decode-qname/runtime (qname namespaces attributep)
85 (handler-case
86 (multiple-value-bind (prefix local-name)
87 (split-qname qname)
88 (values local-name
89 (if (or prefix (not attributep))
90 (cdr (assoc prefix namespaces :test 'equal))
91 "")
92 prefix))
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)
98 &body body)
99 args
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)
104 (if namespace
105 (compile-avt namespace env)
106 (values nil t))
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)
116 (when namespace
117 (setf uri namespace))
118 (lambda (ctx)
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*))
126 (lambda (ctx)
127 (let ((qname (funcall name-thunk ctx)))
128 (multiple-value-bind (local-name uri prefix)
129 (decode-qname/runtime qname namespaces nil)
130 (when ns-thunk
131 (setf uri (funcall ns-thunk ctx)))
132 (unless uri
133 (setf uri ""))
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)))
141 (words str))))
142 (lambda (ctx)
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
149 (multiple-value-bind (name-thunk constant-name-p)
150 (compile-avt name env)
151 (multiple-value-bind (ns-thunk constant-ns-p)
152 (if namespace
153 (compile-avt namespace env)
154 (values nil t))
155 (let ((value-thunk (compile-instruction `(progn ,@body) env)))
156 (if (and constant-name-p constant-ns-p)
157 (compile-attribute/constant-name name namespace env value-thunk)
158 (compile-attribute/runtime name-thunk ns-thunk value-thunk)))))))
160 (defun compile-attribute/constant-name (qname namespace env value-thunk)
161 ;; the simple case: compile-time decoding of the QName
162 (multiple-value-bind (local-name uri prefix)
163 (decode-qname qname env nil)
164 (when namespace
165 (setf uri namespace))
166 (lambda (ctx)
167 (write-attribute local-name
169 (with-text-output-sink (s)
170 (with-xml-output s
171 (funcall value-thunk ctx)))
172 :suggested-prefix prefix))))
174 (defun compile-attribute/runtime (name-thunk ns-thunk value-thunk)
175 ;; run-time decoding of the QName, but using the same namespaces
176 ;; that would have been known at compilation time.
177 (let ((namespaces *namespaces*))
178 (lambda (ctx)
179 (let ((qname (funcall name-thunk ctx)))
180 (multiple-value-bind (local-name uri prefix)
181 (decode-qname/runtime qname namespaces nil)
182 (when ns-thunk
183 (setf uri (funcall ns-thunk ctx)))
184 (write-attribute local-name
185 (or uri "")
186 (with-text-output-sink (s)
187 (with-xml-output s
188 (funcall value-thunk ctx)))
189 :suggested-prefix prefix))))))
191 (defun remove-excluded-namespaces
192 (namespaces &optional (excluded-uris *excluded-namespaces*))
193 (let ((koerbchen '())
194 (kroepfchen '()))
195 (loop
196 for cons in namespaces
197 for (prefix . uri) = cons
199 (cond
200 ((find prefix kroepfchen :test #'equal))
201 ((find uri excluded-uris :test #'equal)
202 (push prefix kroepfchen))
204 (push cons koerbchen))))
205 koerbchen))
207 (define-instruction xsl:literal-element (args env)
208 (destructuring-bind
209 ((local-name &optional (uri "") suggested-prefix) &body body)
210 args
211 (let ((body-thunk (compile-instruction `(progn ,@body) env))
212 (namespaces (remove-excluded-namespaces *namespaces*)))
213 (lambda (ctx)
214 (with-element (local-name uri
215 :suggested-prefix suggested-prefix
216 :extra-namespaces namespaces)
217 (funcall body-thunk ctx))))))
219 (define-instruction xsl:literal-attribute (args env)
220 (destructuring-bind ((local-name &optional uri suggested-prefix) value) args
221 (let ((value-thunk (compile-avt value env)))
222 (lambda (ctx)
223 (write-attribute local-name
225 (funcall value-thunk ctx)
226 :suggested-prefix suggested-prefix)))))
228 (define-instruction xsl:text (args env)
229 (destructuring-bind (str) args
230 (lambda (ctx)
231 (declare (ignore ctx))
232 (write-text str))))
234 (define-instruction xsl:processing-instruction (args env)
235 (destructuring-bind (name &rest body) args
236 (let ((name-thunk (compile-avt name env))
237 (value-thunk (compile-instruction `(progn ,@body) env)))
238 (lambda (ctx)
239 (write-processing-instruction
240 (funcall name-thunk ctx)
241 (with-text-output-sink (s)
242 (with-xml-output s
243 (funcall value-thunk ctx))))))))
245 (define-instruction xsl:comment (args env)
246 (destructuring-bind (str) args
247 (lambda (ctx)
248 (declare (ignore ctx))
249 (write-comment str))))
251 (define-instruction xsl:value-of (args env)
252 (destructuring-bind (xpath) args
253 (let ((thunk (compile-xpath xpath env)))
254 (xslt-trace-thunk
255 (lambda (ctx)
256 (write-text (xpath:string-value (funcall thunk ctx))))
257 "value-of ~s = ~s" xpath :result))))
259 (define-instruction xsl:unescaped-value-of (args env)
260 (destructuring-bind (xpath) args
261 (let ((thunk (compile-xpath xpath env)))
262 (lambda (ctx)
263 (write-unescaped (xpath:string-value (funcall thunk ctx)))))))
265 (define-instruction xsl:copy-of (args env)
266 (destructuring-bind (xpath) args
267 (let ((thunk (compile-xpath xpath env))
268 ;; FIXME: what was this for? --david
269 #+(or) (v (intern-variable "varName" "")))
270 (lambda (ctx)
271 (let ((result (funcall thunk ctx)))
272 (typecase result
273 (xpath:node-set ;; FIXME: variables can contain node sets w/fragments inside. Maybe just fragments would do?
274 (xpath:map-node-set #'copy-into-result result))
275 (result-tree-fragment
276 (copy-into-result result))
278 (write-text (xpath:string-value result)))))))))
280 (defun copy-into-result (node)
281 (cond
282 ((result-tree-fragment-p node)
283 (stp:do-children (child (result-tree-fragment-node node))
284 (copy-into-result child)))
285 ((xpath-protocol:node-type-p node :element)
286 (with-element ((xpath-protocol:local-name node)
287 (xpath-protocol:namespace-uri node)
288 :suggested-prefix (xpath-protocol:namespace-prefix node)
289 ;; FIXME: is remove-excluded-namespaces correct here?
290 :extra-namespaces (remove-excluded-namespaces
291 (namespaces-as-alist node)))
292 (map-pipe-eagerly #'copy-into-result
293 (xpath-protocol:attribute-pipe node))
294 (map-pipe-eagerly #'copy-into-result
295 (xpath-protocol:child-pipe node))))
296 ((xpath-protocol:node-type-p node :document)
297 (map-pipe-eagerly #'copy-into-result
298 (xpath-protocol:child-pipe node)))
300 (copy-leaf-node node))))
302 (defun make-sorter (spec env)
303 (destructuring-bind (&key select lang data-type order case-order)
304 (cdr spec)
305 ;; FIXME: implement case-order
306 (declare (ignore lang case-order))
307 (let ((select-thunk (compile-xpath (or select ".") env))
308 (numberp (equal data-type "number"))
309 (f (if (equal order "descending") -1 1)))
310 (lambda (a b)
311 (let ((i (xpath:string-value
312 (funcall select-thunk (xpath:make-context a))))
313 (j (xpath:string-value
314 (funcall select-thunk (xpath:make-context b)))))
315 (* f
316 (if numberp
317 (signum (- (xpath:number-value i) (xpath:number-value j)))
318 (cond
319 ((string< i j) -1)
320 ((equal i j) 0)
321 (t 1)))))))))
323 (defun compose-sorters (sorters)
324 (if sorters
325 (let ((this (car sorters))
326 (next (compose-sorters (rest sorters))))
327 (lambda (a b)
328 (let ((d (funcall this a b)))
329 (if (zerop d)
330 (funcall next a b)
331 d))))
332 (constantly 0)))
334 (defun make-sort-predicate (decls env)
335 (let ((sorter
336 (compose-sorters
337 (mapcar (lambda (x) (make-sorter x env)) decls))))
338 (lambda (a b)
339 (minusp (funcall sorter a b)))))
341 (define-instruction xsl:for-each (args env)
342 (destructuring-bind (select &optional decls &rest body) args
343 (unless (and (consp decls)
344 (eq (car decls) 'declare))
345 (push decls body)
346 (setf decls nil))
347 (let ((select-thunk (compile-xpath select env))
348 (body-thunk (compile-instruction `(progn ,@body) env))
349 (sort-predicate
350 (when (cdr decls)
351 (make-sort-predicate (cdr decls) env))))
352 (lambda (ctx)
353 (let ((selected (funcall select-thunk ctx)))
354 (unless (xpath:node-set-p selected)
355 (xslt-error "for-each select expression should yield a node-set"))
356 (let ((nodes (xpath::force
357 (xpath::sorted-pipe-of selected))))
358 (when sort-predicate
359 (setf nodes (sort (copy-list nodes) sort-predicate)))
360 (loop
361 with n = (length nodes)
362 for node in nodes
363 for i from 1
365 (funcall body-thunk
366 (xpath:make-context node (lambda () n) i)))))))))
368 (define-instruction xsl:with-namespaces (args env)
369 (destructuring-bind ((&rest forms) &rest body) args
370 (let ((*namespaces* *namespaces*))
371 (dolist (form forms)
372 (destructuring-bind (prefix uri) form
373 (push (cons prefix uri) *namespaces*)))
374 (compile-instruction `(progn ,@body) env))))
376 (define-instruction xsl:with-excluded-namespaces (args env)
377 (destructuring-bind ((&rest uris) &rest body) args
378 (let ((*excluded-namespaces* (append uris *excluded-namespaces*)))
379 (compile-instruction `(progn ,@body) env))))
381 ;; XSLT disallows multiple definitions of the same variable within a
382 ;; template. Local variables can shadow global variables though.
383 ;; Since our LET syntax makes it natural to shadow local variables the
384 ;; Lisp way, we check for duplicate variables only where instructed to
385 ;; by the XML syntax parser using WITH-DUPLICATES-CHECK:
386 (defvar *template-variables* nil)
388 (define-instruction xsl:with-duplicates-check (args env)
389 (let ((*template-variables* *template-variables*))
390 (destructuring-bind ((&rest qnames) &rest body) args
391 (dolist (qname qnames)
392 (multiple-value-bind (local-name uri)
393 (decode-qname qname env nil)
394 (let ((key (cons local-name uri)))
395 (when (find key *template-variables* :test #'equal)
396 (xslt-error "duplicate variable: ~A, ~A" local-name uri))
397 (push key *template-variables*))))
398 (compile-instruction `(progn ,@body) env))))
400 (define-instruction xsl:with-base-uri (args env)
401 (destructuring-bind (uri &rest body) args
402 (let ((*instruction-base-uri* uri))
403 (compile-instruction `(progn ,@body) env))))
405 (defstruct (result-tree-fragment
406 (:constructor make-result-tree-fragment (node)))
407 node)
409 (define-default-method xpath-protocol:node-p
410 ((node result-tree-fragment))
413 (define-default-method xpath-protocol:string-value
414 ((node result-tree-fragment))
415 (xpath-protocol:string-value (result-tree-fragment-node node)))
417 (defun apply-to-result-tree-fragment (ctx thunk)
418 (let ((document
419 (with-xml-output (stp:make-builder)
420 (with-element ("fragment" "")
421 (funcall thunk ctx)))))
422 (make-result-tree-fragment (stp:document-element document))))
424 (define-instruction let (args env)
425 (destructuring-bind ((&rest forms) &rest body) args
426 (let* ((old-top (length *lexical-variable-declarations*))
427 (vars-and-names (compile-var-bindings/nointern forms env))
428 (vars-and-positions
429 (loop for ((local-name . uri) thunk) in vars-and-names
430 collect
431 (list (push-variable local-name
433 *lexical-variable-declarations*)
434 thunk))))
435 (let ((thunk (compile-instruction `(progn ,@body) env)))
436 (fill *lexical-variable-declarations* nil :start old-top)
437 (lambda (ctx)
438 (loop for (index var-thunk) in vars-and-positions
439 do (setf (lexical-variable-value index)
440 (funcall var-thunk ctx)))
441 (funcall thunk ctx))))))
443 (define-instruction let* (args env)
444 (destructuring-bind ((&rest forms) &rest body) args
445 (if forms
446 (compile-instruction `(let (,(car forms))
447 (let* (,@(cdr forms))
448 ,@body))
449 env)
450 (compile-instruction `(progn ,@body) env))))
452 (define-instruction xsl:message (args env)
453 (compile-message #'warn args env))
455 (define-instruction xsl:terminate (args env)
456 (compile-message #'error args env))
458 (defun namespaces-as-alist (element)
459 (let ((namespaces '()))
460 (do-pipe (ns (xpath-protocol:namespace-pipe element))
461 (push (cons (xpath-protocol:local-name ns)
462 (xpath-protocol:namespace-uri ns))
463 namespaces))
464 namespaces))
466 (define-instruction xsl:copy (args env)
467 (let ((body (compile-instruction `(progn ,@args) env)))
468 (lambda (ctx)
469 (let ((node (xpath:context-node ctx)))
470 (cond
471 ((xpath-protocol:node-type-p node :element)
472 (with-element
473 ((xpath-protocol:local-name node)
474 (xpath-protocol:namespace-uri node)
475 :suggested-prefix (xpath-protocol:namespace-prefix node)
476 :extra-namespaces (namespaces-as-alist node))
477 (funcall body ctx)))
478 ((xpath-protocol:node-type-p node :document)
479 (funcall body ctx))
481 (copy-leaf-node node)))))))
483 (defun copy-leaf-node (node)
484 (cond
485 ((xpath-protocol:node-type-p node :text)
486 (write-text (xpath-protocol:string-value node)))
487 ((xpath-protocol:node-type-p node :comment)
488 (write-comment (xpath-protocol:string-value node)))
489 ((xpath-protocol:node-type-p node :processing-instruction)
490 (write-processing-instruction
491 (xpath-protocol:processing-instruction-target node)
492 (xpath-protocol:string-value node)))
493 ((xpath-protocol:node-type-p node :attribute)
494 (write-attribute
495 (xpath-protocol:local-name node)
496 (xpath-protocol:namespace-uri node)
497 (xpath-protocol:string-value node)
498 :suggested-prefix (xpath-protocol:namespace-prefix node)))
500 (error "don't know how to copy node ~A" node))))
502 (defun compile-message (fn args env)
503 (let ((thunk (compile-instruction `(progn ,@args) env)))
504 (lambda (ctx)
505 (funcall fn
506 (with-xml-output (cxml:make-string-sink)
507 (funcall thunk ctx))))))
509 (define-instruction xsl:apply-templates (args env)
510 (destructuring-bind ((&key select mode) &rest param-binding-specs) args
511 (let* ((decls
512 (when (and (consp (car param-binding-specs))
513 (eq (caar param-binding-specs) 'declare))
514 (cdr (pop param-binding-specs))))
515 (select-thunk
516 (compile-xpath (or select "child::node()") env))
517 (param-bindings
518 (compile-var-bindings param-binding-specs env))
519 (sort-predicate
520 (when decls
521 (make-sort-predicate decls env))))
522 (multiple-value-bind (mode-local-name mode-uri)
523 (and mode (decode-qname mode env nil))
524 (lambda (ctx)
525 (let ((*mode* (if mode
526 (or (find-mode *stylesheet*
527 mode-local-name
528 mode-uri)
529 *empty-mode*)
530 *mode*)))
531 (apply-templates/list
532 (xpath::force
533 (xpath::sorted-pipe-of (funcall select-thunk ctx)))
534 (loop for (name nil value-thunk) in param-bindings
535 collect (list name (funcall value-thunk ctx)))
536 sort-predicate)))))))
538 (define-instruction xsl:apply-imports (args env)
539 (declare (ignore args env))
540 (lambda (ctx)
541 (declare (ignore ctx))
542 (funcall *apply-imports*)))
544 (define-instruction xsl:call-template (args env)
545 (destructuring-bind (name &rest param-binding-specs) args
546 (let ((param-bindings
547 (compile-var-bindings param-binding-specs env)))
548 (multiple-value-bind (local-name uri)
549 (decode-qname name env nil)
550 (setf name (cons local-name uri)))
551 (lambda (ctx)
552 (call-template ctx name
553 (loop for (name nil value-thunk) in param-bindings
554 collect (list name (funcall value-thunk ctx))))))))
556 (defun compile-instruction (form env)
557 (xslt-trace-thunk
558 (funcall (or (get (car form) 'xslt-instruction)
559 (error "undefined instruction: ~A" (car form)))
560 (cdr form)
561 env)
562 "instruction ~s" (car form)))
564 ;;: WTF: "A right curly brace inside a Literal in an expression is not
565 ;;; recognized as terminating the expression."
567 ;;; Da hilft nur tagbody.
568 (defun parse-attribute-value-template (template-string)
569 (with-input-from-string (input template-string)
570 (let ((ordinary (make-string-output-stream))
571 (xpath (make-string-output-stream))
572 (tokens '())
573 (c (read-char input nil :eof)))
574 (flet ((emit ()
575 (let ((o (get-output-stream-string ordinary)))
576 (when (plusp (length o))
577 (push (list :data o) tokens)))
578 (let ((x (get-output-stream-string xpath)))
579 (when (plusp (length x))
580 (push (list :xpath x) tokens))))
581 (collect-ordinary ()
582 (write-char c ordinary))
583 (collect-xpath ()
584 (write-char c xpath)))
585 (macrolet ((goto (target)
586 `(progn
587 (setf c (read-char input nil :eof))
588 (go ,target))))
589 (tagbody
590 ordinary
591 (case c
592 (#\{
593 (goto seen{))
594 (#\}
595 (goto seen-stray-}))
596 (:eof
597 (go done)))
598 (collect-ordinary)
599 (goto ordinary)
601 seen{
602 (case c
603 (#\{
604 (collect-ordinary)
605 (goto ordinary))
606 (#\'
607 (collect-xpath)
608 (goto in-single-quote))
609 (:eof
610 (xslt-error "unexpected end of avt")))
611 (emit)
612 (collect-xpath)
613 (goto xpath)
615 xpath
616 (case c
617 (#\'
618 (collect-xpath)
619 (goto in-single-quote))
620 (#\"
621 (collect-xpath)
622 (goto in-double-quote))
623 (#\}
624 (goto seen-closing-}))
625 (:eof
626 (xslt-error "unexpected end of avt")))
627 (collect-xpath)
628 (goto xpath)
630 in-single-quote
631 (case c
632 (#\'
633 (collect-xpath)
634 (goto xpath))
635 (:eof
636 (xslt-error "unexpected end of avt")))
637 (collect-xpath)
638 (goto in-single-quote)
640 in-double-quote
641 (case c
642 (#\"
643 (collect-xpath)
644 (goto xpath))
645 (:eof
646 (xslt-error "unexpected end of avt")))
647 (collect-xpath)
648 (goto in-double-quote)
650 seen-closing-}
651 (case c
652 (#\}
653 (collect-xpath)
654 (goto xpath))
655 (#\{
656 (emit)
657 (goto xpath))
658 (:eof
659 (goto done)))
660 (emit)
661 (collect-ordinary)
662 (goto ordinary)
664 seen-stray-}
665 (case c
666 (#\}
667 (collect-ordinary)
668 (goto ordinary)))
669 (xslt-error "unexpected closing brace in avt")
671 done
672 (emit))))
673 (nreverse tokens))))
675 (defun compile-avt (template-string env)
676 (let* ((constantp t)
677 (fns
678 (mapcar (lambda (x)
679 (ecase (car x)
680 (:data
681 (constantly (second x)))
682 (:xpath
683 (setf constantp nil)
684 (compile-xpath (second x) env))))
685 (parse-attribute-value-template template-string))))
686 (values (lambda (ctx)
687 (with-output-to-string (s)
688 (dolist (fn fns)
689 (write-string (xpath:string-value (funcall fn ctx)) s))))
690 constantp)))
693 ;;;; Indentation for slime
695 (defmacro define-indentation (name (&rest args))
696 (labels ((collect-variables (list)
697 (loop
698 for sub in list
699 append
700 (etypecase sub
701 (list
702 (collect-variables sub))
703 (symbol
704 (if (eql (mismatch "&" (symbol-name sub)) 1)
706 (list sub)))))))
707 `(defmacro ,name (,@args)
708 (declare (ignorable ,@(collect-variables args)))
709 (error "XSL indentation helper ~A used literally in lisp code"
710 ',name))))
712 (define-indentation xsl:element
713 ((name &key namespace use-attribute-sets) &body body))
714 (define-indentation xsl:literal-element ((name &optional uri) &body body))
715 (define-indentation xsl:attribute ((name &key namespace) &body body))
716 (define-indentation xsl:literal-attribute ((name &optional uri) &body body))
717 (define-indentation xsl:text (str))
718 (define-indentation xsl:processing-instruction (name &body body))
719 (define-indentation xsl:comment (str))
720 (define-indentation xsl:value-of (xpath))
721 (define-indentation xsl:unescaped-value-of (xpath))
722 (define-indentation xsl:for-each (select &body decls-and-body))
723 (define-indentation xsl:message (&body body))
724 (define-indentation xsl:terminate (&body body))
725 (define-indentation xsl:apply-templates ((&key select mode) &body decls-and-body))
726 (define-indentation xsl:call-template (name &rest parameters))
727 (define-indentation xsl:copy-of (xpath))
729 ;;;;
731 (defun test-instruction (form document)
732 (let ((thunk (compile-instruction form (make-instance 'lexical-environment)))
733 (root (cxml:parse document (stp:make-builder))))
734 (with-xml-output (cxml:make-string-sink)
735 (funcall thunk (xpath:make-context root)))))