Perlで10行テンプレートエンジン
コードジェネレータ書いてるときに生成するコード量が多いと,スクリプト中にコピペできる程度にミニミニなテンプレートエンジンが欲しいと思うことがあるので書いた.エスケープとかは必要ないので,ベリーシンプルな置換とevalで書ける.
my $Token = join '', map { chr(65 + rand(26)) } 1..64; sub compile_template { my ($tmpl) = @_; $tmpl =~ s{<%(=)?(.*?)%>\n?|((?:(?!<%).)+)}{ $2 ? $1 ? "\$$Token .= qq{\@{[do{ $2 }]}};" : $2 : "chop( \$$Token .= <<$Token );\n".quotemeta($3)."\n$Token\n"; }gse; eval "sub { my \$$Token = ''; $tmpl \$$Token }" or die "ERROR: Cannot compile template: $@"; } my $renderer = compile_template <<'EOS'; static const key_value_t <%= $_[0]->{name} %>[] = { <% for my $d (@{ $_[0]->{elems} }) { %> { <%= $d->{key} %>, <%= $d->{value} %> }, <% } %> }; EOS print $renderer->({ name => 'hogehoge', elems => [ { key => 'hoge', value => 'fuga' }, { key => 'foo', value => 'bar' }, ], });
こんな感じで.perlのコードがそのまま書けて,forとかifとか使えるのが良い.シンタックスはText::MicroTemplate風で.
実装としては,<% %>で囲まれたperlのコードをそのまま残し,<%= %>で囲まれた式の評価結果と,それ以外の文字列を変数にどんどんappendしていくように置換していく感じ.置換結果をsub{ }でくくってevalしてcoderefにして返すので,evalのコストはコンパイルの時だけ.
変数名が被ったり,heredocが予想外に終端されることを防ぐために64文字のランダムな文字列を生成して使用している.