The Ecore model (or meta model) of a textual language describes the structure of its abstract syntax trees (AST).
Xtext uses Ecore’s EPackages to define Ecore models. Ecore models are declared to be either inferred (generated) from the grammar or imported. By using the generate directive, one tells Xtext to derive an EPackage from the grammar.
Xtext creates
an EPackage
for each generate-package declaration. After the directive generate a list of parameters follows. The name of the EPackage will be set to the first parameter, its nsURI to the second parameter. An optional alias as the third parameter allows to distinguish generated EPackages later. Only one generated package declaration per alias is allowed.
an EClass
for each return type of a parser rule. If a parser rule does not define a return type, an implicit one with the same name as the rule itself is assumed. You can specify more than one rule that return the same type but only one EClass will be generated.
for each type defined in an action or a cross-reference.
an EEnum
for each return type of an enum rule.
an EDataType
for each return type of a terminal rule or a data type rule.
All EClasses, EEnums, and EDataTypes are added to the EPackage referred to by the alias provided in the type reference they were created from.
While walking through the grammar, the algorithm keeps track of a set of the currently possible return types to add features to.
Entering a parser rule the set contains only the return type of the rule.
Entering a group in an alternative the set is reset to the same state it was in when entering the first group of this alternative.
Leaving an alternative the set contains the union of all types at the end of each of its groups.
After an optional element, the set is reset to the same state it was before entering it.
After a mandatory (non-optional) rule call or mandatory action the set contains only the return type of the called rule or action.
An optional rule call does not modify the set.
A rule call is optional, if its cardinality is ' ?' or ' *'.
While iterating the parser rules Xtext creates
an EAttribute in each current return type
of type EBoolean for each feature assignment using the ' ?=' operator. No further EReferences or EAttributes will be generated from this assignment.
for each assignment with the ' =' or ' +=' operator calling a terminal rule. Its type will be the return type of the called rule.
an EReference in each current return type
for each assignment with the ' =' or ' +=' operator in a parser rule calling a parser rule. The EReference's type will be the return type of the called parser rule.
for each assigned action. The reference’s type will be set to the return type of the current calling rule.
Each EAttribute or EReference takes its name from the assignment or action that caused it. Multiplicities will be 0...1 for assignments with the ' =' operator and 0...* for assignments with the ' +=' operator.
Furthermore, each type that is added to the currently possible return types automatically extends the current return type of the parser rule. You can specify additional common super types by means of “artificial” parser rules, that are never called, e.g.
CommonSuperType:
SubTypeA | SubTypeB | SubTypeC;
For each alternative defined in an enum rule, the transformer creates an enum literal, when another literal with the same name cannot be found. The literal property of the generated enum literal is set to the right hand side of the declaration. If it is omitted, you will get an enum literal with equal name and literal attributes.
enum MyGeneratedEnum:
NAME = 'literal' | EQUAL_NAME_AND_LITERAL;
In the next step the generator examines all generated EClasses and lifts up similar features to super types if there is more than one subtype and the feature is defined in every subtypes. This does even work for multiple super types.
As a last step, the generator invokes the post processor for every generated Ecore model. The post processor expects an Xtend file with name MyDslPostProcessor.ext (if the name of the grammar file is MyDsl.xtext) in the same folder as the grammar file. Further, for a successful invocation, the Xtend file must declare an extension with signature process(xtext::GeneratedMetamodel). E.g.
process(xtext::GeneratedMetamodel this) :
process(ePackage)
;
process(ecore::EPackage this) :
... do something
;
The invoked extension can then augment the generated Ecore model in place. Some typical use cases are to:
set default values for attributes,
add container references as opposites of existing containment references, or
add operations with implementation using a body annotation.
Great care must be taken to not modify the Ecore model in a way preventing the Xtext parser from working correctly (e.g. removing or renaming model elements).
The following conditions cause an error
An EAttribute or EReference has two different types or different cardinality.
There is an EAttribute and an EReference with the same name in the same EClass.
There is a cycle in the type hierarchy.
An new EAttribute, EReference or super type is added to an imported type.
An EClass is added to an imported EPackage.
An undeclared alias is used.
An imported Ecore model cannot be loaded.