Templaty Language Reference¶
This page explains in detail how the Templaty language is structured.
Sample Templaty File¶
The following example demonstrates most of Templaty’s capabilities. It generates a C program that prints a predefined array of random numbers.
{% if 'author' in globals() %}
// Copyright {{year}} {{author}}
// All rights reserved
{% endif %}
{!
from random import randint
MAX_RANDOM = 10
!}
int randomData[] = [
{% join i in range(0, random_data_length) with ',' %}
{{randint(0, MAX_RANDOM)}}
{% endjoin %}
]
int main() {
{% for i in range(0, random_data_length) %}
{% noindent %}
#if defined(SAMPLE_DEBUG)
{% endnoindent %}
fprintf(stderr, "Going to write random number #{{i}} ...");
{% noindent %}
#endif // SAMPLE_DEBUG
{% endnoindent %}
printf("%i\n", randomData[{{i}}])
{% endfor %}
}
Regular Text¶
Regular text is just passed along as-is, without any preprocessing done to it.
Expressions¶
Expressions are used in the control-flow statements and can also appear
anywhere in the template to generate specific text. If you want to add an
expression to a piece of text, you have to wrap it between {{
and }}
,
like so:
// Did you know that 25 + 17 equals {{25 + 17}}?
Variable References¶
Any identifier that is not a keyword may be used to reference one of the built-in expressions or an expression that was defined elsewhere in the template.
/* This file was generated on {{now}} by {{author}}. */
Regular Code¶
At any time, you can write regular Python code using {!
and !}
.
The text surrounding the code block will be automatically stripped so that no excessive newlines are generated.
{!
my_var = 42
!}
The answer is {{my_var}}.
Control Flow¶
The following statements are used in Templaty to change what text is written when the template is run. Most of them should be very familiar, as they resemble the constructs found in most regular programming languages.
Conditional Code Generation¶
Conditionals are used to generate a piece of text depending on whether a given predicate is met. Just like in regular programming languages, you can have conditionals with multiple alternatives or none at all.
{% if header %}
// This header is only generated if 'header' is set to true
// in the environment. You can create a JSON file that contains
// this variable, or define it somewhere in the template itself.
{% endif %}
The else
-directive is used to provide some text when no predicate matched,
like so:
{% if long_header %}
// This file does stuff. It is really cool because it first does
// stuff and then some more stuff. Once the stuff is finished, it calls
// a 'thing' to do other stuff.
{% else %}
// This file does stuff.
{% endif %}
Generating Repititions¶
Templates for code generation wouldn’t be particularly useful if we couldn’t
use them to auto-generate repetitive code. The for
-statement is one of the
simplest methods for generating (possibly huge) amounts of code.
IDENTITY_MATRIX = [
{% join i in range(0, 10) with ',' %}
[{% join j in range(0, 10) with ',' %}{% if j == i %}1{% else %}0{% endjoin %}]
{% endjoin %}
]
Generates the following code:
IDENTITY_MATRIX = [
[1,0,0,0,0,0,0,0,0,0],
[0,1,0,0,0,0,0,0,0,0],
[0,0,1,0,0,0,0,0,0,0],
[0,0,0,1,0,0,0,0,0,0],
[0,0,0,0,1,0,0,0,0,0],
[0,0,0,0,0,1,0,0,0,0],
[0,0,0,0,0,0,1,0,0,0],
[0,0,0,0,0,0,0,1,0,0],
[0,0,0,0,0,0,0,0,1,0],
[0,0,0,0,0,0,0,0,0,1]
]
Indentation Control¶
A feature of Templaty that stands out is how it handles indentation and whitespaces. Because the code generated by Templaty might be read by other developers, special care has been taken that spaces and newlines are correctly generated.
Consider the following template code for a Python program:
def main():
{% if enable_print_foo %}
foo = get_foo();
if foo == 2:
print("Foo is two!")
else:
print("Foo is not two :(")
{% endif %}
Some users might be surpised to learn that this template generates the following code:
def main():
foo = get_foo()
if foo == 2:
print("Foo is two!")
else:
print("Foo is not two :(")
However, the rules are quite natural. Templaty takes the indentation of the
leading {%
and applies it to each line that is generated within the block.
In order to make sure there isn’t too much indentation, Templaty removes any
indentation that is shared by all the lines inside the statement block.
This rule also works when nesting multiple statements inside each other. For example:
POINTS = [
{% join i in range(0, 10) with ',' %}
{% if use_vector %}
Vec({{i}}, {{i}})
{% else %}
({{i}}, {{i}})
{% endif %}
{% endjoin %}
]
A call to this program with use_vector
set to True
could result in the
following code:
POINTS = [
Vec(7, 3),
Vec(4, 9),
Vec(9, 1),
Vec(3, 2),
Vec(4, 5),
Vec(8, 3),
Vec(5, 8),
Vec(1, 8),
Vec(1, 6),
Vec(2, 1)
]
The setindent
-block¶
The special statement {% setindent indent_level %}
can be used to override
the auto-inferred indentation level.
int main() {
{% noindent %}
#ifndef FOO
{% endnoindent %}
fprintf(stderr, "Warning: FOO was not defined at compile-time.");
{% noindent %}
#endif // #ifndef FOO
{% endnoindent %}
}
Output:
int main() {
#ifndef FOO
fprintf(stderr, "Warning: FOO was not defined at compile-time.");
#endif // #ifndef FOO
}
If you need even more control over the indentation level, you can make use of
the special indent()
function. When called with no arguments, it increases
the indentation with one level for the rest of the file. When called with an
integer, it will set the indentation level to that number.
if not prompt("Attempt no 1"):
{% for i in range(2, 3) %}
{! indent() !}
if not prompt("Attempt no {{i}}"):
{% endfor %}
error("I gave up.");
{! clearindent() !}
The above snippet will generate the following code:
if not prompt("Attempt no 1"):
if not prompt("Attempt no 2"):
if not prompt("Attempt no 3"):
error("I gave up.")
Built-in Variables and Functions¶
Templaty contains a growing number of built-in functions and variables to make it easy for programmers to write their templates without much hassle. The folllowing is an incomplete list of functions and variables that are supported out-of-the-box.
v |> f
A special operator that applies a given function
f
tov
.This operator allows you to write code such as:
'FooBarBaz' |> snake |> upperWhich is equivalent to the following code:
upper(snake('FooBarBaz'))Note the similarity with Jinja2’s filter concept, with the difference that Templaty implements it as a regular operator rather than a syntactic extension.
upper(text)
Simply converts the given text to uppercase, using Python’s standard behaviour.
lower(text)
Simply converts the given text to lowercase, using Python’s standard behaviour.
indent(level)
Warning
This feature is currently under development.
When called with no arguments, it increases the indentation with one level for the rest of the file. When called with an integer, it will set the indentation level to that number.
if not prompt("Attempt no 1"): {% for i in range(2, 3) %} {! indent() !} if not prompt("Attempt no {{i}}"): {% endfor %} {! indent(0) !} error("I gave up.");The above snippet will generate the following code:
if not prompt("Attempt no 1"): if not prompt("Attempt no 2"): if not prompt("Attempt no 3"): error("I gave up.")
snake(name)
Converts an identifier to snake-case.
This function should work on most common use-cases. For more complex ones, you probably should write your own logic.
snake('FooBarBaz')Output:
FOO_BAR_BAZ
now
A variable holding the time the generator started, formatted using some default rules.
a + b
Add two expressions to each other.
a - b
Subtract two expressions from one another.
a * b
Multiply two expressions with each other.
a / b
Dividea
byb
, returning the result.
a % b
Find the remainder after the division of the two given numbers.