lua-CodeGen
a template engine

Manual

For the impatient

local CodeGen = require 'CodeGen'

tmpl = CodeGen {    -- instanciation
    tarball = "${name}-${version}.tar.gz",
    name = 'lua',
}
tmpl.version = 5.1
output = tmpl 'tarball'     -- interpolation
print(output) --> lua-5.1.tar.gz

The instanciation

The instanciation of a template is done by the call of CodeGen with optional parameters. This first parameter is a table. This table uses only string as key and could contains 3 kinds of value :

  • chunk of template which is a string and could contains primitives i.e. ${...}
  • data which gives access to the data model
  • formatter which is a function which accepts a string as parameter and returns it after a transformation. The typical usage is for escape sequence.

The other parameters allow inheritance (ie. access to field) from other templates or simple tables.

A common pattern is to put this step in an external file.

-- file: tarball.tmpl
return CodeGen {
    tarball = "${name}-${version}.tar.gz",
}
tmpl = dofile 'tarball.tmpl'

Setting data and other alteration

After the instanciation and before the interpolation, all member of the template are accessible and modifiable like in a table.

Typically, data from the model are added after the instanciation.

The interpolation

The interpolation is done by calling the template with one string parameter which is the keyname of the entry point template.

The interpolation returns a string as result and an optional string which contains some error messages.

The 4 primitives in template

The data could be in the form of foo.bar.baz.

1. Attribute reference

The syntax is ${data[; separator='sep'][; format=name]}.

An undefined data produces an empty string.

The option format allows to specify a formatter function, which is a value of the template referred by the key name. The default behavior is given by the standard Lua function tostring.

When data is a table, the option separator is used as parameter of table.concat(data, sep). This parameter could be simple quoted or double quoted, and it handles escape sequence like Lua. The characters { and } are forbidden, there must be represented by a decimal escape sequence \ddd.

local CodeGen = require 'CodeGen'

tmpl = CodeGen {
    call = "${name}(${parameters; separator=', '});",
}
tmpl.name = 'print'
tmpl.parameters = { 1, 2, 3 }
output = tmpl 'call'
print(output) --> print(1, 2, 3);

2. Template include

The syntax is ${name()} where name is the keyname of a chunk template.

If name is not the keyname of a valid chunk, there are no substitution and an error is reported.

3. Conditional include

The if syntax is ${data?name1()} and the if/else syntax is ${data?name1()!name2()} where name1 and name2 are the keyname of a chunk template and data is evaluated as a boolean.

4. Template application

The syntax is ${data/name()[; separator='sep']} where data must be a table. The template name is called for each item of the array data, and the result is concatened with an optional separator.

The template has a direct access in the item, and inherits access from the caller. If the item is not a table, it is accessible via the key it.

Examples

A generic template for rockspec.

-- file: rockspec.tmpl
return CodeGen {
    rockspec = [[
package = '${name}'
version = '${version}-${revision}'
${_source()}
${_description()}
${_dependencies()}
]],
    _source = [[
source = {
    url = ${_url()},
    md5 = '${md5}',
    dir = '${name}-${version}',
},
]],
    _description = [[
description = {
    ${desc.summary?_summary()}
    ${desc.homepage?_homepage()}
    ${desc.maintainer?_maintainer()}
    ${desc.license?_license()}
},
]],
    _summary = 'summary = "${desc.summary}",',
    _homepage = 'homepage = "${desc.homepage}",',
    _maintainer = 'maintainer = "${desc.maintainer}",',
    _license = 'license = "${desc.license}",',
    _dependencies = [[
dependencies = {
${dependencies/_depend()}
}
]],
    _depend = [[
    '${name} >= ${version}',
]],
}

A specialization for all my projects.

-- file: my_rockspec.tmpl
local parent = dofile 'rockspec.tmpl'

return CodeGen({
    lower = string.lower,
    _tarball = "${name; format=lower}-${version}.tar.gz",
    _url = "'http://cloud.github.com/downloads/fperrad/${name}/${_tarball()}'",
    _homepage = 'homepage = "http://fperrad.github.com/${name}",',
    desc = {
        homepage = true,
        maintainer = "Francois Perrad",
        license = "MIT/X11",
    },
}, parent)

And finally, an use for this project.

CodeGen = require 'CodeGen'

local rs = dofile 'my_rockspec.tmpl'
rs.name = 'lua-CodeGen'
rs.version = '0.1.0'
rs.revision = 1
rs.md5 = 'XxX'
rs.desc.summary = "a template engine"
rs.dependencies = {
    { name = 'lua', version = 5.1 },
    { name = 'lua-testmore', version = '0.2.1' },
}
print(rs 'rockspec')

The output is :

package = 'lua-CodeGen'
version = '0.1.0-1'
source = {
    url = 'http://cloud.github.com/downloads/fperrad/lua-CodeGen/lua-codegen-0.1.0.tar.gz',
    md5 = 'XxX',
    dir = 'lua-CodeGen-0.1.0',
},
description = {
    summary = "a template engine",
    homepage = "http://fperrad.github.com/lua-CodeGen",
    maintainer = "Francois Perrad",
    license = "MIT/X11",
},
dependencies = {
    'lua >= 5.1',
    'lua-testmore >= 0.2.1',
}