lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


On Tue, 19 Jan 2010, Romulo wrote:
> 
> Interesting. My approach is to use a Lua script as the source.
> Although it is convenient to have a full language available instead of
> hacks for macros and subroutines (got tired of parsing
> if/while/for/forelse special tags/commands), the mental effort to
> switch between environments makes me wonder if it is really a good
> idea. The good news is that writing a "driver" for reading XML and
> then passing it to the code generator is somewhat easy.
I very much like the idea of scripting templates because it comes
naturally to the coder. However, I worked and work together with
designers and they do have such a hard time wrapping their head aruond a
syntax like this. The experience I have with kid showed me how much
easier the interaction between programmers and designers is if they see
a familiar structure they can work with. Parclate's pages can be almost
interpreted as html in an browser. Well, parsing the tag-commands is
tricky, but I cheat. I don't really parse them because the value of a
tag command is usually plain Lua code. I concatenate like that:

<tag l:for="k,v in pairs(table)"> to for k,v in pairs(table) do

I have to parse the XML anyway, so I get the attributes and their
values. That's rather straight forward. If the user hands me garbage I
let the interpreter or the VM complain. Makes for ugly debugging, but I
can work on that later to make it easier. Parclate is far from kids
feature completeness and probably will never reach it, however, I had a
good look at what we ended up using and I will implement only what
makes sense. Inheritance is one nice and useful feature that's hard to
implement but it is definitely on my list. While I like the idea in
Django that it is really impossible to run Python code in the templates
I think it's overkill to implement an entire language with all these
filters. For parclate I foresee these things to be written in plain Lua
and then handed to the template as a function. Yes, that opens a
backdoor but if people really wanna shoot their own feet, so be it.

> Although I just did a quick glance at your code I see that you use a
> table for holding the output, and then concatenates at the exit point.
> Our code generator expects a vararg function to be called (named
> "out") that, well, outputs the generated (x)html. The code generator
> tries to optimize constants by concatenating adjacent strings and
> automatically sanitize dynamic expressions.
This is something that I might try to implement later, especially since
wsapi expects a yieldable coroutine. I did approach the whole thing the
other way around though, I started writing the server portion first. In
an event driven environment (select(), dev/poll, etc.) it's the server
that decides, how much can be sent in a chunk in order to guarantee a
non-blocking execution. This somewhat clashes with wsapi approach to
define the chunks on the application side since, either the chunk is too
small and it could be sent more per cycle or the tiny chunk takes two
cycles just to send the last bytes of a chunk from the iterator. So I
just went for a buffered application output, let the server send chunks
as large as it likes and have the gc clean up, which isn't too bad since
the allocated chunks are big (as opposed to many tiny chunks). For the
big buffer I just keep a pointer set to the end of the last sent chunk
for the next cycle. Once done, I remove the reference in the lua_State
and let the gc do it's job. As a bonus, I actually can end the HTTP
Content-Length header.(Web)clients appreciate that :P

Parclate deals with adjacent strings when it compiles the template, so
it only generates a constant if there is no command in between.
> 
> ~~ template.lua
> 
> return
> html{
>   head{ title 'My Website - Under construction' },
>   body{
>     p{
>       _for = 'i = 1, 10',
>       span{ class = '${ i % 2 == 0 and "odd" or "even" }',
>         'Hello, ${ i } world'
>       }
>     }
> } }
That looks a little like orbits pages coding, or is that proprietory?
> 
> ~~ generated code
> 
> return function( out, env )
> local __buildattr, __sanitize = __buildattr, __sanitize
> setfenv(1,env)
> out( "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"
> \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\";>\
> <html>\
> <head><title>My Website - Under construction</title></head>\
> <body>" )
> for i = 1, 10 do
>     out( "\
> <p><span", __buildattr( "class", i % 2 == 0 and "even" or "odd"  ),
> ">Hello, ", __sanitize(  i  ), " world</span></p>" )
> end
> out( "</body></html>" )
> end
> 
> (the "setfenv" part is just to illustrate that the generated function
> will be called with a user-supplied global environment)
> 
> __buildattr checks if the expression returns nil. If so, it omits the attr.
That's something I wanna do too. At the moment Parclate just breaks if
variables to the template are not assigned. A bit more graceful handling
would be nice.
> 
> Do you plan to implement a forelse special attribute (i.e: if the loop
> never enters, then the "else" value shall be evaluated). If so, with
> what semantics?
Hm, if I get you right, you mean an empty table so it never loops, so I
wanna display another element. I think that can be done with tools
on board, even if not really pretty. For a numeric table it's easy
though:
<elem l:for="i,v in ipairs(table)" l:strip="">
	<b>${i}.</b> <span>${v}</span>
</elem>
<elem l:if="#table==0">
	It was just empty, too bad :(
</elem>
That shall be easy enough to write. Now for hash tables the "if empty"
expression is not as easy but that always depends on the situation. 

> 
> --rb