【问题标题】:Xtext field dot-notation scope providerXtext 字段点符号范围提供程序
【发布时间】:2019-12-22 23:29:44
【问题描述】:

以下语法的字段点表示法是否可以在以下 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 块,但它不起作用,而且会很草率。

【问题讨论】:

标签: xtext


【解决方案1】:

根据 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 获得完整解析和明确定义的跟踪。

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

【讨论】:

    猜你喜欢
    • 2016-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-31
    • 2011-04-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多