[Templates] Can there be two?

Paul Seamons mail@seamons.com
Fri, 5 May 2006 08:20:06 -0600


=46or a little background...

I had a module that was a simple TT style variable
replacer. =A0All it could handle were things like [% a %] or [% a.0 %]
or [% a.a.0 %]. =A0It didn't allow for argument passing, or directives,
or operators. =A0All it did was variables. =A0The perldoc made clear the
limitations and said to use Template::Toolkit if anything more
complicated was desired.

Years went on. =A0I decided it would be useful to have basic IF
directive support as well as allowing for argument passed to functions
(such as [% a(b) %]). =A0I added these features, played around with
things, toyed with tag parsing methods. =A0At first there was no optree
=2D the finished document was built as the parse progressed. =A0But then I
discovered that blocks and meta tags can be out of order in the
document. =A0It became easier to build an optree. =A0It was relatively
easy. =A0It was only 1800 lines and took about 80 hours to write.

I then tried it against the TT 2.14 test suite. =A0It took another 20
hours to make it pass the rest of the TT test suite. =A0I was done
mostly around late January 2006 - but I've been waiting for the
anticipated 2.15 release so I could make sure that the new module is
fully compliant with any of the bug fixes that have gone into TT. =A0I'm
still waiting - but I'm getting to the point I'd like to release the
code.

So the first big question: =A0Is there room for two engines that implement
the Template Toolkit specification?

Obviously there are pros and cons to releasing another module that does
the same thing that Template::Toolkit does.

Next question. =A0If I do release it - should I go to lengths to support all
of the extended options that TT uses? =A0For example, there is no support
for V1DOLLAR and there won't be. =A0I've also removed support for the
ANYCASE option (Increased the overall string parsing from 339% listed in the
example below to 359%. =A0That isn't a major increase. =A0And removing ANYC=
ASE has
no effect on compiled templates at all.)

Some other options to consider removing in CET:

=A0 =A0- Direct access to blocks located in other templates.

=A0 =A0- "References"

=A0 =A0- PERL and RAWPERL blocks (CET doesn't use perl for execution)
=A0 =A0 =A0I'd still leave the eval_perl option though.

The big things missing that won't be implemented are TT2 views, and I
may or may not include the new magic TT3 provider namespaces such as
"hash:" or "file:" (etc).

Whether I release a separate module or not, the TT dev team (mostly
Andy) will be welcome to cut out what ever portions of code they may
find useful. =A0The CET code base is somewhat different from TT in that
TT uses a separate object for each phase of execution while CET uses a
separate method. =A0This makes CET either easier or harder to override
functionality depending upon which programming camp you come from.

=46inally - Is releasing this premature? =A0Will TT3's speed increase? =A0Is
increasing the speed a factor for anybody else? =A0Does anybody even
care if there is another module?

I apologize that this letter doesn't include the source code. =A0I'd
rather wait until I can see the general feelings of people on the
matter. =A0I'm sure there will be split feelings either way.

If you have something scathing to say in response, you are certainly
welcome to. =A0But please decide if it is appropriate for the entire
list to hear. =A0Otherwise just email me directly.

Paul Seamons


///----------------------------------------------------------------///

The following three files will be sent as follow-ups to this email (the fir=
st=20
time I tried I put them all in the same email and it exceeded the mailing=20
list max).

7_template_00_base.t - The file used to test most of the language construct=
s=20
of CGI::Ex::Template. =A0If you uncomment the line that says $module =3D=20
'Template' it will use Template instead. =A0It is useful for finding edge=20
cases.

bench_template.pl - Used to test relative performance. =A0All benchmarks sh=
ould=20
be taken with a grain of salt. =A0The output of the run on my machine is li=
sted=20
below. =A0The output varies depending upon memory, disk and processor speed.

perldoc - This is perldoc CGI::Ex::Template > perldoc =A0- It is the unfini=
shed=20
perldoc for CGI::Ex::Template. =A0Did I mention that it is unfinished.

///----------------------------------------------------------------///

The following results are the output of the bench_template.pl script
included in the email. =A0The computer used is a 1.86 Pentium M with 1
Gig of ram running Breezy Kubuntu. =A0All percentages are
CGI::Ex::Template vs TT2.14. =A0The first column shows the "template"
that is being processed. =A0The second column is both engines using
compiled templates that are already in memory. =A0For the third column
the engines use a new object each time. =A0In the fourth column the
engine is using file side caches (CACHE_EXT is set) (it needs to load
a new object each time). =A0The fifth column is passing the template as
a scalar ref rather than loading from file. =A0The last column is the
number of iterations per second of CET running in memory (iterations
used for determining the first column).

Copying and pasting this into a fixed width display will make things line u=
p a=20
little better.

=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ### All percents are=20
CGI::Ex::Template vs TT2
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ### (The percent that CET=20
is faster than TT)
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 Existing object by=20
string ref #
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
 =A0 =A0 =A0 =A0 =A0 =A0New object with CACHE_EXT set=20
# =A0 =A0 =A0 =A0#
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
 =A0 =A0 =A0 =A0 =A0 =A0New object each time # =A0 =A0 =A0 =A0
# =A0 =A0 =A0 =A0#
=A0 =A0 =A0 =A0 =A0This percent is compiled in memory (repeated calls) # =
=A0 =A0 =A0 =A0# =A0 =A0 =A0 =A0
# =A0 =A0 =A0 =A0#
"", =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
 =A0 =A0 =A0 =A0 =A0 =A0 # =A0236% =A0# =A0645% =A0# =A0337% =A0
# =A0457% =A0# 19787.6/s #
"[% one %]", =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0 =A0# =A0168% =A0# =A0592% =A0# =A0421% =A0
# =A0476% =A0# 14355.1/s #
"[% one %]"x100, =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0# =A0 31% =A0# =A0341% =A0# =A0 79% =A0
# =A0348% =A0# 940.2/s #
"[% SET one =3D 2 %]", =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0# =A0161% =A0# =A0534% =A0# =A0419% =A0
# =A0406% =A0# 14121.9/s #
"[% SET one =3D 2 %]"x100, =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0# =A0 12% =A0# =A0279% =A0# =A0 37% =A0
# =A0276% =A0# 864.5/s #
"[% SET one =3D [0..30] %]", =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0# =A0 42% =A0# =A0320% =A0# =A0247% =A0
# =A0211% =A0# 7518.1/s #
"[% hash.a %]", =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
 =A0 =A0 =A0 # =A0173% =A0# =A0619% =A0# =A0423% =A0
# =A0494% =A0# 13334.4/s #
"".((" "x100)."[% one %]\n")x10, =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0# =
=A0 82% =A0# =A0517% =A0# =A0261% =A0
# =A0476% =A0# 5848.1/s #
"".((" "x10)."[% one %]\n")x100, =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0# =
=A0 23% =A0# =A0432% =A0# =A0116% =A0
# =A0422% =A0# 867.7/s #
"".("[% \"".(" "x100)."\$one\" %]\n")x10, =A0 =A0 =A0 =A0 =A0 # =A0-15% =A0=
# =A01415% =A0# =A0102% =A0
# =A01467% =A0# 2700.5/s #
"".("[% \"".(" "x10)."\$one\" %]\n")x100, =A0 =A0 =A0 =A0 =A0 # =A0-50% =A0=
# =A0299% =A0# =A0 =A02% =A0
# =A0302% =A0# 329.3/s #
"[% 2 %]", =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0 =A0# =A0177% =A0# =A0602% =A0# =A0459% =A0
# =A0459% =A0# 15838.0/s #
"[% 1 + 2 %]", =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0# =A0 87% =A0# =A0435% =A0# =A0334% =A0
# =A0299% =A0# 10746.9/s #
"[% 1 + 2 + 3 + 5 + 6 + 8 %]", =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0#=
 =A0 67% =A0# =A0320% =A0# =A0301% =A0
# =A0218% =A0# 9570.1/s #
"[% c.d.0.hee.0 %]", =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0# =A0171% =A0# =A0616% =A0# =A0416% =A0
# =A0527% =A0# 13717.7/s #
"[% SET c.d.0.hee.0 =3D 2 %]", =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0# =A0156% =A0# =A0519% =A0# =A0364% =A0
# =A0413% =A0# 10628.0/s #
"[% c.d.0.hee.0 %]"x100, =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0# =A0 54% =A0# =A0464% =A0# =A0 82% =A0
# =A0467% =A0# 752.8/s #
"[% SET c.d.0.hee.0 =3D 2 %]"x100, =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0#=
 =A0107% =A0# =A0341% =A0# =A0 91% =A0
# =A0341% =A0# 342.0/s #
"[% t =3D 1 || 0 ? 0 : 1 || 2 ? 2 : 3 %][% t %]", =A0 =A0 # =A0 73% =A0# =
=A0291% =A0# =A0256% =A0
# =A0210% =A0# 8674.9/s #
"[% a=3D1 %][% IF a %]Two[% END %]", =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0# =
=A0126% =A0# =A0481% =A0# =A0347% =A0
# =A0378% =A0# 11166.3/s #
" =A0 =A0 =A0 =A0 [% IF a %]Two[% END %]", =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0# =A0161% =A0# =A0596% =A0# =A0412% =A0
# =A0492% =A0# 13846.3/s #
"[% IF a %]A[% ELSE %]B[% END %]", =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0# =A0=
141% =A0# =A0526% =A0# =A0378% =A0
# =A0422% =A0# 12728.4/s #
"[% IF a %]A[% ELSIF b %]B[% ELSE %]C[% END %]", =A0 =A0# =A0131% =A0# =A05=
19% =A0# =A0359% =A0
# =A0407% =A0# 11386.8/s #
"[% FOREACH i =3D [0..10] =A0 ; i ; END %]", =A0 =A0 =A0 =A0 =A0 =A0# =A0 1=
6% =A0# =A0230% =A0# =A0154% =A0
# =A0155% =A0# 2376.1/s #
"[% FOREACH i =3D [0..100] =A0; i ; END %]", =A0 =A0 =A0 =A0 =A0 =A0# =A0-1=
5% =A0# =A0 38% =A0# =A0 13% =A0
# =A0 18% =A0# 362.5/s #
"[% FOREACH [0..10] =A0 =A0 =A0 ; i ; END %]", =A0 =A0 =A0 =A0 =A0 =A0# =A0=
 23% =A0# =A0245% =A0# =A0162% =A0
# =A0169% =A0# 2463.0/s #
"[% FOREACH [0..100] =A0 =A0 =A0; i ; END %]", =A0 =A0 =A0 =A0 =A0 =A0# =A0=
 -4% =A0# =A0 56% =A0# =A0 28% =A0
# =A0 32% =A0# 384.9/s #
"[% f =3D 10 %][%WHILE f%][%f=3Df- 1%][%f%][% END %]", =A0# =A0-14% =A0# =
=A0152% =A0# =A0 50% =A0
# =A0102% =A0# 1279.5/s #
"[% f =3D 10; WHILE (g=3Df) ; f =3D f - 1 ; f ; END %]", =A0# =A0-18% =A0# =
=A0115% =A0# =A0 34% =A0
# =A0 75% =A0# 1014.6/s #
"[% f =3D 1; =A0WHILE (g=3Df) ; f =3D f - 1 ; f ; END %]", =A0# =A0 38% =A0=
# =A0302% =A0# =A0193% =A0
# =A0223% =A0# 5061.4/s #
"[% PROCESS bar.tt %]", =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
 =A0 # =A0236% =A0# =A0592% =A0# =A0397% =A0
# =A0506% =A0# 10189.6/s #
"[% INCLUDE baz.tt %]", =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
 =A0 # =A0157% =A0# =A0427% =A0# =A0296% =A0
# =A0370% =A0# 6549.1/s #
"[% BLOCK foo %]Hi[% END %][% PROCESS foo %]", =A0 =A0 =A0# =A0162% =A0# =
=A0592% =A0# =A0422% =A0
# =A0487% =A0# 10030.6/s #
"[% BLOCK foo %]Hi[% END %][% INCLUDE foo %]", =A0 =A0 =A0# =A0140% =A0# =
=A0557% =A0# =A0387% =A0
# =A0462% =A0# 8555.5/s #
"[% MACRO foo BLOCK %]Hi[% END %][% foo %]", =A0 =A0 =A0 =A0# =A0 79% =A0# =
=A0413% =A0# =A0301% =A0
# =A0309% =A0# 7363.9/s #
"[% MACRO foo(n) BLOCK %]Hi[%n%][%END%][%foo(2)%]", # =A0 65% =A0# =A0296% =
=A0# =A0265% =A0
# =A0219% =A0# 6081.4/s #
"[% MACRO foo PROCESS bar;BLOCK bar%]7[%END;foo%]", # =A0 96% =A0# =A0449% =
=A0# =A0316% =A0
# =A0355% =A0# 5962.3/s #
"[% n =3D 1 %][% n | repeat(2) %]", =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 # =
=A0128% =A0# =A0432% =A0# =A0370% =A0
# =A0336% =A0# 9705.7/s #
"[% n =3D 1 %][% n FILTER repeat(2) %]", =A0 =A0 =A0 =A0 =A0 =A0 =A0# =A0 9=
1% =A0# =A0367% =A0# =A0325% =A0
# =A0256% =A0# 8025.8/s #
"[% n=3D1; n FILTER echo=3Drepeat(2); n FILTER echo%]", # =A0 36% =A0# =A03=
17% =A0# =A0241% =A0
# =A0243% =A0# 5321.4/s #
"[% constants.simple %]", =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 # =A0176% =A0# =A0617% =A0# =A0473% =A0
# =A0448% =A0# 15760.8/s #
"[%one=3D'ONE'%][% PERL %]print \"[%one%]\"[%END%]", =A0# =A0 66% =A0# =A04=
47% =A0# =A0300% =A0
# =A0357% =A0# 6472.9/s #
"[% 'hi' | \$filt %]", =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0# =A0 97% =A0# =A0517% =A0# =A0348% =A0
# =A0400% =A0# 9500.9/s #
"[% ' ' | uri %]", =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0# =A0127% =A0# =A0620% =A0# =A0402% =A0
# =A0505% =A0# 11498.0/s #
"[% foo | eval %]", =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
 =A0 =A0 # =A0326% =A0# =A0600% =A0# =A0498% =A0
# =A0517% =A0# 5022.9/s #
"[% b =3D \\code(1); b(2) %]", =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0# =A0 60% =A0# =A0270% =A0# =A0239% =A0
# =A0174% =A0# 6451.9/s #
"[% foo =3D BLOCK %]Hi[% END %][% foo %]", =A0 =A0 =A0 =A0 =A0 =A0# =A0105%=
 =A0# =A0438% =A0# =A0312% =A0
# =A0326% =A0# 9962.1/s #
"$longer_template", =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
 =A0 =A0 # =A0 51% =A0# =A0305% =A0# =A0139% =A0
# =A0264% =A0# 1098.6/s #
=A0 =A0 =A0 =A0# overall =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0 =A0 =A0# =A0 94% =A0# =A0439% =A0# =A0268% =A0
# =A0359% =A0#
=A0 =A0 =A0 =A0# overall (with Stash::XS) =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 # =A0 27% =A0# =A0371% =A0# =A0212% =A0
# =A0307% =A0#

# Note that TT is faster on for loops and while loops. =A0The opcode tree m=
ethod=20
of CET can't outperform the low level loops in perl.