MPS 2023.3 Help

Generator Language

Generator Language Reference

Mapping Configuration

Mapping Configuration is a container for generator rules, mapping label declarations and references to pre- and post-processing scripts. A generator model can contain any number of mapping configurations - all of them will be involved in the generation process, if the owning generator module is involved. Mapping configuration also serves as a minimal generator unit that can be referenced in the mapping priority rules.

Generator Rule

Generator Rule specifies a transformation of an input node to an output node (except for the conditional root rule which doesn't have an input node and simply creates a new node in the output model). All rules consist of two parts - a premise and a consequence (except for the abandon root rule, which doesn't have a consequence and simply ignores the input node). Any generator rule can be tagged by a mapping label.

All generator rules' functions have the following parameters:

  • node - the current input node (all except the condition-function in conditional root rule)

  • genContext - generation context - allows searching for output nodes, generating of unique names and others

Generator Rules:

Rule

Description

Premise

Consequence

conditional root rule

Generates a root node in the output model. Applied only one time (max) during a single generation step.

condition function (optional), missing condition function is equivalent to a function always returning true.

root template (ref)

root mapping rule

Generates a root node in the output model.

  • concept - applicable concept (concept of the input node)

  • inheritors - if true then the rule is applicable to the specified concept and all its sub-concepts. If false (default) then the sub-concepts are not applicable.

  • condition function (optional) - see conditional root rule above.

  • keep input root - if false then the input root node (if it's a root node) will be dropped. If true then input root will be copied to the output model.

root template (ref)

weaving rule

Allows to insert additional child nodes into the output model. Weaving rules are processed at the end of a generation micro-step just before map_src and reference resolving. The rule is applied on each input node of the specified concept. The parent node for insertion should be provided by the context function.

  • concept - same as above

  • inheritors - same as above

  • condition function (optional) - same as above

  • external template (ref)

  • weave-each

  • context - function that computes the (parent) output node into which the output node(s) generated by this rule will be inserted.

  • anchor (available in Inspector) - specifies a node within the context collection, in front of which the nodes should be inserted, null means insert at the end of the collection

reduction rule

Transforms the input node while this node is being copied to the output model.

  • concept - same as above

  • inheritors - same as above

  • condition function (optional) - same as above

  • external template (ref)

  • in-line template

  • in-line switch

  • dismiss top rule

  • abandon input

pattern rule

Transforms the input node, which matches the pattern.

  • external template (ref)

  • in-line template

  • dismiss top rule

  • abandon input

abandon root rule

Allows to drop an input root node which otherwise would be copied into the output model.

  • applicable concept ( including all its sub-concepts)

  • condition function (optional) - same as above

n/a

drop attribute rule

For a transformed node, controls which attributes get copied from the input node

  • concept - concept of the attribute node (subconcept of jetbrains.mps.lang.core.structure.Attribute)

  • inheritors - if true then the rule is applicable to the specified concept and all its sub-concepts. If false (default) then the sub-concepts are not applicable.

  • condition function (optional) - further restrictions to trigger the rule

n/a

Attribute is not copied when the rule matches

Rule Consequences:

Consequence

Usage

Description

root template (ref)

  • conditional root rule

  • (root) mapping rule

Applies the root template

external template (ref)

  • weaving rule

  • reduction rule

  • pattern rule

Applies an external template. Parameters should be passed if required, they can be one of:

  • pattern captured variable (starting with # sign)

  • integer or string literal

  • null, true, false

  • query function

weave-each

weaving rule

Applies an external template to a set of input nodes.

Weave-each consequence consists of:

  • foreach function - returns a sequence of input nodes

  • reference on an external template

in-line template

  • reduction rule

  • pattern rule

Applies the template code which is written right here.

in-line switch

reduction rule

Consists of set of conditional cases and a default case.

Each case specify a consequence, which can be one of:

  • external template (ref)

  • in-line template

  • dismiss top rule

  • abandon input

dismiss top rule

  • reduction rule

  • pattern rule

Drops all reduction-transformations up to the point where this sequence of transformations has been initiated by an attempt to copy the input node to the output model. The input node will be copied 'as is' (unless some other reduction rules are applicable). The user can also specify an error, warning or an information message.

abandon input

  • reduction rule

  • pattern rule

Prevents the input node from being copied into the output model.

Root Template

Root Template is used in conditional root rules and (root) mapping rules. The Generator language doesn't define specific concept for root template. The generator language defines a special kind of annotation - root template header, which is automatically added to each new root template. The root template header is used for specifying an expected input concept (i.e. concept of input node). MPS uses this setting to perform a static type checking of code in various macro-functions, which are used in the root template.

External Template

External Template is a concept defined in the generator language. It is used in weaving rules and reduction rules.

In external templates the user specifies the template name, input concept, parameters and a content node.

The content node can be any node in the output language. The actual template code in external templates is surrounded by template fragment 'tags' (the template fragment is also a special kind of annotation concept). The code outside template fragment serves as a framework (or context) for the real template code (template fragment) and is ignored by the generator. In external template for weaving rule, the template's context node is required (it is a design-time representation of the rule's context node), while the template for reduction rule can be just one context-free template fragment. An external template for a reduction rule must contain exactly one template fragment, while a weaving rule's template can contain more than one template fragments.

Template fragment has a mapping label property, which is edited in Inspector view.

Mapping Label

Mapping Labels are declared in a mapping configuration and references stored to this declaration are used to label generator rules, macros and template fragments. Such marks allow finding of an output node by a known input node.

Properties:

  • name

  • input concept (optional) - expected concept of the input node of the transformation performed by the tagged rule, macro or template fragment

  • output concept (optional) - expected concept of the output node of the transformation performed by the tagged rule, macro or template fragment

MPS makes use of the input/output concept settings to perform static type checking in get output ... operations.

Macro

Macro is a special kind of an annotation concept which can be attached to any node in template code. Macro brings dynamic aspect into otherwise static template-based model transformation.

Property- and reference-macros are attached to property- and reference-cells respectively, while node-macros (which come in many variations - LOOP, IF etc.) are attached to cells representing the whole node in the cell-editor. All properties of a macro are edited using the inspector view.

All macros have the mapping label property - reference to a mapping label declaration. Additionally, all macros can be parameterized by various macro-functions - depending on the type of the macro. Any macro-function has at least the three following parameters:

  • node - the current input node;

  • genContext - generation context - allows searching for output nodes, generating of unique names and others;

  • operationContext - instance of jetbrains.mps.smodel.IOperationContext interface (used rarely).

Many types of macros have the mapped node (or mapped nodes) function. This function computes the new input node - a substitution for the current input node. If the mapped node function returns null or the mapped nodes function returns an empty sequence, then the generator will skip this macro altogether. I.e. no output will be generated in this place.

Macro

Description

Properties (if not mentioned above)

Property macro

Computes the value of a property.

value function:

  • return type - string, boolean or int - depending on the property type.

  • parameters - standard + templateValue - value in the template code wrapped by the macro.

Reference macro

Computes the referent node in the output model.

Normally executed at the end of a generation micro-step, when the output model (tree) is already constructed.

Can also be executed earlier, if the user code is trying to obtain the target of the reference.

Reference macro supports SNodeReference as specification of a new target. With that, templates don't need access to a target's node model the moment they are applied.

referent function:

  • return type

    • node (type depends on the reference link declaration)

    • SNodeReference

    • string identifying the target node.

  • parameters - standard + outputNode - source of the reference link (in the output model).

IF

The wrapped template code is applied only if the condition is true. Otherwise the template code is ignored and an 'alternative consequence' (if any) is applied.

condition function

>

alternative consequence (optional) - any of:

  • external template (ref)

  • in-line template

  • abandon input

  • dismiss top rule

LOOP

Computes new input nodes and applies the wrapped template to each of them. It also introduces a LOOP variable that becomes available for queries on macros that are attached to nodes wrapped by the LOOP macro. The LOOP variable gives access to the node that is currently being processed by the LOOP macro. For example, LOOP.index holds the index of the current node in the iteration, LOOP.inputNode holds the current node itself.

mapped nodes function

INCLUDE

The wrapped template code is ignored (it only serves as an anchor for the INCLUDE-macro), a reusable external template will be used instead.

Null input makes INCLUDE effectively a no-op.

  • mapped node function (optional)

  • include template - reference to a reusable external template

CALL

Invokes template and replaces wrapped template code with the result of template invocation. Supports templates with parameters.

Null input node is tolerated, and the template is ignored altogether in this case, i.e. CALL yields empty collection of nodes as a result when input/mapped node is null.

  • mapped node function (optional)

  • call template - reference to a reusable external template

  • argument - one of

    • pattern captured variable

    • integer or string literal

    • null, true, false

    • query function

SWITCH

Provides a way to many alternative transformations in the given place in the template code.

The wrapped template code is applied, if none of switch cases is applicable and no default consequence is specified in #template switch.

For null input node, SWITCH may react with a message (specified along with its rules), anchor template node is ignored, and SWITCH macro yields no results.

  • mapped node function (optional)

  • template switch - reference to a template switch

CALL-SITE

Facilitates insertion of a node attributed with a SWITCH/CALL macro in a designated place of the invoked template. In the scenarios when the node wrapped with the SWITCH/CALL macro is relevant in the invoked template/switch, this macro attaches it as a 'call site node'. The node is in such cases processed as a regular template (including potential macros) and the outcome is then supplied to the invoked template/switch as an implicit argument, ready to get inserted into desired location.

Templates and switch declarations have a flag (toggled by intention) that indicates that they like to use the call site node. It's an error to use the $CALL-SITE$ macro inside a template/switch that didn't flag the need to get call site. There's no need to specify any explicit argument in CALL/SWITCH, The MPS generator notices that a template/switch needs its call site and in such cases evaluates further templates (including the macros) prior to processing it. This functionality comes handy in scenarios like conditional type casts (IF needCast (Type) expression ELSE expression) or conversions when there are multiple sources of expression that may serve as an input, and it's therefore impractical to have dedicated switch/template for each case:

switch { case Double : Double.valueOf(expression); case Integer : Integer.valueOf(expression, 16); ... }

COPY-SRC

Copies an input node to the output model. The wrapped template code is ignored.

mapped node function - computes the input node to be copied.

COPY-SRCL

Copies input nodes to the output model. The wrapped template code is ignored.

Can be used only for children with multiple aggregation cardinality.

mapped nodes function - computes the input nodes to be copied.

MAP-SRC

Multifunctional macro, can be used for:

  • marking a template code with a mapping label;

  • replacing the current input node with a new one;

  • perform a non-template based transformation;

  • accessing the output node.

    The MAP-SRC macro is executed at the end of a generator micro-step - after all node- and property-macros, but before any reference-macro is run.

  • mapped node function (optional)

  • mapping func function (optional) - performs non-template based transformation.

    If defined then the wrapped template code will be ignored.

    Parameters: standard + parentOutputNode - parent node in the output model.

  • post-processing function (optional) - give access to the output node.

    Parameters: standard + outputNode

MAP-SRCL

Same as MAP-SRC but can handle many new input nodes (similar to the LOOP-macro)

  • mapped nodes function

  • mapping func function (optional)

  • post-processing function (optional)

WEAVE

Allows to insert additional child nodes into the output model in a similar way Weaving rules are used. The node wrapped in the WEAVE macro (or provided by the use input function) will have the supplied template applied to it and the generated nodes will be inserted.

use input a function returning a collection of nodes to apply the macro to weave reference to a template to weave into the nodes supplied as the input

VAR

Introduces one or more named values into the generator context, which can then be obtained from the genContext object. The stored variables are read-only and live in the generator context within the scope of the VAR macro.

Each variable has the following parameters:

  • type the optional BaseLanguage type of the stored value, defaults to the type inferred from the value function.

  • value function - returns the value to store in the variable.

Template Switch

A template switch is used in pair with the SWITCH-macro (the TemplateSwitchMacro concept). A single template switch can be re-used in many different SWITCH-macros. A template switch consists of set of cases and one default case. Each switch case is a reduction rule, i.e. a template switch contains a list of reduction rules.

The default case consequence can be one of:

  • external template (ref)

  • in-line template

  • abandon input

  • dismiss top rule

    .. or can be omitted. In this case the template code surrounded by corresponding SWITCH-macro will be applied.

A template switch can inherit reduction rules from other switches via the extends property. When the generator is executing a SWITCH-macro it tries to find most specific template switch (available in scope). Therefore the actually executed template switch is not necessarily the one that is defined in the template switch property of the SWITCH-macro.

Through the null-input message property the user can specify an error, warning or info message, which will be shown in the MPS messages view in case when the mapped node function in SWITCH-macro returns null (by default no messages are shown and the macro is skipped altogether).

A template switch can accept parameters, the same way as template declarations. A use of parametrized switch mandates arguments to be supplied in the  SWITCH  macro. The TemplateSwitchMacro concept supports switches both with and without arguments.

Generation Context (operations)

Generation context (the genContext parameter in macro- and rule-functions) allows finding of nodes in the output model, generating unique names and provides other useful functionality.

Generation context can be used not only in the generator models, but also in utility models - as a variable of type gencontext.

Operations of genContext are invoked using the familiar dot-notation: genContext.operation

Finding Output Node

get output <mapping label> for model ( <model> )

Returns the output node generated by a labeled conditional root rule in a specified model.

Issues an error, if there is more than one matching output node.

get output <mapping label> for ( <input node> )

Returns the output node generated from the input node by a labeled generator rule, a macro or a template fragment.

Issues an error if there is more than one matching output node.

pick output <mapping label> for ( <input node> )

only used in the context of the referent function in a reference-macro and only if the required output node is a target of the reference, which is being resolved by that reference-macro.

Returns the output node generated from the input node by a labeled generator rule, a macro or a template fragment. The difference between this and the previous operation is that this operation can automatically resolve the many-output-nodes conflict - it picks the output node, which is visible in the given context.

get output list <mapping label> for ( <input node> )

Returns a list of output nodes generated from the input node by a labeled generator rule, a macro or a template fragment.

get copied output for ( <input node> )

Returns the output node, which has been created by copying of an input node. If during the copying, the input node has been reduced, but the concept of the output node is the same (i.e. it wasn't reduced into something totally different), then this is still considered 'copying'.

Issues an error if there is more than one matching output node.

Generating Unique Name

unique name from <base name> in context <node>

The uniqueness is secured throughout the whole generation session.

Clashing with names that weren't generated using this service is still possible.

The context node is optional, though we recommend to specify it to guarantee generation stability. If specified, then MPS tries its best to generated names 'contained' in a scope (usually a root node). Then when names are re-calculated (due to changes in the input model or in the generator model), this won't affect other names outside the scope.

Template Parameters

#patternvar

Value of the captured pattern variable

available only in rule consequences

param

Value of the template parameter

available only in external templates

Getting Contextual Info

inputModel

The current input model

originalModel

The original input model

outputModel

The current output model

templateNode

The template code surrounded by the macro.

It is only used in macro-functions

The primary flaw is that this operation implies interpreted templates. There is no template model when templates are generated.

Besides, contract of the operation is vague (i.e. what does it give in a context of argument query for a template call).

get prev input <mapping label>

Returns the input node that has been used for enclosing the template code surrounded by the labeled macro.

It is only used in macro-functions.

Transferring User Data

During generation MPS maintains three maps of user objects, each of which has different life span:

  • session object - kept throughout the whole generation session;

  • step object - kept through a single generation step;

  • transient object - only alive during a micro step.

The developer can access the user object maps using the array (square brackets) notation:

session object [ <key> ] step object [ <key> ] transient object [ <key> ]

The key can be any object (java.lang.Object).

Logging

show info <message text> -> <node> show error <message text> -> <node> show warning <message text> -> <node>

Creates message in the MPS message view. If the node parameter is specified then clicking on the message will navigate to that node. In case of an error message, MPS will also output some additional diagnostic info.

Utilities (Re-usable Code)

If you have duplicated code (in rules, macros, etc.) and want to say, extract it to re-usable static methods, then you must create this class in a separate, non-generator model.

If you create an utility class in the generator model (i.e. in a model with the 'generator' stereotype), then it will be treated as a root template (unused) and no code will be generated from it.

Mapping Script

A Mapping script is user code, which is executed either before a model transformation (pre-processing script) or after it (post-processing script). It should be referenced from #Mapping Configuration to be invoked as a part of it's generation step. Mapping scripts provide the ability to perform non-template based model transformations.

Pre-processing scripts are also commonly used for collecting certain information from input model that can be later used in the course of template-based transformation. The information collected by script is saved as a transient-, step- or session-object.

Script sample:

mapping script myPreProcessingScript script kind : pre-process input model modifies model : false (model, genContext)->void { for (node<MyConcept> te : model.nodes(MyConcept)) { // manipulate the model } }

Properties:

script kind

  • pre-process input model - the script is executed at the beginning of a generation step, before the template-based transformation;

  • post-process output model - the script is executed at the end of a generation step, after the template-based transformation.

modifies model

only available if script kind = pre-process input model

If set to true and the input model is the original input model, then MPS will clone it into a transient input model before applying the script. The script is then free to make changes to the model. The changes made by the script will then be visible to the following phase of code generation.

If set to false, the input model is not cloned and the script is not allowed to make changes to the model. If the script tries to modify the input model MPS will raise an error.

Code context:

model

The current model

genContext

The generation context to give access to transient/session or step objects.

Generator performance tips

The MPS team puts effort into making the generator fast. However, with the increasing size of your project and in particular with the growing number of root nodes in your models, you may get into situations when the generation time becomes a problem. This chapter provides some tips on how to improve the generator performance by refactoring your generator definition.

  • Use the Model generation performance report facility - The MPS settings dialog allows you to enable a detailed performance report to be created by the generator. The report sorts the most time-consuming generator steps first. One can easily identify time-consuming generator steps and the activities within a single step.

  • Move code to the runtime - Only the code in the generator templates needs to be generated. The more code you extract from the templates and move it into a language's runtime the better the performance of teh generator. Indeally, only the code that changes based on the input model belongs to the templates.

  • Avoid using dynamic references - Reference macros may return a node/node-ptr or a string value. The string value represents an unresolved referece (called dynamic reference by the MPS generator), which the generator tries to resolve it at each generation step. This slows down the generation process considerably, as can be easily observed in the time spent in restoring references section of the Model generation performance report.


    If you can, use the node/node-ptr return type instead. Alternatively, you can resort to generating single-purpose concepts that hold string properties instead of references and use them instead of the dynamic references in a way similar to how the jetbrains.mps.baseLanguageInternal is used to represent references when BaseLanguage is generated.


  • Avoid type calculations in the generator - The jetbrains.mps.lang.typesystem language provides a .type operation to determine the type of an arbitrary node in the generator. This operation, however, is very time-consuming, so it is advisable to avoid using it repetitively in the generator. Instead, the type may be calculated once and stored in a transient object in the generator context for later use.

You can find more rationale behind some of theese tipps in the Generator performance study.

Last modified: 07 March 2024