如何使用类似C ++的语言对限定名称进行范围界定

时间:2016-12-14 13:47:36

标签: xtext

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

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

我正在试图找出范围。我已经完成的一些关键部分:

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

    所以问题如下:

    我如何(有效!)获取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而我只需要找出我需要使用哪一个。

    谢谢!我很乐意写更多详细信息,但我认为这是微不足道的。

1 个答案:

答案 0 :(得分:0)

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

在这种情况下,您需要一个像

这样的规则
MethodImpl:
  containingClass=[ClassDeclaration] '::' method=[Method] ;

在范围提供程序中,您必须为&#34;方法&#34;定义范围方法。为containingClass的所有方法创建范围的属性,例如:

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

更棘手的部分是containingClass的范围: C ++没有像导入机制那样的东西。您只能为之前在同一文件中或直接或间接#include d头文件中定义的类提供方法实现。 因此,如果您的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
    ]
}