The templates used to generate output files are little more than just “documents with holes” in the most simple case. They contain parts (“string chunks”) that will be placed verbatim to the output file and special named placeholders that may refer to other templates or parameters.
Here is an example of a simple template:
Good morning, <$Title$> <$Name$>! It is good to see you.
Let “GoodMorning” be the name of the template. This template has two placeholders: <$Title$>
and <$Name$>
. If parameter “Name” has value “Freeman” and “Title” has value “Dr.”, the above template will evaluate to the following:
Good morning, Dr. Freeman! It is good to see you.
As you can see from the example, newline characters in string chunks are also placed verbatim to the resulting text.
<$
and $>
are the markers to denote the beginning and the end of a placeholder. If necessary, user-defined values can be used as the markers instead of the default ones. The new values can be specified in the configuration file of the appropriate template group and will affect the whole group (template groups are described in detail in Section 4, “Template Groups and Subtemplates”).
Whitespace characters (spaces, tabs and newlines) are ignored after the opening marker and before the closing marker of a placeholder. The following template is equivalent to “GoodMorning” template above:
Good morning, <$ Title $> <$ Name $>! It is good to see you.
A template may refer to other templates (from the group it belongs to) in the same way it refers to the parameters. That is, assume “Name” is not a parameter but there is rather a template with name “Name” and the following contents:
<$first_name$> <$last_name$>
If “Title” is “Dr.” as above, “first_name” is “Gordon” and “last_name” is “Freeman”, “GoodMorning” template will evaluate to the following:
Good morning, Dr. Gordon Freeman! It is good to see you.
The placeholders referring to subtemplates and parameters have the same syntax (a parameter can actually be viewed as a special case of a subtemplate). How a placeholder is interpreted depends only on the presense of a template with the appropriate name in the corresponding template group. For example, <$Title$>
is a reference to the template with name “Title” if there is such template in the group, but a reference to a parameter otherwise.
The value of each parameter is a string. There are no types of values here, like integer, float, etc. The file generator operates on strings only.
If a parameter has no value, its value is assumed to be an empty string.
A parameter may have more than one value (multi-valued parameter). Consider the following “GoodMorning” template similar to the one from the examples above:
Good morning, <$Title$> <$Name$>!
Now let “Name” and “Title” parameters have 3 values each, “Freeman”, “Vance”, “Grigory” and “Mr.”, “Dr.”, “Mr.”, respectively. The order of values of each particular parameter is important. Note that it is possible for a parameter to have two or more identical values, like “Mr.” in case of “Title” parameter.
“GoodMorning” template will have 3 values as a result:
Good morning, Mr. Freeman!
Good morning, Dr. Vance!
Good morning, Mr. Grigory!
As you can see, the first values of “Name” and “Title” are used to create the first resulting value, the second values of these parameters are used for the second one, etc.
Note that it is exactly 3 values that the template will have as a result rather than a single string with these values concatenated. To concatenate these values into a single string, you need to use join()
construct described below.
If you use MiST Engine command line tool to evaluate a template group and the main (top-level) template in the group is multi-valued as a result, mist_engine tool will report this as an error. Other templates can be multi-valued - as long as the top-level template has only one value.
On the other hand, MiST Engine API functions do not consider multi-valued top level template as an error. mist_tg_evaluate()
returns all resulting values in this case.
The number of resulting values is the maximum number of values associated with the placeholders in the template. If there are less values associated with a particular placeholder than the number of the resulting values, the last value associated with this placeholder is used when creating the remaining resulting strings.
For example, assume that “Name” parameter has the 3 values described above but “Title” has only two: “Doctor” and “Mr.”, in this order. “GoodMorning” template will have the following 3 values in this case:
Good morning, Doctor Freeman!
Good morning, Mr. Vance!
Good morning, Mr. Grigory!
Even if “Title” parameter had only a single value, “Mr.”, the template would have 3 values as a result:
Good morning, Mr. Freeman!
Good morning, Mr. Vance!
Good morning, Mr. Grigory!
That is, the only value of “Title” parameter is used in conjunction with each of the three values of “Name” parameter to produce three resulting strings.
Now assume we have two templates: “GoodMorning” and “Person”.
“GoodMorning” (note that it refers to “Person” subtemplate, see Section 4.2, “References in the templates” for details):
Good morning, <$Person$>!
“Person”:
<$Title$> <$Name$>
If “Title” or “Name” parameter is multi-valued, “Person” and “GoodMorning” templates will also have more than one value.
Sometimes it is necessary to join the values of a multi-valued template or parameter. join()
construct in the placeholder can be used to do this. Suppose we rewrite “GoodMorning” template as follows:
Good morning, <$Person : join(, )$>!
Let “Name” and “Title” parameters have 3 values each, “Freeman”, “Vance”, “Grigory” and “Dr.”, “Mr.”, “F.” respectively. “GoodMorning” template will have a single value in this case:
Good morning, Dr. Freeman, Mr. Vance, F. Grigory!
The values of “Person” subtemplate are joined here into a single string with “, ” inbetween as a separator. This separator sequence was specified in the parentheses in join()
. So, a construct like this allows to create various list-like structures in the resulting files.
The syntax of a placeholder with join()
construct is as follows:
<begin_marker>
<name>
: join (<separator>
)<end_marker>
Space, tab and newline characters are optional before and after the colon, before the opening parenthesis and after the closing parenthesis. These whitespace characters are ignored.
The whitespace characters are not ignored between the parentheses. That is, the sequence between the first '(' character after join
and the last ')' character of the placeholder is taken as a whole as the separator string. This also allows to use '(' and ')' characters in the separator string without escaping.
If there is nothing between the parentheses in a join()
construct, an empty string will be used as a separator as you could expect.
If the subtemplate or parameter referred to in a placeholder with join()
has only one value, the join()
construct is ignored. As a result, the placeholder will have the same value as if there were no join()
there.
The following character sequences have special meaning when used in the separator string in join()
. They are interpreted in the similar way as in the strings in C programming language.
\\
- '\' character.
\n
- LF character.
\r
- CR character.
\t
- TAB character.
These escaped sequences are supported to provide a convenient way to specify tabs and newline characters in the separator strings. Nevertheless, a newline can still be specified in the separator literally, for example:
Good morning, <$Person : join(, )$>!
The following variant is often more convenient to use here instead:
Good morning, <$Person : join(,\n)$>!
If is sometimes needed to include one or another part in a template depending on whether some particlular parameter is defined or not. Conditional constructs in the templates allow to do this. There are two forms of conditional constructs (with and without “else” branch):
<$ifcond
$>then-branch
[<$else$>else-branch
] <$endif$>
and
<$ifcond
$>then-branch
<$endif$>
<$if … $>
, <$else$>
and <$endif$>
are placeholders of a special sort.
cond
is a conditional expression which will be checked to select appropriate branch. It can be a name of a parameter or a subtemplate or it can be a concat-expression (see below).
then-branch
and else-branch
can be arbitrary sequences of string chunks and placeholders. These branches may also contain conditional constructs (nested conditionals).
Note that join()
constructs are not allowed in the conditional expression (cond
). If you need to use a joined value as the condition, you should prepare a subtemplate, join the appropriate values there and use the name of the subtemplate as the conditional expression instead.
If you need the branches to be chosen depending on whether a parameter or a template has at least one non-empty value, you can also use a concat-expression as a conditional expression.
The most simple case is when the template or the parameter referred to by cond
has the only value (if it is not defined at all, it is considered the same as if it was defined and had only one value, an empty string).
Consider the following extract from a template of a makefile for the group of tests:
TEST_EXT = <$if TEST_SOURCE_EXT$>.<$TEST_SOURCE_EXT$><$else$>.cpp<$endif$>
In this example, “TEST_SOURCE_EXT” is a parameter than may be specified in the configuration file for the test suite or for the group of tests. It defines the extension of the file names used for the sources of the tests. If “TEST_SOURCE_EXT” is defined and is not empty, its value will be assigned to TEST_EXT
variable in the makefile with a dot prepended. For example, if “TEST_SOURCE_EXT” has value “dxx”, the value of the conditional construct will be “.dxx”. If “TEST_SOURCE_EXT” is empty or is not defined, the value of the conditional construct will be “.cpp”.
If cond
is multi-valued, things get more complicated. The template engine processes such conditional constructs as follows.
First, for each value of cond
, the template engine determines which branch should be evaluated and evaluates it (if it has not done that yet).
Second, the number of values the conditional construct will have (nvals
) is determined. It is the maximum among the numbers of values of cond
and of the branches that are actually taken. That is, if cond
has such values that one branch is never chosen, this branch is not evaluated and the number of its values is not taken into account as well.
Finally, each of nvals
resulting values of the conditional construct is prepared. To obtain the value #i, the value #i of cond
and the value #i of the corresponding branch are taken. If the branch does not have value #i, its last value is used instead, same for cond
.
To make it clearer, consider the following example. Suppose we have a template:
<$if PC$>[<$PA$>]<$else$>-<$PB$>-<$endif$>
Suppose the parameters PA
, PB
and PC
have the following values:
PA
: “A1”, “A2”, “A3” (3 values)
PB
: “B1”, “B2”, “B3”, “B4” (4 values)
PC
: “C”, “”, “C”, “”, “”, “C” (6 values, 3 of which are empty)
The template will have 6 values as a result: “[A1]”, “-B2-”, “[A3]”, “-B4-”, “-B4-”, “[A3]”.
If PC
had only one value, “C”, the template would have 3 values as a result: “[A1]”, “[A2]”, “[A3]”.
If PC
had 6 values, each of which was empty, the template would have 6 values as a result: “-B1-”, “-B2-”, “-B3-”, “-B4-”, “-B4-”, “-B4-”.
It can be helpful to be able to choose a branch depending on whether a parameter or a subtemplate has at least one non-empty value. This is equivalent to checking whether a string created by concatenating (“gluing”) the values of that parameter or subtemplate is non-empty. This can be achieved by using a subtemplate where join()
is applied to these values but it is often more convenient to use a concat-expression instead.
A concat-expression has the following form: concat(
, where name
)name
is the name of the corresponding parameter or a subtemplate. The expression can only be used within <$if ...$>
placeholder. The conditional construct is interpreted the same way in this case as if the conditional expression was a parameter having only one value which was non-empty if and only if name
had at least one non-empty value.
Consider a modified version of the template from the example above:
<$if concat(PC)$>[<$PA$>]<$else$>-<$PB$>-<$endif$>
Suppose PA
and PB
have the same values as above and PC
has 6 values: “C”, “”, “C”, “”, “”, “C”. The template will have 3 values as a result: “[A1]”, “[A2]”, “[A3]”. PC
has some non-empty values, so only then-branch is used.
If PC
had only one value, “C”, the result would be the same.
If PC
had 6 values, each of which was empty, the template would have 4 values as a result: “-B1-”, “-B2-”, “-B3-”, “-B4-”. Note the difference from the example without a concat-expression. This is because concat(PC)
is an expression having only one value even though PC
has 6 values.