This page follows on from some introductory information that I have put about Xtext here and information about the grammar here.
The '.text' file not only specifies the grammar and generates the lexer and parser but it also has an additional function, to specify the mapping to the Ecore model.
I am not planning to fully explain the ECore (EMF) model structure in this page (More on ECore here and on the Lars Vogel site here. ), but roughly, we have an EPackage and inside that a set of EClasses and inside that a set of EDatatype, EReference. and EAttribute s, Like this: | EPackage { EClass MyModel { containment entities : Entity[] } EClass Entity { name : EString extends : Entity // the crosslink } } |
All these model elements (such as EPackage, EClasses, EDatatype, EReference. and EAttribute) extend EObject. EObject is the base of all model elements in the same way that all java classes extend Object.
Simple Example
Lets just take a simple grammar to find out how it gets mapped to a model, lets take the default grammar that appears when we add a new xtext project.
So here is the .xtext file: | grammar com.euclidea... generate myTest "http... Model: greetings+=Greeting*; Greeting: 'Hello' name=ID '!'; |
This generates an ecore file to represent the model like this, but the text version is not very readable: | <?xml version="1.0" en... <ecore:EPackage xmi:version="2.0" ... xmlns:ecore="http://www.eclipse.or... <eClassifiers xsi:type="ecore:EClass... <eStructuralFeatures xsi:type="ecore... eType="#//Greeting" contai... </eClassifiers> <eClassifiers xsi:type="ecore:ECla... <eStructuralFeatures xsi:type="ec... </eClassifiers> </ecore:EPackage> |
We can see the structure better if we open the ecore file using the model editor: | |
Or even more graphically using the diagram editor. (for information about how to generate the diagram see this page). | |
The Eclipse modelling framework generates java code from this '.ecore' file. The java interface for 'Model' looks like: |
public interface Model extends EObject { EList<Greeting> getGreetings(); } // Model |
The java interface for 'Greeting' looks like: | public interface Greeting extends EObject { String getName(); void setName(String value); } // Greeting |
It also generates a java implementation of the model. | public class GreetingImpl extends MinimalEObjectImpl.Container implements Greeting { protected static final String NAME_EDEFAULT = null; protected String name = NAME_EDEFAULT; protected GreetingImpl() { super(); } @Override protected EClass eStaticClass() { return MyTestPackage.Literals.GREETING; } public String getName() { return name; |
x |
|
x |
|
x |
Naming Example
So lets take a another simple grammar file and see how names get mapped to the model:
grammar com.euclideanspace.naming.Example with org.eclipse.xtext.common.Terminals generate example "https://www.euclideanspace.com/naming/Example" Model: a+=Outer*; Outer: Middle; Middle: Inner; Inner: 'c'; |
This grammar accepts only a sequence of the letter 'c'
When we run the mwe2 file our model consists of a string array. |
So there is not a one to one correspondance between the grammar rules and the model elements. So what control do we have about what model elements are created?
So lets try allocating a name in each rule:
This time there is a model element created for each rule:
grammar com.euclideanspace.naming.Example with org.eclipse.xtext.common.Terminals generate example "https://www.euclideanspace.com/naming/Example" Model: a+=Outer*; Outer: name=Middle; Middle: name=Inner; Inner: name='c'; |
|
So to create a model element from a rule then at least one of its values must be assigned. | |
Perhaps we can see this better by generating an ecorediag (for information about how to do this see this page). This shows that each element contains the element below it. The containment relationship is shown by an arrow with a dimond at one end and a closed arrow at the other. |
However there is another way to associate an element with a grammar rule, that is to use curly brackets. So lets remove the name assignments and add curly brackets:
grammar com.euclideanspace.naming.Example with org.eclipse.xtext.common.Terminals generate example "https://www.euclideanspace.com/naming/Example" Model: a+=Outer*; Outer: Middle {Outer.value=current} ; Middle: Inner {Middle.value=current}; Inner: name='c'; |
|
This produces a more complex model using supertypes: | |
Again we can get a better idea of what is going on here by generating an ecorediag: Again Model contains one or more instances of Outer and Outer can contain an instance of Middle. But this time Outer is a supertype of Middle and Middle is a supertype of Inner. On the diagram type extension is shown by a closed arrow with a white centre. |
This really needs to be extended to explain why we might want to use supertypes.