Eclipse Xtext Grammar to Model

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:
xtext to model
Or even more graphically using the diagram editor. (for information about how to generate the diagram see this page).
xtext to model

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;
} public void setName(String newName) { String oldName = name; name = newName; if (eNotificationRequired()) eNotify(new ENotificationImpl(this, Notification.SET, MyTestPackage.GREETING__NAME, oldName, name)); } @Override public Object eGet(int featureID, boolean resolve, boolean coreType) { switch (featureID) { case MyTestPackage. GREETING__NAME: return getName(); } return super.eGet(featureID, resolve, coreType); } @Override public void eSet(int featureID, Object newValue) { switch (featureID) { case MyTestPackage. GREETING__NAME: setName((String)newValue); return; } super.eSet(featureID, newValue); } @Override public void eUnset(int featureID) { switch (featureID) { case MyTestPackage. GREETING__NAME: setName(NAME_EDEFAULT); return; } super.eUnset(featureID); } @Override public boolean eIsSet(int featureID) { switch (featureID) { case MyTestPackage. GREETING__NAME: return NAME_EDEFAULT == null ? name != null : !NAME_EDEFAULT.equals(name); } return super.eIsSet(featureID); } @Override public String toString() { if (eIsProxy()) return super.toString(); StringBuffer result = new StringBuffer(super.toString()); result.append(" (name: "); result.append(name); result.append(')'); return result.toString(); } } //GreetingImpl
 
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. ecore model

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. ecore model
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. ecore diagram

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: ecore model

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.

ecore 4

This really needs to be extended to explain why we might want to use supertypes.

Further Reading

More on ECore here and on the Lars Vogel site here.


metadata block
see also:

More on ECore here and on the Lars Vogel site here.

Correspondence about this page

This site may have errors. Don't use for critical systems.

Copyright (c) 1998-2023 Martin John Baker - All rights reserved - privacy policy.