This design document proposes that TT3 keywords will be lower case by default.
In TT2 all keywords are upper case by default.
[% INCLUDE header %]
You can use the ANYCASE option to enable the use of keywords
in upper, lower or mixed case.
[% include header %] [% Include header %]
In TT3 keywords will be lower case by default.
[% include header %]
There will be a configuration option to enable upper case keywords for those who prefer them.
my $tt = Template->new( keywords => 'upper' ); # tentative
There will also be a generic facility to map the default keywords to an alternative or range of alternative keywords.
my $tt = Template->new( keywords => {
include => 'INCLUDE', # upper case INCLUDE
elsif => ['elsif', 'elseif', 'elif'], # aliases for elsif
});
If you have a variable that shares the same name as a keyword then you
will be able to use an explicit $ prefix to indicate that it
is a variable.
[% $include %] # the variable 'include', not the keyword
This can also be used to access variables that have spaces or other reserved characters in their names. Yes, it's Bad Idea[tm] to have reserved characters in variable names, but it happens (occasionally) in the Real World[tm] (e.g. passing a hash of product codes/descriptions to a template)
[% $'XYZ-42A/m' %] # a variable called XYZ-42A/m
This will work woth dotops Just Fine[tm].
[% $'XYZ-42A/m'.price %] # the .price of the variable XYZ-42A/m
The C<$> serves only as a hint to the parser. There is no additional
interpolation of the variable.
# TT3 [% foo = 'bar' %] [% foo %] # bar # variable with implicit '$' [% $foo %] # bar # same as above, explicit '$'
Note that this is different to TT2 which treats a leading $
on a variable as an indicator of an extra level of indirection. It
interpolates the first variable to get the name of the second variable
which is then interpolated.
# TT2 [% foo = 'bar' %] [% bar = 20 %] [% foo %] # bar # value of foo [% $foo %] # 20 # value of bar, named in foo
TT3 will support a generic namespace mechanism for partitioning variable
names separately from keywords and/or other token classes (e.g. plugin
names, template names, filter names). The more verbose way to indicate
that a token should be parsed as variable is to add a var:
prefix.
[% $include %] # short form [% var:include %] # long form Thus, the C<$> symbol is little more than syntactic sugar for C<var:>.
Lower case keywords are easier to type and easier to read. The reason why they aren't in lower case in TT2 (or TT1/TT0) is due to potential conflicts with variables of the same name.
TT2 cannot distinguish between keywords and non-keywords variables in a
template. There is no (easy) way to define or use a variable called
INCLUDE, for example.
This is because the TT2 parser first splits a template into tokens and then feeds them into the parser which recognises them according to the grammar rules. In the initial tokenising stage, the parser can only identify a keyword or non-keyword based on what it looks like. It has no way of knowing what rule is being parsed, or at what point within a rule it is currently positioned at because it hasn't even started to think about rules and grammars yet.
This kind of up-front tokenising is "dumb tokenising" (although you
probably won't see that term in the Dragon Book). It means that wherever
you write INCLUDE, it will be recognised as the
INCLUDE keyword, even if it's not valid to have an
INCLUDE keyword in that position.
[% example.INCLUDE %] # FAIL: unexpected token (INCLUDE)
Thus, the reason for making TT2's directives UPPER CASE - to avoid
potential clash with lower case variable names. Although
include isn't a very likely name for a variable, for
example, things like next and last are much
more common. If you enable the ANYCASE option in TT2, then
you can no longer write things like this:
[% page.next %] # FAIL: unexpected token (next) [% list.last %] # FAIL: unexpected token (last)
It's a large helping of FAIL when enabling the ANYCASE
option breaks an inbuilt vmethod. It has to go!
The TT3 parser is much smarter and can distinguish keywords from non-keywords based on position. It's also much more flexible in allowing keywords to appear in places that they weren't allowed to appear in with TT2.
[% page.include %] # no problem: 'include' is not in a
# position where keywords are allowed
In TT2, a leading $ indicates that a variable should be
interpolated before use. This is applied consistently for both top-level
and dotted variables.
[% # TT2
foo = 10;
name = 'foo';
bar = { foo = 20 };
foo # 10
$name # 10 - name interpolated twice, to foo, then 10
bar.foo # 20
bar.$name # 20
%]
TT3 will subtly change the meaning of $ to indicate simply
that the word following is the name of a variable. Whether or not it is
interpolated depends on the context in which it is being used. Dotops
will continue to treat a variable as something that needs to be
interpolated first, but top-level definitions will effectively ignore it,
given that those variables are already being interpolated.
[% # TT3
foo = 10;
name = 'foo';
bar = { foo = 20 };
foo # 10
$foo # 10
$name # foo - name interpolated once, to foo
bar.foo # 20
bar.$name # 20 - bar.$name is same as bar.foo
%]
In summary, a leading $ makes TT2 interpolate a variable
twice, whereas TT3 will only interpolate it once. A $
following a dotop will behave the same in both TT2 and TT3.
I don't imagine that many people are relying on TT2's current symbolic
interpretation rules, and any that do will have to add a second
$ to really drive the message home.
[% $$name %]