Xtext 导入命名空间验证

Xtext import namespace validation

我正在尝试通过使用 java 类包、类 和导入定义语法来学习 Xtext。我的语法片段看起来像这样,CompilationUnit 是根对象。

CompilationUnit:
  packageDeclaration=PackageDeclaration?
  imports+=ImportDeclaration*
  topClass=Class
;

packageDeclaration:
  'package' path=QualifiedName ';'
;

ImportDeclaration:
  'import' importedNamespace=QualifiedNameWithWildCard ';'
;

Class:
  {Class} visibility=Visibility isStatic?='static' 'class' name=ID ('extends' superClass=[Class|QualifiedName])? body=ClassBody
;

为了导入交叉引用,我使用了 DefaultGlobalScopeProvider 并且我已经用我自己的版本覆盖了 QualifiedNameProvider,该版本附加了包名称作为 topClass 的 QualifiedName 的前缀。为了自动化自己的包导入,我编辑了项目特定的 ScopeProvider。所有这一切似乎都运行得很好,并且使用生成的 Eclipse IDE 我能够使用 "import [packageName].[*|ClassName]" 从其他文件导入 类。 (与引荐来源网址在同一包中的 Classes 的自动导入仍在进行中,但目前我可以通过显式导入进行管理)

我要尝试的下一步是验证导入和包声明。我想实现与 Java 相同的限制,即文件的包声明应等于文件的相对路径,另一方面我想验证导入的 Class 或包的存在。问题是,通过 EObject 的 eResource,我只能访问资源的完整 URI(例如平台:/resource/Sample/src/mypack/Sample.myjava),而相对于源文件夹的路径名会更短( mypack/Sample.我的java)。我还没有想出我是应该用一些逻辑来剪辑 URI 还是有一些完全不同的方法。

一个可能的想法可能是以某种方式获取项目的每个类路径目录的 URI 并从那里开始工作,但我还没有想出如何做到这一点。

知道我应该如何验证我的包和导入声明吗?我总觉得我很亲近,但又那么远。

编辑:删除了对 DefaultGlobalScopeProvider 行为的误解。这与文件层次结构无关,而只是限定名称。我还可以自动导入自己的包。

更新:考虑到这一点,我应该仅通过枚举可用资源并检查其限定名称来适当地验证导入。然后只有包声明验证需要文件层次结构检查。

Update2:在eclipse 中资源URI 似乎总是格式"platform:/resource/[Project]/[src folder]/..."。假设这意味着我可以严格检查它,但是在语法项目验证器中这样做会创建对 Eclipse 的语法级依赖性,这对于任何严肃的 DSL 项目来说可能不是一个好主意。然而,这个 post 中的评论让我认为也许我应该考虑在语法级别根本不进行包位置验证,而只在 UI 项目中(我还没有改变) ).这个想法是资源的位置可能应该以比期望传统文件系统层次结构更抽象的形式保留。

我能够创建一个解决方案。以下代码是验证导入声明的粗略实现,可能需要一些清理,但基本上可以做到这一点。

@Check
def checkImportSanity(ImportDeclaration imp) {

    val importQN = qualifiedNameConverter.toQualifiedName(imp.importedNamespace)
    val hasWildcard = isImportWildcard(importQN.getLastSegment)

    for (r:imp.eContainer.eResource.resourceSet.resources) {
        val classQN = qualifiedNameProvider.getFullyQualifiedName((r.getContents().get(0) as CompilationUnit).topClass)
        if (hasWildcard && classQN.skipLast(1).equals(importQN.skipLast(1))) {
            return
        }
        else if (classQN.equals(importQN)) {
            return
        }
    }

    error("This import doesn't match any class!",
        imp, MyJavaPackage.Literals.IMPORT_DECLARATION__IMPORTED_NAMESPACE
    );
}

简而言之,它只是遍历所有资源并比较导入的限定名称和资源的顶部 class,忽略任何文件名。 qualifiedNameProvider 是我自己的,它将包名称附加到 class 名称。像这样它只能验证顶级 class 级别的导入,但我可以稍后再进一步。不知道这是否足以导入外部库,但这是我目前不感兴趣的另一个话题。

此外,我决定不根据文件名验证包名。虽然 Java 这样做,但我想在当今世界,最好将其保留为编辑器级别的约束并在语法级别忽略它。