You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ANTLR4 C++目标中捕获解析异常?

解决ANTLR4 C++目标捕获解析异常并获取详细错误信息的问题

我明白你遇到的困扰了——BailErrorStrategy确实不会在抛出的ParseCancellationException中携带有用的错误信息,这是它的设计逻辑:它的核心作用是遇到错误就立即终止解析流程,而非收集和生成详细的错误描述。下面给你两种可行的解决方案,帮你拿到类似line 1:19 missing ';' at ''的错误信息,并重新抛出自定义异常。

方案一:使用StrictErrorStrategy捕获RecognitionException

如果你想要类似BailErrorStrategy的「遇错即停」行为,同时又能拿到详细错误信息,StrictErrorStrategy是更合适的选择。它会在遇到第一个语法错误时终止解析,并抛出带有完整错误上下文的RecognitionException。

修改后的代码如下:

try {
  ANTLRInputStream instream(sql);
  MyLexer lexer(&instream);
  CommonTokenStream tokens(&lexer);
  MyParser parser(&tokens);
  
  // 替换BailErrorStrategy为StrictErrorStrategy
  parser.setErrorHandler(std::make_shared<antlr4::StrictErrorStrategy>());
  
  auto ctx = parser.singleStatement();
  // ... 后续处理逻辑
} catch(antlr4::RecognitionException& e) {
  // 构建包含行号、列号和错误描述的完整信息
  std::string errorMsg = "Parse error at line " + std::to_string(e.getOffendingToken()->getLine()) 
                        + ":" + std::to_string(e.getOffendingToken()->getCharPositionInLine())
                        + " - " + e.getMessage();
  
  std::cout << errorMsg << std::endl;
  throw MyParseException(errorMsg); // 抛出自定义异常
} catch (std::exception& e) {
  std::cout << e.what() << std::endl;
}

说明:

  • StrictErrorStrategy是ANTLR提供的严格错误策略,遇到错误就终止解析,行为接近BailErrorStrategy,但会生成详细错误信息
  • 通过RecognitionException的getMessage()可以拿到错误描述,getOffendingToken()能获取错误发生的行号和列号,方便精准定位问题

方案二:结合BailErrorStrategy和自定义ErrorListener

如果你一定要保留BailErrorStrategy,可以通过自定义ErrorListener来捕获错误信息——因为BailErrorStrategy在抛出异常前,会先调用错误监听器的syntaxError方法。

首先定义一个自定义错误监听器:

class CustomErrorListener : public antlr4::BaseErrorListener {
public:
  std::string lastError;
  
  void syntaxError(antlr4::Recognizer *recognizer, antlr4::Token *offendingSymbol,
                   size_t line, size_t charPositionInLine,
                   const std::string &msg, std::exception_ptr e) override {
    // 拼接完整错误信息
    lastError = "line " + std::to_string(line) + ":" + std::to_string(charPositionInLine) + " " + msg;
  }
};

然后在解析代码中使用这个监听器:

// 把错误监听器声明在try外部,确保catch块能访问到
std::shared_ptr<CustomErrorListener> errorListener = std::make_shared<CustomErrorListener>();

try {
  ANTLRInputStream instream(sql);
  MyLexer lexer(&instream);
  CommonTokenStream tokens(&lexer);
  MyParser parser(&tokens);
  
  // 移除默认的控制台错误监听器,添加自定义监听器
  parser.removeErrorListeners();
  parser.addErrorListener(errorListener);
  
  // 继续使用BailErrorStrategy
  parser.setErrorHandler(std::make_shared<antlr4::BailErrorStrategy>());
  
  auto ctx = parser.singleStatement();
  // ... 后续处理逻辑
} catch(antlr4::ParseCancellationException& e) {
  std::string errorMsg = errorListener->lastError.empty() ? "Unknown parse error" : errorListener->lastError;
  
  std::cout << errorMsg << std::endl;
  throw MyParseException(errorMsg); // 抛出自定义异常
} catch (std::exception& e) {
  std::cout << e.what() << std::endl;
}

说明:

  • BailErrorStrategy触发错误时,会先调用CustomErrorListener的syntaxError方法,我们在这里提前记录错误信息
  • 捕获ParseCancellationException后,直接从监听器中取出之前记录的错误内容即可

总结

  • 优先推荐方案一,StrictErrorStrategy兼顾了「遇错即停」的需求和错误信息的完整性,代码更简洁
  • 如果必须使用BailErrorStrategy,方案二的自定义监听器可以帮你捕获到所需的错误详情

内容的提问来源于stack exchange,提问作者mmch

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 07:42:50