If you have a DSL that describes structure (e.g. like an Entity DSL) you often need to “walk” on this structure using dot/path-expressions.
Let us asume we have a grammar like
Model:
entities+=Entity*
;
Entity:
"entity" name=ID "{"
features+=Feature*
"}"
;
Feature:
Attribute | Reference
;
Attribute:
"attr" name=ID ":" type=DataType
;
enum DataType:
string | int
;
Reference:
"ref" name=ID ":" type=[Entity]
;
and a model like
entity A {
attr a1 : int
attr a2 : string
ref b : B
ref c : C
}
entity B {
attr b1 : string
attr b2 : string
ref a : A
ref c : C
}
entity C {
attr c1 : string
attr c2 : int
}
and want to have expressions like
use A.b.b2
use A.b.c.c1
use A.a1
use A.b.a.a1
but how to do this with Xtext? there are several possibility but the following was working well in my usecase:
Model:
entities+=Entity*
usages+=Usage*
;
Usage:
"use" ref=DotExpression
;
DotExpression returns Ref:
EntityRef ({DotExpression.ref=current} "." tail=[Feature])*
;
EntityRef returns Ref:
{EntityRef} entity=[Entity]
;
a bit of scoping
class MyDslScopeProvider extends AbstractDeclarativeScopeProvider {
def IScope scope_DotExpression_tail(DotExpression exp, EReference ref) {
val head = exp.ref;
switch (head) {
EntityRef : Scopes::scopeFor(head.entity.features)
DotExpression : {
val tail = head.tail
switch (tail) {
Attribute : IScope::NULLSCOPE
Reference : Scopes::scopeFor(tail.type.features)
default: IScope::NULLSCOPE
}
}
default: IScope::NULLSCOPE
}
}
}
and it works fine.
as an additional note: the ast of the expressions look like