测试新的 Java 解析器

Testing of a new Java parser

纯粹作为自学练习,我正在尝试使用 Parse::RecDescent 模块在 Perl 中编写 Java 解析器。稍后我可能会使用 Antlrbison 等其他工具重新实现解析器

但是我如何确保我的解析器确实根据 Java 语言规范生成正确的解析?意思是,它正确处理悬空 else、运算符关联性和 -precedence 等

一种方法是将我的解析器与已知的无错误解析器进行比较,方法是让两个解析器都为大量测试 Java 程序生成 AST,然后比较两组 AST。

如果这确实是唯一的方法,我在哪里可以找到一大套完全涵盖整个 Java 语言规范的测试 Java 程序?

我看过 JavaParser 但它似乎没有详尽的测试数据集。

另一种方法当然是自己手写数以万计的测试Java程序,这对我来说是非常不切实际的,不仅在时间上而且在确保其详尽性方面也是如此!

要确定您的答案是否正确,理想情况下您必须与某种标准进行比较。这对计算机语言来说很难。

比较 AST 会很困难,因为没有这样的标准。每个构建 AST 的解析器都会构建一个 AST,其结构由编写解析器的人设计。

这意味着如果您构建了一个生成 AST 的解析器,并且您得到了其他人的生成 AST 的解析器,您会发现您选择的 AST 节点与另一个 AST 不匹配。现在你必须构建一个从你的 AST 到另一个 AST 的映射(你怎么知道映射是有效的?)。您可以尝试让您的解析器从另一个解析器生成 AST,但您会发现您生成的 AST 受您使用的解析技术的影响。

我公司生产的 Java 前端也有类似的问题(如果您想了解更多信息,请参阅简介)。我们满足于测试答案是否 自洽 然后我们对大段代码进行大量长期的经验测试。

我们的解决方案是:

  • (构建一个解析器,使用我们能得到的最强解析技术(GLR)。这意味着我们可以识别某些不容易被其他解析技术(LL, LR, ...)识别的结构,从而产生AST其他解析器很难生成的节点。请参阅下面的评论以了解这很重要的示例。即使如此,we produce AST nodes in way that avoids completely our having to hand-code AST node construction 也是大多数其他解析技术所要求的;这往往会产生与手动编码略有不同的 AST)。
  • 解析大量 Java 代码(生成 AST)以确保我们没有解析错误。 [JDK 是一个很好的尺寸示例,很容易得到]
  • 我们的工具可以采用 AST 并重新生成(漂亮打印)源代码,其中包含注释,但布局可能有所不同。我们验证 parsed-then-prettyprinted 代码也会解析。我们重新prettyprint解析出来的prettyprinted版本;这应该与 prettyprinted 版本相同,因为我们总是生成相同的布局。这个测试很好地表明我们的 AST 设计和实现没有丢失任何关于源代码的信息
  • 构建符号表,解析名称的含义,并根据我们的前端验证合法的Java程序类型检查。这并没有告诉你任何关于 AST 本质的信息,除了它已经足够好了(事实上,已经足够好了!)因为类型检查任务非常复杂(去检查你的本地 Java 标准),它也很脆弱。如果您没有把所有事情都做对,那么当应用于大量代码时类型检查可能会失败。同样,JDK 是对此的一个很好的测试。 注意:Java 没有名称和类型解析的解析器在实践中不是很有用
  • 生成 JavaDoc 之类的交叉引用,其中包含来自上述结果的超链接源代码。这意味着很容易手动检查位代码以查看名称解析(因此 AST 构造)是否正常。
  • 接受结果,将前端​​应用于各种程序 代码的分析和转换。我们发现偶尔出现的问题并修复它。

很难做到这一点;你必须靠近并持续保持测试压力,特别是因为 Java 语言在不断发展。 (我们在 Java 8,Java 9 受到威胁)。底线:构建这样的解析器并检查其完整性是一项 很多 的工作。

我们很想有一套独立的测试,但我们还没有在野外看到过。如果这些测试存在(我假设 Oracle 和 IBM 有),我希望它们真的不会直接测试 parsingname resolution,但是而是测试一些代码编译和 运行s 产生已知结果。因为我们不是在构建编译器,所以如果我们有这样的测试,我们将无法 运行。我们将能够进行名称解析和类型一致性检查,这会很有帮助。

[我们实际上为许多语言前端这样做。你觉得 Java 很难,用 C++ 试试这个]

我同意@ira-baxter 的观点。

简而言之,很容易验证您的解析器是否可以解析所有有效的源代码(只需向它扔很多代码:我们这样做是为了测试 JavaParser,我们只是没有将数百个代码放入存储库中兆字节的源代码)。

很难验证您生成的 AST 是否具有正确的形状,因为您可以使用许多不同的方法来解析相同的代码并构建不同但同样有效的 AST。有些形状与 w.r.t 一起使用对其他形状更容易,但没有 "truth" 可以坚持。

如果您的工具生成的 AST 与另一个解析器生成的 AST 不同,这并不一定意味着这两个解析器中的任何一个都是错误的,这可能只是一种设计选择。