Skip to content

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 idmodel-transformation-service
Display nameModel Transformation
DescriptionLanguage support for model transformation definitions (.mt files)
Default URL/plugin/model-transformation
Sourceapp/packages/service-model-transformation, app/packages/language-model-transformation, app/packages/editor-model-transformation
Depends onThe Metamodel plugin; executions go to model-transformation-execution

Languages contributed

Language idNameExtensionTextual editorGraphical editorGenerated
model-transformationModel Transformation.mt
model-transformation_genGenerated Model Transformation.mt_gen

The model transformation language

A .mt file names its metamodel and then contains a sequence of statements.

mt
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:

ElementSyntaxMeaning
Objectname: Class { ... }An object of that class must exist
Linksource[.property] -- target[.property]A link must exist between two matched objects
Referencename { ... }Constrain or update an object matched in an enclosing scope
Deletedelete nameRemove an object matched earlier
Variablevar name[: type] = expressionBind a value for later use in the pattern
Conditionwhere expressionAn arbitrary boolean condition on the match

Objects and links can carry a modifier:

ModifierEffect
(none)The element must exist and is left untouched
createThe element is added
deleteThe element is removed
forbidThe match is rejected if the element exists
requireThe element must exist but is not bound to the rewrite

Inside an object's braces, = assigns a property while ==, !=, <, >, <= and >= constrain the match:

mt
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

mt
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
}
StatementMeaning
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
stopEnd the transformation successfully
killAbort 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 keyContents
astThe serialised AST
typed-astThe type-annotated AST the execution service interprets
model-transformation-textThe 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.

Released under the terms of the repository licence.