This tutorial assumes you have installed Melange following the installation instructions.
One of the core features of Melange is the support for executable metamodeling. Using Melange, one can define the syntax and semantics of her language (for example, using an interpreter). In this tutorial, we define a simple language for finite-state machine (FSM) modeling. We detail how to create the syntax of our FSM language, its semantics, and how to execute the models conforming to it.
Abstract syntax
We use a metamodel to describe the abstract syntax of the FSM language. Because Melange relies heavily on the Eclipse Modeling Framework, we use Ecore for writing metamodels. First, let’s identify the concepts of our FSM language, starting from Wikipedia’s definition:
A FSM is conceived as an abstract machine that can be in one of a finite number of states. The machine is in only one state at a time; the state it is in at any given time is called the current state. It can change from one state to another when initiated by a triggering event or condition; this is called a transition. A particular FSM is defined by a list of its states, and the triggering condition for each transition.
From this definition, we derive the following Ecore metamodel:
Defining the Ecore metamodel
To define this metamodel, we follow the usual procedure of EMF:
File > New > Project...
Eclipse Modeling Framework > Empty EMF Project >
Next > Name:
“fr.inria.diverse.minifsm” >Finish
.- In the new project, right-click the ‘model’ folder and go in
New > Others...
SelectEclipse Modeling Framework > Ecore model
, thenNext
Name it “minifsm.ecore” and clickFinish
- The new Ecore file contains a package with no name.
Open the Properties view (MenuShow View > Other... > General > Properties
)
Set its name and nsPrefix to “minifsm”, and its nsURI to “http://minifsm/” - For each element of the FSM language add a new class in the root package: right-click >
New Child > EClass
Properties of the class (such as its name) can be edited using the Properties view. - To add an attribute or reference inside a class, right-click it and select
New Child > EAttribute
orEReference
In the end, our new metamodel should look like this:
Generating Java code from the metamodel
- Right-click the model folder,
New > Other...
and selectEclipse Modeling Framework > EMF Generator Model
- Name it “minifsm.genmodel” and put it in the model folder
- Select the previously created Ecore model
- Select its root package
- Open minifsm.genmodel, right-click its root and select
Generate Model Code
- The corresponding Java code is generated in the src/ folder
Defining an interpreter
To define the operational semantics of the FSM language, we implement an interpreter for it. In this section, we use the K3 meta-language for that purpose.
- Open the menu
File > New > Project...
and selectKermeta 3 > K3 Project
Give it the name “fr.inria.diverse.minifsm.interpreter” - Open the
META-INF/MANIFEST.MF
of the new project and add the project containing the FSM metamodel (fr.inria.diverse.minifsm) to its dependencies - In the src/ folder, change the name of the “sample” package to “minifsm.aspects”
- Create a new file “minifsmAspects.xtend” in this package and open it.
The general idea of the K3 meta-language is to allow “re-opening” classes of the metamodel to insert new features (references, attributes, methods). Let’s look at a simple example for the FSM language:
package minifsm.aspects
import minifsm.Transition
import fr.inria.diverse.k3.al.annotationprocessor.Aspect
@Aspect(className = Transition)
class TransitionAspect {
def boolean isActivated() {
return _self.event === null || _self.fsm.currentEvent == _self.event
}
}
The @Aspect annotation takes as argument a className which specifies the class of the metamodel to re-open (think about it as the aspect “pointcut”). All the features of the class annotated with @Aspect are woven on the target class of the metamodel using static introduction: this is the aspect’s advice. In this example, a new method checking if a given transition is fireable is inserted. The @Aspect defines a new implicit variable named _self which references the base class (Transition in this case).
The attribute ‘currentState’ doesn’t exist in FSM but we can add it by aspect:
@Aspect(className=FSM)
class FSMAspect {
public var State currentState
public var String currentEvent
def void execute(EList<String> events){
println("Start")
val eventIt = events.iterator
if(eventIt.hasNext)
_self.currentEvent = eventIt.next
_self.currentState = _self.initialState
while(_self.currentState !== null){
_self.currentState.execute
if(_self.currentState instanceof FinalState)
_self.currentState = null
else{
val candidate = _self.transitions.findFirst[input === _self.currentState && isActivated]
_self.currentState = candidate?.output
}
}
}
}
And of course State need execute():
@Aspect(className=State)
class StateAspect {
def void execute(){
println("Exec "+_self.name)
}
}
@Aspect(className=FinalState)
class FinalStateAspect extends StateAspect {
def void execute(){
println("End")
}
}
Then you need some imports at the begin of minifsmAspect.xtend to enable the call of aspect’s methods for base classes
import static extension minifsm.aspects.FSMAspect.*
import static extension minifsm.aspects.StateAspect.*
import static extension minifsm.aspects.FinalStateAspect.*
import static extension minifsm.aspects.TransitionAspect.*
For more details, have a look at the Kermeta 3 documentation
Define a Melange Language
We have a project defining the domain model and another defining its interpreter.
We will now define a Melange Language to assemble theses two concerns in one entity.
The first step is to create a Melange project:
- Open the menu
File > New > Project...
and selectMelange > Melange Project
, then clickNext
. - Name the project “fr.inria.diverse.melange.fsm” and click
Next
. - Check
Create a plug-in using one of the templates
and selectSimple Melange project
, thenNext
. - Set “fr.inria.diverse.melanger” as package, and change language name to MiniFSM. Browse the location to select minifsm.ecore from the “fr.inria.diverse.minifsm” project, then
Finish
. - Open the
META-INF/MANIFEST.MF
and in thedependencies
tab add Plug-ins- fr.inria.diverse.minifsm.interpreter
fr.inria.diverse.minifsm should be already there thanks to the wizard.
This step is needed to have both metamodel and interpreter projects in the classpath of the Melange project.
- fr.inria.diverse.minifsm.interpreter
You should have these declarations in the .melange file:
language MiniFSM {
syntax "platform:/resource/fr.inria.diverse.minifsm/model/minifsm.ecore"
exactType MiniFSMMT
}
If you look at the outline at the right side, you can see the language typed by its own ‘exactType’.
Each language is defined by an .ecore file but remember we added behaviors with Aspects. So we have to put them also in the languages declaration.
language MiniFsm {
syntax "platform:/resource/fr.inria.diverse.minifsm/model/minifsm.ecore"
with minifsm.aspects.FinalStateAspect
with minifsm.aspects.TransitionAspect
with minifsm.aspects.StateAspect
with minifsm.aspects.FSMAspect
}
As you see the keyword with refer to the class with the @Aspect
Do a right click on the .melange file and select Melange > Language Runtime
.
The new created project contains the metamodel and the aspects declared in the .melange file. They are simple copies from the domain model project and the Kermeta project but you can note that the nsURI of the metamodel and packages of the aspects are differents.
In more complex Languages this content is the result of the differents operators applied (such as merge or slice)
Using the language runtime
Now we will test if the MiniFSM works fine.
To do so, we will declare a transformation that will load a MiniFSM model an execute it.
@Main
transformation main() {
val events = newArrayList('an event')
val model = MiniFSM.load("input/Sample.xmi")
val fsm = model.contents.head as minifsm.minifsmmt.FSM
fsm.init()
fsm.eval(events)
}
Here we have:
- MiniFSM.load(<file.xmi>)
To load a model conform to MiniFSM - @Main
In Melange we can declare several transformations. To set one of them the entry point we tag it with @Main. - model.contents.head as minifsm.minifsmmt.FSM
We get the first element of the model and cast it to the right type.
minifsm.minifsmmt is the package containning all classes of the exactType of the MiniFSM language.
To launch the main(), look inside the folder src-gen
(where Melange generates Java files) and inside the package minifsm, right click on Main.java and select Run As > Java Application
.