[Templates] #line style directives in TT?

Andy Wardley abw@wardley.org
Thu, 26 Jul 2007 10:22:10 +0100


Paul Seamons wrote:
> Maybe the FILE name is only used when throwing errors - I can sort of see 
> that one - but not too much. 

Yep, that's it.  When TT compiles the template down to Perl code, it adds

    #file $file line $line

lines which tell Perl where to report errors from.  It's these, and only 
these, that the FILE/LINE directives would control.  (err... I think).

I see them being used mostly for debugging purposes.  Or those edge cases like 
Brad's where you need to fix up what's not quite right.  So they probably 
won't be enabled by default.

> The words FILE and LINE and particularly 
> file and line are used extensively in existing templates.  I don't think that 
> the proposed directives add enough functionality to warrant making them 
> reserved words.

I agree that they're of limited value.  However, TT3 allows you to enable or 
disable individual directives.  So if you're using a lot of FILE variables, 
then you could disable (or more like, not enable) the FILE directive.

Also, we don't have to be as worried about reserved words in TT3 as we do in 
TT2 because a directive keyword isn't actually a reserved word.  A keyword 
only has significance in a position where keywords are expected.  ISTR 
Template::Alloy is the same.

   [% GET FILE     %]    # no problem
   [% wibble(FILE) %]    # cool

The more general solution to this problem is namespaces.  By using an explicit 
namespace prefix you can tell the parser what you really mean:

   [% var:FILE  %]        # the variable 'FILE'
   [% dir:FILE  %]        # the directive 'FILE'
   [% file:FILE %]        # the file called 'FILE'

(I'm not sure about 'dir:' as a prefix, BTW. It could be confused with a 
directory, but whatever it's called, you get the idea).

Namespaces provide general purpose parser hooks.  As well as being used to 
disambiguating tokens for the parser, they can be used to implement all kinds 
of other interesting compile time behaviour.

So we'll have a namespace handler that you can enable/disable which provides 
access to the file/line data and anything else relating to the template.  e.g.

   [% template:file = 'badger' %]
   [% template:line = 42 %]

   The is line [% template:line %] of [% template:file %] which was last
   modified at [% template:time %] on [% template:date %].

The nice thing is that these "variables" are resolved at compile time, but can 
be used at runtime.  So if you write:

   [% call_my_sub(template:file, template:line) %]

Then the code generated is more like:

   [% call_my_sub('Badger', 42) %]

As an aside, this also gives you an easy way to define your own constants. 
Something like this:

   Template->new(
     namespaces => {
       site => {
         constants => {
           title  => 'Badger World',
           images => '/images',
           logo   => '/images/badger.png',
         },
       },
     },
   );

And then this:

   Welcome to [% site:title %].
   <img src="[% site:logo %]"/>

Which is compiled down to this:

   Welcome to Badger World
   <img src="/images/badger.png" />

> Rather than add FILE and LINE, I would greatly suggest 
> adding the CONFIG directive that was proposed some time ago but seemed to be 
> warnocked.

Given the ability to enable/disable directives at will, there's no reason why 
we can't have all three and let the end user decide what they need on a 
case-by-case basis (assuming that in most cases, they'll be using the sensible 
set of defaults that we provide).

I'm not opposed to the idea of having one master CONFIG directive, but then 
I'm not sure that I'm totally for it.

On the positive side, it provides somewhere for all the different config data 
to go.  On the negative side, it dumps all the different config data in one 
place, where it becomes harder to manage...

> So you'd have [% CONFIG FILE => 'foo', LINE => 42 %].
> You are on line [% CONFIG LINE %].
[...]
> TAGS directive ought to be deprecated in favor of using [% CONFIG TAGS => 
> ['[%', '%]'] %] or [% CONFIG TAGS => 'html' %].  

[thinking out loud]

The problem I have with this from an implementation point of view is that it 
could result in one monolithic CONFIG directive that has to know about all the 
different possible options, how to parse them, and what to do with them after 
that.

It's not particularly difficult to implement that way, but I think it will 
lead to an implementation which is less flexible than the current one where 
each directive does just one thing and can be enabled or disabled at will.

So if we do have a CONFIG directive then it should just act as a container for 
other directives.  Rather than doing any parsing itself, it would just enable 
a bunch of other directives within that scope (i.e. to the end of the current
tag)

That way we can keep TAGS, EMBED, IGNORE, etc., as self-contained directives 
that each do one thing and do it well.  In this scenario, they would be 
disabled by default until you hit a CONFIG directive.  At that point, they 
become enabled (in the same way that 'CASE' is "enabled" within the context of 
a 'SWITCH' directive, for example).

Hope that makes some kind of sense.  In essence, yes, I think we can do that, 
or something like it.

Cheers
A