0

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 )
   }
}
Aubin
  • 14,617
  • 9
  • 61
  • 84

1 Answers1

1

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)?;
Christian Dietrich
  • 11,778
  • 4
  • 24
  • 32
  • No, it's not the solution (I've tried it) because the resolution of a field should be made in the scope of the type of the instance. Sample text upgraded. Clicking on 'n' always link to the first type regardless the instance. – Aubin Sep 02 '19 at 12:22
  • The second proposition is not compliant to my grammar. QName is always prefixed by a Data : Data . Field. – Aubin Sep 02 '19 at 12:26
  • sorry i cannot reproduce that. will update the answer with more code. – Christian Dietrich Sep 02 '19 at 15:05