gemma(2008/09/28 05:08:24 PDT):Schemerは目に優しい丸括弧のSXMLで片付けてしまいますが、テンプレートエンジンもあると便利かなと思って、半日仕事ででっちあげました。単純なXSLTです。 作りかけです。バグがあります。コードもひどいです。コメントお願いします。
挿入 <p> Hello, #{S式} </p> HTMLエスケープなし <p> Hello, ${S式} </p> HTMLエスケープあり <p> Hello, <?scm S式 ?> </p> HTMLエスケープなし XMLのPIなので、S式中でダブルクオートなどのHTML特殊文字が使える
条件処理 <choose test="真偽値を返すS式"> <then>...</then> <else>...</else> </choose> thenノード、elseノードは省略可
繰り返し処理 <foreach var="リストを返すS式"> ... </foreach>
<table> <foreach i="(list 1 2 3)"> <tr> <foreach j="(list 1 2 3)"> <td>#{(* i j)}</td> </foreach> </tr> </foreach> </table>
<table> <tr> <td>1</td> <td>2</td> <td>3</td> </tr> <tr> <td>2</td> <td>4</td> <td>6</td> </tr> <tr> <td>3</td> <td>6</td> <td>9</td> </tr> </table>
<foreach n="(list 0 1 2 3 4 5 6)"> <choose test="(= n 6)"> <then> <p>That's all.</p> </then> <else> <p>Factorial #{n} = #{(fact n)}</p> </else> </choose> </foreach>
<p>Factorial 0 = 1</p> <p>Factorial 1 = 1</p> <p>Factorial 2 = 2</p> <p>Factorial 3 = 6</p> <p>Factorial 4 = 24</p> <p>Factorial 5 = 120</p> <p>That's all.</p>
<foreach fruit="items"> <p href="http://localhost/fruit/${fruit}"> Yummy <?scm (string-append fruit "!") ?> </p> </foreach>
<p href="http://localhost/fruit/apple"> Yummy apple!</p> <p href="http://localhost/fruit/orange"> Yummy orange!</p> <p href="http://localhost/fruit/melon"> Yummy melon!</p>
<choose test="(= 1 1)"> <then> <foreach i="(list 0 1 2 3)"> <choose test="#t"> <then> <foreach j="(list 0 1 2 3)"> <p>#{(+ i j)}</p> </foreach> </then> </choose> </foreach> </then> </choose>
(use srfi-1) (use sxml.ssax) (use sxml.serializer) (use sxml.tools) (use sxml.tree-trans) ;;文字列 "Factorial #{n} = #{(fact n)}"から ;;S式 (string-append "Factorial " (x->string n) " = " (x->string (fact n))) を生成する (define (process-text text) (if (not (string? text)) text (let loop ((l '()) (text text)) (cond ((#/\#\{([^\}]*)\}/ text) => (lambda (m) (loop (cons `(x->string ,(read-from-string (m 1))) (cons (m 'before) l)) (m 'after)))) ((#/\$\{([^\}]*)\}/ text) => (lambda (m) (loop (cons `(sxml:string->html (x->string ,(read-from-string (m 1)))) (cons (m 'before) l)) (m 'after)))) ((null? l) text) (else (list 'unquote `(string-append ,@(reverse (cons text l))))))))) (define (transform sxml) ;; (@ ((href "http://localhost") (name "hoge))) のcdrを取り出す (define (attributes-ref a) (and (not (null? a)) (eq? '@ (car a)) (not (null? (cdr a))) (cdr a))) (pre-post-order sxml `((choose *macro* . ,(lambda (tag a . elems) (and-let* ((attr (attributes-ref a))) (let ((test-exp (read-from-string (cadar attr))) (then-exp (or (assq 'then elems) '(then ""))) (else-exp (or (assq 'else elems) '(else "")))) (list 'unquote `(if ,test-exp ,(list 'quasiquote (cadr then-exp)) ,(list 'quasiquote (cadr else-exp)))))))) (foreach *macro* . ,(lambda (tag a . elems) (and-let* ((attr (attributes-ref a))) (let ((item (caar attr)) (items (read-from-string (cadar attr)))) `(unquote-splicing (append-map (lambda (,item) ,(list 'quasiquote elems)) ,items)))))) (*PI* . ,(lambda (tag target str) (list 'unquote (read-from-string str)))) (*text* . ,(lambda (trigger x) (process-text x))) (*default* . ,(lambda x x))))) (define (eval-sxml env sxml) (eval `(let ,env ,(list 'quasiquote sxml)) (interaction-environment))) (define (template-engine env in) (format #t (srl:sxml->html (eval-sxml env (transform (ssax:xml->sxml in '())))))) (define (main args) (define env '((name "Gemma") (items '("apple" "orange" "melon")) (fact (lambda (x) (apply * (iota x 1)))))) (if (null? (cdr args)) (template-engine env (current-input-port)) (call-with-input-file (cadr args) (lambda (in) (template-engine env in)))))Tags: sxml.tree-trans, SXSLT