Xtext字段点表示法作用域提供程序

时间:2019-12-03 16:48:09

标签: xtext

在下面的Xtext范围提供程序中,可以为以下语法的字段点标记避免“内存”,这是标准的,除了LocalRelation的字段点标记。

grammar test.path.Path with org.eclipse.xtext.common.Terminals

generate path "http://www.path.test/Path"

Model:
    (elements+=AbstractElement)*;

PackageDeclaration:
    'package' name=QualifiedName '{'
    (elements+=AbstractElement)*
    '}';

AbstractElement:
    PackageDeclaration | Entity | Import;

Import:
    'import' importedNamespace=QualifiedNameWithWildcard;

Entity:
    'entity' name=ID '{'
    relations+=Relation*
    '}';

Relation:
    GlobalRelation | LocalRelation;

GlobalRelation:
    ('id')? name=ID ':' ('*' | '+' | '?')? ref=[Entity|QualifiedName];


LocalRelation:
    ('id')? name=ID ':' ('*' | '+' | '?')? 'this' '.' head=[Relation|ID] ('.' tail+=[Relation|ID])*;


QualifiedNameWithWildcard:
    QualifiedName '.*'?;

QualifiedName:
    ID ('.' ID)*;

以下语法实例演示了LocalRelation点概念。

entity A {
    b : B 
}

entity B {
    a : A 
}    

entity C { 
    b : B 
    x1 : this.b  
    x2 : this.x1 
//  x3 : this.x3  No self ref
    x4 : this.b.a     
    x5 : this.x1.a 
    x6 : this.x1.a.b.a.b.a.b.a
}          

entity D { 
    c : C
    x1 : this.c.b.a 
} 

GlobalRelations的作用域解决方案是开箱即用的,但是LocalRelations的作用域当然不能。我想出了以下工作范围提供程序,但是它使用全局映射来跟踪点深度,并使用特殊的头将计数器设置为零,因为在定义引用之前,无法对引用的值进行采样因为这会导致无限循环。

class PathScopeProvider extends AbstractPathScopeProvider {

    @Inject extension IQualifiedNameProvider


    override getScope(EObject context, EReference reference) {
        if (context instanceof LocalRelation) {
            return if (reference == PathPackage.Literals.LOCAL_RELATION__HEAD)
                getScopeLocalRelation_HEAD(context as LocalRelation, context.eContainer as Entity)
            else if (reference == PathPackage.Literals.LOCAL_RELATION__TAIL)
                getScopeLocalRelation_TAIL(context as LocalRelation, context.eContainer as Entity)
        }
        return super.getScope(context, reference);
    }

    def IScope getScopeLocalRelation_HEAD(LocalRelation contextLocalRelation,
        Entity contextLocalRelationContainingEntity) {
        // Don't touch contextLocalRelation.head not contextLocalRelation.tail!
        val result = newArrayList
        contextLocalRelationContainingEntity.relations.filter(
            target |
                target != contextLocalRelation
        ).forEach [ target |
            {
                result.add(EObjectDescription.create(QualifiedName.create(target.name), target))
                resetDepth(contextLocalRelation)
            }
        ]
        return new SimpleScope(IScope.NULLSCOPE, result)
    }

    def IScope getScopeLocalRelation_TAIL(LocalRelation contextLocalRelation,
        Entity contextLocalRelationContainingEntity) {
        // Note that head is well-defined, while tail is well-defined up to depth
        val head = contextLocalRelation.head
        val result = newArrayList
        val depthSoFar = getDepth(contextLocalRelation)
        incDepth(contextLocalRelation)
        val targetSoFar = if(depthSoFar === 0) head else contextLocalRelation.tail.get(depthSoFar - 1)
        if (targetSoFar instanceof GlobalRelation) {
            val targetSoFar_Global = targetSoFar as GlobalRelation
            targetSoFar_Global.ref.relations.forEach [ t |
                result.add(EObjectDescription.create(QualifiedName.create(t.name), t))
            ]
        } else if (targetSoFar instanceof LocalRelation) {
            var Relation i = targetSoFar as LocalRelation
            while (i instanceof LocalRelation) {
                i = if(i.tail.empty) i.head else i.tail.last
            }
            (i as GlobalRelation).ref.relations.forEach [ t |
                result.add(EObjectDescription.create(QualifiedName.create(t.name), t))
            ]
        }
        return new SimpleScope(IScope.NULLSCOPE, result)
    }

    // DEPTH MEMORY
    val enity_relation__depthSoFar = new HashMap<String, Integer>()

    private def void resetDepth(LocalRelation r) {
        enity_relation__depthSoFar.put(r.fullyQualifiedName.toString, 0)
    }

    private def int getDepth(LocalRelation r) {
        enity_relation__depthSoFar.get(r.fullyQualifiedName.toString)
    }

    private def int incDepth(LocalRelation r) {
        enity_relation__depthSoFar.put(r.fullyQualifiedName.toString, getDepth(r) + 1)
    }

}
  • 可以以任何方式避免这种额外的深度记忆吗?
  • 是否有某种内部方法可以检测到到目前为止范围内的尾巴深度?

我已经尝试过使用try-catch块,但这不起作用,而且还是很草率。

1 个答案:

答案 0 :(得分:0)

下面的解决方案是根据克里斯蒂安·迪特里希(Christian Dietrich)的建议Xtext and Dot/Path-Expressions得出的,如果您希望实现标准的场点路径作用域,则可以使用该示例。

上面的语法需要进行一些小的更改。

LocalRelation:
    ('id')? name=ID ':' ('*' | '+' | '?')? 'this' '.' path=Path;

Path: head=[Relation|ID] ({Path.curr=current}  "." tail+=[Relation|ID])*;

唯一的变化是{Path.curr=current},这就是使一切与众不同的“神奇成分”。范围提供程序现在几乎是微不足道的,尤其是与OP中的怪物相比。

override getScope(EObject context, EReference reference) {
    if (context instanceof Path) {
        if (reference == PathPackage.Literals.PATH__HEAD) {
            // Filter self reference
            return new FilteringScope(super.getScope(context, reference), [ e |
                !Objects.equals(e.getEObjectOrProxy(), context)
            ]);
        } else  { // DOT_EXPRESSION__TAIL 
            val target = context.curr.followTheDots
            if (target === null) {
                return IScope::NULLSCOPE
            }
            return new FilteringScope(super.getScope(target, reference), [ e |
                !Objects.equals(e.getEObjectOrProxy(), context)
            ]);
        }
    }
    return super.getScope(context, reference);
}

def Entity followTheDots(Path path) {
    var targetRelation = if(path.tail === null || path.tail.empty) path.head else path.tail.last;
    return if (targetRelation instanceof GlobalRelation)
        targetRelation.ref
    else if (targetRelation instanceof LocalRelation)
        targetRelation.path.followTheDots
    else null // happens when dot path is nonsense
}


def GlobalRelation followTheDots(LocalRelation exp) {
    var targetRelation = if(exp.tail === null || exp.tail.empty) exp.head else exp.tail.last;
    return if (targetRelation instanceof GlobalRelation)
        targetRelation
    else if (targetRelation instanceof LocalRelation)
        targetRelation.followTheDots
    else null // happens when dot path is nonsense
}

虽然我们无法在范围提供者中“接触” tail,但“魔术成分” {LocalRelation.curr=current}允许迭代范围提供者完全访问以前的完整解决方案通过curr定义的引用。在进行范围界定后,可以通过headtail获得完整的解决方案和定义明确的线索。

[...已编辑,因为原始文件有误! ...]