Open Modelica Template Programming
Open Modelica Template Programming
Users Guide
Revisions
v0.3 2011-04-07 v6 Pavol Privitzer. Introduced new Section 2.9.1 on interface package import,
Section 2.10 on template file import into another template package, and
Section 2.11 on template error handling. Also added description of
countEmpty and separateEmpty options in Section 2.8.2. Also updated Section
2.8.3 about empty and countEmpty options.
v0.3 2011-03-31 v6 Peter Fritzson. Some corrections after Stavåkers comments. Also update of
indexby to hasindex changes and from to fromindex after info from Per and
Pavol. Included unfinished template import Section 2.9.1 and error handling
Section 2.11 after information from Martin.
v0.2 2010-04-21 v5 Peter Fritzson. Fixed some errors found by Martin and Anton
v0.2 2010-04-19 v4 Peter Fritzson. Restructured the document and made the first full draft.
v0.1 2010-04-01 v1 Peter Fritzson. Started the work.
Table of Contents
Table of Contents ..................................................................................................................... 2
Chapter 1 Background............................................................................................................. 4
1.1 Intended Use .......................................................................................................................... 4
1.2 User Profile ............................................................................................................................ 4
1.3 Background and Motivation .................................................................................................. 4
1.4 Definition of Text Template Language .................................................................................. 5
1.5 Design Principles for the Modelica Template Language Susan ............................................ 5
Chapter 2 Template Language Features ................................................................................ 7
2.1 Preliminaries .......................................................................................................................... 7
2.1.1 Predefined Text Data Type ............................................................................................................. 7
2.2 Template Text-with-hole Constructors and Template Holes ................................................. 7
2.3 Template Expressions ............................................................................................................ 8
2.3.1 Automatic Conversion to String Data ............................................................................................. 8
2.3.2 Bound Variable Reference, name or $'name' ................................................................................. 8
2.3.3 Verbatim String Constants .............................................................................................................. 9
2.3.4 Parentheses ...................................................................................................................................... 9
2.3.5 Reduction Expressions .................................................................................................................... 9
2.3.6 Conditional Expressions.................................................................................................................. 9
2.3.7 Vector/list Constructor {} ............................................................................................................ 10
2.4 Template Function Declaration and Call ............................................................................. 10
2.4.1 Template Function Declaration ..................................................................................................... 10
2.4.2 Declaring External Imported MetaModelica Functions ................................................................ 11
2.4.3 Template Function Call ................................................................................................................. 11
2.4.4 Call of Imported External MetaModelica or C Functions ............................................................. 11
2.4.4.1 Using return value natively with its original type ................................................................ 12
2.4.4.2 Call with return value converted to string............................................................................ 12
2.4.4.3 Call with empty return value ............................................................................................... 12
2.4.5 Declaring Reference (buffer) Formal Parameters in a Template Function.................................... 13
2.4.6 Using Reference (buffer) Formal Parameters in a Template Function .......................................... 13
2.4.7 Allowed Side-Effects in Template Functions................................................................................ 13
2.5 Match Expressions and the Idea of Pattern Matching .......................................................... 13
2.5.1 The MetaModelica uniontype Construct ....................................................................................... 13
2.5.2 Match Expressions and Pattern Matching ..................................................................................... 14
2.5.2.1 Simple Pattern Matching ..................................................................................................... 15
2.5.2.2 Using Pattern Matching in Template Functions .................................................................. 16
2.5.3 Pattern Expressions in General...................................................................................................... 16
2.5.4 Record Constructor Pattern Expressions ....................................................................................... 17
2.5.5 Pattern Expression Variable Binding Using as ............................................................................. 17
2.5.6 Implicit Opening of Record Constructor Scopes in Patterns ......................................................... 17
2.6 Iterator Expressions ............................................................................................................. 18
2.6.1 Iterator Expressions with Iteration Index Values .......................................................................... 20
2.7 Let Expressions with Name Bindings and Text Buffers ...................................................... 20
2.7.1 let Binding of Local Named Text Values ...................................................................................... 20
2.7.2 let Binding of Buffer Variables and their Use ............................................................................... 21
2.7.3 Appending a String to a buffer Variable ....................................................................................... 21
2.7.4 Reference (buffer) Formal Parameters in Template Functions ..................................................... 21
2.8 Formatting, Separator, and Indentation Options .................................................................. 21
2.8.1 Indentation controlling options ..................................................................................................... 22
2.8.2 Multi-Value Formatting Options ................................................................................................... 22
2.8.3 Options .......................................................................................................................................... 22
2.9 Interface Packages ............................................................................................................... 23
2.9.1 Interface Package Import into a Template File ............................................................................. 26
2.10 Template File Import into Another Template File ............................................................... 26
2.11 Template Error Handling ..................................................................................................... 26
Chapter 3 References ............................................................................................................. 28
Chapter 1
Background
2.1 Preliminaries
Template holes are started by <% and ended by %>, i.e., as in <%name%>. When <% is needed as text, its first
character need to be ecaped by \, i.e., \<%. There are currently two forms of template text-with hole constructors:
Single-quote ' ' text-with-hole constructor. All characters are included verbatim as-is, except holes
starting with <% and ' which ends the text. Those can be included by prefixing with the escape code
backslash as in \<% and \'.
Multi-Line << >> text-with-hole constructor. The template text starts on the line after << and ends
including the line before >>. All characters are included verbatim as-is, except holes starting with <% and >>
which ends the text. Those can be included by prefixing with the escape code backslash as in \<% and \<<.
It is actually possible to have multiple lines also in the single-quote variant, but then line-counting, alignment and
indentation options do not work. Such options are only supported by the Multi-line variant.
Example of the single-quote ' ' text-with-hole constructor:
'Output text <%templ-expr%>.'
An alternative dollar-quoted variant is similar to but different from Modelica's single-quoted identifiers:
$'valueName'
The $'valueName' means the same identifier as valueName, whereas the Modelica 'valueName' includes the
single-quotes in the identifier.
The value of a referenced bound variable name is retrieved and automatically converted to text according to
Section 2.3.1. Any auto-to-string-convertible bound value can be used..
Examples where dollar sign or space or + is in the identifier, or just an ordinary name:
$'quoted id+23121'
$'$'
ordinaryName
Examples when the keyword let needs to be an identifier, e.g. as in these examples:
field.$'let'
case RECORD($'let'=FOO) then ...
2.3.4 Parentheses
Parentheses are allowed, and has the normal interpretation used in almost all programming langaugs, i.e. giving
priority in the order of evaluation. Expressions in innermost parenthesis will be evaluated first.
The condition is intended to test values for their non-zero/zero-like values. Values of these types are allowed
with the following semantics. Zero-like values are treated as false in the Boolean sense:
Boolean true / false
Integer or Real non-0 / 0
String non-empty / ""
list or Array non-empty / { } empty list/array
Option SOME / NONE
However, testing for the result of a template function (returning result of type Text) is not allowed:
if templ() then ... // Error, cannot test conditionally on template function!
Example:
This can be used for construction of a general list with many elements, as in the following:
{ expr1, expr2, expr3, ... exprN } ;separator = ","
package SimCode
function crefSubIsScalar
input DAE.ComponentRef cref;
output Boolean isScalar;
end crefSubIsScalar;
end SimCode;
end MyInterface;
Buffer arguments passed to reference formal parameters need to be prefixed by & in the call. The Text result of
the template evaluated with the actual parameters is the output of the template function call. Formal parameters
are strongly typed. Automatic to-string conversion of actual parameters applies when appropriate.
Here the return value from the external imported function crefSubIsScalar is a boolean which is used as a
boolean in the if-expression. The return value could also have been stored in a variable resBool as follows:
template crefSubIsScalarHuman(DAE.ComponentRef cref) ::=
let resBool = crefSubIsScalar(cref)
if resBool then
"this cref has scalar subscript"
else
"this cref does not have scalar subscript"
end crefSubIsScalarHuman;
Alternatively the return value from crefSubIsScalar could be passed immediately to another template function
crefSubIsScalarHumanFromBool like this:
template crefSubIsScalarHuman(DAE.ComponentRef cref) ::=
crefSubIsScalarHumanFromBool(crefSubIsScalar(cref))
end crefSubIsScalarHuman;
The return value could be used natively in other contexts as well where it makes sense.
For example,
template writeSomeTextToFile() ::=
let content = "some text"
let () = textFile(content, "file_name.txt") // Call with empty result, e.g. to create file.
() // This template function returns an empty result, e.g. as void in C, only its side effect is used.
end writeSomeTextToFile;
2.4.5 Declaring Reference (buffer) Formal Parameters in a Template Function
In a template function header, & is used before the reference parameter name, and the type in such cases is always
Text.
Example:
template funcname(Argtype1 arg1, Text &arg2Reference) ::= ...
In the template function functionBody we declare a buffer varDecls that can be updated. This buffer is
passed to the template function tempDecl, prefixed with & in the call.
Further down, within the template function tempDecl, a side effect is performed on the reference parameter
varDecls, it is updated, i.e., appended to by the &varDecls += operation.
Using the Exp abstract syntax definition, the abstract syntax tree representation of the simple expression 12+5*13
will be as shown in Figure 2-1. The Integer data type is predefined. Other predefined data types are Real,
Boolean, and String as well as the parametric type constructors array, list, tuple, and Option.
ADDop
INTconst MULop
12
INTconst INTconst
5 13
The templ-expi template expression of the first case that matches the value-exp against its pattern-expi is
evaluated as the result of the whole expression.
The value computed by the expression after the match keyword is matched against each of the patterns
after the case keywords in order; if one match fails the next is tried until there are no more case-branches
in which case (if present) the else-branch is executed.
When no pattern matches, the result is empty string.
When a pattern matches, all pattern variables in the pattern become bound to corresponding parts in the
structured value value-exp. Also, the scope of the top-most record constructor, if present in the pattern, is
opened to make all its fields available as read-only variables, see Section 2.5.6.
The following is an example of pattern matching against a tree structure as in Figure 2-1, e.g.
match inExp
case INTconst(int=v1) then ...
case ADDop(exp1=e1,exp2=e2) then ...
case SUBop(__) then ...
case MULop(__) then ...
case DIVop(__) then ...
case NEGop(__) then ...
end match
The first case uses named pattern pattern matching (Section 2.5.4 where the single named field of the INTconst
record is int, and the pattern variable is v1:
case INTconst(int=v1) then ...
The interpretation is to match the inExp value against the special case pattern INTconst(int=v1) If there is a
match, the pattern variable v1 will be bound to the corresponding part of the tree. For the left subtree in Figure
2-1, INTCONST(12), v1 will be bound to 12.
We now turn to the second rule, which has a named pattern for the ADDop node:
case ADDop(exp1=e1,exp2=e2) then ...
For this case to apply, the pattern ADDop(exp1=e1,exp2=e2) must match the actual inExp value, which is an
abstract syntax tree. If there is a match, the variables e1 and e2 will be bound the two child nodes of the ADDop
node, respectively, visible in Figure 2-1.
A more convenient way of matching is to use the implicit scope opening (Section 2.5.6) of patterns that match.
Instead of specifying a named pattern with a number of field names and pattern variables, one can use the
following form that matches any ADDop node:
case ADDop(__) then ...
The scope of the ADDop node is automatically opened (Section 2.5.6), to make the fields exp1 and exp2
available in the current scope bound to the corresponding subtrees of the inExp value. Thus, it is unnecessary to
introduce the pattern variables e1 and e2. Instead we can use exp1 and exp2 directly to refer to the subtrees of
the ADDop node.
The end match is usually optional, but mandatory in case of nested match expressions as below:
match a
case RECORD1(__) then
match b
case RECORD2(__) then expr
end match
case ...
The above template function exp with an optional end match added:
template exp(Exp ep) ::=
match ep
case ICONST(__) then value
case PLUS(__) then '(<%exp(lhs)%> + <%exp(rhs)%>)' // Open scope, see Section 2.5.6
end match
end exp;
This construct is the same in MetaModelica and Susan, and essentially the same as what is found in several
functional language.:
for
uniontype Statement
record ASSIGN
Exp lhs; Exp rhs;
end ASSIGN;
record WHILE
Exp condition;
list<Statement> statements;
end WHILE;
end Statement;
Only the scope for the outermost constructor REC1, is opened in a nested pattern expression. Use as-notation for
the nested ones.
Example, with both REC1 and REC2 containing field1:
REC1(... x as REC2(...) ...)
Use of field1 will denote field1 within REC1, and x.field1 will denote field1 within REC2.
The general form of the operator allows a general pattern to iterate over and match the elements in element-
list. Only the elements that match elem-pattrn will be forwarded and used to construct instances of
template-expression. This is typically used for filtering applications.
element-list |> elem-pattrn => template-expression
The output of the template function call gentlemen({Adam, Eric, Carl}) will be:
Hello Mr Adam, Mr Eric, Mr Carl!
This filters out only values of the record type ICONST, see also the match expression Section 2.5.
template intConstantsList(list<Exp> expLst) ::=
(expLst |> ICONST(__) => value ;separator=", ")
end intConstantsList;
Example:
let removedPart = (removedEquations |> eq =>
'<%equation_(eq, contextSimulationNonDiscrete, varDecls)%>' ;separator="\n")
Example:
(list1 |> x
=> 'I love <%x%> do') |> y => 'Dumb <%y%>;'
More examples:
Ex 1:
<%zeroCrossingsNeedSave |> vars => (
<<
case <%vars.index0%>:
<%vars |> SIMVAR(__)=>'save(<%cref(name)%>);' ;separator="\n"%>
break;
>> )
;separator ="\n"%>
The variable after hasindex gives the ordinal number of the corresponding element in elements starting counting
from 0 as default, but it is possible to start from another number, e.g., 1, by using fromindex 1.
The following examples use the hasindex keyword. One example is using the optional fromindex keyword
to specify the start index offset to specify non-zero start indexes such as having 1 as the lowest index (as in
Modelica):
<multi-val-expr |> el hasindex myindex0 => templ(el, myindex0) ;options>
<multi-val-expr |> el hasindex myindex1 fromindex 1 => templ(el, myindex1)>
mem_state = get_memory_state();
<%nonSateDiscPart%>
<%removedPart%>
restore_memory_state(mem_state);
return 0;
}
>>
end functionDaeOutput2;
The reference, name, is immutable, but the contents is mutable since it is a buffer. The value of the buffer is
initialized to the text-expression. As usual, the let-binding of name can be accessed within the scope of the
body-expression.
The following language rules apply regarding text buffers
A text buffer can only be appended to by the += operator in a let expression (Section 2.7.3).
A text buffer can be passed as an argument to a template function by marking it with & at the call. This is a
reference parameter that can be modified by the called function, i.e., an in-out parameter (Section 2.4.6)
A text buffer can only be referenced inside text template expressions in the template function it is declared
in. (text buffers can only be appended to in template functions they are passed to)
Example 1:
let &preExp += 'create_index_spec(&<%tmp%>, <%nridx_str%>, <%idx_str%>);<%\n%>'
Example 2:
template tempDecl(String ty, Text &varDecls) ::=
let newVar = 'tmp<%System.tmpTick()%>'
let &varDecls += '<%ty%> <%newVar%>; <%\n%>' // varDecls is updated, appended
newVar // return newVar
end tempDecl;
It is usually used inside a hole, and can optionally be enclosed in parentheses as any other expression, e.g.:.
(templ-exp ;opt1=val1 ;opt2; ... ;optn=valn)
This is a semi-colon 2n+1-ary left associative operator. All options and option-values are collected, and
simultaneously applied to the leftmost operand, the templ-exp.
2.8.3 Options
Expression options can be specified only in the direct lexical context of < … > or (…).
Indentation controlling options control indentation that occurs before outputting the first non-space character
after a new line inside of the option affected output text.
All indentation options are of type integer where usage without ‘=’ defaults to 0.
The indent option outputs the specified number of spaces immediately and then behaves like relIndent.
Multi-values formatting options can be applied for all expressions that (possibly) results in concatenation of
multiple results, i.e., list or array values, map expressions and (pseudo)list construction expressions.
The separator option is used for inserting a separator string between elements of multiple-value expressions t.
Default separator value is empty string.
The align and wrap options are of type integer where a positive value means a number of results or characters,
respectively, after which a value of the alignSeparator or wrapSeparator will be output. Default values of
alignSeparator and wrapSeparator is new-line character. Default value of align option is 10 and the
default of wrap option is 100 (these values are used when the option is specified without a value – i.e., not using
‘=’).
The alignOffset option can be used to set the start counting offset from whichthe align option counts its
effect. Default is 0.
The empty option is of string type (more precisely, Option<String>), and when specified, its value is used
whenever the concatenated result is an empty string. This option has impact on the counting and separation
semantics (which is not intuitive in several scenarios). By default, this option is “unset” (internally NONE) that
means that there is no replacement for empty results and the separation and index counting by default DOES NOT
count/separate empty results. When this option is used without a value (without ‘=’ after it), it is equivalent to
empty=”” (empty string) and it means that “there is a value” for every result, so the counting and separation takes
the value as non-empty (it is counted/separated).
To address this complex semantics (a mess), the two options countEmpty and separateEmpty will be
implemented to make the semantics much clearer and explicit. Particularly, this option will only dictate the
replacement value for empty result and it will have by default the value of an empty string (not NONE as now).
The countEmpty – To be implemented - option is of Boolean type with default value true (in the current
implementation it is false). When set to true, it means that an index variable defined by hasindex for this multi-
value expression is advanced for empty results, otherwise not. It is important to note that the emptiness is checked
against the actual result value regardless of the empty option value (so the empty option is used effectively only
for replacement of the empty results while not affecting the emptiness checks).
See also Section 2.6.1, for more information about countEmpty and hasindex.
Examples:
//note the automatic indentation by 2 spaces
template lines2(list<String> lines) ::= <<
<%lines ;separator=\n%>
>>
end lines2;
/* example output:
int[] myArr = { 1, 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 15, 16,
17, 18, 19, 20 };
*/
uniontype Operator
record PLUS end PLUS;
record TIMES end TIMES;
record LESS end LESS;
end Operator;
end OriginalPackageName;
...
end InterfacePackageName;
The OriginalPackageName is the name of the original MetaModelica package where types corresponding to
the type views in the interface package are fully defined. An interface package can use types from several
packages. It usually specifies a subset of the original types defined in several packages and from these types
suitable parts can be selected. For example, there can be additional union tags in the Statement type, but only
those specified in the type view in the interface package can be used by templates that use this view. Similarly,
more record fields can be originally defined in the ASSIGN record but only lhs and rhs can be read inside the
template package with the view imported.
Interface package files with AST type views can be shared across different target languages as a kind of type
interface to the compiler generated output ASTs (e.g., simulation code ASTs). It is also an essential feature to
support scenarios where users are not allowed to see all original types (e.g., a commercial Modelica compiler) but
still can see and use the intended subset to extend the code generator.
In addition to type views in interface packages, templates automatically understand all MetaModelica built-in
types: String, Boolean, Integer, Real, list, Option, tuple, and Array types.
You can import multiple interface packages.
Example:
interface package SimCodeInterface
...
package builtin
end builtin;
package SimCode
function crefSubIsScalar
input DAE.ComponentRef cref;
output Boolean isScalar;
end crefSubIsScalar;
...
uniontype Context
record SIMULATION
Boolean genDiscrete;
end SIMULATION;
record OTHER
end OTHER;
end Context;
...
type Variables = list<Variable>;
type Statements = list<Statement>;
type VariableDeclarations = Variables;
uniontype Variable
record VARIABLE
DAE.ComponentRef name;
Type ty;
Option<DAE.Exp> value;
list<DAE.Exp> instDims;
end VARIABLE;
end Variable;
uniontype Statement
record ALGORITHM
list<DAE.Statement> statementLst;
end ALGORITHM;
end Statement;
...
end SimCode;
package DAELow
uniontype ZeroCrossing
record ZERO_CROSSING
DAE.Exp relation_;
end ZERO_CROSSING;
end ZeroCrossing;
...
end DAELow;
...
end SimCodeInterface;
2.9.1 Interface Package Import into a Template File
To import the above example interface package into a template file, use the following syntax:
import interface SimCodeInterface;
By default, all imported symbols (imported types, functions, constants) are also accessible without the need of a
package qualification, although it is recommended to visually distinguish from a template call to use proper
package names in front of imported functions calls, for example (an extract from the SimcodeTV)
interface package SimCodeTV
…
package System
…
function tmpTick
output Integer tickNo;
end tmpTick;
…
end System;
…
end SimCodeTV;
(Undocumented feature - One can force usage of qualified lookup of names of an imported package by the usage
of the protected keyword in front of the imported package in the interface file, but this usage of the protected
keyword is might be redesigned in the future; with ?forcequalified?)
The unqualified import does not require package name paths to be used when referencing an imported symbol. It
is still possible to use a (fully-)qualified name when needed, for example when a local symbol hides the imported
one (local symbols take precedence over imported ones).
The (fully-)qualified import of a template package forces the usage of fully qualified but shorter paths to
referenced imported symbols (now, effectively, only the package name).
References