Constraints

Last modified: 18 June 2020

The Structure Language may sometimes be insufficient to express advanced constraints on the language structure. The Constraints aspect gives you a way to define such additional constraints.

Default concrete concept

For abstract concepts the Constraints aspect can be used to indicate, which concept should be used instead when a node of the abstract concept needs to be created.

image2018 9 12 15 43 19

For example, if a child collection is supposed to hold AbstractCommands and the user hits enter to insert a new child into the collection, a node of the EmptyLine concept is created and inserted.

Similarly. the smodel commands that create new nodes and accept the <default> parameter will correctly create nodes of the concrete concept instead of the abstract one.

image2018 9 12 15 50 5

Can be child/parent/ancestor/root

These are the first knobs to turn when defining constraints for a concept. They determine whether instances of this concept can be hooked as children (parents, ancestors) nodes of other nodes or root nodes in models. You specify them as boolean-returning closures, which MPS invokes each time when evaluating allowed possition for a node in the AST.

Constraintsx1

Languages to import

You will most likely need at least two languages imported in the constraints aspect in order to be able to define constraints - the j.m.baselanguage and j.m.lang.smodel languages. 

can be child

Return false if an instance of the concept is not allowed to be a child of specific nodes.

can be parent

Return false if an instance of concept is not allowed to be a parent of specific concept node (in a given role).

can be ancestor

Return false if an instance of the concept is not allowed to be an ancestor of specific nodes.

can be root

This constraint is available only for rootable concepts (instance can be root is true in the concept structure description). Return false if instance of concept cannot be a root in the given model.

Instance icon

Concepts can have icons associated with them as part of the structure definition:

cicon2
This icons is used whenever the concept is visualized to accompany the name or other textual description of the concept. The nodes of this concept inherit the concept icon, but it can be overriden. The instance icon section of the constraints aspect allows you to override the icon specified on the concept level and react to the actual location and content of the node. You have to import the jetbrains.mps.lang.resources language to be able to specify icon resources
cicon1
This way the icon can be customized to better express the kind of the node beyond just its concept.
cicon3
When null is returned from the constraints, the icon defined in the structure aspect is used.

Property constraints

Technically speaking, "pure" concept properties are not properties in its original meaning, but only public fields. Property constraints allow you to make them real properties. Using these constraints, the behavior of concept's properties can be customized. Each property constraint is applied to a single specified property.

property - the property to which this constraint is applied.

get - this method is executed to get property value every time property is accessed.

set - this method is executed to set property value on every write. The property value is guaranteed to be valid.

is valid - this method should determine whether the value of the property is valid. This method is executed every time before changing the value, and if it returns false, the set() method is not executed.

Example - customizing the description in the completion menu

The completion menu lists available nodes together with some additional descriptive information:

sd1

In order to customize the additional information and provide more details on the individual options listed in the completion menu, you can override the getter of the shortDescription property of the target concept:

 

sd2
sd3

Referent constraints

Constraints of this type help to add behavior to concept's links and make them look more properties-like.

referent set handler - if specified, this method is executed on every set of this link.


scope - defines the set of nodes to which this link can point. The method returns a Scope instance. Please refer to the Scopes documentation for more information on scoping. There are two types of scope referent constraint:

  • inherited

  • reference

While inherited scope simply declares the target concept, the reference scope provides a function that calculates the scope on the fly from the parameters.

If scope is not set for the reference then default scope from the referenced concept is used. If the default scope is also not set then "global" scope is used: all instances of referent concept from all imported models.


presentation (deprecated - the editor aspect now specifies presentation of references, see Editor) - here you specify how the reference will look like in the editor and in the completion list. Sometimes it is convenient to show reference differently depending on context. For example, in Java all references to an instance field f should be shown as this.f, if the field is being shadowed by the local variable declaration with the same name. By default, if no presentation is set, the name of the reference node will be used as its presentation (provided it is an INamedConcept).

Default scope

Suppose we have a link pointing to an instance of concept C and we have no scope defined for this link in referent constraints. When you edit this link, all instances of concept C from all imported models are visible by default. If you want to restrict set of visible instances for all links to concept C you can set default scope for the concept. As in referent constraint you can set search scope, validator and presentation methods. All the parameters are the same.

Please refer to the Scopes documentation for more information on scoping.