范围复杂的 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 )
}
}
示例文本中的错误是:
- 第 23 行:
sampleB->aaa->num = 55: Couldn't resolve reference to Field 'num'.
- 第 24 行:
sampleB->aaa->str = "Yes!": Couldn't resolve reference to Field 'str'.
如何改进作用域 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)
}
}
语法:
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 )
}
}
示例文本中的错误是:
- 第 23 行:
sampleB->aaa->num = 55: Couldn't resolve reference to Field 'num'.
- 第 24 行:
sampleB->aaa->str = "Yes!": Couldn't resolve reference to Field 'str'.
如何改进作用域 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)
}
}