Skip to content

2015_xtext

Gábor Szárnyas edited this page Apr 11, 2015 · 3 revisions

Xtext

The logo of Xtext

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

Install Xtext

Install from eclipse market place: search for Xtext -> Click Install -> etc.

Eclipse Marketplace... inside Help menu

Install Xtext from Marketplace

Note: This will also install the Xtend packages.

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.

Install Xtext from Marketplace

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:
    	attributeTypes+=AttributeType*
    	entities+=Entity+
    	relations+=Relation*
    ;
    

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

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

    '*' -> 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.

  3. 'ID' terminal.

    Definition of AttributeType rule:

    AttributeType:
    	'type' 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 AttributeType rule starts with the type keyword, than an identifies that is stored in a name attribute, and finally an optional ';' character comes.

    1. Reference an instance of a rule

    Definition of Entity and Attribute rules:

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

    With the previous rules, we could declare variables, but these rules reference already declared variables. To achieve this, use the following syntax: '[' eclass ']'

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

  4. Enumeration, grouping expressions, unordered expressions, boolean expression

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

    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. We can group expressions with brackets to add cardinality character to the complex grouped expression. The '&' character defines an unordered list of the rules. In this case, the following solutions are applicable:

    • one nullable
    • nullable one
    • many nullable
    • nullable many
    • one
    • many

    The last two example is valid because of the (...)? expression around the 'nullable' case.

  5. 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:
    	attributeTypes+=AttributeType*
    	entities+=Entity+
    	relations+=Relation*
    ;
    
    // Attribute type rule
    AttributeType:
    	'type' name=ID ';'?	
    ;
    
    //Entity rules
    Entity:
    	'entity' name=ID ('isA' isA=[Entity])?
    	'{'
    	((attributes+=Attribute) 
    	(',' attributes+=Attribute)*)?
    	'}'
    ;
    
    Attribute:
    	name=ID ':' type=[AttributeType] (isKey?='key')?
    ;
    
    //Relation rules
    Relation:
    	'relation'
    	leftEnding=RelationEnding
    	rightEnding=RelationEnding
    ;
    
    RelationEnding:
    	target=[Entity] '(' (multiplicity=Multiplicity & (nullable?='nullable')? ) ')'
    ;
    
    enum Multiplicity:
    	One = "one" | Many = "many"
    ;
    

Building infrastructure

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

Generate infrastructure

The generation may fail due to a missing plug-in. To solve this problem, add the org.eclipse.equinox.common plug-in to the MANIFEST.MF file.

Add plug-in to MANIFEST.MF

Try our new language

  1. Create a general project

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

    General Project

  2. 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

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

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

  4. Now, you have a working language.

Check out the generated AST

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

    type String
    type Int
    
    entity person isA car {
    	name : String,
    	id : String key
    }
    
    entity car {
    	numberPlate : String key
    }
    
    relation car (one) person (many nullable)
    
  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

Create an Xtext language with existing AST metamodel

  1. Import the projects from here.

  2. Switch the AST line

    From (this line implies to generate AST metamodel):

    generate eRDiagramDSL "http://www.bme.hu/mit/mdsd/erdiagram/text/ERDiagramDSL"
    

    To (this line imports our metamodel):

    import "platform:/resource/hu.bme.mit.mdsd.erdiagram/model/erdiagram.ecore" as er
    

    The metamodel can be access via er:: prefix.

  3. Change return values of rules and correct the reference and attribute names:

    grammar hu.bme.mit.mdsd.erdiagram.text.ERDiagramDSL 
    with org.eclipse.xtext.common.Terminals
    
    import "platform:/resource/hu.bme.mit.mdsd.erdiagram/model/erdiagram.ecore" as er
    
    ERDiagram returns er::EntityRelationDiagram:
    	attributetypes+=AttributeType*
    	entities+=Entity+
    	relations+=Relation*
    ;
    
    Relation returns er::Relation:
    	'relation'
    	leftEnding=RelationEnding
    	rightEnding=RelationEnding
    ;
    
    RelationEnding returns er::RelationEnding:
    	target=[er::Entity] 
    	'(' 
    	(multiplicity=Multiplicity & 
    		(nullable?='nullable')?
    	) ')'
    ;
    
    enum Multiplicity returns er::MultiplicityType:
    	One = "One" | Many = "Many"
    ;
    
    Entity returns er::Entity:
    	'entity' name=ID ('isA' isA+=[er::Entity])? 
    	'{' 
    	((attributes+=Attribute) 
    	(',' attributes+=Attribute)*)?
    	'}'
    ;
    
    Attribute returns er::Attribute:
    	name=ID ':' type=[er::AttributeType] (isKey?='key')?
    ;
    
    AttributeType returns er::AttributeType:
    	'type' name=ID ';'?
    ;
    
  4. Do not forget to delete the exported packages in the MANIFEST.MF and to rebuild the infrastructure

  5. Finished From now, the language uses our metamodel to build AST

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 AbstractDeclarativeScopeProvider {
    
    	def scope_Entity_isA(Entity ctx, EReference ref){
    		Scopes::scopeFor((ctx.eContainer as EntityRelationDiagram).entities.filter[x | x != ctx]);
    	}
    
    }

    A scope method follows the scope_[EClass]_[EStructuralFeature](EClass param1, EReference ref) syntax. 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 (further description: 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 our validator

    Validator

  2. Create the following method with @Check annotation

    class ERDiagramDSLValidator extends AbstractERDiagramDSLValidator {
    
    	public static val CYCLE = "CYCLE";
    
    	@Check
    	def checkCycleInInheritance(Entity ctx) {
    		checkCycleInInheritance(ctx, ctx.isA)
    	}
    
    	def checkCycleInInheritance(Entity ctx, Collection<Entity> parents) {
    		if (parents.contains(ctx)) {
    			error("Cycle in the inheritance graph", ERDiagramPackage.Literals.ENTITY__IS_A,CYCLE);
    			return;
    		}
    		for (parent : parents) {
    			checkCycleInInheritance(ctx, ctx.isA);
    		}
    	}
    
    }

    Note: not all cases are covered with this code, so don't use it a real project.

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

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