范围复杂的 QName(递归语法)

Scoping complex QName (recursive 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:
   ( scalar=Scalar | composite=[Struct|ID] ) name=ID
;
Data:
   type=[Struct|ID] name=ID
;
Assignment:
   qname=QName '=' value=Value
;
QName returns Ref:
   DataRef ( {QName.ref=current} '->' field=[Field|ID] )+
;
DataRef returns Ref:
   {DataRef} data=[Data|ID]
;
Scalar:
     'number'
   | 'string'
;
Value:
     INT
   | STRING
;

一个有两个错误的文本实例(故意):

 1 struct SampleA {
 2    number num
 3    string str
 4 }
 5 struct SampleB {
 6    number  num
 7    SampleA aaa
 8 }
 9
10 SampleA sampleA1
11 SampleA sampleA2
12 SampleB sampleB
13
14 sampleA1->num = 12
15 sampleA1->str = "Hello"
16 sampleA1->aaa->num = 22        // Must be an error because aaa
17                                // is not a field of struct SampleA
18
19 sampleA2->num = 12
20 sampleA2->str = "Hello"
21
22 sampleB->num      = 42
23 sampleB->aaa->num = 55         // Ctrl-clic on 'num' must go to line #2
24 sampleB->aaa->str = "Yes!"
25 sampleB->str      = "toto"     // Must be an error because str
                                  // is not a field of struct SampleB

xtend 范围 class:

package sample.types.scoping

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 sample.types.types.DataRef
import sample.types.types.QName
import sample.types.types.Struct
import sample.types.types.TypesPackage

class TypesScopeProvider extends AbstractTypesScopeProvider {

   override IScope getScope( EObject context, EReference reference ) {
      if( reference === TypesPackage.Literals.QNAME__FIELD ) {
         if( context instanceof QName ) {
            val head = context.ref
            switch( head ) {
               DataRef: return Scopes::scopeFor( head.data.type.fields )
               QName: {
                  val tail = head.field
                  switch( tail ) {
                     Struct: return Scopes::scopeFor( tail.composite.fields )
                  }
               }
               default: return IScope::NULLSCOPE
            }
         }
      }
      super.getScope( context, reference )
   }
}

示例文本中的错误是:

如何改进作用域 class 以递归到复合字段?

感谢 Christian,这是范围界定的最终版本 class:

package sample.types.scoping

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 sample.types.types.Composite
import sample.types.types.DataRef
import sample.types.types.QName
import sample.types.types.TypesPackage

class TypesScopeProvider extends AbstractTypesScopeProvider {

   override IScope getScope( EObject context, EReference reference ) {
      if( reference === TypesPackage.Literals.QNAME__FIELD ) {
         if( context instanceof QName ) {
            val head = context.ref
            switch( head ) {
               DataRef: if( head?.data?.type?.fields !== null ) {
                  return Scopes::scopeFor( head.data.type.fields )
               }
               QName: {
                  val tail = head.field
                  switch( tail ) {
                     Composite: if( tail?.composite?.fields !== null ) {
                        return Scopes::scopeFor( tail.composite.fields )
                     }
                  }
               }
               default: return IScope::NULLSCOPE
            }
         }
      }
      super.getScope( context, reference )
   }
}

我建议在这种情况下使用 https://www.dietrich-it.de/xtext/2013/05/18/xtext-and-dot-expressions.html

中描述的递归语法
DotExpression returns Ref:
    EntityRef ({DotExpression.ref=current}  "." tail=[Feature])*
;

您可以在此处 https://www.eclipse.org/forums/index.php?t=msg&th=1074397&goto=1722095&#msg_1722095

找到未从 AbstractDeclarativeScopeProvider 继承的新范围提供程序的端口
override getScope(EObject context, EReference reference) {
    if (reference == MyDslPackage.Literals.DOT_EXPRESSION__TAIL) {
        if (context instanceof DotExpression) {
            val head = context.ref;
            switch (head) {
                EntityRef:
                    return Scopes::scopeFor(head.entity.features)
                DotExpression: {
                    val tail = head.tail
                    switch (tail) {
                        Attribute: return IScope::NULLSCOPE
                        Reference: return Scopes::scopeFor(tail.type.features)
                        default: return IScope::NULLSCOPE
                    }
                }
                default:
                    return IScope::NULLSCOPE
            }
        }
    }

    return super.getScope(context, reference)
}

在你的情况下,这看起来像

class MyDslScopeProvider extends AbstractMyDslScopeProvider {
    override IScope getScope(EObject context, EReference reference) {
        if (reference === MyDslPackage.Literals.QNAME__FIELD) {
            if (context instanceof QName) {
                val head = context.ref
                switch ( head ) {
                    DataRef: {
                        return Scopes::scopeFor(head.data.type.fields)
                    }
                    QName: {
                        val tail = head.field
                        if (tail.composite !== null) {
                            return Scopes::scopeFor(tail.composite.fields)
                        }
                    }
                    default:
                        return IScope::NULLSCOPE
                }
            }
        }
        super.getScope(context, reference)
    }
}