Unlike any other language aspect, the generator aspect is not a single model. Generator specification can comprise many generator models as well as utility models. Generator model contains templates, mapping configurations and other constructions of the generator language.
Generator model is distinguished from a regular model by the model stereotype - 'generator' (shown after the model name as <name>@generator).
The screenshot below shows the generator module of the smodel language as an example.
Creating a New Generator
New generator is created by using the New -> Generator command in the language's popup menu.
Technically, it is possible to create more than one generator for one language, but at the time of writing MPS does not provide full support for this feature. Therefore languages normally have only one (if any) generator. For that reason, the generator's name is not important. Everywhere in the MPS GUI a generator module can be identified by its language name.
When creating a new generator module, MPS will also create the generator model 'main@generator' containing an empty mapping configuration node.
As a module, generator can depend on other modules, have used languages and used devkits (see Module meta-information).
The generator properties dialog also has two additional properties:
- depends on generators - specifies the dependencies on other generators; this allows making references on templates in another generator;
- mapping constraints - priority relationships between mapping rules can be specified. If such a relationship involves other generator rules, then declaring a dependency on that generator is also required. For details on mapping constraints, see Mapping Priorities, Generation Process: Defining the Order of Priorities, Demo 6: Dividing Generation Process into Steps.
MPS generator engine (or the Generator language runtime) uses mixed compilation/interpretation mode for transformation execution.
Templates are interpreted and filled at runtime, but all functions in rules, macros, and scripts must be pre-compiled.
To avoid any confusion, always follow this rule: after any changes made to the generator model, the model must be re-generated (Shift+F9). Even better is to use Ctrl+F9, which will re-generate all modified models in the generator module.
The transformation is described by means of templates. Templates are written using output language and are edited using the same cell editor as any other 'regular code' in that language. Therefore, the 'template editor' right away has the same level of tooling support - syntax/error highlighting, auto-completion, etc.
Mapping Configuration is a minimal unit, which can form a generation step. It contains #Generator Rules, defines mapping labels and may include pre- and post-processing scripts.
Applicability of each transformation is defined by generator rules.
There are six types of generator rules:
- conditional root rule
- root mapping rule
- weaving rule
- reduction rule
- pattern rule
- abandon root rule
Each generator rule consists of premise and consequence (except for the abandon root rule, whose consequence is predefined and is not specified by the user).
All rules except for the conditional root rule contain a reference on concept of input node (or just input concept) in its premises. All rule premises also contain an optional condition function.
Rule consequence commonly contains a reference to an external template (i.e. a template declared as a root node in the same or different model) or so-called in-line template (conditional root rule and root mapping rule can only have reference to an external template). There are also several other versions of consequences.
The following screenshot shows the contents of a generator model and a mapping configuration example.
Template code can be parameterized by means of macros. The generator language defines three kinds of macros:
- property macro - computes property value;
- reference macro - computes the target (node) of a reference;
- node macro - is used to control template filling at generation time. There are several versions of node macro - LOOP-macro is an example.
Macro implements a special kind of so-called annotation concept and can wrap property, reference or node cells (depending on the kind of macro) in a template code.
Code wrapping (i.e. the creation of a new macro) is done by pressing Ctrl+Shift+M or by applying the 'Create macro' intention.
The following screenshot shows an example of a property macro.
Macro functions and other parameterization options are edited in the inspector view. Property macro, for instance, requires specifying the value function, which will provide the value of the property at generation time. In the example above, output class node will get the same name that the input node has.
The node parameter in all functions of the generator language always represents the context node to which the transformation is currently being applied (the input node).
Some macros (such as LOOP and SWITCH-macro) can replace the input node with a new one, so that subsequent template code (i.e. code that is wrapped by those macros) will be applied to the new input node.
External templates are created as a root node in the generator model.
There are two kinds of external templates in MPS.
One of them is root template. Any root node created in generator model is treated as a root template unless this node is a part of the generator language (i.e. mapping configuration is not a root template). Root template is created as a normal root node (via Create Root Node menu in the model's popup).
The following screenshot shows an example of a root template.
This root template will transform input node (a Document) into a class (baseLanguage). The root template header is added automatically upon creation, but the concept of input node is specified by the user.
It is good practice to specify the input concept, because this allows MPS to perform a static type checking in a macro function's code.
Root template (reference) can be used as a consequence in conditional root rule and root mapping rule. ( When used in a conditional root rule, the input node is not available).
The second kind of template is defined in the generator language and its concept name is 'TemplateDeclaration'. It is created via the 'template declaration' action in the Create Root Node menu.
The following screenshot shows an example of template declaration.
The actual template code is 'wrapped' in a template fragment. Any code outside template fragment is not used in transformation and serves as a context (for example you can have a Java class, but export only one of its method as a template).
Template declaration can have parameters, declared in the header. Parameters are accessible through the #generation context.
Template declaration is used in consequence of weaving, reduction and pattern rules. It is also used as an included template in INCLUDE-macro (only for templates without parameters) or as a callee in CALL-macro.
A template switch is used when two or more alternative transformations are possible in a certain place in template code. In that case, the template code that allows alternatives is wrapped in a SWITCH-macro, which has reference to a Template Switch. Template Switch is created as a root node in generator model via the Create Root Node menu (this command can be seen on the 'menu' screenshot above).
The following screenshot shows an example of a template switch.