Creating A Domain Specific Language (DSL) With Xtext
Creating A Domain Specific Language (DSL) With Xtext
1 Overview
This report explains how a Domain Specific Language (DSL) can be defined and used by means of
Eclipse and the Xtext plug-ins. Section 2 describes the steps needed to install Eclipse and all required
plug-ins. The creation of a new DSL is explained in Section 3. Hints on solving problems can be found
in Section 4. A number of miscellaneous topics are described in Section 5. Background information
and various small topics can be found in Section 6. Section 7 contains a number of useful commands
and shortcuts.
Note 1: it is possible to use another Eclipse version, i.e. not the DSL version. Then the Xtext
and Xtend plugins have to downloaded in a similar way. For instance, for Eclipse 2021-09 use
2021-09 - http://download.eclipse.org/releases/2021-09:
open “Modeling”, select “Xtext Complete SDK”
open “Programming Languages”, select “Xtend IDE”
Note 2: the Antlr generator library is usually downloaded automatically during the first
build, but if this fails (e.g. because of security settings), it can be installed explicitly using
Help > Install new software;
This section uses a small example to show the details of these ingredients and additional concepts
such as validation and scoping.
Switch workspace
preferXtendStubs = true
We have to do this to make sure that we can use the Xtend language instead of Java in all
the generated classes later on.
4
The project structure of the previous step is very suitable for version management, see Section 5.3
for a brief overview on how to use with Git. There is no need to archive the large .metadata folder.
This defines a grammar to describe tasks of persons by means of a number of production rules:
5
The first production rule determines the start nonterminal of the grammar. In this case, rule
Planning expresses that the grammar consists of a number of PersonTasks; the “*” indicates
zero or more occurrences. The results of the PersonTasks is assigned to feature “personlist”;
the notation “+=” indicates a list of elements. In this case the list can be empty.
The rule for PersonTasks indicates that it starts with terminal string ‘Person’, next feature
“name” is defined as an ID which is a predefined terminal, as shown below. Finally, a number
of Tasks is assigned to feature “tasks”; the notation “+” indicates one or more elements, so
the “tasks” list is not empty.
A Task starts with terminal string ‘Task’, next an Action is assigned to feature “action”, as
indicated by the “=” notation. Similarly, a priority is defined as an INT, which is a non-
negative number. Finally, a duration is defined by an INT and a TimeUnit. The duration part
is optional, as indicated by the “?” notation.
An Action is defined as the choice (indicated by “|”) between four types of actions. Note that
these four actions are defined using predefined terminals ID, STRING, and INT.
Finally, a TimeUnit is defined as an enumeration, consisting of four cases. Each contains a
notation for the meta model on the left in capitals and a terminal on the right (between
quotes) for the concrete syntax. As a shorthand, the following can be used:
enum TimeUnit:
min |
hour |
day |
week
In general, we prefer the more explicit version.
The use of an enumeration type for an optional feature is a bit tricky; when the option is not
present, the feature will not obtain the value “null”, but the default value, which is the first
element of the enumeration. In this case, “unit” will obtain the value “MINUTE”. In Section
3.4.3 we will show how this can be improved.
grammar org.eclipse.xtext.common.Terminals
hidden(WS, ML_COMMENT, SL_COMMENT)
terminal ID : ’^’?(’a’..’z’|’A’..’Z’|’_’)(’a’..’z’|’A’..’Z’|’_’|’0’..’9’)*;
terminal INT returns ecore::EInt: (’0’..’9’)+;
terminal STRING :
’"’ ( ’\\’(’b’|’t’|’n’|’f’|’r’|’u’|’"’|"’"|’\\’) | !(’\\’|’"’) )* ’"’ |
"’" ( ’\\’(’b’|’t’|’n’|’f’|’r’|’u’|’"’|"’"|’\\’) | !(’\\’|"’") )* "’";
terminal ANY_OTHER: .;
This leads to a number of message on the console, with a “Done” at the end, see Figure 3.
Ecore model:
The ecore model TaskDSL.ecore, which has been generated from the Xtext grammar, can be
found in folder model/generated of project persons.tasks. Often the meta model is called
the abstract syntax, whereas the grammar is the concrete syntax.
Observe that the cardinalities of the ecore model are based on the assignments in the grammar:
A basic assignment “=” leads to cardinality [0..1]
A list assignment “+=” leads to cardinality [0..*]
The iterators “*” and “+” of the grammar are not used, so in our example an instance of PersonTasks
with zero Tasks is allowed by the ecore model.
Note that the diagram can be edited to improve the lay-out if needed.
The runtime workspace can be launched from the meta-level workspace by right-clicking on the
project person.tasks in the Package Explorer and select Run As -> Eclipse Application. This leads to a
new Eclipse instance with plug-ins for the new language, henceforth called the runtime workspace.
Observe that the generation of the language infrastructure and the launch of the runtime workspace
are available on the down arrow right of the green button; see Figure 6.
Close the welcome screen in the runtime workspace and create an instance as follows:
Create a new project: File > New > Project, select General > Project; Next
o Give name, e.g.: persons.tasks.models (use default location); Finish
Create a new folder: right-click on project:
o New > Folder, parent: persons.tasks.models
o Give it a name, e.g., Folder name: specs ; Finish
Create a new file in folder for concrete model – right-click on folder:
o New > File
Parent: persons.tasks.models/specs
File name: spec1.tdsl (use extension “.tdsl” specified before, see Section
3.1.1)
Finish; on the question: “Do you want to convert ‘persons.tasks.models’ to
an Xtext project?” answer: Yes
Edit spec1.tdsl, e.g. according to the example below.
o During editing, “Ctrl-Space” gives possible completions or inserts a keyword if there
is no choice.
Next we explain how to view the DSL instance as an instance of the ecore model, see the next figure:
To view this instance of the ecore model, right-click on file spec1.tdsl, select “Open With / Other …”,
choose (from Internal editors): Sample Ecore Model Editor, click OK. Unfold the model and right-click
on an element and select Show Properties View to inspect details, such as the value of the prio
attribute and the values of the optional duration.
To return to the DSL view, right-click on spec1.tdsl and select “Open With / TaskDSL editor”.
Persons are identified by their name and the feature “person” of a Task refers to a person, indicated
by the square brackets around the type “[Person]”. Implicitly, this means that the “name” feature of
the type is used to refer to it. More explicitly, this can also be written as “[Person | ID]”; in this way
also other feature can be used for references. The other parts of the grammar are not changed.
In case an ecore diagram has been generated (Section 3.1.3), answer Yes to the question
about removing the TaskDSL.aird file, which is related to the taskDSL class diagram.
In the runtime workspace, observe that the previous instance “spec1.tdsl” now contains many
errors. If the errors are not visible, perform Project > Clean – click Clean. Create a new file
“spec2.tdsl” and define an instance, such as the one below:
When the cursor is placed on a reference, e.g. a person name in a task, then <F3> navigates to the
definition.
To use the new grammar, regenerate the language infrastructure and launch the runtime workspace
if there are no errors. Create an instance, for instance “spec3.tdsl”:
Planning DepartmentABC
Person: Bob
Note that it is possible to add comments to an instance using // and /* …. */, see the predefined
Xtext terminals SL_COMMENT (Single Line Comment) and ML_COMMENT (Multi Line Comment)
mentioned in Section 3.1.2.
Next we define the method “toText”, using a class with auxiliary methods.
13
Auxiliary methods
It is often convenient to define general methods in a separate class such that they can be used by
different transformations. As a basic example, we define a general method in an xtend class.
Right-click on package “persons.tasks.generator”; select New > Xtend Class; Name:
Auxiliary; Finish
Define class Auxiliary as follows:
class Auxiliary {
The definitions above will lead to errors because types cannot be resolved. This can be solved by
clicking on the error and selecting a quick fix. A general solution is to organize the imports using
<CTRL>-<SHIFT>-O (pressing all three keys simultaneously). In this case this will lead to pop-up
windows where a user has to make a choice. Choose in such a way that the following imports are
obtained:
import java.util.ArrayList
import java.util.List
import persons.tasks.taskDSL.Action
import persons.tasks.taskDSL.Planning
import persons.tasks.taskDSL.Task
Note:
The definitions above are based on the containment hierarchy of classes, going down in this
hierarchy using the attributes of classes. It is also possible to go up in this hierarchy using
“eContainer” which returns for an EObject the containing object or null if there is no such
object. The result has to be casted to the required type, as done in the next example:
Note that the definition of getActions could also have been written in a very concise way as follows:
def static List<Action> getActions(Planning root) {
return root.tasks.map[t|t.action]
}
Text generation
Similarly, create in the same package an Xtend file “TextGenerator.xtend” with the following
contents - the brackets << … >> can be obtained by typing <CTRL>-<SPACE> :
class TextGenerator {
def static toText(Planning root)'''
Info of the planning «root.name»
All Persons:«"\n"»
14
«FOR p : root.persons»«"\t"»«p.name»«"\n"»«ENDFOR»
All actions of tasks:
«FOR t : root.tasks BEFORE "====== \n"
Note that the “FOR” construct allows a few optional parts which specify what to insert
BEFORE: before all iterations (if there is at least one iteration)
SEPARATOR: between all iterations (if there are at least two iterations)
AFTER: after all iterations (if there is at least one iteration)
Method “action2Text” is defined for each subtype of type Action in a polymorphic way using the
“dispatch” keyword.
For more info about Xtend, see [4] and for instance [5]. Note that a wrong use of the angle brackets
<< … >> might lead to strange error messages.
15
Xtend can be considered as a DSL on top of Java. The Java programs generated from the Xtend code
mentioned above can be found in persons.tasks/xtend-gen/persons.tasks.generator. Note that this
layering might accidentally lead to strange errors in the Xtend editor. After corrections it might be
needed to close and reopen the editor or even restart the workspace.
Launch the runtime workspace. Since we already have a correct instance (spec3.tdsl), we can
generate a text file from it by running from the menu:
Project > Clean (Clean all projects)
Note that the first time folder src-gen is generated. In general, do not place manually written files in
this folder. The same holds for other generated folders such as xtend-gen and model/generated.
In this example, src-gen contains the generated text file which is partly shown in Figure 9.
Make a few changes to the instance and observe that the generator is called again when the
instance is saved. Define a few more instance; observe that:
the transformation is only applied to correct instances
file PlanningInfo.txt corresponds to the last saved instance
after Project > Clean file PlanningInfo.txt corresponds to the last instance
16
When the generation takes a considerable amount of time, it might not be convenient that it is
executed automatically after every save (<CRTL>-S). Then the automatic build can be switched off by
selecting Project and unchecking “Build automatically” (it can also be unchecked in: Window >
Preferences > General > Workspace). Then the generation can be started using the options under
Project or using <CRTL>-B.
Finally observe that the translation of the optional duration part of a task is not correct. If this part is
not present in the instance, the translation shows the default values 0 (for INT) and “m” (the first
element of the enum). This will be solved in the next section.
Regenerate the language infrastructure. This will lead to an error in TextGenerator.xtend. In this file,
rewrite infoAction(Task t) as follows:
Note: if the Xtend file reports an error on a missing “duration” feature, regenerate the language
infrastructure again or even restart the workspace (to empty the cache).
Html generation
Create a new Xtend class “HtmlGenerator.xtend” in package persons.tasks.generator, as described
before. Insert the following:
class HtmlGenerator {
def static toHtml(Planning root)'''
<html>
<head>
<style>
table, th, td
{ border:1px solid black;
padding:5px;}
table
{ border-spacing:15px; }
</style>
17
</head>
<body>
<H1>Planning: «root.name»</H1>
«listTasks(root)»
</body>
</html>'''
Extend generator
We add the html generator to the TaskDSLGenerator and place the generated files in a directory
which depends on the file name of the instance:
class TaskDSLGenerator extends AbstractGenerator {
In this mode it is possible to place breakpoints in files of the meta-level workspace, for instance, in
TaskDSLGenerator.xtend. To place or remove a breakpoint, right-click on a line and select “Toggle
Breakpoint”. When, after a Project > Clean or a change and save in the runtime workspace, a
breakpoint is hit, there will be a question at the meta-level workspace whether the Debug
perspective has to be opened; answer “yes”. In this perspective, for instance, tracing information
and values of variables can be inspected.
In the debug mode, changes in the body of methods are immediately available at the runtime level.
This avoids time-consuming restarts of the runtime workspace. This is still needed for larger changes,
e.g., when adding or deleting methods, but then the user is warned to restart the runtime
workspace.
Then feature “anonymous” is true if and only if keyword ‘anonymous’ is present in an instance. It
can be used, for instance, to check in HtmlGenerator.xtend whether person names have to be
shown:
<td>«t.prio»</td>
</tr>
«ENDFOR»
</table>'''
3.7 Validation
This section explains how to add validation checks to the DSL developed above. Starting point of
validation is the file “TaskDSLValidator.java” in package src/persons.tasks.validation, see Figure 10.
To report [potential] faults to the user, in the body of a validation check three pre-defined methods
can be called:
info(…): leads to an “i” in the margin of the instance editor;
warning(...): leads to a yellow exclamation mark in the margin and text in the instance is
underlined yellow;
error(...): leads to a red cross in the margin and text in the instance editor is underlined red;
this also ensures that no code can be generated.
Example
As an example, modify file “TaskDSLValidator.java” as follows:
@Check
void checkTimeUnit(Task task) {
Duration duration = task.getDuration();
if (duration != null){
switch(duration.getUnit()) {
20
case MINUTE:
if (duration.getDl() > 1000)
warning("Rewrite to other unit",null);
break;
case HOUR:
break;
case DAY:
if (duration.getDl() > 150)
info("Maybe rewrite to weeks",null);
break;
case WEEK:
if (duration.getDl() > 52)
error("Deadline longer than 1 year not
allowed",null);
break;
}
}
}
@Check
void checkDoublePersons(Planning planning){
EList<Person> plist = planning.getPersons(); // lists start at
position 0
for (int i = 0; i < plist.size() ; i++){
for (int j = i+1; j < plist.size() ; j++){
if
(plist.get(i).getName().equals(plist.get(j).getName())) {
error("Double name",null);
}
}
}
}
}
Launch the runtime workspace and observe the notifications to the user when changing some
numbers in durations and adding a person name twice to the list of persons of a task.
Validation in Xtend
It is also possible
In Xtend validation methods have the following form:
@Check
def anyName(SomeClassFromYourMetaModel instance) {
…
}
As an example, instead of “TaskDSLValidator.java” one might use the following file
“TaskDSLValidator.xtend”:
package persons.tasks.validation
import org.eclipse.xtext.validation.Check
import persons.tasks.taskDSL.Planning
import persons.tasks.taskDSL.Task
import persons.tasks.taskDSL.TimeUnit
21
@Check
def checkTimeUnit(Task task) {
if (task.duration !== null){
switch(task.duration.unit){
case TimeUnit::MINUTE: if (task.duration.dl > 1000)
{warning("Rewrite to other unit",null)}
case TimeUnit::HOUR: null
case TimeUnit::DAY: if (task.duration.dl > 150)
{info("Maybe rewrite to weeks",null)}
case TimeUnit::WEEK: if (task.duration.dl > 52)
{error("Deadline longer than 1 year not
allowed",null)}
}
}
}
@Check
def checkDoublePersons(Planning planning){
var plist = planning.persons // lists start at position 0
for (var i= 0; i < plist.size ; i++){
for (var j = i+1; j < plist.size ; j++){
if (plist.get(i).name.equals(plist.get(j).name)) {
error("Double name",null)
}
}
}
}
}
More information
Concerning the parameters of the three notification methods, there are a number of patterns to
indicate the location in the instance editor. We list a number of convenient parameters:
error("Error message", null) : the error is reported on the full instance of the type
mentioned in the corresponding @Check method
error("Error message", Literals.SOME_CLASS_FROM_YOUR_META_MODEL__SOME_FIELD) :
the error is reported for the field “someField” of the instance of the type mentioned in the
corresponding @Check method
error("Error message", Literals.SOME_CLASS_FROM_YOUR_META_MODEL__SOME_FIELD,
index) : the error is reported for occurrence “index” of the field “someField” of the
instance of the type mentioned in the corresponding @Check method (useful if “someField”
has a list type)
error("Error message", instanceOfSomeEObject, null, -1) : the error is reported on the
full “instanceOfSomeEObject”
Combinations of the above patterns and many other possibilities are also available. All possible
parameter combinations can be viewed by placing the cursor on info/warning/error and pressing
<F3>.
22
Since validation takes place while the user is typing a concrete model instance, it is important to take
into account that most types may have the null value (this does not hold for lists). For these types,
always test on null values in the validation checks to avoid exceptions on the console. Also cyclic
relations (e.g., a parent relation) may give problems (call stack overflow), so it is good practice to be
robust w.r.t. cyclic relations.
3.8 Expressions
We illustrate the definition of expressions by adding integer expressions and Boolean expressions to
our running example. For an explanation, see [2] and [6].
Integer expressions
In file “TaskDSL.xtext”, change the PaymentAction to
Regenerate the language infrastructure. Extend the file TextGenerator.xtend with methods for
integer expressions:
'''(-«generateExpression(expr.sub)»)'''
Note the feature “amount” has not been used in the html generator, so currently there is no need to
extend the html generator.
Remove the errors by organizing the imports (<CTRL>-<SHIFT>-O). Launch the runtime workspace
and experiment with the use of expressions.
Boolean expressions
To illustrate Boolean expressions, we add an optional “If” clause to the payment action. Change the
PaymentAction in file “TaskDSL.xtext” to
BooleanExpression: BooleanExpressionLevel1
;
BooleanExpressionLevel1 returns BooleanExpression: // Left associativity
BooleanExpressionLevel2
( {ExpressionBinOp.left=current} bop = BinaryBooleanOperator
right=BooleanExpressionLevel2
)*
;
enum BinaryBooleanOperator:
AND = 'AND' |
OR = 'OR'
;
BooleanExpressionLevel2 returns BooleanExpression:
NotExpression |
BooleanExpressionLevel3
;
NotExpression:
"NOT" sub = BooleanExpressionLevel3
;
25
BooleanExpressionConstant:
value=BOOL_LITERAL
;
terminal BOOL_LITERAL returns ecore::EBoolean: 'true' | 'false' | 'TRUE' | 'FALSE'
;
Observe that “BooleanExpressionLevel3” contains the syntactic predicate “=>” which instructs the
parser to try to match ComparisonExpression first, before trying BooleanExpressionBracket. For
more information, see [8], [9], and [10]. This is needed here to avoid errors during language
generation about an ambiguity with brackets. An alternative is to use other brackets, such as “[ …]”
in BooleanExpressionBracket.
Regenerate the language infrastructure. Extend the file TextGenerator.xtend with methods for
Boolean expressions:
}
def static dispatch CharSequence generateExpression(ExpressionCompOp expr)
'''(«generateExpression(expr.left)» «genCompOp(expr.op)»
«generateExpression(expr.right)»)'''
More information
We list three patterns to define expressions, depending on the desired associativity.
ExpressionLevel2
( ({ExpressionAnd.left=current} "&&" right=ExpressionLevel2)
| ({ExpressionOr.left=current} "||" right=ExpressionLevel2)
)?
;
3.9 Scoping
To investigate scoping issues, change the grammar to
Planning ScopingTest
Person: Alice
Person: Carol
Then the runtime shows a problem if we try to use a project name defined in another task:
Note that the problem does not appear if the “name” feature of Task is removed.
If we want to keep the task name, a solution is to extend the scoping rules, see also [11].
First step is to add the possibility for a task to refer to another task. As an example, we add the
optional “extends” feature to make explicit to which other task(s) a task may refer:
package persons.tasks.scoping
import java.util.Collection
import java.util.LinkedList
import org.eclipse.emf.ecore.EObject
import org.eclipse.emf.ecore.EReference
import org.eclipse.xtext.scoping.IScope
import org.eclipse.xtext.scoping.Scopes
import persons.tasks.taskDSL.ProjectUse
import persons.tasks.taskDSL.Task
import persons.tasks.taskDSL.TaskDSLPackage.Literals
/**
* This class contains custom scoping description.
*
* See
https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#scoping
* on how and when to use it.
*/
class TaskDSLScopeProvider extends AbstractTaskDSLScopeProvider {
override getScope(EObject context, EReference reference) {
if (context instanceof ProjectUse &&
reference == Literals.PROJECT_USE__PROJECT
) {
return getScopeProjectUseProject(context as ProjectUse);
}
return super.getScope(context, reference);
}
The added implementation of TaskDSLScopeProvider computes the scope of projects using the
“extends” relation in the DSL. The overridden method “getScope” first checks if it concerns an
instance of type ProjectUse and that it concerns the scope of “project” in the “ProjectUse” rule
(using the literal PROJECT_USE__PROJECT). Then the scope is computed using auxiliary method
“getScopeProjectUseProject”. Otherwise the default scoping mechanism (super.getScope) is used.
Method “getScopeProjectUseProject” first searches for a containing object of type Task higher in the
hierarchy (see also the note in Section 3.4.1). Next the “extends” feature is used to extend the
scope. Variable “visited” ensures termination if the “extends” relation contains a cycle.
Open the runtime workspace and add an “extends” relation to the second task:
Note that the error disappeared. Note that a task C which extends B can also use Alpha:
Alternative solution:
In the solution above the “extends” feature defines which projects are in the scope of a task. An
alternative is to extend the scope with the projects of all tasks in the planning. This can, for instance,
be achieved by changing method “getScopeProjectUseProject” to:
Add import persons.tasks.taskDSL.Planning to resolve the error. Next observe in the runtime
workspace that all projects are in scope:
Task: A Report Strategy persons: Alice Carol priority: 5
defineProjects: Alpha Beta useProjects: Alpha
Task: B Meeting "Training" persons:Carol priority: 7 duration: 3 day
defineProjects: Gamma useProjects: Alpha Gamma Delta
Task: C Lunch Snackbar persons: Alice priority: 1
defineProjects: Delta useProjects: Alpha
4 Problem solving
This section contains a few hints about solving problems. A general approach is to regenerate the
language infrastructure in the meta-level workspace, to perform a Project > Clean in the runtime
30
workspace, or to restart Eclipse. Section 4.1 addresses a problem with the generation. A few notes
about possible grammar errors can be found in Section 4.2. Section 4.3 explains how to repair a
broken runtime workspace.
Observe that removing “name = ID” feature from the Planning rule
leads to a warning: “The entry rule 'Planning' may consume non empty input without object
instantiation. Add an action to ensure object creation, e.g. '{Planning}'.” .
In general, without an explicit object creation action, object creation take place when the first
assignment is executed, which may not occur in this example.
The warning disappears indeed by adding the action (or removing the key word):
Planning: {Planning} 'Planning'
(persons += Person | tasks += Task)*
;
Stop the hanging start-up by clicking on the red button in the Console view of meta-level
workspace.
Outside Eclipse, remove the “.metadata” folder in the runtime workspace.
Start the runtime workspace again; this leads to an empty runtime workspace.
In the empty runtime workspace, select
File->Import…->General->Existing Projects into Workspace
Behind “Select root directory” click on Browse and next on OK to accept the proposed
directory.
Under “projects” the existing projects are selected automatically. Finally, click Finish.
5 Miscellaneous
In the section we collect a number of useful topics. Section 5.1 contains help facilities and tools
Importing and exporting is explained in Section 5.2. Section 5.3 discusses version management.
Options in the “.mwe2” files are presented in Section 5.4.
When using Git as version management system the .gitignore file below is recommended. This file
must be placed alongside the projects folder (under TaskDSLMeta if you followed Section 3.1)
**/src-gen/*
**/xtend-gen/*
**/bin/
**/test-bin/
**/model/generated/
!.gitkeep
.antlr-generator-*-patch.jar
# Below is only needed when Git repo is inside the Eclipse workspace
.metadata/
Important: before committing to Git, create an empty file called .gitkeep in the src, src-gen and
xtend-gen directories. In this way Git will preserve those empty directories which are required for
Eclipse to build the projects. If you do not do this your projects will not build after a fresh checkout
of the Git repository.
By default Eclipse creates the Xtext projects (Section 3.1.1) in the Eclipse workspace. If you want to
move these projects to an existing Git repository first copy the projects from your Eclipse workspace
to your Git repository (in this case copy the projects from the TaskDSLMeta directory). Next select all
projects in Eclipse, right mouse click and press “Delete”. Check “Delete projects contents on disk
(cannot be undone)” and press “Ok”. Now follow the steps described in Section 5.3.2 to import the
projects from an existing Git repository.
A popular Git client which is bundled with Eclipse is EGit. It is also possible to use a generic Git client
like the Git command line or Sourcetree. We sketch the use of both in the following subsections.
5.3.1 EGit
To check out an existing project from Git:
File > Import > Git > Projects from Git -> Next
If you already have a checkout of the Git repository locally, select “Existing local repository”;
otherwise select “Clone URI”.
33
Regenerate serializers, parsers and other infrastructure code as described in Section 3.1.2
To retrieve latest changes from Git:
right-click on project, select Team > Pull
when needed, regenerate files as described in Section 3.1.2
To create a Git repository for an existing project:
Right-click on project, select Team > Share Project …
To upload project updates to Git:
right-click on project, select Team > Commit
validator = {
// composedCheck = "org.eclipse.xtext.validation.NamesAreUniqueValidator"
It can be enabled by uncommenting the corresponding line. After a change, regenerate the language
infrastructure.
6 Background information
For more info about the Eclipse Modeling Framework (EMF) see for instance [7].
There are three different assignment operators for features, each with different semantics.
34
The simple equal sign = is the straight forward assignment, and used for features which take
only one element.
The += sign (the add operator) is used for a multi-valued feature (i.e., a list of values) and
adds the value on the right hand to that feature. Since “attribute_name += ...” is translated
in the meta model to a list, it is convenient to use a plural term (e.g., “values”) for
attribute_name. Similarly, it is a good convention to use a singular term (e.g., “name”) for
the attribute_name in grammar fragment “attribute_name = ...”.
The ?= sign (boolean assignment operator) is used for a feature of type Boolean.
Note that multiple assignments to a feature are allowed, for instance:
'{' tasks += Task ( ',' tasks += Task )* '}'
6.3 Parsing
Xtext uses ANTLR (http://www.antlr.org/) which is a LL(*) recursive-descent parser generator. See,
e.g., http://en.wikipedia.org/wiki/LL_parser. LL parsers do not allow left recursive grammars, see
also the Xtext documentation.
3. Linking: the parse tree is converted into a graph based on the cross-references.
These phases are executed sequentially and, hence, earlier phases cannot use knowledge of later
phases:
lexing has no information about the tokens that are expected by the parser;
parsing cannot use knowledge about possible cross references.
This is ambiguous, because input 3.4 can be tokenized in two ways (even if the grammar rules out
certain combinations): as one token (REAL) or as three tokens (INTEGER; ‘.’; INTEGER). Xtext does
not give a warning in this case; it will be tokenized as a REAL. More problematic is the input “3.”,
which will be parsed as a REAL (no backtracking) and hence yields an error.
This is ambiguous because token input ID can be parsed in two ways, even if the declaration of the
ID clearly indicates, for instance, that it is a constant (so cross-referencing would excludes one of the
possibilities). In general, ambiguity of a grammar is undecidable. If backtracking is not enabled (see
section 5.4), there are limitations on the grammars and often Xtext reports ambiguities after
performing “Generate Language Infrastructure”. Since backtracking can have a negative impact on
performance and the clarity of error messages, it is advisable to avoid backtracking as much as
possible.
Split screen: open two tabs, click on one tab and drag it to the text area; then a vertical or
horizontal screen outline will become visible; release the mouse for the desired lay-out.
7.2 Navigation
Outline view:
o Window -> Show view -> Outline
o <CTRL>-O opens a quick outline that is searchable (including wildcards)
Hyper-linking to referenced elements:
o <F3> while the cursor position is at a cross-reference
o <CTRL>-click on a cross-reference
Find references:
o <CTRL>-<SHIFT>-<G>
Label (with extra information):
o Point at a cross-reference
7.3 Editing
Content assist: (including templates for system and data/process/cluster classes)
o <CTRL>-<SPACE>
Rename element: (including any cross-references)
o <ALT>-<SHIFT>-R
Automatic formatting: (pretty printing)
o <CTRL>-<SHIFT>-F
Organize imports:
o <CTRL>-<SHIFT>-O
References
[1] Xtext overview http://www.eclipse.org/Xtext/
[2] Xtext documentation & short tutorials http://www.eclipse.org/Xtext/documentation/
[3] Xtend overview http://www.eclipse.org/xtend/
[4] Xtend documentation http://www.eclipse.org/xtend/documentation/
[5] 20 Facts about Xtend
http://jnario.org/org/jnario/jnario/documentation/20FactsAboutXtendSpec.html
37