バイナリ化
パースされたものは次にバイナリ化するのだが、その前にも少し操作がある。
オフセットの計算
パースされたものは各々下記のようにオフセット計算される。
# $raPartsにパースされた結果の配列の参照が格納されている my $ofs = 0; for my $raPart (@{$raParts}) { $raPart->{ofs} = $ofs; my $type = $raPart->{type}; if ( $type == TYPE_PLAIN ) { $ofs += 8; } elsif ( $type == TYPE_REPLACE ) { $ofs += 12; } elsif ( $type == TYPE_IF ) { $ofs += 24; } elsif ( $type == TYPE_ELSE ) { $ofs += 12; } elsif ( $type == TYPE_LOOP ) { $ofs += 12; } elsif ( $type == TYPE_QSA ) { $ofs += 8; } elsif ( $type == TYPE_LB ) { $ofs += 4; } elsif ( $type == TYPE_RB ) { $ofs += 4; } elsif ( $type == TYPE_END ) { $ofs += 4; } }
このオフセットは、XSのときにテンプレートを読み込む時に用いられる。次に、IF/ELSE/LOOPでの飛び先を、配列の番号からオフセットに変更する処理が行われる。
次に文字列は、テンプレートの後ろの方に移動させる。文字列が入っていたところは、その文字列の位置を指すように変更する。例えば、プレーンテキストの場合。
$raPart->{text} = useStringPos(\$strBuf, \%strPos, $raPart->{text});
$raPart->{text}には最初に文字列が入っているが、useStringPos関数を実行した後は、文字列の位置を示すようになる。useStringPos関数は次のような処理を行っている。
sub useStringPos { my ($rStrBuf, $rhStrPos, $str) = @_; # 同じ文字列がすでにあったら、そこを参照するようにする if (exists($rhStrPos->{$str})) { return($rhStrPos->{$str}); } # 新たな文字列だったら、$rStrBufの後ろに文字列を登録、その位置を返す my $newPos = length(${$rStrBuf}); $rhStrPos->{$str} = $newPos; ${$rStrBuf} .= ($str . chr(0)); return($newPos); }
ここで、終了。後は、パース結果の配列をpack関数でバイナリ化する。
ファイルを書き込む時は「パース結果配列のバイナリ化した時のサイズ+パース結果配列のバイナリ化部分+文字列」の形でバイナリテンプレートとしてファイル出力される。
以上がテンプレートコンパイル処理。