SModel Language
The purpose of SModel language is to query and modify MPS models.
Treatment of null values
In OO-language like Java it's common to have a lot of expr == null and expr != null checks, which have to be created in order to fix null pointer exceptions. In queries, these checks often make code unreadable. In order to alleviate this problem, we have liberal treatment of null values. For example, if you ask a null node for a property, you will get null value. If you ask a null node for its children list, you will get empty list, etc.
Types
SModel language has the following types:
- node<ConceptType> - corresponds to an AST node
- nlist<ConceptType> - corresponds to a list of AST nodes
- sconcept<Concept> - corresponds to an AST node that represents a concept
- smodel - corresponds to an instance of the MPS model
- search scope - corresponds to a search scope of a node's reference
Most of the SModel language operations are applicable to these types.
Operation parameters
Many operations in the SModel language have parameters. They can be specified by entering < at the end of an operation.
Queries
Features access
SModel language can be used to access the following features:
- children
- properties
- references
- concept properties
- concept references
To access them, the following syntax is used:
<node expression>.featureName.
If feature is property, then the type of expression is property's type. If feature is a reference or child of 0..1 or 1 cardinality, then the type of this expression is node<LinkTarget>, where LinkTarget is a target concept in a reference or child declaration.
Null checks
Since we treat nulls liberally, we need a way to check for null value. To do this, we have isNull and isNotNull operations.
IsInstanceOf check and type casts
Often, we need to check whether a node is an instance of a particular concept. We can't use Java's instanceof operator since it checks java objects, not MPS nodes. To perform this type of check, the following syntax should be used:
<node expression>.isInstanceOf(Concept)
Also, we also have the isExactly operation which checks whether a node's concept is exactly the one specified by a user.
Once we've checked a node's type against a concept, we usually want to cast an expression to a concept instance and access some of this concept's features. To do so, the following syntax should be used:
<node expression> : Concept
Parent
In order to find a node's parent, we have parent the operation.
Sibling queries
When me manipulate AST, we often want to access a node's siblings (that is, nodes with the same role and parent as the node under consideration). For this task we have the following operations:
- next-sibling/prev-sibling - returns next/previous sibling of a node. If there is no such sibling, null is returned.
- next-siblings/prev-siblings - returns nlist of next/previous siblings of a node. These operations have an optional parameter that specifies whether to include the current node.
Model queries
Often we want to find all nodes in a model which satisfy a particular condition. We have several operations that are applicable to expressions of model type:
- roots(Concept) - returns all roots in a model which are instances of Concept
- nodes(Concept) - returns all nodes in a model which are instances of Concept
Ancestors
During model manipulation, it's common to find all ancestors (parent, parent of a parent, parent of a parent of a parent, etc) of a specified node. For such cases we have two operations:
- ancestor - return one ancestors
- ancestors - returns all ancestors
Both of them have the folowing parameters: - concept type constraint: concept=Concept, concept in [ConceptList]
- whether to include current node: +
Descendants
It's also useful to find all descendants (direct children, children of children etc) of a specified node. We have the descendants operation for such purposes. It has the following parameters:
- concept type constraint: concept=Concept, concept in [ConceptList]
- whether to include current node: +
Search scope queries
In some situations, we want to find out which references can be set in a specified node. For such cases we have search scope operation. It can be invoked with the following syntax:
<node expression>.search scope(link, operationContext)
Concept literal
Often we want to have a reference to a specified concept. For this task we have the concept literal. It has the following syntax:
concept/ConceptName/
Concept operation
If you want to find the concept of a specified node, you can use the concept operation.
Concept hierarchy queries
We can query super/sub-concepts of expression with the concept type. We have the following operations:
*hierarchy - returns inheritance hierarchy of a concept
*super-concepts - returns all super-concepts of the specified concept. There is an option to include/exclude the current concept.
*sub-concepts - returns sub-concepts
Is Role operation
Sometimes we may want to check whether a node has a particular role. For this we have the following syntax:
<node expression>.isRole(Concept : child)
Downcast to lower semantic level
SModel language generates code that works with raw MPS classes. They are low-level, but in some exceptional cases we need to access them. To access low-level objects, you should use a downcast to lower semantic level construct. It has the following syntax:
<node expression>/
Modification operations
Feature changes
The most common change operation is changing a feature. In order to set the value of a property, 0..1 or 1 children, and 0..1 or 1 reference, you can use assignment or set operation. In order to add a child to 0..n or 1..n children collection, you can use .add operation from collection language.
New node creation
There are several ways to create a new node with:
- new operation: new node<Concept>()
- new instance operation on a model: model.newInstance()
- add new(Concept) and set new(Concept) operations applied to feature expressions
- replace with new(Concept) operation
- new root node(Concept) operation applied to a model. In this case the concept should be rootable.
Copy
To create a copy of a node, you can use the copy operation.
Replace with
To replace a node with an instance of another node, you can use the 'replace with' operation. If you want to replace and create at the same time, there is a shortcut operation 'replace with new(Concept)', which takes a concept as a parameter.
Delete operation
If you want to delete a node from its parent, you can use the delete operation.
Add Comment