Old Text Template

lua-users home
wiki


[!] VersionNotice: The below code pertains to an older Lua version, Lua 4. It does not run as is under Lua 5. The latest version of this page is at TextTemplate.

A good exercise for new Lua programmers is to implement Jon Bentley's M1 macro processor [1] in Lua. Then look at test/examples/www/staff.lua [2], and one appreciates that the major drawback of macro processors is that they don't have data structures, and that it really isn't too hard to add them. The goal is to have one function template for creating templates and another function fillin which takes values from a table and substitues them in place of their references in the template. The template syntax is pretty simple. Table members and global variables are refered to by their name bracketed with |, environments variables are prefixed with $, function calls are prefixed with @ and Lua code is wrapped in @{ and }. Here is an example of how to use template and fillin.
require'temp.lua'
t1 = template[[
==============================================================================
you can access variables: |v|
or environment variables: $HOME
you can call functions: @concat( list, ', ' )
this list has |list.n| elements
   @strrep( '=', list.n )
   @concat( list )
   @strrep( '=', list.n )
or evaluate code inline
     @{ for i=1,4 do OUT = OUT .." list[".. i .."] = ".. list[i] .."\n" end }
you can access global variables:
This example is from |mjd| at |mjdweb|
@{ x = getn(list) }The Lord High Chamberlain has gotten @getn(list)
things for me this year.
@{ diff = x - 2
   more = 'more'
   if diff == 0 then
     diff = 'no'
   elseif diff < 0 then
     more = 'fewer'
   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 = {}
for i = 1,5 do tinsert( L, strchar(64+i) ) end
x = {
  v = 'this is v',
  list = L,
  ref = "|mjd| made Text::Template.pm" 
}
-- fill in the template t1 with values in table x
write( fillin( x, t1 ) )
Here is the output from the code above. TextTemplateExampleOutput The code for temp.lua is pretty long so I've put it on another page with line numbers. TextTemplateCode Note that this code requires Lua 4.1 (beta). The template function strips out all the function calls and Lua code in the tempate string and replace them with the names of Magic global varialbes. For the above example, this is what template returns.
==============================================================================
you can access variables: |v|
or environment variables: /home/administrator
you can call functions: |2concat|
this list has |1list| elements
   |3strrep|
   |4concat|
   |5strrep|
or evaluate code inline
     |7ANY|
you can access global variables:
This example is from |mjd| at |mjdweb|
|8ANY|The Lord High Chamberlain has gotten |6getn|
things for me this year.
|9ANY|
That is |diff| |more| than he gave me last year.
values can have other variables: |ref|
==============================================================================
template also wraps up all the function calls and Lua code that it strips out of the template string and puts them in a table indexed by the Magic variables names and makes all the Magic variables point to that table. For the example above, here is the code that template generates and evaluates with dostring.
local Magic = settype( {}, newtype'magic' )
Magic['1list'] = function()
  local self=globals()
  return list.n --
end
setglobal('1list', Magic )
Magic['2concat'] = function()
  local self=globals()
  return concat( list, ', ' ) --
end
setglobal('2concat', Magic )
Magic['3strrep'] = function()
  local self=globals()
  return strrep( '=', list.n ) --
end
setglobal('3strrep', Magic )
Magic['4concat'] = function()
  local self=globals()
  return concat( list ) --
end
setglobal('4concat', Magic )
Magic['5strrep'] = function()
  local self=globals()
  return strrep( '=', list.n ) --
end
setglobal('5strrep', Magic )
Magic['6getn'] = function()
  local self=globals()
  return getn(list) --
end
setglobal('6getn', Magic )
Magic['7ANY'] = function()
  local OUT=''
  local self=globals()
  do  for i=1,4 do OUT = OUT .." list[".. i .."] = ".. list[i] .."\n" end  end
  return OUT
end
setglobal('7ANY', Magic )
Magic['8ANY'] = function()
  local OUT=''
  local self=globals()
  do  x = getn(list)  end
  return OUT
end
setglobal('8ANY', Magic )
Magic['9ANY'] = function()
  local OUT=''
  local self=globals()
  do  diff = x - 2
   more = 'more'
   if diff == 0 then
     diff = 'no'
   elseif diff < 0 then
     more = 'fewer'
   end
 end
  return OUT
end
setglobal('9ANY', Magic )
The fillin function takes a table of values and the string returned from template and uses gsub to fill in the values. Before it does this, it sets the table of values to be the table of globals, and sets the getglobal tag method for nil values. So if a variable cannot be found in the table of values, it tries to looking in the sharedvars table, and failing that, the real global variable table. This seems overly complicated, but it lets the code snippets in the template access both the table values and the global variables as if they were in scope. It also calls the function associated with any Magic variables it finds. Please fix this if there is a better way.
RecentChanges · preferences
edit · history
Last edited May 28, 2007 8:45 pm GMT (diff)