Skip to content

Structure Aspect¶ ⧉

The structure aspect contains all the concept and interface declarations of a language.

Concepts¶ ⧉

Concept declarations are the main root nodes that you add to this aspect. When you use nodes use concepts from your language, and the language has generator configurations, the generator of this language will be invoked by default to transform the languages.

How can you create an MPS concept declaration programmatically?

Create an MPS concept declaration programmatically ⧉ (Specific Languages' blog)

Concept XY wasn't found in the language 🔰

Concept not found ⧉ (Specific Languages' blog)

How do you find examples of MPS concepts? 🔰

Finding examples of MPS concepts ⧉ (Specific Languages' blog)

When a concept extends another concept/implements some interfaces, which method is called when they are defined in more than one concepts/interfaces?

The used algorithm is a variant of C3 linearization (source). When the concept doesn't implement the method itself. First, the implemented interfaces are recursively considered in declaration order, then the extended concept. You can try it yourself, for example, for the concept ClassConcept ⧉:

1
2
3
BehaviorRegistryImpl r = (BehaviorRegistryImpl) ConceptRegistry.getInstance().getBehaviorRegistry();
BHDescriptor d = r.getBHDescriptor(concept/ClassConcept/);
#print r.getMRO().calcLinearization(_SAbstractConcept.wrap(concept/ClassConcept/));
If you want to call a specific type() implementation, you have just to cast the node to the corresponding concept and then call the method, for example, myNode as MyITypeable.type()

How can you get the short node ID from the node ID and vice versa?

  • Short one: new IdEncoder().toText(node.getNodeId())
  • Long one: new IdEncoder().parseNodeId(nodeId)

How can I assign node IDs manually?

Read Assigning node IDs explicitly ⧉ (Specific Languages' blog).

The same principal applies to model IDs. The regular model IDs that MPS generate are instances of RegularSModelId and are based on UUIDs ⧉ (using an optional suffix). They are globally unique. There is also an integer-based solution (IntegerSModelId) where the IDs are not globally unique. They are used, for example, for various descriptor models.

Can I find a node by its ID?

You can write a script on the console to find it. For example:

find nodes on the console by ID

The Navigate main menu also contains an action Go to node by ID.

Can I cast concept objects to super-concept objects?

I have the following successful boolean check: someConcept.isSubConceptOf(MySuperConcept) But if I put this code below, it fails with an exception that it cannot perform the cast: ((concept<MySuperConcept>) someConcept) The type of the expression someConcept is concept<>.

There is a cast ⧉ for that. For example:

concept<> cls = node/AbstractMethodError/.concept;
#print cls:BaseConcept.getLanguage().getQualifiedName();

Can I get the ConceptEditorDeclaration for a given concept?

1
2
3
concept<> concept = myNode/.getConcept(); 
model editorModel = LanguageAspect.EDITOR.get((Language) concept.getLanguage().getSourceModule());
#print editorModel.roots(ConceptEditorDeclaration).findFirst({~it => it.conceptDeclaration :eq: concept.asNode; });

Interfaces¶ ⧉

Interfaces work the same way as Java interfaces. They can be used to mark common classes, contain common methods and support the same aspects as concepts. A special interface is INamedConcept ⧉ that defines the name of nodes when a concept implements it.

The concept implements ISuppressErrors ⧉, but the node still contains errors.

You have to implement IDontApplyTypesystemRules and ISkipConstraintsChecking. Suppressing Errors | MPS has some more information.

What's IMainClass ⧉ used for?

It allows to execute Base Language code. Shapes tutorial - Running the code ⧉ contains an example.

Properties¶ ⧉

You can use properties to save data in the model using primitive types, enumerations or regular expressions. string is also considered a primitive type. Think twice before you use this type. Users can type anything as a value if you don't at a constraint to this property.

String properties are also not referencable. For example, let's consider a property that defines a root node's kind. When you use a child instead that can be referenced, you can reuse kinds or even provide a predefined list of kinds with certain constraints. Even an enumeration can be a better choice in some cases unless you need the ability to enter any value.

Changing this design decision later, means deprecating the string property and introducing a migration. If no standard migration is possible, users might even have to change their models manually.

How do you use a list of string properties? 🔰

Wrap your property into a new concept and use a child collection:

Kroki

Can properties be overridden?

No, MPS-17143 ⧉ contains more information.

How to get the name of a concept property?

The easiest way to do this is via PropertyIdRefExpression ⧉. Then you can directly reference the property in question like this: property/MyConcept : myProperty/.getName() With this implementation, every property name change is calculated correctly.

contributed by: @abstraktor

References¶ ⧉

To refer to other nodes in the model, use references. They have a scope and can be automatically created (smart references) for nodes that have a name. References that are out of scopes still work, they are part of the model and are affected by generation. As the name it implies, they only reference nodes which means that the referenced node or property won't change when you edit the cell in the editor. There is also a distinction between optional and mandatory references, especially when trying to delete them.

How do you get the previous label of a reference?

1
2
3
((SReference)node.getReference(link)).getResolveInfo()` 
// or
node.reference<ref>.resolveInfo

Can you use the resolve info to fix a broken reference?

ResolverComponent.getInstance().resolve(...)

How can you find broken references on the console?

1
2
3
#instances<scope = global>(BaseConcept).where({~it => 
    it.references.any({~it => it.target == null; });
})

Deleting references doesn't work when using the editor cell ref. presentation

Example: [> thingref [> ( % thing % -> ref. presentation ) <] <]

Ref presentation automatically uses the name of the interface INamedConcept. If you add auto-deletable: true to the cell and the cardinality is [1], MPS deletes the name but doesn't remove the reference. When the cardinality is [0..1], it deletes the reference. You expect this behavior because the reference is mandatory in the first case; in the second case, it is optional.

Comments