< April 2007 >
1 2 3 4 5 6 7
8 91011121314


This is mtemplate, a very simple text templating system. It has been written for some small code generators ( cfsm in particular), but you might find it useful too. It may be used for generating code (like I do), HTML, configuration files or any other text format.

It is built on top of a simple generic type library for C (mobject), loosely modelled on Python. The fundamental types supported are "None", arrays, dictionaries (string-keyed lookups), strings and integers. Types may be nested inside multi-valued types; e.g. arrays may contain other arrays as an element, dictionaries may contain dictionaries of arrays, etc. The library also supports iteration over arrays and dictionaries.

The template language is designed to be simple but useful. Template directives are enclosed in double curly braces, e.g. "{{else}}". Variable substitution is performed by placing the name of the variable in curly braces, for example "{{users.djm.history[5]}}". mtemplate uses the mobject namespace functions for variable substitutions - the syntax is similar to that of Python or Javascript.

The directive opening sequence itself can be inserted using the "{{{{}}" escape sequence.

Comments are supported as "{{#this is a comment}}" and are ignored when generating output.

Conditional statements are supported with the "{{if CONDITION}}", "{{else}}" and "{{endif}}" directives. E.g.

{{if a.b}}text is a.b is true...
{{else}}more text if a.b is false...{{endif}}

The 'CONDITION' to an 'if' directive must be a valid namespace variable reference, which is evaluated to a boolean using one of the following rules:

Integer variables
True if integer is non-zero
String variables
True if string is not empty
Array variables
True if array has one or more elements
Dictionary variables
True if dict has one or more elements

Loops are supported too, over libmobject arrays and dictionaries, using the "{{for}}" and "{{endfor}}" keywords. For example:

{{for v in a.b}}

The loop variable ('v' in this example) is placed in the namespace with two members, 'key' and 'value'. When iterating over a libmobject array, 'key' will contain the array index (starting from zero), and 'value' will contain the array element at that position. When iterating over a libmobject dictionary, 'key' will contain the dictionary key and 'value' the value referenced by that key. Please note that dictionary iteration does not guarantee any particular ordering.

Using mtemplate in your programs looks like this:

struct mtemplate *tmpl;
struct mobject *namespace, *tmp;
char errbuf[1024];

/* Parse a text string into a template program */
if ((tmpl = mtemplate_parse(template_text, errbuf, sizeof(errbuf))) == NULL)
        errx(1, "mtemplate_parse: %s", errbuf);

/* Initialise the variable namespace */
if ((namespace = mdict_new()) == NULL)
        errx(1, "mdict_new failed");

/* Add some values to the namespace (ignoring errors) */
mdict_insert_ss(namespace, "program_name", "example"); /* String */
mdict_insert_si(namespace, "program_version", 2008);   /* Integer */
mdict_insert_sa(namespace, "features");                /* Empty array */
mdict_insert_sd(namespace, "credits");                 /* Empty dictionary */

/* Fill in the array and dictionary */
tmp = mdict_item_s(namespace, "features");
marray_append_s(tmp, "hydrocoptic marzelvanes");
marray_append_s(tmp, "baseplate of prefamulated ammulite");
tmp = mdict_item_s(namespace, "credits");
mdict_insert_ss(tmp, "author", "Unknown");
mdict_insert_si(tmp, "Copyright", 2007);

/* Run the template with this namespace, and send the output to stdout */
if (mtemplate_run_stdio(tmpl, namespace, stdout, errbuf, sizeof(errbuf)) == -1)
        errx(1, "mtemplate_run_stdio: %s", errbuf);

The "mtc" program can process mtemplate templates from the commandline. E.g.

mtc -Dusers.djm.name="Damien Miller" -Dusers.djm.uid=12345 \
    -Dusers.djm.groups[0]=djm -Dusers.djm.groups[1]=users \
    -o example.out example.x

mtemplate is licensed under a ISC/BSD licence. Please see the LICENSE file in the source for details.


Tue, 17 Apr 2007: mtemplate in CVS

mtemplate is now in CVS. This is a small and simple text templating system for C programs, based around a simple generic type library with Python/Javascript-style variable resolution (e.g. it supports nesting of arrays and dictionaries and a lookup syntax like "dict.member.array[10].blah").

mtemplate is intended to be easy to use and have no external dependencies. The code in CVS is stable enough for me to have rewritten cfsm using it. At present it is only available in CVS, but if it interests you then please mail me and I will consider making a release.

[permanent link]


mtemplate is available here: