This is the second part of Martin Fowler’s state machine example in MPS. The first one was about creating editor for Miss Grant’s Controller and at the end of it there was a nice editor like this:

While this is cool, this editor doesn’t help to create anything useful like Java code which you could actually execute. So this part will be about code generation for Miss Grant’s Controller.
What it’s going to be like in the end?
There will be java code based on existing set of java classes. Something like this:

Importing java classes
In introductory example Martin Fowler shows some code and a diagram to explain what the state machine is like. To reproduce this state machine framework I copied all the code from introductory example into IDE. (There are some problems with code, so if you try doing it yourself, don’t expect it to compile out-of-the-box.) I also didn’t feel ok copying diagram or linking to it (after all it’s a part of the book), so here is a diagram I got in IntelliJ after copy-pasting code and making some minor changes:

In a way this diagram is very similar to AST from the previous part. StateMachine receives Events and changes its State. States can have several rules called Transitions describing which State StateMachine will be in after receiving some Event. When state machine transitions to a new state, it executes Commands associated with new State. There is also StateMachineFactory which creates StateMachines.
First off, let’s create generator and empty template.

In order to use existing java classes in template, we need to add these java classes to MPS project:

After adding classes to language properties we have to import them into template. There are two ways to do it. The simple one is to press Ctrl+M and start typing package name:

The more complicated way is to add package in generator properties:

After importing the package we’ll be able to use all the state machine classes in template code:

Now that we have simple template let’s map StateMachine concept to this template…

… and see if it’s working

Adding initialState
Before going further it worth noticing that there is probably a missing part in Martin Fowler’s example. There is a notion of resetEvents which return state machine to its initial state and there is even code which does it…

… but state machine DSL doesn’t have any constructs to specify initial state. Let’s fix it by adding reference to StateMachine concept:

Let’s also configure initial state to idle, as it is in the introductory example.

Generating Events, Commands and States
First off, let’s make generated class name more descriptive by prefixing it with state machine name. In editor state machine name is the topmost line:

We add it as property macro and replace spaces in state machine name, otherwise it will be invalid java class name.

Let’s add generating code to create Event, Command and State objects. It will cover the following areas of the state machine DSL (note that State objects won’t be fully configured after this step):

Let’s start writing template by adding some code as if it was written in plain java. Code for creating Event object will be like this:

Now we can add macros to loop over all the events:

Inside the $LOOP$ macro we add property macro to change variable name for each loop cycle:

Similarly we wrap with property macros event “name” and “code” parameters. After writing similar macros for Command and State, template will look like this:

Let’s see what this template generates…

Generating initial state
So far we’ve been generating code which instantiates StateMachine with null initial state. Let’s add macro to initialize it with real state. We replace null with reference to state variable and wrap this variable with reference macro:

Reference macro can return node or string so we just copy initial state name (which is string) from StateMachine node:

After generation StateMachine will be constructed with idle initial state.

Adding transitions and actions
Event and Command objects are now fully constructed but States has yet to be configured with transitions and actions. Let’s start by writing template code for adding transition as if it was plain java:

What we want now is to copy that line for each transition in each state. In pseudo code it will look like this:

At the end equivalent template code will be:

We start by creating outer loop…

… and then inner loop

Parameterizing event and target state is easy. We can simply take names of triggerEvent and targetState references:

Getting name of the current state is more complicated. The problem is that inner $LOOP$ macro doesn’t know anything about outer $LOOP$ macro. There is a way to do it using mapping label (see this thread) but in this case we can do it by taking Transition node’s parent which is a State node:

When generated, the code will look like this:

We do similar thing to generate addAction() statements.

Reset events
The only missing part now is reset events. This is pretty straightforward. We loop over resetEvents and use reference macro to generate parameter names forv addResetEvents() method.

Setting up output path
It’s great to have generated code, but we need to be able to actually use it. Let’s save generated files in the folder with source code from introductory example. It can be done in Solution properties by specifying generator output path:

Now java project for introductory example can be configured to use src_gen folder as a source folder, so whenever state machine changes in MPS, generated files will be seen by IntelliJ.

What’s next?
While having all this code is great we still need:
- unit tests for state machine
- unit tests for state machine DSL