This example features an executable language using GEMOC Sequential Engine.
It illustrates the GEMOC animation framework on a Finite State Machine language.
While similar to the K3FSM example (cf. Chapter 16, K3FSM Example), this example focuses on the separation of the base language: FSM and the executable sequential language: XSFSM by using language polymorphism.
The tree editor and graphic editor are defined on top of the FSM language. They are considered as if they were pre-existing (Ie. legacy).
Then, Melange is used to built the XSFSM executable language by inheriting from FSM. The animation layer is also built as an extensions of the graphical editor.
This allows to use the model polymorphism. Ie. it is possible to edit FSM models and then run them as XSFSM with the appropriate animation extensions.
More information and the sources of these projects are available online on Github.
Install the projects of this language:
Create and start an eclipse runtime:
Install sample models for the language:
The following figure presents :
Please note that for simplification of the diagram, the associations that doesn’t represent a containment, have been represented as attribute rather than links.
Main eclipse plugins:
The figure Figure 17.2, “Language relations, packages and classes view.” presents an overview of the main artefacts in the projects:
By following these instructions, you’ll be able to reproduce this example from scratch.
You may apply some of these steps in a different order. This is only an example of a valid scenario.
In the following step, we’ll create a language with usual EMF technologies: Ecore + Sirius
It will be considered as a "legacy" language (ie. as if it was developped by a third party).
Create an Ecore project:
Menu File → New → Ecore Modeling Project
In the org.eclipse.gemoc.example.k3fsm project, open the genmodel file,
org.eclipse.gemoc.example.melangek3fsm (on the package: Fsm → Fsm → section All)In the org.eclipse.gemoc.example.melangek3fsm.fsm.model project, open the genmodel file,
You’ll obtain the following projects: org.eclipse.gemoc.example.melangek3fsm.fsm.model.edit and org.eclipse.gemoc.example.melangek3fsm.fsm.model.editor
Create a representation for edition
in the Default layer; New Diagram Element → Container; ID = "StateContainer"; Domain class = "State"; Semantic candidate expression = [self.ownedStates/]
[self.eContainer().oclAsType(FSM).initialState = self/]; create another gradient in it and set a border size to 4.in the Default layer; New Diagram Element → Element Based Edge; ID = "TranditionEdge"; Domain class = "Transition"; Source mapping = "StateContainer"; source finder expression = [self.source/]; target Mapping = "StateContainer"; Target Finder Expression = [self.target/]
aql:self.getLabel()import org.eclipse.gemoc.example.k3fsm.Transition; )public String getLabel(Transition transition) {
final StringBuilder res = new StringBuilder();
res.append(transition.getName());
res.append("\n");
res.append("");
res.append(transition.getInput());
res.append(" / ");
res.append(transition.getOutput());
return res.toString();
}The following actions allow to declare a language for both GEMOC and Melange using the ecore defined previously.
| Label | Value |
|---|---|
Package name | org.eclipse.gemoc.example.melangek3fsm |
Language name | FSM |
Melange file name | FSM |
Ecore file location | browse to find org.eclipse.gemoc.example.melangek3fsm.fsm.model/model/fsm.ecore |
external language FSM {
syntax "platform:/resource/org.eclipse.gemoc.example.melangek3fsm.fsm.model/model/fsm.ecore"
}name = org.eclipse.gemoc.example.melangek3fsm.fsm.FSM ecore = platform:/resource/org.eclipse.gemoc.example.melangek3fsm.fsm.model/model/fsm.ecore metaprog = org.eclipse.gemoc.metaprog.ecore
Fill the wizard with the following:
| Label | Value |
|---|---|
Aspect package prefix | org.eclipse.gemoc.example.melangek3fsm |
Aspect package sufffix | .aspects |
Aspect file name | FSMAspects |
Aspect classes suffix | Aspect |
Ecore base package name | org.eclipse.gemoc.example.melangek3fsm |
Ecore file path | browse to find org.eclipse.gemoc.example.melangek3fsm.fsm.model/model/fsm.ecore |
package org.eclipse.gemoc.example.melangek3fsm.fsm.k3dsa
import fr.inria.diverse.k3.al.annotationprocessor.Aspect
import fr.inria.diverse.k3.al.annotationprocessor.InitializeModel
import fr.inria.diverse.k3.al.annotationprocessor.Main
import fr.inria.diverse.k3.al.annotationprocessor.Step
import org.eclipse.gemoc.example.melangek3fsm.fsm.State
import org.eclipse.gemoc.example.melangek3fsm.fsm.StateMachine
import org.eclipse.gemoc.example.melangek3fsm.fsm.Transition
import static extension org.eclipse.gemoc.example.melangek3fsm.fsm.k3dsa.StateAspect.*
import static extension org.eclipse.gemoc.example.melangek3fsm.fsm.k3dsa.StateMachineAspect.*
import static extension org.eclipse.gemoc.example.melangek3fsm.fsm.k3dsa.TransitionAspect.*
import org.eclipse.emf.common.util.EList
@Aspect(className=StateMachine)
class StateMachineAspect {
public State currentState
public String unprocessedString
public String consummedString
public String producedString
@Step
@Main
def void main() {
try{
while (!_self.unprocessedString.isEmpty) {
_self.currentState.step(_self.unprocessedString)
}
} /* catch (NoTransition nt){
println("Stopped due to NoTransition"+nt.message)
} catch (NonDeterminism nt){
println("Stopped due to NonDeterminism"+nt.message)
} */ catch (Exception nt){
println("Stopped due to "+nt.message)
}
println("unprocessed string: "+_self.unprocessedString)
println("processed string: "+_self.consummedString)
println("produced string: "+_self.producedString)
}
@Step
@InitializeModel
def void initializeModel(EList<String> args){
_self.currentState = _self.initialState;
_self.unprocessedString = args.get(0)
_self.consummedString = ""
_self.producedString = ""
}
}
@Aspect(className=State)
class StateAspect {
@Step
def void step(String inputString) {
// Get the valid transitions
val validTransitions = _self.outgoingTransitions.filter[t | inputString.startsWith(t.input)]
if(validTransitions.empty) {
//throw new NoTransition()
throw new Exception("No Transition")
}
if(validTransitions.size > 1) {
//throw new NonDeterminism()
throw new Exception("Non Determinism")
}
// Fire transition
validTransitions.get(0).fire
}
}
@Aspect(className=Transition)
class TransitionAspect {
@Step
def void fire() {
println("Firing " + _self.name + " and entering " + _self.target.name)
val fsm = _self.source.owningFSM
fsm.currentState = _self.target
fsm.producedString = fsm.producedString + _self.output
fsm.consummedString = fsm.consummedString + _self.input
fsm.unprocessedString = fsm.unprocessedString.substring(_self.input.length)
}
}
/* need to be enabled when feature request */
class NoTransition extends Exception{
}
class NonDeterminism extends Exception{
}runtime data added via aspect | |
a method with @InitializeModel annotation in order to use the launch configuration parameters to set up the runtime data when the model execution starts. | |
a method with @Main annotation that will be used to start the execution. | |
a method with the @Step annotation that will be observable (ie. the debugger can do a pause when a object instance of this class call this method). | |
another method with the @Step annotation that will be observable |
| Label | Value |
|---|---|
Package name | org.eclipse.gemoc.example.melangek3fsm.xsfsm |
Language name | XSFSM |
Melange file name | XSFSM |
Base language name | FSM |
Ecore file location | browse to find org.eclipse.gemoc.example.melangek3fsm.fsm.model/model/fsm.ecore |
K3 DSA Project name | browse to find org.eclipse.gemoc.example.melangek3fsm.fsm.k3dsa |
check the XSFSM.melange file so that it correctly finds the inherited dsl.
package org.eclipse.gemoc.example.melangek3fsm.xsfsm
import-dsl org.inria.gemoc.example.melangek3fsm.fsm.FSM
language XSFSM inherits org.eclipse.gemoc.example.melangek3fsm.fsm.FSM {
with org.eclipse.gemoc.example.melangek3fsm.fsm.k3dsa.StateAspect
with org.eclipse.gemoc.example.melangek3fsm.fsm.k3dsa.StateMachineAspect
with org.eclipse.gemoc.example.melangek3fsm.fsm.k3dsa.TransitionAspect
}This generates a new GEMOC language project: org.eclipse.gemoc.example.melangek3fsm.xsfsm.xsfsm
Debug capabilities (breakpoint support, basic highlighting, …)
Fill the wizard with the following:
| Label | Value |
|---|---|
Project name | org.eclipse.gemoc.example.melangek3fsm.xsfsm.design |
Viewpoint Specification Model name | org.eclipse.gemoc.example.melangek3fsm.xsfsm.XSFSM.odesign |
Viewpoint name | MelangeK3FSM_XSFSMViewpoint |
Diagram name | org.eclipse.gemoc.example.melangek3fsm.xsfsm.XSFSM |
-> Finish
Domain specific animation
This is similar to <<K3FSM-example>> (see section Domain specific animation <<K3FSM-example-debug-animation>>).
It has some specificities: As the fsm.design is based on fsm.ecore but the executed model is actually based on xsfsm.ecore, the typing must use as much as possible the lazy typing of AQL (Sirius query language).
Ie. write `State` instead of `fsm.State`
Additionally, the java service classes should handle both case: edition and running
so the service methods defined in _/org.eclipse.gemoc.example.melangek3fsm.fsm.design/src/org/eclipse/gemoc/example/melangek3fsm/fsm/design/services/FSMServices.java_ that were dedicated to FSM must be duplicated and adapted to work with XSFSM in _/org.eclipse.gemoc.example.melangek3fsm.xsfsm.design/src/org/eclipse/gemoc/example/melangek3fsm/xsfsm/design/services/XFSMServices.java_
The following online resources are related to this examples.
as these resources have been written with a previous version of the GEMOC Studio, their content may need some adaptations to 100% work with the latest version.
[67] asciidoc source of this page: https://github.com/eclipse/gemoc-studio/tree/master/official_samples/MelangeK3FSM/docs/MelangeK3FSM_sequential.asciidoc.