The objectives of this step are the following:

  • Use a DSL workbench to create a language (MPS 2017.2)
  • Uses a template-based code generation approach (TextGen)

The LED example, using MPS

Creating a new project

We start by creating a new project for a language named ArduinoML, associated with a sandbox solution (to write programs in ArduinoML) and a runtime solution (for test purposes).

Creating the concepts

Each concept of the ArduinoML abstract syntax tree is modelled as a concept in MPS.

  • The AppActuator and State concepts implement the INamedConcept interface as one can use their name to reference it;
  • The App concept is considered as a root, as it is the entry point of the language;
  • The SIGNAL concept is an Enumeration, containing the HIGH and LOW value. One can change the way the value is displayed using the presentation field;
  • Modelling concepts in MPS:
    • Properties are used to model simple attributes (here the SIGNAL to send to a given actuator)
    • Children are used to modelling elements that are contained by the concept. An element can only be contained by a single container;
    • References are used to link an element to another one.

Right-click on the project name and select Make to synchronize the language with the other solutions (and do this operation each time the tool underlines your models stating that “generation is required”).

Creating Models

Using these concepts, one can create a program in ArduinoML. Right-click on the sandbox project and create a new App (proposed as it is defined as a root concept). In MPS, the syntax is made by projecting the AST, and a default project does exist in the tool.

It is important to notice that you are not editing text, but directly the AST, using a fill in the blanks (the red parts) approach. The LED example is modelled by filling these holes (we added an isInitial property in the State concept between the two screenshots).

Modifying the projections

The default project has the advantage of existing for free but is not really user-friendly. To create our own projects, we define Editors associated with the concepts.

MPS provides a DSL to model editors. The DSL relies on the definition of collections (horizontal or vertical) to assemble the attributes associated with a given concept in the proper way. This is a tabular approach (which is arguable in terms of design choices and defined syntax) where you need to think of your projection as imbricated boxes.

One must notice that automatic completion is mandatory (using CRTL-space) to find the right symbol while defining the projection.

  • [/ … /]: vertical collection, all elements between these elements will be displayed in a vertical way;
  • [> … <]: horizontal collection;
  • [- … -]: horizontal collection supporting indentation, using ---> to specify the indentation level;
  • { x }: refers to the property x defined in the current concept;
  • (/ % x % … /): a vertical collection of children nodes defined in x.
  • empty: an empty line

As we are defining a set of projections, building the language automatically update the LED model we defined previously.

Specifying Constraints

Constraints are specified as logical expressions evaluated on concept instances. For example, to specify an invariant stating that a pin associated with an actuator must be in [1,13], we associated a logical check on the Actuator concept.

A more interesting constraint is the unique property associated with the state names. To implement it, we add a constraint that looks inside the state parent node and checks for other states with the very same name

Constraints are hard properties. One can define more soft guidelines. For example, a single initial state should be defined in the FSM. This is done thanks to a checking rule in the type system definition.

Controlling Scope

Concept instances are linked together at the global level, as it is the default scope in MPS. As a consequence, if one creates a second App named led2 in the sandbox solution, it is possible to refer to actuators or states defined in led2 inside the led application.

MPS provides scoping mechanisms to support this task. To state that the State concept must not use the default global scope but a homemade one, we add a constraint to the State stating that when looking to fill the next reference with a State, it must inherit it from something defined in its containment hierarchy.

We use the App concept as our scope provider (it must then implements the ScopeProvider interface in addition to the INamedConcept one). We create a behaviour associated with the App concept and override (use the CTRL-O shortcut to open a list of overridable methods) the getScope method. The implementation is straightforward: if asking for a State, we return all the states defined in the App. We do something similar for the actuators and return null when asked for something else.

Generating Code

MPS supports a template-based generation mechanism named TextGen. It also supports language composition mechanisms, which are more expressive but also more complex. In this lab, we will rely on the simple TextGen.

For each concept, we define a TextGenComponent describing how the concept must be projected into plain text. For example, the actuator concept is projected by declaring an integer variable named like the actuator and containing the PIN number. We use the append keyword to add text to the generation buffer.

A root concept will also define the generated filename and extension. When appending a model element, TextGen will recursively call the associated template. The system can automatically iterate on a collection of objects, using the $list keyword.


Expected Work

  • Adapt the language to support sensors and transitions associated with sensors;
  • Identify the abstractions needed in the language to support the 7-segment display;
  • Adapt the language to support it.

Feedback Questions

  • What is the cost/benefits ratio of using a workbench?
  • What are the limitations of such an approach?
  • What about vendor lock-in?