First a short introduction: I've just joined the list today and started
having a look at Velocity mainly because in a lot of ways it's close to
a servlet based template engine (Takhini) that we've been developing in
house (with a view to O/S release). I'll post a brief outline of that
separately -- I'm hopeful that some of the work we've done may be useful
here, and I'm quite prepared -- if we can see a way to migrate our
existing Takhini based systems to Velocity -- to unfork and merge any
useful bits of Takhini with Velocity.
I hope regulars here will forgive me for venturing an opinion on this
subject. Normally I'd lurk for a while, but because we've covered this
ground with Takhini I'm hopeful that I might have something to
contribute immediately. Please feel free to put me in my place if I
overstep the mark.
Anyway, the first thing that occurs to me is that while having all
directives pluggable seems in some ways very cool, I suspect that in
practice once you've provided a full range of control structures (if,
for, while, repeat, case, whatever) you're unlikely to want to plug in
any additional directives with anything other than procedure call
semantics (i.e. a name followed by some arguments). Pluggable syntax for
control structures is a whole can of worms if you're going to have
things like matching opening and closing keywords (#foreach .. #end)
because you have to have pluggable syntax checking for them, including
support for nested directives.
Our approach with Takhini has been to hardwire the control structures
(and things like assignments) into the language (we only currently have
if/else and loop structures) and also make expression syntax hardwired,
but make it possible for user code to extend the language by exposing
functions (variadic) and variables/constants.
Next, a word about loops. Here's a little fragment of Takhini (Takhini
substitutions are marked with '%'):
%.name=['Fred','Jim','Albert']% <!-- a list of names -->
%.age=[36,67,82]% <!-- a list of ages -->
<table>
%{% <!-- start of loop -->
<tr><td>%name%</td><td>%age%</td></tr>
%}% <!-- end of loop -->
</table>
which translates into
<table>
<tr><td>Fred</td><td>36</td></tr>
<tr><td>Jim</td><td>67</td></tr>
<tr><td>Albert</td><td>82</td></tr>
</table>
To make sense of that you need a little background. Firstly list values
are vaguely magical in Takhini; for example most of the operators do
special things with lists, so
1 + 2 --> 3
"Hello, " + "World" --> "Hello, World"
"Hello, " + ["World", "Chums"] --> ["Hello, World", "Hello, Chums"]
and loops have a special understanding of list objects, viz a loop will
iterate once for each element in the largest list that is evaluated
inside it, so the act of evaluating |name| and |age| inside the loop
makes it iterate three times (if one list had four elements an empty
string would have been provided for the missing element in the smaller
string).
While at first glance this might look a little brain dead it actually
makes a lot of sense in practice. One of the traditional problems with
foreach style loops is that you can't iterate over the corresponding
elements in multiple lists at one time. Also with foreach loops you
don't typically have access to the loop index. In Takhini we have a
special variable ('$') that represents the current loop index. Enclosing
loop indexes are $$ (for the innermost enclosing loop), $$$ for the one
outside that and so on.
I'm going to post some more background about Takhini separately --
hopefully we've got some bits and pieces that may be of interest.
Jason van Zyl wrote:
>
> Hi,
>
> I've been playing around with directives for the
> last day, thinking about how pluggable directives
> would work best and what some of the current
> problems are.
>
> Brian Goetz and I have been shooting a couple
> emails back and forth regarding the subject
> and he has found that using a JAVACODE
> production solves the problem for WM. A JAVACODE
> production is basically a mini hand written
> parser in the midst of a generated JavaCC
> parser. The need for a JAVACODE production
> arises from contextual ambiguity. For example:
>
> #foreach $element in $list $element #end
> 1 2 3 4
>
> If all directives are pluggable then there
> is nothing specific in the grammar to
> catch a #foreach $element in $list. You are basically
> looking for #anything, then a list of arguments.
> In the above example it's not possible to tell
> that (4) is in the body of the #foreach without
> giving the parser a hint i.e. the number of
> arguments to the directive. So a #foreach has
> three arguments so what follows must be the
> body of loop.
>
> Now what Brian has come up with is really
> quite ingenious! But I believe there is a
> simpler solution. The JAVACODE production is
> required by the mere presence of contextual
> ambiguity. So I say remove the contextual
> ambiguity and use a much simpler mechanism,
> removing the need for a JAVACODE production.
>
> I've had discussions with Terence about this,
> having well defined entry/exit
> points for certain parsing operations. For example
> JavaDoc has the very simple /** */ entry/exit
> markers. And in the chat room last night Geir
> suggested the same type of system for pluggable
> directives.
>
> So to remove the contextual ambiguity, and the
> requirement of a JAVACODE production I would say
> that delimiting the arguments for a directive
> would be much simpler and actually be more
> flexible:
>
> #foreach ($element in $list) $element #end
>
> What this would also allow is a variable
> number of arguments to the directives. Here's
> hypothetical examples of where a variable number
> of directives might be useful.
>
> #set $greeting = "Hi $name! Well you know $today is your lucky day!"
>
> #expand ($greeting with $a $b)
>
> But you could put as many parameters in $greeting as you
> liked, and you could expand the greeting with the list
> of values to match those parameters. Maybe a contrived
> example and useless but I'm sure a variable number
> of arguments to a directive might be useful.
>
> The #set directive would be an exception, it would
> look as it does now. But even single argument directives
> would have to be enclosed in the delimiters. So we
> would have things like the following:
>
> #foreach ($element in $list)
> $element
> #end
>
> #expand ($greeting with $a $b)
>
> #escape ( $a ) [ not in VM just using a WM example]
>
> But directives with no arguments could probably
> just be left as:
>
> #compress
> text to
> compress
> #end
>
> I wanted to throw the idea out and see what people
> thought. I think it would make things a great
> deal simpler WRT pluggable directives and a lot
> more flexible. You could decide to have optional
> parameters to a directive, and you could look
> for the number of directive arguments passed to
> the directive then decide how to proceed.
>
> If people are in agreement then I would like to
> make the changes ASAP and alter the testbed
> and give it a whirl!
>
> Comments?
>
> jvz.
>
> --
>
> Jason van Zyl
> [EMAIL PROTECTED]
--
Andy Armstrong, Tagish