ANTLR4的errorListener和errorHandler有什么区别?

问题描述:

我想获得ANTLR4解析器的特定错误信息。 我发现有两种方法可以处理错误:errorListener和errorHandler。ANTLR4的errorListener和errorHandler有什么区别?

// set error handler 
parser.removeErrorListeners(); 
parser.addErrorListener(new QueryErrorListener()); 

parser.setErrorHandler(new BailErrorStrategy()); 

但我很困惑他们之间的差异。

我发现,errorListener可以得到特定的错误信息,但它只能打印或记录它,不能抛出异常。

errorListener的FPGA实现波纹如:

public class QueryErrorListener extends BaseErrorListener { 

    private static final Logger LOGGER = LoggerFactory.getLogger(QueryDispatcher.class); 


    @Override 
    public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, 
          int line, int charPositionInLine, String msg, 
          RecognitionException e) 
    { 
     List<String> stack = ((Parser)recognizer).getRuleInvocationStack(); Collections.reverse(stack); 
     String errorMessage = "line "+line+":"+charPositionInLine+" at "+ 
       offendingSymbol+": "+msg; 
     LOGGER.error("rule stack: "+stack); 
     LOGGER.error(errorMessage); 
     QueryParseErrorStrategy queryParseErrorStrategy = new QueryParseErrorStrategy(); 

    } 
} 

同时,所述的ErrorHandler只能抛出异常ParseCancellationException而没有任何特定的消息。

public class BailErrorStrategy extends DefaultErrorStrategy { 
    /** Instead of recovering from exception {@code e}, re-throw it wrapped 
    * in a {@link ParseCancellationException} so it is not caught by the 
    * rule function catches. Use {@link Exception#getCause()} to get the 
    * original {@link RecognitionException}. 
    */ 
    @Override 
    public void recover(Parser recognizer, RecognitionException e) { 
     for (ParserRuleContext context = recognizer.getContext(); context != null; context = context.getParent()) { 
      context.exception = e; 
     } 

     throw new ParseCancellationException(e); 
    } 

    /** Make sure we don't attempt to recover inline; if the parser 
    * successfully recovers, it won't throw an exception. 
    */ 
    @Override 
    public Token recoverInline(Parser recognizer) 
     throws RecognitionException 
    { 
     InputMismatchException e = new InputMismatchException(recognizer); 
     for (ParserRuleContext context = recognizer.getContext(); context != null; context = context.getParent()) { 
      context.exception = e; 
     } 

     throw new ParseCancellationException(e); 
    } 

    /** Make sure we don't attempt to recover from problems in subrules. */ 
    @Override 
    public void sync(Parser recognizer) { } 
} 

我试着找到一个解决方案,添加一个传输方法来获取来自ParseCancellationException的详细消息,如下图所示。

我发现我可以从RecognitionException的Token对象得到一些消息,但是我只能找到/ charPositionInLine/offendingSymbol消息,我不知道详细信息在哪里,比如“missing'xxx'期待'yyy'“

public class ANTLRExceptionTransfer { 

    public static SemanticException transfer(RecognitionException re) { 
     String errorMsg = ""; 
     Recognizer<?, ?> recognizer = re.getRecognizer(); 
     Token offendingSymbol = re.getOffendingToken(); 
     int line = offendingSymbol.getLine(); 
     int charPositionInLine = offendingSymbol.getCharPositionInLine(); 
     // ???????? 
     String msg = ""; 

     List<String> stack = ((Parser)recognizer).getRuleInvocationStack(); 
     Collections.reverse(stack); 

     String errorMessage = "rule stack: "+stack; 
     errorMessage = "\nline "+line+":"+charPositionInLine+" at "+ 
       offendingSymbol+": "+msg; 
     return new SemanticException(errorMessage); 


    } 
} 

这是使用errorHandler的正确方法吗? 如何获得具有特定错误信息的异常?

+0

我现在可以从RecognitionException中获取基本信息,但仍不知道如何构建可读消息,如errorListener did [line 1:4632 at [@ 1289,4632:4632 ='}',,1:4632 ]:无关输入'}'期待',']。有没有ANTLR4提供的方法? – lulijun

我觉得setErrorHandler这个名字有点混乱。它应该与你可以在那里设定的一致。这是为了设置一个错误策略(当然这也是一些类似的处理...)。

错误监听器和错误策略都是应用程序处理解析错误的方法。为每个遇到的错误调用错误侦听器,并允许应用程序收集它们(例如,在GUI中显示它们)。您将得到一个预先生成的错误消息,或者可以通过传入的参数创建一个自己的错误消息。

错误策略是一个类,它确定发现错误后如何继续。默认状态是尝试同步到输入流并继续解析。但有时您希望解析器立即停止并在发现错误后避免冗长的操作。这种所谓的纾困策略是ANTLR4中的另一个类,通常用于SLL解析。请参阅one of my projects了解使用方法。

在纾困错误策略中抛出的ParseCancellationException是没有任何附加信息的例外。它不是意味着错误处理(就发送给应用程序/用户而言,你有错误处理程序),而是抛出一个不属于常见解析器异常的异常,以绕过所有错误捕获并尽快找到解决正在进行的解析运行的方法。你必须在你自己的代码中捕获这个异常,否则它会冒泡到应用程序的根上下文(并且可能导致应用程序退出,具体取决于目标语言)。

+0

我可以从ParseCancellationException中获取特定的诊断信息吗?或者是否有可能将errorlistener的错误消息发送给errorHandler?而且我发现,如果我同时设置errorHandler和errorListener,有时errorListener不起作用,但有时会起作用。它们是否有冲突? – lulijun

+0

好吧,由于错误策略可以随时取消解析运行(例如,救援策略),可能会发生并非所有错误都通过错误侦听器报告的情况。 'ParseCancellationException'没有任何信息,并且只提供一个目标:我在我的答案中解释(取消当前的分析运行)。 –

+0

RecognitionException包含errorListener可以获取的所有信息。 RecognitionException有一个成员'recognitionizer',我们可以从它得到错误堆栈,并且另一个成员'offendingToken',我们可以得到line(error line)和charPositionInLine(error position)&its(msg)。但是我们似乎需要自己传输错误信息,因为错误信息就像'[@ 1,1:12 =''selectmust'',,1:1]',我们需要将它转换为更具可读性的格式。谢谢你回答我的问题〜 – lulijun