Skip to content

2016_xtext

András Szabolcs Nagy edited this page Apr 1, 2016 · 6 revisions

Xtext

The logo of Xtext

homesite: https://eclipse.org/Xtext/

Install Xtext

Install Xtext 2.9 using the releases update site: http://download.eclipse.org/modeling/tmf/xtext/updates/composite/releases/

(You can find the link here: https://eclipse.org/Xtext/download.html)

Click Help > Install New Software and complete with next, next finish.

Install New Software

As you can see, we will need Xtend as well.

Create an Xtext language without existing AST metamodel

  1. Create a new Xtext project with the following name: hu.bme.mit.mdsd.erdiagram.text. Name of the language will be hu.bme.mit.mdsd.erdiagram.text.ERDiagramDsl. It should conform to a fully qualified class name. Extension will be er.

New project

You can hit finish, or on the next page you can disable the "Testing support" as we won't need that. This will produce a simple Hello language with greetings messages. It is worth to check this language.

  1. Declare our language

    grammar hu.bme.mit.mdsd.erdiagram.text.ERDiagramDsl with org.eclipse.xtext.common.Terminals
    
    generate eRDiagramDSL "http://www.bme.hu/mit/mdsd/erdiagram/text/ERDiagramDsl"
    

    The grammar keyword declares the name of our language. The with keyword defines an inheritance from an other language. In this case, we are inherited from the Terminals language which enables us to use the ID rule. generate keyword is responsible for generating AST metamodel from the language definition. Package name will be eRDiagramDsl and ns uri will be http://www.bme.hu/mit/mdsd/erdiagram/text/ERDiagramDsl. Name of the EClasses will be the same as the name of the rules.

  2. Entry rule

    Each Xtext language is built up from rules. The entry (or main) rule is the first defined rule which will be the ERDiagram in our case:

    ERDiagram:
    	entities+=Entity+
    	relations+=Relation*
    ;
    

    Syntax: rule name ':' ... ';'

    This rule states that our language consists of one or more Entity object and zero or more Relation objects (rules). The output of a rule can be stored in AST. To do this, we can define references for AST which will be entities and relations in this case.

    ' ' -> exatly one
    '*' -> zero, one or more
    '+' -> one or more
    '?' -> zero or one
    
    _reference_  = _eclass_  -> zero or one reference
    _reference_ += _eclass_  -> zero, one or more reference
    _reference_ ?= _keyword_ -> boolean reference
    

    Note: in this case, 'eClass' equals with a rule name, because the generated AST uses rule names as type names (eClass names).

  3. Attribute type as an enumeration:

    enum AttributeType:
    	INT = 'int' | DOUBLE = 'double' | STRING = 'string' | BOOLEAN = 'boolean' | DATETIME = 'datetime'
    ;
    

    We can define enumerable rules which is mapped to an EMF enumeration in the generated AST. It starts with enum keyword. The key-value pairs are separated by '|' character.

  4. 'ID' terminal.

    First definition of Entity rule:

    Entity:
    	'entity' name=ID ';'?	
    ;
    

    Between apostrophe characters, we can define terminals (or keywords) for our language. The 'ID' terminal comes from the Terminals language, and defines a unique identifier rule. An Entity rule starts with the entity keyword, than a string comforming to the 'ID' terminal comes from the Terminals language, whic is stored in a name attribute, and finally an optional ';' character (keyword) comes.

  5. Reference an instance of a rule with [...]. Group expressions.

    Definition of Entity and Attribute rules:

    Entity:
    	'entity' name=ID ('isA' isA+=[Entity])*
    	('{'
    	(attributes+=Attribute) 
    	(',' attributes+=Attribute)*
    	'}')?
    ;
    
    Attribute:
    	name=ID ':' type=AttributeType (isKey?='key')?
    ;
    

    If we omit the square brackets (isA+=Entity instead of isA+=[Entity]), then we would have to apply the rule again starting with entity keyword, when we would try to use the language. With the square brackets we declare that only a reference is needed to a rule instance: '[' eclass ']'.

    Note: in this case, 'eclass' equals with a rule name, because the generated AST uses rule names as type names.

    We can group expressions with brackets to add cardinality character to the complex grouped expression similarly to the body of the entity we defined: if an entity doesn't have any attribute, then the curly braces can be omitted.

  6. Unordered expressions.

    Relation:
    	leftEnding=RelationEnding
    	'is related with'		
    	rightEnding=RelationEnding
    ;
    
    RelationEnding:
    	(multiplicity=Multiplicity & (nullable?='nullable')?) target=[Entity]
    ;
    
    enum Multiplicity:
    	One = "one" | Many = "many"
    ;
    

    The '&' character defines an unordered list of the rules. In this case, the following solutions are applicable before the entity reference:

    • one nullable
    • nullable one
    • many nullable
    • nullable many
    • one
    • many
  7. The full Xtext code

    grammar hu.bme.mit.mdsd.erdiagram.text.ERDiagramDsl with org.eclipse.xtext.common.Terminals
    
    generate eRDiagramDsl "http://www.bme.hu/mit/mdsd/erdiagram/text/ERDiagramDsl"
    
    //Entry rule
    ERDiagram:
    	entities+=Entity*
    	relation+=Relation*
    ;
    
    Entity:
    	'entity' name=ID ('isA' isA+=[Entity])*
    	('{'
    	attributes+=Attribute
    	(',' attributes+=Attribute)*
    	'}')?
    ;
    
    Attribute:
    	name=ID ':' type=AttributeType (isKey?='key')?
    ;
    
    enum AttributeType:
    	INT = 'int' | DOUBLE = 'double' | STRING = 'string' | BOOLEAN = 'boolean' | DATETIME = 'datetime'
    ;
    
    Relation:
    	leftEnding=RelationEnding
    	'is related with'
    	rightEnding=RelationEnding
    ;
    
    RelationEnding:
    	(multiplicity=MultiplicityType & (nullable?='nullable')?) target=[Entity]
    ;
    
    enum MultiplicityType:
    	One = 'one' | Many = 'many'
    ;
    

Building infrastructure

When you modify your xtext files, you have to build the infrastructure for your language. The following figure shows where to click to generate.

Generate infrastructure

Try our new language

  1. Start a runtime Eclipse.

  2. Create a general project

    New->Project...->General->Project Name: hu.bme.mit.mdsd.erdiagram.text.example

    General Project

  3. Create a file with 'er' extension

    New->File Name: example.er

    General File with 'er' extension

    Add xtex nature in the pop-up window.

    Xtext nature pop-up

  4. (Optional, if you missed the pop-up window) Add Xtext nature

    Right click on project -> Configuration -> Add Xtext nature

  5. Now, you can try out the language. Notice that you can use auto completion and quick fixes as well.

Check out the generated AST

  1. Create an example file with 'er' extension and fill it with the following content:

    entity person {
    	name : string,
    	id : int key
    }
    
    entity driver isA person {
    	licence : string
    }
    
    entity car {
    	numberPlate : string key
    }
    
    one person is related with nullable many car
    
  2. Open with Simple Ecore Model Editor

    Right click on the file -> Open -> Open with... -> Simple Ecore Model Editor

    Open with Simple Ecore Model Editor

    This will show you the AST built from the text.

    AST of the text

Scoping

Scoping defines which elements are referable by a given reference. For instance, we don't want to enable self inheritance.

  1. Open our scope provider

    Scope Provider

  2. Create the following method:

    class ERDiagramDslScopeProvider extends AbstractERDiagramDslScopeProvider {
    
    	override IScope getScope(EObject context, EReference reference) {
    		if (context instanceof Entity) {
    			return Scopes.scopeFor(
    				(context.eContainer as ERDiagram)
    				.entities.filter[x | x != context]
    			)
    		}
    		return super.getScope(context, reference)
    	}
    
    }

    This scope restrict the available objects for the isA reference of all the Entity EClass. The Scopes class contains static methods to create scope descriptions from a list of EObjects.

    Note: This is an Xtend file, simple Java code is generated under the xtend-gen folder (further description about the language can be found here: http://eclipse.org/xtend/)

  3. Check out in our example (Runtime Eclipse, example.er file).

Validation

Static analysis is always required for any language. In this example, we want to raise an error if a cycle occurs in the inheritance graph.

  1. Open the validator Xtend file (ERDiagramDslValidator.xtend).

  2. A validation method for given type requires the following things: @Check annotation, one parameter with the correct type, using the error, warning or info methods to create markers on the editor.

    class ERDiagramDslValidator extends AbstractERDiagramDslValidator {
    
    	Set<Entity> entitiesAlreadyChecked = new HashSet
    
    	@Check
    	def checkCyclicInheritance(Entity entity) {
    		checkCyclicInheritance2(entity)
    		entitiesAlreadyChecked.clear
    	}
    
    	def checkCyclicInheritance2(Entity entity) {
    		entitiesAlreadyChecked += entity
    		for (parent : entity.isA) {
    			if (entitiesAlreadyChecked.contains(parent)) {
    				error("Cyclic inheritance is not allowed.", 						
    					ERDiagramDslPackage.Literals.ENTITY__IS_A)
    				return;
    			}
    			checkCyclicInheritance2(parent)
    		}
    	}
    
    }

    Note: in a real project much more validation would be needed.

  3. Check out in our example (Runtime Eclipse, example.er file)

Generate files on build

  1. Open the ERDiagramDslGenerator.xtend file.

  2. Create the following method:

    class ERDiagramDslGenerator extends AbstractGenerator {
    
    	override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) {
    		fsa.generateFile('entities.txt', 'Entities: ' + 
    			resource.allContents
    				.filter(typeof(Entity))
    				.map[name]
    				.join(', '))
    	}
    }

    The will generate the an entities.txt file after modifying and building the example ERDiagram model containing the name of each entities.

  3. Check it out.

    Scope Provider

Create an Xtext language with existing AST metamodel

You may have noticed the model folder in the Xtext project, which contains a generated ecore and genmodel file. Xtext uses EMF under the hood. Xtext can also work with existing ecore models and can generate a basic json like language.

To make it work you either generate a new xtext project along with a new grammer from the existing ecore model or change your grammer int the following two points: (1) instead of generate you have to import the ecore model and (2) rules must be accompanied with a returns <existing EClass>.

In this tutorial, we will generate a new language based on the previously create erdiagram and will highlight the differences.

  1. Clone and check out any branch from this repository, then import the project with the erdiagram ecore model in it.

  2. Create a new Xtext Project From Existing Ecore Models

    New Xtext Project From Existing Ecore Models

  3. Choose the existing ecore model and select the root element as the entry rule.

    Select ecore model

  4. On the next page you can give the project name, extension, etc. As for this tutorial you can leave everything on default.

  5. You may have to add Xtext nature to the *.erdiagram project.

  6. Examine the generated grammar (MyDsl.xtext). Header and entry rule:

    // automatically generated by Xtext
    grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.common.Terminals
    
    import "hu.bme.mit.mdsd.erdiagram" 
    import "http://www.eclipse.org/emf/2002/Ecore" as ecore
    
    EntityRelationDiagram returns EntityRelationDiagram:
    	{EntityRelationDiagram}
    	'EntityRelationDiagram'
    	name=EString
    	'{'
    		('entities' '{' entities+=Entity ( "," entities+=Entity)* '}' )?
    		('relations' '{' relations+=Relation ( "," relations+=Relation)* '}' )?
    		('temporalAttributes' '{' temporalAttributes+=Attribute ( "," temporalAttributes+=Attribute)* '}' )?
    	'}';
    

References

Lab material

MDSD 2021

MDSD 2020

MDSD 2019

(Gradually replaced with updated content)

MDSD 2018

MDSD 2017

MDSD 2016

MDSD 2015

MDSD 2014

System Integration 2014

Clone this wiki locally