【问题标题】:How to do scoping for qualified names in a C++-like language如何在类 C++ 语言中对限定名称进行范围界定
【发布时间】:2017-01-01 05:11:23
【问题描述】:

我已经为一种语言实现了一种语法,这种语言最好被描述为 C++ 的脚本版本,而无需预处理器。我正在尝试使范围界定工作。 (语法大约有 500 行,所以这可能让您了解包含了多少 C++ 语法以及遗漏了多少。它包括枚举、类、全局和类成员函数,以及一些时髦的东西在这里并不重要。)

我觉得问这么宽泛的问题可能有一个简单的答案有点愚蠢,但我觉得在这一点上我已经花了足够的时间在 XText 文档、书籍、网络搜索、相关博客和看看XText的代码最好问问。

我正在尝试确定范围。我已经在工作的一些关键部分:

  1. 它提供了适当的限定名称,因此类 C 的对象成员变量 x 是 C.x。理论上,一旦我有一个类型系统(如 XText 书中的那个)工作,这应该可以让我确定一个对象的类型,然后使用它来导入该类的范围。
  2. 在语法中词法分析器规则可能具有多个相应数据类型的地方,我已经弄清楚了如何使用 ref=[ecore::EObject|GenericDataTypeRule],其中我有 GenericDataTypeRule: name=ID;

所以问题如下:

我如何(有效地!)让我的 MyCppLikeDSLScopeProvider 允许以下类型的引用:

class MyClass {
    void memberFunction();
    Integer j;
    ...
}

Integer MyClass::memberFunction() {
     return j;
}

我尝试过的一些事情:

  • 我尝试过使用 Scopes.scopeFor(context.getAllContentsOfType(ClassDecl),如果类是在同一个文件中定义的,那效果很好,但不知何故,如果类在另一个资源中。例如,如果所有内容都在同一个文件中,并且我正在寻找可以是类、变量、函数或枚举的东西,我可以这样做:

    def scope_SymbolicValue_ref(EObject context, EReference eRef) {
        var Iterable<EObject> crossRefClassDeclTargets = context.getAllContentsOfType(ClassDecl).map[it as EObject]
        var Iterable<EObject> crossRefDataDefTargets = context.getAllContentsOfType(DataDef).map[it as EObject]
        var Iterable<EObject> crossRefFxnTargets = context.getAllContentsOfType(FunctionSpec).map[it as EObject]
        var Iterable<EObject> crossRefEnumTargets = context.getAllContentsOfType(SimpleEnum).map[it as EObject]
    
        var List<EObject> allCrossRefTargets = new ArrayList()
        allCrossRefTargets.addAll(crossRefClassDeclTargets)
        allCrossRefTargets.addAll(crossRefDataDefTargets)
        allCrossRefTargets.addAll(crossRefEnumTargets)
        allCrossRefTargets.addAll(crossRefFxnTargets)
        return Scopes.scopeFor(allCrossRefTargets)
    }
    
  • 本示例中不包含类继承,但我已经设法按照书中的示例并自定义多重继承来使其工作,尽管它并没有真正执行正确的 DFS 或 BFS,它更像是“愚蠢但终止搜索”:在每一步中,查看每个顶点的边,并在步骤结束时查看顶点数是否等于您开始的顶点数;所以也许我应该使用非愚蠢的图形搜索来加快速度。
  • 我确实尝试过使用 context.resourceSet 并迭代所有资源并过滤所有类,但我的代码库大约 300 个文件中有大约 300k 行,这太慢了。
  • 我尝试编写一个实现 IScope 的自定义类。 Scopes.ScopeFor 将 EObject 的列表转换为 EObjectDescription 的列表,但是如果我有两个 EObjectDescription 列表(例如,我可以从两个不同的实现 IScope 的类中得到它)想要组合呢?当然,必须有一种简单的方法来合并两个范围,而我只是脑死亡,找不到它。
  • 我已经开始考虑编写自己的对象缓存,这将使我转向一个完全 100% 自定义的类,从头开始实现 IScope,但显然 XText 有相当多的类继承自 AbstractScopeProvider,我只需要找出我需要使用哪一个。

谢谢!我很乐意写更多细节,但我想这很简单。

【问题讨论】:

  • 你能提示一下你真正想要的范围吗?
  • 对于你也不需要任何范围界定的课程。方法调用需要它
  • 和 ref=[ecore::EObject|GenericDataTypeRule] 的东西没有意义。请解释

标签: xtext


【解决方案1】:

如果您需要能够引用 any 全局可见的类/方法/等。在当前或其他模型中,这是由访问 Xtext 索引的全局范围提供程序完成的。

在这种情况下,您需要像这样的规则

MethodImpl:
  containingClass=[ClassDeclaration] '::' method=[Method] ;

在范围提供程序中,您必须为“方法”属性定义一个范围方法,该方法为containingClass 的所有方法创建一个范围,例如:

def scope_MethodImpl_method(MethodImpl methodImpl, EReference ref) {
  Scopes::scopeFor(methodImpl.containingClass.members.filter(Method))
}

比较棘手的部分是containingClass 的范围: C++ 没有任何类似于导入机制的东西。您只能在同一文件中或直接或间接在#included 头文件中为您之前定义的类提供方法实现。 因此,如果您的 C++ 子集允许 #include,则您必须以某种方式模拟这种行为(这可能涉及创建自定义 IDefaultResourceDescriptionStrategy 或类似的东西,但这不是问题的一部分)。

在任何情况下,使用缓存可能最有意义,正如您所写的,您应该使用IResourceScopeCache 来缓存来自直接或间接包含文件的所有类。 忽略循环包含,它可能看起来像这样:

@Inject
IResourceScopeCache cache

def Set<ClassDeclaration> getImportedClasses(Module module) {
    cache.get("INCLUDED_CLASSES", module.eResource) [
        (module.classDeclarations + module.includes.map[module.importedClasses].flatten).toSet
    ]
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 2015-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-11
    相关资源
    最近更新 更多