首页 > 解决方案 > ANTLR 查找和捕获解析错误的最佳实践

问题描述

这个问题涉及如何在 Visual Studio 的 C# 中从 ANTLR4 解析器中获取错误消息。我向 ANTLR 解析器提供了一个已知错误的输入字符串,但我没有看到在(错误的)解析期间抛出任何错误或解析异常。因此,我的异常处理程序在解析期间没有机会创建和存储任何错误消息。

我正在使用我知道是正确的 ANTLR4 语法,因为我可以通过 Visual Studio Code 的 ANTLR 扩展以图形形式看到正确的解析操作输出。我知道生成的解析器代码是正确的,因为我可以正确编译它而不会出错,覆盖访问者基类,并使用覆盖的 VisitXXX 方法从解析树中打印出各种信息。

此时,我正在运行一个非常简单的测试用例,它输入错误的输入字符串,并在我的存储解析错误列表中查找非零计数。我对错误处理代码很有信心,因为它适用于另一种语法的类似情况。但是错误处理代码必须捕获解析异常以生成错误消息。(也许这不是捕获/检测解析错误的正确方法,例如输入流中的意外标记或其他错误。)

这是我用来替换默认词法分析器和解析器错误侦听器的代码。

 // install the custom ErrorListener into the parser object
 sendLexer.RemoveErrorListeners();
 sendLexer.AddErrorListener(MyErrorListener.Instance);
 Parser.RemoveErrorListeners();
 Parser.AddErrorListener(MyErrorListener.Instance);

我附上了图形输出的屏幕截图,显示输入字符串中存在意外标记。

Q1。为什么意外的标记不会导致我可以用我的异常处理程序捕获的解析异常?所有解析错误都应该引发异常吗?

Q2。如果捕获解析异常不是正确的方法,有人可以建议我遵循的策略来检测意外的令牌错误(或其他不引发解析异常的错误)吗?

Q3。是否有捕获或查找解析错误的最佳实践方法,例如通过遍历解析树生成错误,而不是希望 ANTLR 会为每个意外标记抛出解析异常?(我想知道意外标记是否应该生成解析异常,而不是生成恰好包含意外标记的合法解析树?如果是这样,它们是否只是在解析树中显示为意外的孩子?)

谢谢你。

屏幕截图显示(故意的)错误输入字符串中的意外标记以触发错误:

在此处输入图像描述

更新:

目前,解析器和单元测试正在运行。如果我将错误的输入字符串输入解析器,默认的解析器错误侦听器会生成合适的错误消息。但是,当我安装自定义错误侦听器时,它永远不会被调用。我不知道为什么在未安装自定义错误侦听器时看到错误消息时不会调用它。

我现在有解析器和单元测试工作。当我注入错误的输入字符串时,默认的解析错误侦听器会打印出一条消息。但是当我安装一个自定义错误监听器时,它永远不会被调用。1)放置在错误侦听器中的断点永远不会被命中,并且 2)(因此)没有收集或打印错误消息。

这是我的单元测试调用的 C# 代码ParseText

// the unit test
public void ModkeyComboThreeTest() {
  SendKeysHelper.ParseText("this input causes a parse error);
  Assert.AreEqual(0, ParseErrors.Count);


// the helper class that installs the custom error listener
public static class SendKeysHelper {
  public static List<string> ParseErrorList = new List<string>();
  public static MyErrorListener MyErrorListener;

  public static SendKeysParser ParseText(string text) {
    ParseErrors.Clear();
    try {
      var inputStream = new AntlrInputStream(text);
      var sendLexer = new SendKeysLexer(inputStream);
      var commonTokenStream = new CommonTokenStream(sendLexer);
      var sendKeysParser = new SendKeysParser(commonTokenStream);
      Parser = sendKeysParser;

      MyErrorListener = new MyErrorListener(ParseErrorList);
      Parser.RemoveErrorListeners();
      Parser.AddErrorListener(MyErrorListener);

      // parse the input from the starting rule
      var ctx = Parser.toprule();
      if (ParseErrorList.Count > 0) {
        Dprint($"Parse error count: {ParseErrorList.Count}");
      }
 ...
}


// the custom error listener class
public class MyErrorListener : BaseErrorListener, IAntlrErrorListener<int>{
  public List<string> ErrorList { get; private set; }

  // pass in the helper class error list to this constructor
  public MyErrorListener(List<string> errorList) {
    ErrorList = errorList; 
  }

  public void SyntaxError(IRecognizer recognizer, int offendingSymbol, 
    int line, int offset, string msg, RecognitionException e) {
    var errmsg = "Line " + line + ", 0-offset " + offset + ": " + msg;              

    ErrorList.Add(errmsg);
  }
}

因此,我仍在尝试回答有关如何从失败的解析中获取错误信息的原始问题。安装时没有语法错误,1)默认错误消息消失(表明我的自定义错误侦听器已安装),但 2)我的自定义错误侦听SyntaxError器方法不会被调用来注册错误。

或者,或者,我保留默认错误侦听器并添加我的自定义错误侦听器。在调试器中,我可以看到它们都注册在解析器数据结构中。发生错误时,会调用默认侦听器,但不会调用我的自定义错误侦听器(这意味着自定义侦听器中的断点不会被命中)。单元测试中没有语法错误或操作错误,除了我的自定义错误侦听器似乎没有被调用。

也许对自定义侦听器的引用以某种方式损坏或无法正常工作,即使我可以在解析器数据结构中看到它。或者,也许正在调用我的自定义侦听器的基类版本。很奇怪。

更新

由于某种原因,此线程的有用讨论/答案已被删除。它提供了很多关于为 ANTLR4 编写自定义错误侦听器和错误策略的有用信息。

我在这里打开了第二个问题ANTLR4 错误未报告给自定义词法分析器/解析器错误侦听器,这表明了为什么我无法从 ANTLR4 中获取错误消息的根本原因。但是第二个问题没有解决这篇文章的主要问题,即关于最佳实践的问题。我希望删除此线程的管理员取消删除它以使最佳实践信息再次可见。

标签: parsingantlr4parse-error

解决方案


推荐阅读