Template

template

Note: This page is based on Go's text/template, adapted to suit the Ra environment.

Templates are executed by applying them to a tuple. Annotations in the template refer to attributes of the tuple to control execution and derive values to be displayed. Execution of the template walks the tuple and sets the cursor, represented by a period '.' and called "dot", to the value at the current location in the tuple as execution proceeds.

Ra assumes that template authors are trusted. It does not auto-escape output, so injecting code into a template can lead to arbitrary code execution if the template is executed by an untrusted source.

"Actions" - data evaluations or control structures - are delimited by « and »; all text outside actions is copied to the output unchanged.

Here is a trivial example that prints "17 items are made of wool".

import("http")

template_register("b1", dee(
	template_name:="eg1", 
	template_content:="«.Count» items are made of «.Material»"
))
print(template_execute("b1", "eg1", tuple{Count:=17, Material:="wool"}))

More intricate examples appear below.

Text and spaces

By default, all text between actions is copied verbatim when the template is executed. For example, the string " items are made of " in the example above appears on standard output when the program is run.

However, to aid in formatting template source code, if an action's left delimiter («) is followed immediately by a minus sign and white space, all trailing white space is trimmed from the immediately preceding text. Similarly, if the right delimiter (») is preceded by white space and a minus sign, all leading white space is trimmed from the immediately following text. In these trim markers, the white space must be present: "«- 3»" is like "«3»" but trims the immediately preceding text, while "«-3»" parses as an action containing the number -3.

For instance, when executing the template whose source is

"«23 -» < «- 45»"

the generated output would be

"23<45"

For this trimming, the definition of white space characters is space, horizontal tab, carriage return, and newline.

Actions

Here is the list of actions. "Arguments" and "pipelines" are evaluations of data, defined in detail in the corresponding sections that follow.

«/* a comment */»
«- /* a comment with white space trimmed from preceding and following text */ -»
	A comment; discarded. May contain newlines.
	Comments do not nest and must start and end at the
	delimiters, as shown here.

«pipeline»
	The default textual representation (the same as would be
	printed by print) of the value of the pipeline is copied
	to the output.

«if pipeline» T1 «end»
	If the value of the pipeline is empty, no output is generated;
	otherwise, T1 is executed. The empty values are false, 0, and any
	string of length zero.
	Dot is unaffected.

«if pipeline» T1 «else» T0 «end»
	If the value of the pipeline is empty, T0 is executed;
	otherwise, T1 is executed. Dot is unaffected.

«if pipeline» T1 «else if pipeline» T0 «end»
	To simplify the appearance of if-else chains, the else action
	of an if may include another if directly; the effect is exactly
	the same as writing
		«if pipeline» T1 «else»«if pipeline» T0 «end»«end»

«range pipeline» T1 «end»
	The value of the pipeline must be a table/array, set or integer.
	If the value of the pipeline has length zero, nothing is output;
	otherwise, dot is set to the successive elements of the table 
	and T1 is executed.

«range pipeline» T1 «else» T0 «end»
	The value of the pipeline must be a table/array, set or integer.
	If the value of the pipeline has length zero, dot is unaffected and
	T0 is executed; otherwise, dot is set to the successive elements
	of the table and T1 is executed.

«break»
	The innermost «range pipeline» loop is ended early, stopping the
	current iteration and bypassing all remaining iterations.

«continue»
	The current iteration of the innermost «range pipeline» loop is
	stopped, and the loop starts the next iteration.

«template "name"»
	The template with the specified name is executed with nil data.

«template "name" pipeline»
	The template with the specified name is executed with dot set
	to the value of the pipeline.

«block "name" pipeline» T1 «end»
	A block is shorthand for defining a template
		«define "name"» T1 «end»
	and then executing it in place
		«template "name" pipeline»
	The typical use is to define a set of root templates that are
	then customised by redefining the block templates within.

«with pipeline» T1 «end»
	If the value of the pipeline is empty, no output is generated;
	otherwise, dot is set to the value of the pipeline and T1 is
	executed.

«with pipeline» T1 «else» T0 «end»
	If the value of the pipeline is empty, dot is unaffected and T0
	is executed; otherwise, dot is set to the value of the pipeline
	and T1 is executed.

«with pipeline» T1 «else with pipeline» T0 «end»
	To simplify the appearance of with-else chains, the else action
	of a with may include another with directly; the effect is exactly
	the same as writing
		«with pipeline» T1 «else»«with pipeline» T0 «end»«end»

Arguments

An argument is a simple value, denoted by one of the following.

Arguments may evaluate to any type. If an evaluation yields a function value, the function is not invoked automatically, but it can be used as a truth value for an if action and the like. To invoke it, use the call function, defined below.

Pipelines

A pipeline is a possibly chained sequence of "commands". A command is a simple value (argument) or a function call, possibly with multiple arguments:

Argument
	The result is the value of evaluating the argument.
functionName [Argument...]
	The result is the value of calling the function associated
	with the name:
		function(Argument1, etc.)
	Functions and function names are described below.

A pipeline may be "chained" by separating a sequence of commands with pipeline characters '|'. In a chained pipeline, the result of each command is passed as the last argument of the following command. The output of the final command in the pipeline is the value of the pipeline.

The output of a command will be either one value or two values, the second of which has type error. If that second value is present and evaluates to non-nil, execution terminates and the error is returned to the caller of template_execute.

Variables

A pipeline inside an action may initialise a template variable to capture the result. The initialisation has syntax

$variable := pipeline

where $variable is the name of the template variable. An action that declares a variable produces no output.

Variables previously declared can also be assigned, using the syntax

$variable = pipeline

If a "range" action initializes a variable, the variable is set to the successive elements of the iteration. Also, a "range" may declare two variables, separated by a comma:

range $index, $element := pipeline

in which case $index and $element are set to the successive values of the index and element, respectively. Note that if there is only one variable, it is assigned the element.

A template variable's scope extends to the "end" action of the control structure ("if", "with", or "range") in which it is declared, or to the end of the template if there is no such control structure. A template invocation does not inherit template variables from the point of its invocation.

When execution begins, $ is set to the tuple argument passed to template_execute, that is, to the starting value of dot.

Examples

Here are some example one-line templates demonstrating pipelines and variables. All produce the quoted word "output":

«"output"»
	A string constant.
«print "output"»
	A function call.
«"output" | print »
	A function call whose final argument comes from the previous
	command.
«print (print "out" "put")»
	A parenthesised argument.
«"put" | print "out" | print »
	A more elaborate call.
«"output" | print | print »
	A longer chain.
«with "output"»«print .»«end»
	A with action using dot.
«with $x := "output" | print »«$x»«end»
	A with action that creates and uses a template variable.
«with $x := "output"»«print $x»«end»
	A with action that uses the template variable in another action.
«with $x := "output"»«$x | print »«end»
	The same, but pipelined.

Functions

During execution functions are found in two function maps: first in the template, then in the global function map. By default, no functions are defined in the template but the Funcs method can be used to add them.

Predefined global functions are named as follows.

and
	Returns the boolean AND of its arguments by returning the
	first empty argument or the last argument. That is,
	"and x y" behaves as "if x then y else x."
	Evaluation proceeds through the arguments left to right
	and returns when the result is determined.
call
	Returns the result of calling the first argument, which
	must be a function, with the remaining arguments as parameters.
	Thus "call X 1 2" is, in Ra notation, X(1, 2).
	The first argument must be a function. The function must
	return either one or two result values, the second of which
	is of type error. If the arguments don't match the function
	or the returned error value is non-nil, execution stops.
index
	Returns the result of indexing its first argument by the
	following arguments. Thus "index x 3" is, in Ra syntax,
	x[3] for tables, and "index t attr1" is, in Ra syntax,
	t.attr1. Each indexed item must be a table or a tuple.
js
	Returns the escaped JavaScript equivalent of the textual
	representation of its arguments.
len
	Returns the integer length of its argument.
not
	Returns the boolean negation of its single argument.
or
	Returns the boolean OR of its arguments by returning the
	first non-empty argument or the last argument, that is,
	"or x y" behaves as "if x then x else y".
	Evaluation proceeds through the arguments left to right
	and returns when the result is determined.
print
	Ra print

The boolean functions take any zero value to be false and a non-zero value to be true.

There is also a set of binary comparison operators defined as functions:

eq
	Returns the boolean truth of arg1 = arg2
ne
	Returns the boolean truth of arg1 != arg2
lt
	Returns the boolean truth of arg1 < arg2
le
	Returns the boolean truth of arg1 <= arg2
gt
	Returns the boolean truth of arg1 > arg2
ge
	Returns the boolean truth of arg1 >= arg2

For simpler multi-way equality tests, eq (only) accepts two or more arguments and compares the second and subsequent to the first, returning in effect

arg1=arg2 or arg1=arg3 or arg1=arg4 ...

(Unlike with or in Ra, however, eq is a function call and all the arguments will be evaluated.)

The comparison functions work on comparable values so, as usual, one may not compare an int with a float and so on.

Associated templates

Each template is named by a string specified when it is created. Also, each template is associated with zero or more other templates that it may invoke by name; such associations are transitive and form a name space of templates.

A template may use a template invocation to instantiate another associated template; see the explanation of the "template" action above. The name must be that of a template associated with the template that contains the invocation.

Nested template definitions

When parsing a template, another template may be defined and associated with the template being parsed. Template definitions must appear at the top level of the template, much like global variables in a Ra program.

The syntax of such definitions is to surround each template declaration with a "define" and "end" action.

The define action names the template being created by providing a string constant. Here is a simple example:

«define "T1"»ONE«end»
«define "T2"»TWO«end»
«define "T3"»«template "T1"» «template "T2"»«end»
«template "T3"»

This defines two templates, T1 and T2, and a third T3 that invokes the other two when it is executed. Finally it invokes T3. If executed this template will produce the text

ONE TWO

By construction, a template may reside in only one group. If it's necessary to have a template addressable from multiple groups, the template definition must be registered multiple times to create distinct fragment values.

Register may be called multiple times to assemble the various associated templates; see template_register.

A template may be executed through template_execute, which executes an associated template identified by name. To invoke a particular template explicitly by name,

template_execute("g1", "T2", tuple{})		// no data needed


Portions of this page are modifications based on Go's text/template created and shared by Google and used according to terms described in the Creative Commons Attribution 4.0 License.