3. Templates for Output Files

3.1. Basic structure
3.2. Multi-valued parameters and templates
3.3. Joining values
3.4. Conditionals

3.1. Basic structure

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.

3.2. Multi-valued parameters and templates

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.

Important

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.

3.3. Joining values

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.

Note

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)$>!

3.4. Conditionals

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):

<$if cond$> then-branch [<$else$> else-branch] <$endif$>

and

<$if cond$> 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

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.

  1. 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).

  2. 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.

  3. 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(name), where 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.