Here is my 'Types' Xtext grammar:
grammar sample.types.Types with org.eclipse.xtext.common.Terminals
generate types "http://www.types.sample/Types"
Model:
structs += Struct*
data += Data*
assignments += Assignment*
;
Struct:
'struct' name=ID '{'
fields += Field*
'}'
;
Field:
type=Type name=ID
;
Type:
'number'
| 'string'
;
Data:
type=[Struct|ID] name=ID
;
Assignment:
qname=QName '=' value=Value
;
QName:
data=[Data|ID] '.' path=[Field|ID]
;
Value:
INT
| STRING
;
Here is an instance of the 'Types' grammar:
struct SampleA {
number n
string s
}
struct SampleB {
number n
string s
}
SampleA sampleA1
SampleA sampleA2
SampleB sampleB
sampleA1.n = 12
sampleA1.s = "Hello"
sampleA2.n = 12
sampleA2.s = "Hello"
sampleB.n = 42
sampleB.s = "Hello"
The six last lines, which reference fields 'n' and 's', generates the error:
Couldn't resolve reference to Field 'x'.'
I've coded the following custom scope provider without success:
class TypesScopeProvider extends AbstractTypesScopeProvider {
override getScope( EObject context, EReference reference ) {
if( reference === TypesPackage.Literals.QNAME__PATH ) {
val model = EcoreUtil2.getContainerOfType(context, Model)
if( model !== null ) {
val result = newArrayList
for( data : model.data ) {
for( field : data.type.fields ) {
result.add(
EObjectDescription.create(
QualifiedName.create( data.name, field.name ),
field ))
}
}
return new SimpleScope(IScope.NULLSCOPE, result)
}
}
super.getScope( context, reference )
}
}
In your grammar you have
QName:
data=[Data|ID] '.' path=[Field|ID]
;
thus a.b
will become scoped into two references. thus you either have to reflect that in your scope probvider
// TODO: context will be a qname. ask it for its data. ask that for its data and collect fields from there and then
// scope for path
EObjectDescription.create(
QualifiedName.create(field.name ),
field ))
e.g.
override getScope(EObject context, EReference reference) {
if (reference === MyDslPackage.Literals.QNAME__PATH) {
if (context instanceof QName) {
val result = newArrayList
for (field : context.data.type.fields) {
result.add(EObjectDescription.create(QualifiedName.create(field.name), field))
}
System.err.println(result)
return new SimpleScope(IScope.NULLSCOPE, result)
}
}
super.getScope(context, reference)
}
or you have the grammar to reflect your scoping
DataOrField: Data | Field;
QName: dataOrField=[DataOrField|FQN]
FQN: ID ("." ID)?;