Search code examples
dslinstance-variablesxtextxtend

Xtext how to reference variables by qualified name through an instance?


I am having trouble with this concept in building a dsl. I am not sure if it is a simple thing I am missing or something that is not an intended feature of xtext. Hopefully someone can explain it to me in the context of this example.

Given the following minimal grammar:

Model:
    'ns' name=QualifiedName
    classes+=Class*
    instances+=Instance*
    uses+=Use*
;

Class:
    'class' name=ID '{'
        variables+=Variable*
    '}'
;

Variable:
    'var' variable=PrimaryVariable
;

Instance:
    variable=PrimaryVariable '=' 'new' type=[Class]
;

Use:
    reference=[PrimaryVariable|QualifiedName]
;

PrimaryVariable:
    name=ID
;

QualifiedName:
    ID ('.' ID)*
;

I would like to be able to write the following code, which of course is not valid:

ns com.mine
class Class1 {
    var var1
}
instance1 = new Class1
instance1.var1 // <- error here, can't resolve reference

With this grammar and default scoping, only this would work:

ns com.mine
class Class1 {
    var var1
}
instance1 = new Class1
Class1.var1

So my question is: how would I go about implementing the concept of referencing variables by qualified name through an instance variable?

I don't think I could manage qualifiedNameProvider to achieve this because the PrimaryVariable does not have knowledge of what instance it is being used in.

I could of course create a rule which uses two reference (and is what I am currently doing), one to the instance variable and then traverse the instance variable's type to get variables in scope for the variable reference, but this seems like a hack to the way it should be and not as scalable in the case of nested objects.

This is a slightly broad question, I am hoping that I can get informed before I go off doing something completely counter productive.


Solution

  • Here is a sample for the scope provider

    import org.eclipse.emf.ecore.EObject
    import org.eclipse.emf.ecore.EReference
    import org.eclipse.xtext.EcoreUtil2
    import org.eclipse.xtext.naming.QualifiedName
    import org.eclipse.xtext.resource.EObjectDescription
    import org.eclipse.xtext.scoping.IScope
    import org.eclipse.xtext.scoping.impl.SimpleScope
    import org.xtext.example.mydsl6.myDsl.Model
    import org.xtext.example.mydsl6.myDsl.MyDslPackage
    
    class MyDslScopeProvider extends AbstractMyDslScopeProvider {
    
       override getScope(EObject context, EReference reference) {
          if (reference === MyDslPackage.Literals.USE__REFERENCE) {
             val model = EcoreUtil2.getContainerOfType(context, Model)
             if (model !== null) {
                val result = newArrayList
                for (i : model.instances) {
                   result.add( EObjectDescription.create(
                      QualifiedName.create( i.variable.name ), i.variable ))
                   for (v : i.type.variables) {
                      result.add( EObjectDescription.create(
                         QualifiedName.create( i.variable.name, v.variable.name ),
                         v.variable ))
                   }
                }
                println(result)
                return new SimpleScope(IScope.NULLSCOPE, result)
             }
          }
          super.getScope(context, reference)
       }
    }