2

所以我正在使用 ANTLR 在 Java 中编写一个编译器,我对它如何处理错误感到有些困惑。

默认行为似乎是打印一条错误消息,然后尝试通过令牌插入等方式从错误中恢复并继续解析。我原则上喜欢这个;这意味着(在最好的情况下)如果用户提交了多个语法错误,他们将在每个错误中收到一条消息,但它会提及所有错误,而不是强制他们重新编译以发现下一个错误。默认错误消息对我来说很好。当它完成读取所有令牌时,麻烦就来了。

当然,我正在使用 ANTLR 的树构造函数来构建抽象语法树。虽然通过语法错误继续解析很不错,这样用户就可以看到所有错误,但一旦完成解析,我希望得到一个异常或某种指示输入在语法上无效的指示;这样我就可以停止编译并告诉用户“对不起,修复你的语法错误,然后再试一次”。我不希望它根据它认为用户试图说的内容吐出不完整的 AST,并继续进行下一阶段的编译,没有任何迹象表明出现任何问题(除了出现的错误消息)到控制台,我看不到)。然而,默认情况下,它正是这样做的。

Definitive ANTLR Reference提供了一种在检测到语法错误后立即停止解析的技术:覆盖mismatchandrecoverFromMismatchedSet方法来 throw RecognitionExceptions,并添加一个@rulecatch动作来做同样的事情。这似乎失去了从解析错误中恢复的好处,但更重要的是,它只是部分起作用。如果缺少必要的标记(例如,如果二元运算符的一侧只有一个表达式),它会按预期抛出异常,但如果添加了无关的标记,ANTLR 会插入它认为属于那里的标记并继续其愉快的方式,产生一个除了控制台消息之外没有任何语法错误指示的 AST。(更糟糕的是,它插入的令牌是EOF,所以文件的其余部分甚至没有被解析。)

我确信我可以解决这个问题,例如,向isValid解析器添加类似字段的内容并覆盖方法并添加操作,以便在解析结束时,如果有任何错误,它会引发异常。但是有更好的方法吗?我无法想象我正在尝试做的事情在 ANTLR 用户中是不寻常的。

4

1 回答 1

2

... [O] 完成解析后,我想获得一个异常或某种指示输入在语法上无效的指示;这样我就可以停止编译...

您可以getNumberOfSyntaxErrors在解析后调用词法分析器和解析器来确定是否存在由 ANTLR 隐蔽容纳的错误。显然,这并没有告诉您这些错误是什么,但我认为这些方法解决了您问题的“一旦完成解析......停止编译”部分。

Definitive ANTLR Reference 提供了一种在检测到语法错误后立即停止解析的技术:覆盖 mismatch 和 recoverFromMismatchedSet 方法以引发 RecognitionExceptions,并添加 @rulecatch 操作来执行相同操作。

我认为您没有提到您使用的是哪个版本的 ANTLR,但是该方法的 ANTLR v3.4 代码中的文档recoverFromMismatchedSet说它“当前未使用”,并且 Eclipse“全局使用”扫描未发现调用者。您的主要问题既不是这里也不是那里,但我想提一下以作记录。它可能是覆盖您的版本的正确方法。

如果缺少必要的令牌...,[覆盖的代码]会按预期抛出异常,但是如果添加了无关的令牌,ANTLR会插入它认为属于那里的令牌并继续其愉快的方式...

recoverFromMismatchedToken通过分别委托给方法mismatchIsMissingToken和方法来测试可恢复的丢失和无关令牌mismatchIsUnwantedToken。如果适当的方法确定插入或删除将解决问题,recoverFromMismatchedToken则进行适当的更正。如果确定没有操作解决不匹配令牌问题,则recoverFromMismatchedToken抛出MismatchedTokenException.

如果发生恢复操作,reportError则调用 ,它会调用displayRecognitionError详细信息。

这适用于 ANTLR v3.4 和可能的更早版本。

这为您提供了至少两个选项:

  • recoverFromMismatchedToken在细粒度级别上覆盖和处理错误。从这里,您可以将调用委托给超级实现,滚动您自己的恢复代码,或通过异常退出。无论如何,您的代码都将被调用,因此会知道发生了不匹配错误、可恢复或其他错误。此选项可能等同于覆盖recoverFromMismatchedSet.

  • displayRecognitionError在课程粒度级别覆盖和处理错误。方法reportError做了一些状态杂耍,所以我不建议重写它,除非重写实现调用超级实现。MethoddisplayRecognitionError似乎是recovered-token 调用链中最后的调用之一,因此它是确定是否继续的合理位置。我希望它有一个名称,表明它是一个合理的地方,但是哦,好吧。这是一个演示此选项的答案。

我偏向于覆盖displayRecognitionError,因为它很容易提供错误消息文本,并且因为我知道它只会在令牌恢复操作和所需的状态处理之后被调用——我的解析器不需要自己弄清楚如何恢复。getNumberOfSyntaxErrors假设您正在使用相关版本的 ANTLR 并且我完全理解您的问题,这似乎为您提供了您正在寻找的选项。

于 2012-12-15T07:54:11.123 回答