Statestep Code Generation

For power and flexibility, code generation is based on templates. In brief, this means that you take a template for whatever language you want to generate code for, run it through a processing tool to combine it with your Statestep model, and get executable code as your output.

In principle, given a Statestep model in XML format, there are many possible ways to transform it into code. However, the recommended way is to use a free third-party tool: FMPP, the FreeMarker-based text PreProcessor. This is the approach described here and any templates provided on the Statestep website will be FMPP templates.

The good news is that you don't need to know anything much about FMPP (or XML) to use a template to generate source code.

Generating Code With FMPP

1. Install FMPP

The first step is to download and install FMPP. Installation is straightforward. However, you will need to add either the Xalan or Jaxen library to FMPP, described as "custom extensions" at the end of FMPP's installation notes. Do this by putting the Xalan or Jaxen .jar file in the 'lib' subdirectory of your FMPP installation as directed.

2. How FMPP Works

FMPP takes two inputs. The first input is a data file - which will be a Statestep model in XML format. Though you don't need to understand the data format, the contents will look something like this:

<?xml version="1.0" encoding="UTF-8"?>
<model xmlns="http://statestep.com" version="2">
    <variables>
        <variable name="Motor">
            <value name="on"/>
            <value name="off"/>
        </variable>
        <variable name="Valve">
            <value name="open"/>
            <value name="closed"/>
        </variable>
   ...

The second input to FMPP is a template file, which will be part of a template package you've downloaded from the website. This contains program code interspersed with instructions telling FMPP where to insert data taken from the model, for example:

/* Variables. */
typedef enum
{
<#list doc.model.variables.variable as var>
   ${var.@name},
</#list>
   NUM_VARS
} t_variable;

...

After reading these two files, FMPP produces output like this:

/* Variables. */
typedef enum
{
   Motor,
   Valve,
   ...
   NUM_VARS
} t_variable;

...

3. Running FMPP

Command Line

One way to configure FMPP is to pass it options on the command line whenever you run it. Suppose the template file is 'template.c.ftl', your Statestep model is in 'myModel.s2' and you want to put the generated code in a file called 'myModel.c'. Then you run FMPP as follows:

fmpp template.c.ftl -o myModel.c -D "doc:xml(myModel.s2, {namespaceAware : false})"

The '-D' option tells FMPP how to load the data and what to do with it. In this case, it is told that the data is in XML format in a file called 'myModel.s2', that it should put the XML data in a variable called 'doc' for processing, and that it should discard XML namespace specifiers. Any template you download from the Statestep website will assume the Statestep model has been put in a variable called 'doc' and that it is not in any namespace.

If you get an error message mentioning "XPath support", it may be because you haven't added either the Jaxen or Xalan libraries to FMPP (see above).

There's also a little "gotcha" to look out for: Unless you specify an absolute path for the data file, FMPP normally looks for it in the same directory as the template file. If it can't find it, you'll see an error like

Failed to apply the value of the "data" setting.
Caused by: TDD error: Failed to evaluate function "xml".
...

Using a Configuration File

Where many configuration options are required, it makes sense to put these in a configuration file rather than typing them all each time. More complex template packages include such a configuration file, usually called 'config.fmpp'. Before using it, you will need to make minor changes to this file, for example, to specify where to put the generated code. You then run FMPP like this

fmpp -C config.fmpp

In fact, in this case you could just type "fmpp" instead - since by default FMPP will read any options from 'config.fmpp' if it finds a file of this name in the working directory.

If you get an error message mentioning XPath, read the previous section.

If you have a configuration file, you can still pass options on the command line - either to specify additional options or to override some of those in the config file. If you want to learn more, refer to the FMPP documentation.

Customizing Templates

The code produced by a template should be adequate for most purposes so you probably won't need to do this. However, if necessary, you should be able to make minor changes to a template without learning anything about the FreeMarker template language. For example, to change the name of a function in the generated code you need only find the corresponding identifier in the template source. For more complex changes, you'll want to read the documentation included with FMPP. The following notes may also be helpful.

FreeMarker Syntax

Because it looks a bit like XML, you might initially think that every FreeMarker tag should either be self-contained and end with a '/>' (e.g., "<#assign x=1/>") or have a matching closing tag (as in "<#list things as thing> ... </#list>), however this isn't always so, for example: "<#if condition> ... <#else> ... </#if>"; "<#assign x=1>".

Line Breaks and Other Whitespace

By default, any line in a template which contains anything more than a FreeMarker instruction is output, including any whitespace (otherwise ignored when an instruction is on the same line). To prevent the line break being output, you can use <#rt> ("right trim") to direct FreeMarker not to output trailing whitespace on that line.

<#-- this outputs "a, b, c, " -->
<#list ['a', 'b', 'c'] as x>
${x}, <#rt>
</#list>

You can also omit leading whitespace using <#lt> ("left trim"). This allows indentation to be used in the template without affecting the output.

<#-- this outputs " a, b, c," -->
<#list ['a', 'b', 'c'] as x>
   <#lt> ${x},<#rt>
</#list>

The <#t> directive trims both left and right.

<#-- this outputs "a, b, c, " -->
<#list ['a', 'b', 'c'] as x>
   ${x}, <#t>
</#list>

XPath

XPath is a concise way to specify nodes and sets of nodes in an XML document. XPath is supported by FreeMarker and used in Statestep templates. For example, the following instruction uses an XPath expression (the part in double quotes) to specify the nodes of type 'variable' where the node's 'name' attribute has a particular value. (The "[0]" at the end indexes the first element in the returned list; assuming variable names are unique there should only be one.)

<#assign var = doc.model.variables["variable[@name='${varName}']"][0]/>

XPath is very convenient - the alternative in the above example would be to write FreeMarker code to iterate over the variable nodes, looking at the name attribute of each in turn to find the one wanted. XPath is also quite simple and quick to learn. If you're not familiar with it then check out the rapid-fire tutorial by example at zvon.org and Chapter 9 of "XML in a Nutshell" - freely available as a sample chapter on Elliotte Rusty Harold's website.

One thing to be aware of is that XPath expressions work only with respect to a single node, not a list of nodes. For example,

<#assign var = doc.model.variables["variable[@name='${varName}']"][0]/>

works because there is always exactly one 'variables' element in a Statestep model but

<#assign var = doc.model.variables.variable["[@name='${varName}']"][0]/>

produces an error message (unless you have only one variable in your model).