テンプレートエンジンのプログラムは以下のようになります。
template->s-exp関数は、テンプレートの文字列を受け取り、変換されたGaucheのプログラム(S式)を戻します。
(define (template->s-exp templ)
  (define (quote-display m)
    (format "(display ~s)" (m 1)))
  (if (not (#/<%/ templ))
      templ
      (let* ((s (regexp-replace-all* templ
                                     #/<%=(.*?)%>/ "<% (display \\1 *p*) %>"
                                     #/%>(.*?)<%/ quote-display))
             (str-s-exp (regexp-replace* s
                                         #/^(.*?)<%/ quote-display
                                         #/%>(.*?)$/ quote-display)))
    (read-from-string (string-append "(begin " str-s-exp ")")))))
 
(define (rendering-template templ)
  (eval (template->s-exp templ) (interaction-environment)))
【編集部より】
上記のプログラムの一部に間違いがありましたので、訂正しました(2009/11/25)
誤:    (format "(display ~s *p*)" (m 1)))
正:    (format "(display ~s)" (m 1)))
誤:    (read-from-string (string-append "(begin " str-s-exp ")"))))
正:    (read-from-string (string-append "(begin " str-s-exp ")")))))
このプログラムの前半は、正規表現を使った文字列の置き換えです。ただし、テンプレートに<%が含まれない場合は置き換えの必要がないので、そのままの文字列を戻します。
置き換え処理は、
となります。
だだし、HTMLの中には「"」や「\」などがあるので、これらをエスケープする処理をquote-display関数として定義しています。
regexp-replace関数は、変更対象の文字列と正規表現、置き換え文字列を指定しますが、regexp-replace*関数は正規表現、置き換え文字列を複数指定できる便利な関数です。regexp-replace*関数は置き換えを1回しか行いませんが、regexp-replace-all*関数は対象文字列内の正規表現にマッチするすべての文字列を置き換えます。
また、置き換え文字列の部分には関数を指定することもできます、その場合は、関数への引数は正規表現のマッチング結果が渡ります。quote-display関数の中で(m 1)という式がありますが、これは適用可能なオブジェクトで(rxmatch-substring m 1)と同じ意味になります。
適用可能なオブジェクトはGaucheの便利な機能の1つです、詳細は以下のリンクを参照してください。
template->s-exp関数の後半は、
という挙動になります。
最後のrendering-template関数は、template->s-exp関数の結果をevalで実行しているだけです。
次のコードを実行してみてください。
(define sample "
<html><body>
<table>
<% (use math.const)
   (use gauche.collection) %>
<% (for-each (lambda(d) %>
  <tr>
    <td> <%= d %> </td><td> <%= (sin (* d pi/180)) %> </td>
  </tr>
<% ) '#(0 30 45 60 90)) %>
</table>
</body></html>
")
 
(rendering-template sample)
このような結果が得られます。
<html><body>
<table>
 
 
  <tr>
    <td> 0 </td><td> 0.0 </td>
  </tr>
 
  <tr>
    <td> 30 </td><td> 0.49999999999999994 </td>
  </tr>
 
  <tr>
    <td> 45 </td><td> 0.7071067811865475 </td>
  </tr>
 
  <tr>
    <td> 60 </td><td> 0.8660254037844386 </td>
  </tr>
 
  <tr>
    <td> 90 </td><td> 1.0 </td>
  </tr>
</table>
</body></html>
次回は、継続を使ったコントローラの作成を行います。
Copyright © ITmedia, Inc. All Rights Reserved.