Template Syntax

General#

The main purpose of Imperia View templates is to create HTML. But you can easily abuse the template processor to create other text or even binary formats. However, the design goals of the template syntax show a strong focus on HTML creation:

  • Imperia view templates can be written in valid HTML or XHTML
  • When viewed in a browser, templates will preserve all original markup.

Imperia View templates (from now on simply templates) consist consist of a series of chunk, and each chunk can be one of:

  • a template directive,
  • a comment, or,
  • data, which is everything else.

Template directives get replaced by their results, comments are discarded, and all other data is copied verbatim to the output.

Template Directives#

Template directives start with the start tag {% and end with the end tag %}, for example:

<title>{% title %}</title>

Extended start tags can be used in order to control, how adjacent whitespace is handled. See the following summary:

Start tags:

  • {% All whitespace left of the tag is preserved.
  • {%- All whitespace left of the tag is consumed, but only to the beginning of the line.
  • {%= All whitespace left of the tag is consumed, also across line-bound-aries.

End tags:

  • %} All whitespace right of the tag is preserved.
  • -%} All whitespace right of the tag is consumed, until the next line-break (which is also consumed).
  • =%} All whitespace right of the tag is consumed, also across line-bound-aries.

For the following examples, assume that the variable title contains the value “News Of the World” .

<title>
    {% title %}
</title>

This will produce the output:

<title>
News Of the World
</title>

As you can see, only the part between {% and %} is affected.

<title>
    {%- title -%}
</title>

produces:

<title>
News Of the World
</title>

Note the newline right of the line-eating version of the closing tag has also been consumed.

<title>
    {%= title =%}
</title>
<h1> {%= title =%} </h1>

produces:

<title>News Of the World</title>
<h1>News Of the World</h1>

Probably this last version will normally be your choice, because it is best suited for removing all traces of the original template instructions. If you are not satisfied with the results, simply experiment with the other two versions.

Especially in nested structures the effect of the whitespace modifiers is easier to understand, if you realize that they actually modify the template. The whitespace that they consume is never visible to the template processor. Look at this simple example.

{%= #if(num == 1) =%}
    one
{%= #else =%}
    many
{%= #endif =%}

This is absolutely equivalent:

{% #if(num == 1) %}one{% #else %}many{%#endif%}

The idea is to keep both the templates and the output readable. After a little time, you will get used to the concept, and will never want to miss it.

Inside of template directives, whitespace is always insignificant, except for quoted strings. Therefore, all of the following examples produce the same output:

{% #include ('foobar.html') %}
{%#include('foobar.html')%}
{%#include('foobar.html')%}

Comments#

Comments start with <!--% and end at the first -->. They cannot be nested.

As with template directives, extended start and end delimiters exists that control how adjacent whitespace is treated.

Template comments are also valid HTML and XML comments. The difference is that template comments will be discarded, and are not visible in the output. Comments without the percent sign are left untouched, and will also be visible in the output.

As with template directives, extended start and end delimiters exists that control how adjacent whitespace is treated.

Start tags:

  • <!--% All whitespace left of the commment is preserved.
  • <!--%- All whitespace left of the commment is consumed, but only to the beginning of the line.
  • <--%= All whitespace left of the commment is consumed, also across line-bound-aries.

End tags:

  • %--> All whitespace right of the commment is preserved.
  • -%--> All whitespace right of the commment is consumed, until the next line-break (which is also consumed).
  • =%--> All whitespace right of the commment is consumed, also across line-bound-aries.

Control and Loop Constructs#

Conditionals With #if#

{% #if(condition) =%}
    arbitrary code
{% #endif %}

If condition yields a truth value, everything until the matching {% #endif %} is copied to the output. Otherwise, nothing is copied to the output. See Section Conditions for details about what a condition is.

You can also use alternatives:

{% #if(condition) %}
    arbitrary code
{% #else %}
    arbitrary code
{% #endif %}

If condition yields a truth value, everything until the matching {% #else %} is copied to the output. Otherwise, everything between {% #else %} and {% #endif %} is copied to the output.

Finally, you can check multiple conditions at once:

{% #if(condition1) %}
    alternative 1
{% #elsif(condition2) %}
    alternative 2
{% #elsif(condition3) %}
    alternative 3
{% #else %}
    arbitrary code
{% #endif %}

Now, all conditions are checked subsequently, and only the code for the branch with the first condition that yields true is copied to the output.

You can also omit the else branch:

{% #if(condition1) %}
    alternative 1
{% #elsif(condition2) %}
    alternative 2
{% #endif %}

Now, if none of the conditions yield a truth value, no output is generated.

Loops with #for#

You can produce output multiple times in a loop:

{% #for(i = 1; i < 4; ++i) %}
i has the value {% i %}.
{% #end %}

This will produce the output:

i has the value 1.
i has the value 2.
i has the value 3.

The general syntax for the for loop is:

{% #for(init, condition, action) %}
Loop body
{% #end %}

Inititally, the statement init (see Statements) is executed.

Then, for each loop pass the loop condition is checked. If it yields a truth value, the body of the loop is copied to the output, and the statement action is executed. If condition does not yield a truth value, the loop is terminated.

Loops with #foreach#

The foreach loop allows you to iterate over a list:

{% #foreach husband ('Tom', 'Dick', 'Harry') %}
And then there was {% husband %}.
{% #end %}

This will produce:

And then there was Tom.
And then there was Dick.
And then there was Harry.

The general syntax for the foreach loop is:

{% #foreach loopvar (list) %}
    Loop body
{% #end %}

Each value in list (see Lists for details about what a list is) is copied subsequently into the loop variable loopvar. The loop variable loopvar can be any lvalue, i. e. everything that is allowed on the left hand side of an assignment (see Assignments for details about that), with one exception: You are not allowed to use parentheses in the definition of the loop variable, hence no method calls with arguments are possible. This is due to a short-coming in the parser, and will probably not be fixed.

However, you can still use complicated constructs for the loop variable:

{% #foreach foo.bar[baz].bazoo (list) %}
Loop body.
{% #end %}

This will assign to whatever the loop variable points to.

Loops with #while#

{% #while(condition) %}
    Loop body
{% #end %}

For each loop pass, the condition gets evaluated. If it yields a truth value, the loop body gets copied to the output, otherwise the loop is exited.

See Conditions for details about conditions.

##break Out Of Loops#

{% #while(condition) %}
    Loop body
    {% #if(condition) %}
        {% #break %}
    {% #endif %}
{% #end %}

End the current loop immediately. For loops (see Loops with #for) the loop action is not executed.

If you pass a parameter to #break, it does not only terminate the innermost loop, but the number of loops you specified.

Plug-ins#

Plug-in Calls#

Plug-In calls can either have a body, or they can be empty.

  • Example for a plug-in call without a body:

    {% ##plug_in_name(list) %}

  • Example for a plug-in call with a body:

    {% #plug_in_name(list) %} Body of the plug-in. {% #end %}

A regular plug-in call is introduced by a double hash-sign ##, followed by the plug-in name, optionally followed by parameters in parentheses. Plug-ins that are called with a body, only have a single hash-sign #; the body is terminated with an #end directive.

The optional argument to a plug-in call is a list (see Lists for details about what a list is).

If a plug-in doesn't take arguments, you can safely omit the parentheses. The following two examples are equivalent:

{% ##embedded() %}
{% ##embedded %}

Standard Plug-Ins#

A number of plug-ins ship with Imperia. Others may be available at the discretion of the system administrator.

##include#

Examples:

{% ##include('path/to/includedfile', var1 => val,
        var2 => 'other') %}
{% ##include(common.template, var1 => val, var2 => 'other' %}

General syntax:

{% ##include(template_file[, VARIABLES]) %}

The first argument is always the filename to be included. That can either be a literal string (first example) or a variable (second example). Paths are always resolved relative to the include directories (see Search Paths). It is not possible to pass absolute filenames.

You can optionally add a sequence of key value pairs in the argument list. The keys are the names of variables that will only be visible in the included template and its children. The values (of the key-value pairs) are the values of the variables. If you have to distinguish whether a variable was set elsewhere before, or if it was passed in the include statement itself, you can inspect the virtual variable "local" (see Local Variables).

Passing Data To the Includer#

The fancy syntax might fool you into thinking that the optional arguments are actually assignments (see Assignments). They are not! All of them are just ordinary strings.

The following examples are therefore invalid:

<!--%= Syntactically incorrect!!! =%-->
{% ##include('path/to/includedfile',
         common.data.sth => 23) %}
{% ##include(common.template, common.array[23] => 15 %}
{% ##include(common.template, 'common.array[23]', 15 %}

The first two lines are even syntax errors, because you can only use the fat comma (see The Fat Comma Operator (=>)) if the left-hand side operator is a valid variable name or number.

Compare the second and third line: The third line will create a variable with the fancy name common.array[23] and will assign it the value 15. But this is probably not what you meant.

The included file gets a complete copy of all variables defined at the point where the file gets included. In the included template all variables have therefore the same value as in the includer, however — since they are copies — assigning to these variables does not affect the variables in the includer.

If you still want to pass data to the includer, you can use a trick. If the value of a variable happens to be a reference (see References), the reference in the included template points to the same thing as in the includer, and if you assign something there, this will also be visible in the included file.

Read section “References” for details, or just take the following example.

Say, this is the content of parent.html:

1 === parent ===
2 {%= v1 = 'parent'; v2.sth = 'parent'; =%}
3 v1: {% v1 %}, v2.sth: {% v2.sth %}, v3: {% v3 %}
4 {%- #include ('child.html', v3 => 'passed') =%}
5 v1: {% v1 %}, v2.sth: {% v2.sth %}, v3: {% v3 %}
6 === End of parent ===

Now let's look at child.html, the included template:

1 === child ===
2 v1: {% v1 %}, v2.sth: {% v2.sth %}, v3: {% v3 %}
3 {%= v1 = 'child'; v2.sth = 'child'; =%}
4 v1: {% v1 %}, v2.sth: {% v2.sth %}, v3: {% v3 %}
5 === End of child ===

The output generated will look like this:

1 === parent ===
2 v1: parent, v2.sth parent, v3:
3 === child ===
4 v1: parent, v2.sth: parent, v3: passed
5 v1: child, v2.sth: child, v3: passed
6 === End of child ===
7 v1: parent, v2.sth: child, v3:
8 === End of parent ===

Let's go through the output step by step:

  • In the first line, you see the expected output, all variables have the values you assigned to them. Since you never assigned anything to v3, its value is undefined.
  • Inside the included template, all variables have initially the same value as in the parent template. With the exception of v3, which has the value that you passed in the include call has a parameter. In the same fashion, you could also locally overwrite a variable from the parent.
  • In the next line, you see the values, after we have overwritten the variables in the child. This is also clear.
  • Now to the last line, which comes from the includer parent.html. You can see that v1 has been unaffected by the assignment to the variable in the included file. It was just a copy, and the original variable is untouched.
    The case is different with v2.sth. Although the child had its private copy of the variable, the thing that v2.sth points to, is the same as in the parent. Therefore, the assignment in the included template actually changed the target of the reference, and this change is visible everywhere, since that target only exists once.
    And finally v3 only existed in the included template, and is therefore still resp. again undefined.

You should not worry too much, if you did not understand that completely. References are a complicated matter in all programming languages. For most purposes it is usually sufficient to remember a simple rule:

Assigning to a variable with a dot (or angle brackets []) is globally visible, assignments to variables without a dot or angle brackets are only locally visible (Variables can start with a dot. This first dot does not count in this rule of thumb.).

Local Variables#

Sometimes it is crucial to know whether a certain variable was passed explicitely in the include statement.

Take the following (real-world) example:

1 <!--%= link.html -->
2 {%=
3 if (!defined(id)) {
4   id = gen_id();
5    }
6 =%}
7 <a href="{% url %} id="{% id %}">{% content %}</a>

The idea is pretty clear: It should be optional for the includer to pass an id argument. If it is present, the id passed to the included view is used, otherwise a new one will be generated.

These are the intended usages:

1 {%=
2   {% ##include('link.html', id => 'this_special_link' %}
3   {% ##include('link.html') %}
4 =%}

The second usage has a pitfall, though:

  • What if the variable id has been defined elsewhere in the includer, or even in an includer of the includer?
  • And what, if it was actually not intended as the id attribute of the included file link.html?

For that purpose, each included file gets a local copy of its own argument hash in the variable local.

The above file link.html should therefore look like this instead:

1 <!--%= link.html -->
2 {%=
3   if (!defined(local.id)) {
4   id = gen_id();
5   } else {
6   id = local.id;
7   }
8 =%}
9 <a href="{% url %} id="{% id %}">{% content %}</a>

It is okay to assign to local variables but since every included file gets its own fresh copy, these variables are never passed to included files. This is considered a feature.

##source#

Examples:

{% ##source('path/to/includedfile', var1 => val,
    var2 => 'other') %}
{% ##source(common.template, var1 => val, var2 => 'other' %}

General syntax:

{% ##source(template_file[, VARIABLES]) %}

This does exactly the as ##include but all macros (see #macro) and variables defined or changed in the included file are also visible in the file that includes them. In a way this does the same as the source (resp. the dot .) in a standard Unix shell.

In other words, the effect of using ##source instead of ##include is that the included file is treated as it was directly embedded in the file that includes it.

Therefore, if you want a macro or a variable defined in another file to be visible in the current file, you have to source that file, not include it.

##xinclude#

Examples:

{% ##xinclude('path/to/includedfile', var1 => val,
    var2 => 'other') %}
{% ##xinclude(common.template, var1 => val, var2 => 'other' %}

General syntax:

{% ##xinclude(template_file[, VARIABLES]) %}

This does exactly the as ##include but failure is handled gracefully. If the included file does not exist, no error is produced, and execution simply continues.

The ##xinclude directive is a simple way of adding options for customizations. For example, if you have a list of external links, people can extend that list if you ##xinclude a file with more links.

##xsource#

Examples:

{% ##xsource('path/to/includedfile', var1 => val,
    var2 => 'other') %}
{% ##xsource(common.template, var1 => val, var2 => 'other' %}

General syntax:

{% ##xsource(template_file[, VARIABLES]) %}

This does exactly the as ##source but failure is handled gracefully. If the sourced file does not exist, no error is produced, and execution simply continues.

The ##xsource directive is a simple way of adding options for customizations. For example, you can ##xsource a file where people can override variables or macros that you defined before the ##xsource directive. If the file exists, the definitions present in the file are taken from there, otherwise (or for those parts that have not been overridden) the default ones are used.

#macro#

Macros can be thought of as inline include directives. You can access their content by calling them (see call(FUNCTION[, ARGUMENTS...]).

{% #macro(some_name) %}
Arbitrary <em>HTML</> or {% template %} code.
{% #end %}

More <em>HTML</> or {% template %} code.

{% call('some_name', arg1, arg2, arg3) %}

Macros have the same scope as variables. Therefore, if you want to use macros defined in another file, you have to include that file with ##source, not with ##include.

Scope of Variables#

Variables in macros are evaluated, when the macro is called, not where it is defined:

{% var1 = 'before' %}

{% #macro(scope_test) %}
The variable was set {% var1 %} the macro definition.
{% #end %}

{% var1 = 'after' %}

{% call('scope_test') %}

This produces the output:

The variable was set after the macro definition.

Variables defined inside of a macro definition, are not visible outside of the macro:

{% #macro(scope_test) %}
{% var1 = 'inside' %}
{% #end %}

{% var1 = 'outside' %}

{% call('scope_test') %}

The variable was set {% var1 %} of the macro definition.

This produces the output:

The variable was set outside of the macro definition.

Argument passing#

If you want to pass arguments to a macro, you can always define variables before the macro call, and they will be visible inside of the macro (see Scope of Variables):

{% #macro(copyright) %}
Copyright {% year %}, {% company %}, all rights reserved.
{% #end %}

{%
    year = 2018;
    company = 'Imperia AG';
 %}

{% call('copyright') %}

This will work as expected, but is not exactly an elegant solution. You can instead pass the arguments in the call:

{% #macro(copyright) %}
Copyright {% argv[0] %}, {% argv[1] %}, all rights reserved.
{% #end %}

{% call('copyright', 2010, 'Imperia AG') %}

Arguments are present in the special variable argv that is only visible inside of the macro.
This variable is an array reference. If you prefer named arguments in the Perl style, you can achieve that as follows:

1 {% #macro(copyright) %}
2   {% argv = {@{argv}} %}
3 Copyright {% argv.year %}, {% argv.company %}, all rights reserved.
4 {% #end %}
5
6 {% call('copyright', year => 2010, company => 'Imperia AG') %}

The array reference is coerced into a hash reference in line 2, and can be indexed by names as shown in line 3.

#assign#

This plug-in lets you assign the output of a template snippet to a variable:

{% #assign(saved.snippet) %}
Arbitrary <em>HTML</> or {% template %} code.
{% #end %}

This will assign the processed output of the body of the plug-in call to the variable saved.snippet. So, whatever you put into the body of the plug-in call gets evaluated and will be assigned to the variable (or to the location) you specify. That location can be an arbitrary (L-value](views.template-syntax/#l-value) .

If you access to the output, go ahead:

{% saved.snippet %}

Voilà, you have it! Actually, probably not, because the output will be HTML escaped. Usually you will want to avoid that by enforcing raw output:

{% raw(saved.snippet) %}

The feature is particularly useful, if you want to hide some HTML in JavaScript. You could then write something like:

{% #assign (code) %}
Arbitrary <em>HTML</em> content.
{% #end %}
<script>document.write('{% escape_js(code) %}')</script>
<noscript>Your browser does not
understand JavaScript!</noscript>

Now you can simply code the HTML in your template as usual without bothering about JavaScript escaping. But in the generated page, it will be hidden in JavaScript.

A second version with a double-hash sign should exist. This version will include an external file, and assign the result of that processing. This version should also have an argument list.

##import#

{% ##import ('Math') %}
{% ##import ('CGI', 'escape_uri', 'unescape_uri') %}

You use the import plug-in in order to import function definitions into the templating system, so that you can use them in expressions.

The first example would import all functions from the module Math. The second example would import from the module CGI, but only the functions escape_uri() and unescape_uri(). Which modules with which functions are available is at the discretion of the administrator. The modules that ship with imperia are documented in The ViewImport API.

If you import a function that already has already been imported, the new definition will replace (overwrite) the old one globally.

Important

If you do not have full control over the content of your templates, you should consider disallowing this plug-in (see disallowPlugIns(PLUG_IN[, PLUG_INS...])).

##postimport#

This plug-in does almost exactly the same as the import plug-in, see above. The only difference is that imported functions will never overwrite already existing definitions.

Important

If you do not have full control over the content of your templates, you should consider disallowing this plug-in (see disallowPlugIns(PLUG_IN[, PLUG_INS...])).

##perl#

Examples:

{% ##perl('path/to/script.pl',
    var1 => val, var2 => 'other' %}
{% ##perl(scriptname, var1 => val, var2 => 'other' %}

This will search for the file path/to/script.pl in the standard search path, and evaluates it by the Perl interpreter. The return value of the code will be interpolated in the output.

The first argument is always the filename of the script to be executed. That can either be a literal string (first example) or a variable (second example). Paths are always resolved relative to the include directories (see Search Paths). It is not possible to pass absolute filenames.

You can optionally add a sequence of key value pairs in the argument list. The keys are the names of variables that can be accessed in the Perl script. The values (of the key-value pairs) are the values of the variables. Please see ##include for the exact semantics of the list.

The Perl variable @ARGV will contain the Imperia::View::PlugIn object that corresponds to the plug-in invocation. All variables, including those that you passed as arguments to the call, are visible in the member _data of the view object:

use strict;
my $foo = $ARGV[0]->{_data}->{foo};
my $bar = $ARGV[0]->{_data}->{bar};
return “Foo is $foo and bar is $bar.”;

If you are a Perl programmer, it might look a little odd to you, that you return from something that looks like a script. But actually your code is executed inside an eval {...} and therefore a return is okay. Beware though, that in the absence of an explicit return statement, Perl will use the value of the last statement as the return value:

use strict;
$ARGV[0]->{_data}->{current_time} = localtime;

The last statement is the assignment, and the “value” of the assignment is the value of the right-hand-side. Although you probably just wanted to set a value in the data hash, you will see this value in the output of the template processor. If this is not what you want, simply add an explicit return statement, and return either nothing or the empty string.

Important

If you do not have full control over the content of your templates, you should consider disallowing this plug-in (see disallowPlugIns(PLUG_IN[, PLUG_INS...])).

The X Mini Language#

Whereever you can use parameters, for example the parameters of a plug-in call, you actually use the X mini language. This language bears similarities with Perl and C. Its main goal is to be easy to understand, also by non-programmers, and still give you a warm feeling of familiarity, when you can already program in Perl, C, JavaScript or similar languages.

But X is not only used in plug-in parameters, but can also be used directly. If a template directive does not start with a hash sign (#), it is considered an interpolation, and the content of the directive is evaluated with X. This can be as simple as a variable interpolation:

<h1>{% page.title %}</h1>

But it can also be a complicated computation:

The faculty of {% number %} is:
{%
faculty = 1;
for (i = 2; i <= number; i++) {
    faculty *= i;
}
faculty;
%}

Here the faculty of the variable number is calculated.

Statements#

X code consists of a number of statements or loop-like constructs. Statements are separated by a semi-colon. Loop-like constructs are regular loops, conditionals and blocks.

The value of X code, i. e. what is interpolated in the template output, is the value of the last statement. If your code terminates from a return statement, it is the value of the expression after the return, which is possibly empty.

Comments#

Comments start with a /* and are terminated by */. They can span over multiple lines but they cannot be nested.

Example:

{%=
    /*
     * Calculate the value.
     */
    a = b * c;

    /* Now output the result. */
    a;
=%}

Important

The double slash (//) does not start a single-line comment like in Javascript, C++, or Java!

The following example will maybe not do what the author expects:

{%=
    a = b // assign
=%}

Conditions#

The plug-in like template directives like #if, #elsif, #while, and the middle statment of #for loops do not take arbitrary X code as a parameter, but a condition. A condition is just one single statement. It is always evaluated in boolean scalar context. The same applies to the corresponding parameters of the X constructs if, elsif, while, and the middle statement of for loops.

If the statement evaluates to a list, the number of elements of the lists is returned.

Expressions#

An expression is either a list or a single value:

List: {% 1, 2, 'foo', bar.baz %}
Value: {% foobar %}

A value can either be a number, a string in single or double quotes, a variable name or a term.

Single-Quoted Strings#

Strings inside single quotes are string literals. A single quote or a backslash inside a single-quoted string has to be escaped by a leading backslash. You can also escape every other character with a backslash, although this is not necessary:

{% 'single \'quoted\' s\tring' %}

This would produce the output:

single 'quoted' string

Double-Quoted Strings#

Strings inside single double quotes are string literals, and have the semantics to those in Perl. You can use the following escape sequences:

Escape sequence Value
\a Alarm (ASCII 7)
\b Backspace (ASCII 8)
\t Horizontal tab (ASCII 9)
\n Newline (ASCII 10)
\v (Note that the escape sequence \v is not supported by Perl!) Vertical tab (ASCII 11)
\f Formfeed (ASCII 12)
\e Escape (ASCII 27)
\OOO (three octal digits, for example \025 or \177) The character with the corresponding ASCII code as an octal number.
\xHH (two hex digits, for example \x1b) The character with the corresponding ASCII code as a hexadecimal number.
\x{HHHH} (four hex digits, for example \x{20ac} or \x{017D}) The character with the corresponding. Unicode code as a hexadecimal number.
\cC (an uppercase latin letter or @, [, \, ], ^, _) The corresponding control character, for example \cH for Control-H. Please note that the sequence \c\ is not CTRL-Backslash. X is stricter than Perl here.
\l Nothing, but the next character will be converted to lowercase.
\u Nothing, but the next character will be converted to uppercase.
\L Nothing, but convert all following characters until \E to lowercase.
\U Nothing, but convert all following characters until \E to uppercase.
\Q Nothing, but quote all following non-word characters (word characters are upper and lowercase letters, digits and the underscore).
\ A backslash escapes the following character, unless a valid escape sequence is formed.

Escape sequences are not yet implemented!

Assignments#

Regular Assignments#

There are two types of assignments:

{% foo = 25 %}
{% bar := 26 %}

The difference between the two types of assignments is, that the first one just assigns a value, but the value of the assignment itself is the empty string. In the second case, the value is assigned, and the value of the statement is the new value of the variable. You can see that in the output. Whereas the first line does not produce output, the second one produces “26”.

Assignments are only allowed to so-called L-value (L-value because it is allowed on the left side of an assignment). L-values are variables and references (see References):

{% var = 'scalar variable' %}
{% foo.bar.baz = 'reference' %}

Shortcut Assignments#

Just like C and perl, the X mini-language allows a shortcut for assigning the result of a binary operation:

{% var += 5 %}

This is equivalent to:

{% var = var + 5 %}

The following operators are valid:

  • +=
  • -=
  • *=
  • /=
  • **=
  • &=
  • &=
  • |=
  • ^=
  • <<=
  • <<=
  • =

  • >>=
  • &&=
  • &&=
  • ||=
  • //=

This form of the assignment does not have a return value. If you want a return value, you have to use the alternative assignment:

{% var +:= 5 %}

Loops, Control Structures and Blocks#

if Clauses#

{%
    if (condition) {
        variable = 'this value';
    } elsif (other_condition) {
        variable = 'that value';
    } else {
        variable = 'other value';
    }
%}

The semantics of if clauses is like in Perl. The braces ({...}) around the blocks are mandatory!

Both the elsif and the else branch are optional.

Please note the difference to these two pieces of template code:

{% #if (condition) %}
The condition is <em>true</em>!
{% #else %}
The condition is <em>false</em>!
{% #endif %}

and

{% if (condition) {
        state = 'true';
    } else {
        state = 'false';
}
%}
The condition is <em>{% state %}</em>!

Both snippets do the very same thing but in a different way. In the first example, we use #if/#else/#endif, template directives. Read this as, if condition is true, then copy this to the output, otherwise copy that to the output.

The second example uses the X mini language, to assign a value to a variable, and then this variable is later interpolated.

for Loops#

{%
    j = 1;
    for (i = 1; i <= 10; ++i) {
        j = j * i;
    }
%}

The semantics are the same as for the corresponding template directive, see Loops with #for. But also note the subtle difference in meaning.

foreach Loops#

{%
    j = 1;
    foreach i (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) {
        j = j * i;
    }
%}

The semantics are the same as for the corresponding template directive, see Loops with #foreach. But also note the subtle difference in meaning.

while Loops#

{%
    i = j = 1;
    while (i < 10) {
        j = j * i;
        ++i;
    }
%}

The semantics are the same as for the corresponding template directive, see Loops with #while. But also note the subtle difference in meaning.

Lists#

A list is a comma-separated list of values:

List: {% 1, 2, 'foo', bar.baz %}

The way it is evaluated, depends on the context. In so-called scalar context, when only a single value is expected, the list evaluates to the number of elements.

If a list is used in a return statement, or is the last statement of an interpolation, all list elements are concatenated with a single space character inbetween them, and this string is interpolated into the output.

Hashs#

Hashes are lists of key-value pairs:

{% ('foo', 1, 'bar', 2, 'baz', 3, 'foo', 4) %}

In this case, the hash has three keys (foo, bar, and baz). Since all keys must be unique in a hash, the last pair essentially overwrites the first one.

Many people will find it clearer, to use the so-called fat comma (see The Fat Comma Operator (=>)) in hashs:

{% (foo => 1, bar => 2, baz => 3, foo => 4) %}

The fat comma (=>) has exactly the same meaning as a normal comma. Only, the part left of the fat comma is interpreted as a single-quoted string, unless it is already in single or double quotes.

Instead of => you can also use =]. This is mainly useful, if you are concerned that your templates should be valid XML.

References#

There are three types of references, array references, hash references and scalar references.

Array References#

An array reference is a a list surrounded by angle brackets.

{% array = ['foo', 'bar', 'baz'] %}

A reference just points to something. In this case a list is created, and the variable just points to the list. You can access the items in the list by their index. You have several choices:

{% array[0] %}
{% array.1 %}
{% array['2'] %}

If you do not believe that a reference is just a pointer, try the following:

1 {%
2   array = ['foo', 'bar', 'baz'];
3   copy = array;
4   array[2] = 'bazoo';
5 %}
6 {% copy[2] %}
7 {% array[2] %}
  • Although the last element of the list is changed after the assignment of the copy, you can still see that three is only one list around.
  • In line 2, the string “baz” is assigned to element number 2 of array.
  • Since the reference is copied in line 3, element number 2 of copy also contains “baz”.
  • In line 4 element number 2 of array is overwritten with “bazoo”.
  • And in the output (lines 6 and 7) you can see that copy and array point to the same thing.
    Although element number 2 was overwritten after the reference was copied, you always see the same string “bazoo”.

You can also nest array references:

{%
    row1 = [1, 2, 3];
    row2 = [4, 5, 6];
    row3 = [7, 8,9];
    rows = [row1, row2, row3];
%}
The value in the center is {% rows[1][1] %}.

Array references can be nested arbitrarily deep.

If you want to turn an array reference into a list, you can dereference it by putting it inside curly braces after an at sign:

{% #foreach var (@{myarray}) {
The value is {% var %}.
{% #end %}

Unlike Perl, X does not allow assignment to a dereferenced list. The following will not work:

{% @{myarray} = (1, 2, 3, 4) %}

Hash References#

A hash reference is created by a hash surrounded by curly braces.

{% hash = { foo => 1, bar => 2, baz => 3 } %}
The value of foo is {% hash.foo %}.
The value of bar is {% hash['bar'] %}.
The value of baz is {% hash.'baz' %}.

You can dereference it in the same way as an array reference, but not using keys instead of numbers. It is, of course, allowed to use numbers as hash keys. In fact, you can use arbitrary strings, even the empty string.

Albeit rarely useful, you can also turn a hash reference into a list:

{% #foreach key_or_value (%{myhash}) %}
Key or value is: {% key_or_value %}.
{% #end %}

Hash references can be arbitrarily nested:

{%
    people = {
        tom => { age => 23, height 180},
        dick => { age => 25, height => 172, weight => 75},
        harry => { age => 24, height 184, weight => 90},
    };
%}
Dick is {% people.dick.age %} years old
and {% people.dick.height %} cm tall.

It often comes in handy, that hash references are automatically created, if you assign to a non-existing hash reference. The following will work, even if no variable at all has ever been defined:

{% foo.bar.baz = 123 %}
{% foo.bar.baz %}

Unlike Perl, X does not allow assignment to a dereferenced hash. The following will not work:

{% %{myhash} = (foo => 1, bar => 2) %}

Scalar References#

Scalar references are seldom useful. But they work:

{% somevar = 'string' %}
{% ref1 = \somevar %}
{% ref2 = \'other string' %}
ref1 points to {% ${ref1} %}
and ref2 points to {% ${ref2} %}.

You dereference them by putting them inside curly braces, after a dollar sign.

Unlike Perl, X does not allow assignment to a dereferenced scalar. The following will not work:

{% somevar = 'string';
    ref = \somevar;
    ${ref} = 'new string;
%}

You have to create a new reference instead.

{%
    somevar = 'string';
    ref = \somevar;
    ref = \'new string';
%}

Operators#

Precedence and Associativity#

The concept of operator precedence is know to most people from school. You know that 3 + 4 * 5 is 23 and not 35. The multiplation operator (*) has a higher precedence than the addition (+). Likewise, for associativity: When you calculate a sum, say 3 + 4 + 5, you do it in these steps:

3 + 4 + 5 = (3 + 4) + 5 = 7 + 5 = 12

The addition (the operator plus-sign) is left associative. You first calculate the term on the left side of the operator. On the other hand the assignment operator is right associative:

x = 3 + 4 + 5 x = (3 + 4) + 5 x = 7 + 5 x = 12

You first have to calculate the term on the right side of the operator.

Finally there are also non-associative operators, for example the comparison. Non-associative means that these operators cannot be cumulated:

a < b < c

This is illegal, because the operator less-than is non-associative.

The following table gives an overview about precedence and associativity in the X mini language, listing all operators in order of precedence, i. e. from highest to lowest precedence:

Associativity Operator(s)
Left (
None )
Left ++ --
Left **
None - (unary minus) + (unary plus) ! ~
Left . (dot) [] (subscript)
Left * / mod x
Left + -
None < > <= >= lt gt le ge
None == != eq ne
Left &
Left
Left &&
Right ?: (ternary)
Right = := += +:= -= -:= etc.
Left , => (fat comma)

Context and Casting#

Just like in Perl, expressions in X are either evaluated in scalar or in array context. If you evaluate a list (an array) in scalar context, the number of elements in that list is returned:

{%= arrayref = ['foo', 'bar', 'baz'];
{%= num = @{arrayref} =%}
{% num %}

The variable num will now have the value 3. However, only when derefencing array references with the operator @{...}, you will really evaluate an array in scalar context. Let's look at another example, which could be quite confusing:

{% list = 'foo', 'bar', 'baz'; %}
{% list %}

This will trigger the following output:

baz
foo

This is somewhat easier to understand, if you keep in mind, that the assignment operator (=) binds tighter than a comma. The above example is therefore equivalent to this:

{% (list = 'foo'), 'bar', 'baz'; %}
{% list %}

In fact you assign a string to the variable list, and then you use the comma-operator twice. The comma operator evaluates its left and right hand sides, the left-hand value is discarded, and the result is the right-hand value is the result of the operation. In our case: The assignment is evaluated, the result is the empty string. The right hand side is now the two string constants divided by a comma:

{% 'bar', 'baz'; %}

Generally speaking (cutting short some complications), when you have multiple expressions separated by commas, the resulting value is whatever is right of the last comma, but all other expressions are executed respectively evaluated. Now look at this:

{% list = ('foo', 'bar', 'baz'); %}
{% list %}

This produces:

baz

The expressions inside the parentheses evaluate to the last value 'baz', and this is the value of the parenthesed expression, and this gets assigned to the variable.

Another somewhat odd behavior is due to the usage of Perl as the host language of X:

{%= hashref = {foo => 0, bar => 1, baz => 2}
    hash = %{hashref}
=%}
{% hash %}

This produces something like:

3/8

This is only interesting for the developers of Perl. In brief, it gives you the information that 8 buckets have been allocated in the hash, and only three of them are in use.

Strings are automatically cast to numbers, when a number is needed in a certain situation:

{%= num = 3 + 'four' =%}
{% num %}

The variable num will now have the value 3. Since the string does not like a number, it is cast to a number, and short of any smarter idea, the number picked is zero. If the string however looks like a number, things are different:

{%= num = 3 + '4' =%}
{% num %}

The variable num will now have the value 7, since the string looks like a number.

Binary Operators#

The Addition Operator (+)#

The addition operator adds its operands:

{% 3 + 4 + 5 %}

Triggers:

12
The Subtraction Operator (-)#

The subtraction operator subtracts its operands:

{% 5 – 4 - 3 %}
{% 5 – (4 - 3) %}

Triggers:

-2
1
The Multiplication Operator (*)#

The multiplication operator multiplies its operands:

{% 3 * 4 * 5 %}

Triggers:

60
The Division Operator (/)#

The division operator divides its right-hand side operand by its left-hand side operand:

{% 9 / 3 / 2 %}

Triggers:

1.5

In fact the output is locale dependent. If your process runs in a locale environment that uses a decimal comma instead of a decimal point, the output will be 1,5 and not 1.5.

Note that results are not rounded!

The Modulo Operator (mod)#

The modulo operator calculates the modulus of its operands:

{% 9 mod 5 %}

Triggers:

4

The modulus of two numbers is the remainder of the division. 9 divided by 5 is 1, and the remainder is 4. Hence the modulus of 9 and 5 is 5.

Since the operator is left-associative, you can chain it:

{% 9 mod 5 mod 3 %}

Triggers:

1

Important

Most other programming language use a percent sign (%) as the modulo operator.
This does not work in the X mini language!

The Repetition Operator (x)#

The repetition operator repeats its left-hand side operand as many times as its right-hand side operand evaluates to in numeric (integer) context:

{% 'ha' x 3 %}

Triggers:

hahaha

You can chain it:

{% 'ha' x 3 x 2%}

Triggers:

hahahahahaha

However, if the left hand operator is a list, the list is repeated:

{%- #foreach var (('hi', 'ho') x 3) -%}
var is {% var %}.
{%- #end -% }

Triggers:

var is hi.
var is ho.
var is hi.
var is ho.
var is hi.
var is ho.
The Exponentiation Operator (/)#

The exponentation operator raises its left-hand operand to the power of the right-hand operand:

{% 4 ** 3 ** 2 %}

Triggers:

262144
The Numerical Comparison Operators (== != < > <= >=)#

These operators compare their operands numerically (non-numbers are cast to numbers first, see Context and Casting). If the comparison is true according to the semantics of classical logics, a value of 1 is produced, otherwise the empty string (which is always logically false).

There are HTML resp. XML safe variants of the operators that contain XML special characters. These operators can be used, if your template must be well-formed XML:

Normal variant Safe variant
A < B A < B
A > B A > B
A <= B A <= B
A >= B A >= B
The String Comparison Operators (eq ne le ge lt gt)#

These operators compare their operands stringwise (alphanumerically). Two strings are stringwise equal, if they are identical. A string is stringwise “less than” another string, if it collates before that other string. A string is stringwise “greater than” another string, if it collates after that other string. The collation order is locale-dependent. The result of these comparisons can therefore vary depending on the locale (more exactly the LC_COLLATE locale category) of the running process.

The operators have the following semantics:

Operation Result
A eq B true if A and B are identical strings, false otherwise
A ne B true if A and B are not identical strings, false if they are identical
A lt B true if A is stringwise less than (collates before) B, false otherwise
A gt B true if A is stringwise greater than (collates after) B, false otherwise
A le B true if A is stringwise less than (collates before) B or if the two strings are identical, false otherwise
A ge B true if A is stringwise greater than (collates after) B or if the two strings are identical, false otherwise
The Bitwise And Operator (&)#

The operator calculate the bitwise AND of its operands:

{% 85 & 51 & 15 %}

Triggers:

1

You can use &amp; as an XML safe alternative, if you want your templates to be well-formed XML.

The Bitwise Or Operator (|)#

The operator calculate the bitwise OR of its operands:

{% 85 | 51 | 15 %}

Triggers:

127
The Bitwise Xor Operator (^)#

The operator calculate the bitwise exclusive OR (XOR) of its operands:

{% 85 ^ 51 ^ 15 %}

Triggers:

105
The Logical Or Operator (||)#

The operator calculate the logical OR of its operands:

{% foo || bar %}

The expression evaluates to the value of foo if foo is true (defined and not zero or empty). Otherwise to bar if true (defined and empty). If none of them is defined it evaluates to the undefined value.

The Logical And Operator (||)#

The operator calculate the logical AND of its operands:

{% foo AND bar %}

The expression evaluates to the value of bar if fooand bar are true (defined and not zero or empty). Otherwise it evaluates to the undefined value.

The Logical Defined Or Operator (//)#
{% foo // bar %}

The expression evaluates to the value of foo if foo is defined (!). Otherwise to bar if true (defined and not zero or empty). If neither foo is defined, nor bar is true, it evaluates to the undefined operator.

The function is very useful, when you want to provide default values. See this HTML example:

<form action="{% action %}">
    <input name="age" value="{% cgi_params.age // default_values.age %}" /
</form>

Imagine that cgi_params is a hash holding the form variables sent by the browser, and default_values holds a hash filled with the default values by your application.

Say now, your application first wants to fill the form with the default values, and when the form is sent back, it either processes it, or displays it again with an error message, when some kind of input check fails. In that case, you usually want to preserve the input from former sessions.

The above example does exactly this. If the user already supplied an age, it is kept, otherwise the default value is input. In fact, this is almost the same as saying cgi_params.age || default_values.age. But there is a subtle difference: If the user deliberately wiped out the value of the input field or entered 0, the default value would be filled in again. The || operator checks its first argument for a truth value, and the // operator checks its first argument for definedness. Only the // operator therefore handles 0 and the empty string correctly in this case.

The Comma Operator (,)#

The binary comma operator first calculates, then discards its left-hand side operand. It then calculates its right-hand side operand, and returns it. This is exactly the same behavior as in C or Perl.

Inside of a list (see Lists) the comma just separates the elements from each other.

The Fat Comma Operator (=>)#

The fat comma has the same semantics as the comma (see “The Comma Operator (,)” above), but — if the left-hand side operand is a valid variable name — it is interpreted as a string literal, i.e., as if it had been enclosed in single quotes (see Single-Quoted Strings).

Therefore these two ##include directives do exactly the same:

{% ##include('head.html',
    name => 'Imperia') %}
{% ##include('head.html',
    'name', 'Imperia') %}
The Dot and Subscripting Operator#

Both the dot operator, and the subscripting operator do the same, with slightly different semantics:

{%
    foo.bar = 'something';
    foo.'bar' = 'something';
    foo['bar'] = 'something';
    key = 'bar';
    foo[key] = 'something';
%}

This will assign the value “something” to what foo.bar points to, four times. If your key is a valid variable name, you can use the dot. Otherwise you have to put the key into quotes. If your key is variable, you have to use angle brackets, so that everything between the angle brackets gets evaluated, and the result of the evaluation is used as the key.

The logic that is used for evaluating is always the same, and depends on the type of what is left of the dot (respectively the brackets). If it is a hash reference, a hash lookup with the key is made. If it is an array reference, an array subscript is made. If the key is not a number, the index 0 is used instead.

Last, if the term left of the dot or bracket is a Perl object, it is tested, whether this object has a method with the name of the key. If this is the case, that method is invoked on the object.

{% cgi.params %}

If cgi is a Perl object (Perl objects are blessed Perl references), then the method params is invoked on that object without arguments.

If the key is followed by parentheses, the interpretation as a method call is enforced. You can also put arguments to the method call in the parentheses:

{% some.object.mymethod() %}
{% some.object.method (1, 'foo', bar) %}

If the object does not support this particular method, a warning is generated, but only in the case that the interpretation as a method call is enforced by the usage of parentheses. In all other cases — if it cannot be guessed what is meant — for example because a key for a referenced hash does not exist or there is no array element for a particular index, the empty string is silently returned instead.

Unary Operators#

Unary Minus (-)#

The unary minus operator negates its operand numerically. The operand is first cast to a number (see Context and Casting), and then multiplied with -1.

{% +15 %}
{% +'some string' %}
{% +'23' %}

Triggers:

15
0
23
Bitwise negation (~)#

The tilde operator

{% ~15 %}

Triggers (with a 32-bit Perl):

4294967280

With a 64-bit Perl, you will see:

18446744073709551600

Post-increment (++)#

The post-increment operator returns its operand first, and then casts the operand to a number (see Context and Casting), adds 1 to it and assigns that new value to it.

{% var = 15 %}
{% var++ %}
{% var %}

Triggers:

15
16

An undefined (or non-numerical) value is cast to 0 before the operation takes place.

Post-decrement (--)#

The post-decrement operator returns its operand first, and then casts the operand to a number (see Context and Casting), subtracts 1 from it and assigns that new value to it.

{% var = 15 %}
{% var-- %}
{% var %}

Triggers:

15
14

An undefined (or non-numerical) value is cast to 0 before the operation takes place.

Pre-increment (++)#

The pre-increment operator casts its operand to a number (see Context and Casting), adds 1 to it and assigns that new value to it, and finally returns that value.

{% var = 15 %}
{% ++var %}
{% var %}

Triggers:

16
16

An undefined (or non-numerical) value is cast to 0 before the operation takes place.

Please keep in mind that the pre-increment operator is a little bit more efficient than the post-increment operator (see Section “Post-increment (++)” above), and should therefore be preferred if possible.

Pre-decrement (--)#

The pre-decrement operator casts its operand to a number (see Context and Casting), subtracts 1 from it and assigns that new value to it, and finally returns that value.

{% var = 15 %}
{% --var %}
{% var %}

Triggers:

14
14

An undefined (or non-numerical) value is cast to 0 before the operation takes place.

Please keep in mind that the pre-decrement operator is a little bit more efficient than the post-decrement operator (see Post-decrement (--)), and should therefore be preferred if possible.