Model Transformation plugin
Rewrites models. A transformation matches a fragment of a model and changes it; during an optimisation, transformations are the mutation operators — the only moves the search may make.
At a glance
| Plugin id | model-transformation-service |
| Display name | Model Transformation |
| Description | Language support for model transformation definitions (.mt files) |
| Default URL | /plugin/model-transformation |
| Source | app/packages/service-model-transformation, app/packages/language-model-transformation, app/packages/editor-model-transformation |
| Depends on | The Metamodel plugin; executions go to model-transformation-execution |
Languages contributed
| Language id | Name | Extension | Textual editor | Graphical editor | Generated |
|---|---|---|---|---|---|
model-transformation | Model Transformation | .mt | ✅ | ✅ | ❌ |
model-transformation_gen | Generated Model Transformation | .mt_gen | ❌ | ✅ | ✅ |
The model transformation language
A .mt file names its metamodel and then contains a sequence of statements.
using "./shapes.mm"
// A plain match rewrites the model once. Every element of the pattern has to be found
// before any of the marked changes are applied.
//
// * an unmarked element has to exist and stays untouched
// * `create` adds an object or a link
// * `delete` removes one
// * `forbid` rejects the match if the element exists
// * `require` demands the element without binding it to the rewrite
// * `where` adds an arbitrary boolean condition
// * `var` binds a value that can be used further down the pattern
match {
layer: Layer {
index >= 0
}
rectangle: Rectangle {
visible == true
}
forbid circle: Circle { }
require canvas: Canvas { }
var label = rectangle.name
create generated: Circle {
name = label
visible = true
colour = Colour.GREEN
tags = []
radius = 1.0
}
create layer.shapes -- generated
where rectangle.width > rectangle.height
}Patterns
A match { ... } block is a pattern. Every element of the pattern has to be found in the model before any of the marked changes are applied. Elements can be:
| Element | Syntax | Meaning |
|---|---|---|
| Object | name: Class { ... } | An object of that class must exist |
| Link | source[.property] -- target[.property] | A link must exist between two matched objects |
| Reference | name { ... } | Constrain or update an object matched in an enclosing scope |
| Delete | delete name | Remove an object matched earlier |
| Variable | var name[: type] = expression | Bind a value for later use in the pattern |
| Condition | where expression | An arbitrary boolean condition on the match |
Objects and links can carry a modifier:
| Modifier | Effect |
|---|---|
| (none) | The element must exist and is left untouched |
create | The element is added |
delete | The element is removed |
forbid | The match is rejected if the element exists |
require | The element must exist but is not bound to the rewrite |
Inside an object's braces, = assigns a property while ==, !=, <, >, <= and >= constrain the match:
rectangle: Rectangle {
visible == true
}A property may be assigned at most once per object, since a second = on the same property would silently overwrite the first. Comparisons are not restricted: the same property may be constrained several times — width > 10 together with width < 100 — and a property that is compared may still be assigned in the same object.
Names are file-global
Object names must be unique across the whole .mt file, not just within one pattern. An object matched in an outer scope is referred to by name — rectangle { visible = true } — rather than matched again.
Statements
using "./shapes.mm"
// `if match ... then ... else ...` applies a rewrite only when the pattern is found.
// Objects bound by the condition stay in scope inside the `then` block, where they are
// referenced by name instead of being matched again.
if match {
small: Circle {
radius < 1.0
}
} then {
match {
small {
radius = 1.0
}
}
} else {
match {
canvas: Canvas { }
create spare: Layer {
index = 99
}
create canvas.layers -- spare
}
}
// `for match ... do ...` runs the body once per match instead of once in total.
for match {
rectangle: Rectangle { }
} do {
if (rectangle.width > rectangle.height) {
match {
rectangle {
visible = true
}
}
} else {
match {
rectangle {
visible = false
}
}
}
}
// `while match ... do ...` repeats as long as the pattern still matches, and
// `until match ... do ...` repeats until it matches for the first time.
while match {
blank: Annotation {
text == ""
}
} do {
match {
delete blank
}
}
until match {
marker: Layer {
index == 99
}
} do {
match {
target: Canvas { }
create extra: Layer {
index = 99
}
create target.layers -- extra
}
}
// `stop` ends the transformation successfully, `kill` aborts it and discards the result.
if match {
overfull: Canvas {
revision > 100
}
} then {
match {
overfull {
revision = 100
}
}
stop
} else {
match {
broken: Canvas {
revision < 0
}
}
kill
}| Statement | Meaning |
|---|---|
match { … } | Apply the rewrite once |
if match { … } then { … } else { … } | Apply the second block only if the pattern matches |
for match { … } do { … } | Run the body once per match |
while match { … } do { … } | Repeat while the pattern still matches |
until match { … } do { … } | Repeat until the pattern matches |
if (expr) { … } else if (expr) { … } else { … } | Ordinary conditional on an expression |
while (expr) { … } | Ordinary loop on an expression |
stop | End the transformation successfully |
kill | Abort the transformation and discard the result |
Expressions use the same syntax as the Script language — the two share an expression and type system.
The generated model transformation language
.mt_gen files are transformations the platform produced itself. An optimisation run can generate mutation rules from the create / delete / mutate entries of a search block, and writes them into the result tree so you can see exactly which rewrites the search was allowed to perform.
Contribution plugins contributed
None.
Server-side capabilities
| File data key | Contents |
|---|---|
ast | The serialised AST |
typed-ast | The type-annotated AST the execution service interprets |
model-transformation-text | The textual rendering of a generated .mt_gen file |
Execution. A transformation can be run against a model. The plugin forwards the request to the model-transformation-execution service, configured through MODEL_TRANSFORMATION_EXECUTION_SERVICE_URL.
Graphical editor
Transformations have a diagram editor too. Pattern objects and links appear as nodes and edges, with their modifier reflected in the styling, so the effect of a rule can be read at a glance.