如何阻止ANTLR抑制语法错误?

时间:2012-12-15 06:22:19

标签: java antlr error-recovery

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

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

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

最终的ANTLR参考提供了一种在检测到语法错误时立即停止解析的技术:覆盖mismatchrecoverFromMismatchedSet方法以抛出RecognitionException s,并添加@rulecatch操作来执行相同操作。这似乎失去了从解析错误中恢复的好处,但更重要的是,它只能部分工作。如果缺少必要的标记(例如,如果二元运算符只在其一侧有一个表达式),它会按预期抛出异常,但如果添加了一个无关的标记,ANTLR会插入它认为属于的标记并继续它的快乐方式,产生一个AST,除了控制台消息外没有任何语法错误的迹象。 (更糟糕的是,它插入的令牌是EOF,所以文件的其余部分甚至都没有被解析。)

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

1 个答案:

答案 0 :(得分:2)

  

... [O]它已经完成解析我想得到一个异常或某种指示输入在语法上没有效;那样我就可以停止编译......

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

  

确定性ANTLR参考提供了一种在检测到语法错误时立即停止解析的技术:覆盖不匹配和recoverFromMismatchedSet方法以抛出RecognitionExceptions,并添加@rulecatch操作来执行相同操作。

我认为你没有提到你正在使用哪个版本的ANTLR,但是方法recoverFromMismatchedSet的ANTLR v3.4代码中的文档说它“当前没有使用”和Eclipse“全局使用” “扫描发现没有来电者。无论是在这里还是那里都没有你的主要问题,但我想提一下它的记录。这可能是覆盖您的版本的正确方法。

  

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

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

如果进行了恢复操作,则会调用reportError,并使用详细信息调用displayRecognitionError

这适用于ANTLR v3.4以及可能的早期版本。

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

  • 覆盖recoverFromMismatchedToken并处理细粒度级别的错误。从这里,您可以将调用委托给超级实现,滚动自己的恢复代码,或者通过异常进行挽救。无论如何,您的代码都将被调用,因此将意识到发生了不匹配错误,可恢复或以其他方式。此选项可能等同于覆盖recoverFromMismatchedSet

  • 覆盖displayRecognitionError并在课程级别处理错误。方法reportError做了一些状态杂耍,所以除非覆盖实现调用超级实现,否则我不建议覆盖它。方法displayRecognitionError似乎是recover-token调用链中的最后一个调用之一,因此它是确定是否继续的合理位置。我希望它有一个名字,表明这是一个合理的地方,但哦,好吧。展示此选项的Here is an answer

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