Text Template |
|
One drawback of macro processors is they don't have data structures. An example provided in an older release of Lua (lua-4.0.1/test/examples/www/db.lua) shows a good technique for filling in a template file with values from data structures in a configuration file.
If all you want to do is substitute variables within a string with values from a table, then see StringInterpolation.
The following module, Expand.lua
[5]
, uses Bentley's algorithm for macro expansion,
and the GNU make syntax for variable references
within the template string.
It also has a few enhancements taken from Mark J. Dominus's
Text::Template.pm
and Sriram Srinivasan's template-driven code generator
Jeeves
in chapter 17 of O'Reilly's Advanced Perl Programming.
The goal is to have one function called expand
which scans a template
string for variable references, and recursively replaces them with values
found in one of the listed tables or functions.
The template syntax is pretty simple.
References to table members and variables are expressed as either
${varname
} or $(varname)
,
or as $x
if it is a one character variable name.
Simple Lua expression that return a string can also be wrapped in either
${ expr
} or $( expr )
.
Lua statements can be wrapped in one the following:
$(do ... end)
$(if ... end)
$(for ... end)
$(while ... end)
$(repeat ... until ...)
The Lua code can set a variable named OUT
which will be substituted
in place of the code in the template string.
Lua expressions and statements are evaluated by loadstring
with the function environment set to the first table.
The following example shows how to use expand
.
Note that Expand.lua
only works with Lua 5.0.
expand = require'Expand' template = [[ you can access variables: $v or environment variables: ${HOME} you can call functions: ${table.concat(list, ', ')} this list has ${list.n} elements ${string.rep('=', list.n)} ${table.concat(list)} ${string.rep('=', list.n)} or evaluate code inline ${for i=1,list.n do OUT = table.concat{ OUT, ' list[', i, '] = ', list[i], '\n'} end} you can access global variables: This example is from ${mjd} at $(mjdweb) The Lord High Chamberlain has gotten ${L.n} things for me this year. ${do diff = L.n - 5 more = 'more' if diff == 0 then diff = 'no' elseif diff < 0 then diff = -diff more = 'fewer' end end} That is $(diff) $(more) than he gave me last year. values can have other variables: $(ref) ]] mjd = "Mark J. Dominus" mjdweb = 'http://perl.plover.com/' L = { 'A', 'B', 'C', 'D', n=4} local x = { v = 'this is v', list = L, ref = "$(mjd) made Text::Template.pm" } -- fill in the template with values in table x io.write(expand(template, x, _G, os.getenv))
x
,
then the global environment, and lastly by calling the os.getenv function.
Here is the output generated by the example above:
you can access variables: this is v or environment variables: /home/pshook you can call functions: A, B, C, D this list has 4 elements ==== ABCD ==== or evaluate code inline list[1] = A list[2] = B list[3] = C list[4] = D you can access global variables: This example is from Mark J. Dominus at http://perl.plover.com/ The Lord High Chamberlain has gotten 4 things for me this year. That is 1 fewer than he gave me last year. values can have other variables: Mark J. Dominus made Text::Template.pm
$(when varname ...)
syntax.
If varname is not false
,
then the enclosed string will undergo macro expansion.
If varname is a table,
then it will be the first table searched when
the enclosed string undergoes macro expansion.
A useful feature for code generation is the $(foreach tabname ...)
syntax.
If tabname is a table that contains a list of tables,
then macro expansion will be performed on the enclosed string for each
of the tables in tabname
, and the results will be concatenated together.
expand = require'Expand' fun_temp = [[ ============================================================================== $(foreach funcs ${type} x = ${name}( ${table.concat(args, ', ')} ) { $(code) $(when stuff x = $x; y = $y; ) reutrn $(exit); } ) ============================================================================== ]] fun_list = { exit = 1; stuff = false; funcs = { { type = 'int'; name = 'bill'; args = { 'a', 'b', 'c' }; code = 'something'; stuff = { x=99, y=34 }; }; { type = 'char *'; name = 'bert'; args = { 'one', 'two', 'three' }; code = 'something else'; exit = 2 }; }; } io.write(expand(fun_temp, fun_list, _G))
============================================================================== int x = bill( a, b, c ) { something x = 99; y = 34; reutrn 1; } char * x = bert( one, two, three ) { something else reutrn 2; } ==============================================================================
The first version of this page was OldTextTemplate.