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

ANTLR4解析器在_errHandler->sync()调用时崩溃问题咨询

Antlr4 C++解析器崩溃问题排查与解决

问题背景

解析器语法规则如下:

tokens_statement_1 : 
TOK_01 | TOK_02 | TOK_03
  ;
tokens_statement_2 : 
TOK_04 | TOK_05 | TOK_06
  ;
    
rule_1 
@init
{
  // Some C++ code
} :

  statement1=tokens_statement_1
  {
    // Some C++ code
  }
  (
    statement2=tokens_statement_2
    {
      // Some C++ code
    }
  )*
  ;

当可选的tokens_statement_2部分缺失token时,解析器崩溃,报错信息:

terminate called after throwing an instance of 'std::out_of_range'
  what():  bitset::set: __position (which is 1138) >= _Nb (which is 1048)

通过gdb调试确认,崩溃发生在错误处理程序的sync方法中,位置在statement1=tokens_statement_1代码块之后,生成的C++代码片段显示崩溃点:

...
   setState(158);
   _errHandler->sync(this);  // CRASHES HERE
   alt = getInterpreter<atn::ParserATNSimulator>()->adaptivePredict(_input, 94, _ctx);
   ...

核心疑问:是否需要重写ErrorStrategy来避免崩溃?DefaultErrorStrategy难道不应该自动处理这种token缺失的情况吗?

附加调用栈:

#5  0x00007fd734224713 in __cxa_throw ()
  .../64bit/libboost_program_options-mt-d-x64.so.1.79.0
#6  0x00007fd73427dd35 in std::__throw_out_of_range_fmt(char const*, ...) ()
   from libboost_program_options-mt-d-x64.so.1.79.0
#7  0x00007fd733bc2303 in std::bitset<2048ul>::_M_check (this=0x7ffda80e2660, __position=1138, 
    __s=0x7fd733c92fae "bitset::set")
    at .../include/c++/7.3.0/bitset:769
#8  0x00007fd733bc227f in std::bitset<2048ul>::set (this=0x7ffda80e2660, __position=1138, 
    __val=true)
    at .../include/c++/7.3.0/bitset:1083
#9  0x00007fd733c1b4d9 in (anonymous namespace)::LL1AnalyzerImpl::LOOK (this=0x7ffda80e2650, 
    s=0x561d4521c710, stopState=0x0, ctx=...)
    at .../antlr_4_10_1_build/runtime/src/atn/LL1Analyzer.cpp:122
#10 0x00007fd733c1b5a3 in (anonymous namespace)::LL1AnalyzerImpl::LOOK (this=0x7ffda80e2650, 
    s=0x561d4521c6a0, stopState=0x0, ctx=...)
    at .../antlr_4_10_1_build/runtime/src/atn/LL1Analyzer.cpp:128
#11 0x00007fd733c1b605 in (anonymous namespace)::LL1AnalyzerImpl::LOOK (this=0x7ffda80e2650, 
    s=0x561d4521c9b0, stopState=0x0, ctx=...)
    at .../antlr_4_10_1_build/runtime/src/atn/LL1Analyzer.cpp:133
#12 0x00007fd733c1b605 in (anonymous namespace)::LL1AnalyzerImpl::LOOK (this=0x7ffda80e2650, 
    s=0x561d450e3de0, stopState=0x0, ctx=...)
    at .../antlr_4_10_1_build/runtime/src/atn/LL1Analyzer.cpp:133
#13 0x00007fd733c1b4fe in (anonymous namespace)::LL1AnalyzerImpl::LOOK (this=0x7ffda80e2650, 
    s=0x561d4521cf20, stopState=0x0, ctx=...)
    at .../antlr_4_10_1_build/runtime/src/atn/LL1Analyzer.cpp:123
#14 0x00007fd733c1b605 in (anonymous namespace)::LL1AnalyzerImpl::LOOK (this=0x7ffda80e2650, 
    s=0x561d4521cf90, stopState=0x0, ctx=...)
    at .../antlr_4_10_1_build/runtime/src/atn/LL1Analyzer.cpp:133
#15 0x00007fd733c1b605 in (anonymous namespace)::LL1AnalyzerImpl::LOOK (this=0x7ffda80e2650, 
    s=0x561d4521d080, stopState=0x0, ctx=...)
    at .../antlr_4_10_1_build/runtime/src/atn/LL1Analyzer.cpp:133
#16 0x00007fd733c1b605 in (anonymous namespace)::LL1AnalyzerImpl::LOOK (this=0x7ffda80e2650, 
    s=0x561d4521ce40, stopState=0x0, ctx=...)
    at .../antlr_4_10_1_build/runtime/src/atn/LL1Analyzer.cpp:133
#17 0x00007fd733c1b605 in (anonymous namespace)::LL1AnalyzerImpl::LOOK (this=0x7ffda80e2650, 
    s=0x561d4521cdc0, stopState=0x0, ctx=...)
    at .../antlr_4_10_1_build/runtime/src/atn/LL1Analyzer.cpp:133
#18 0x00007fd733c1bb31 in antlr4::atn::LL1Analyzer::LOOK (this=0x7ffda80e2838, s=0x561d4521cdc0, 
    stopState=0x0, ctx=0x0)
    at .../antlr_4_10_1_build/runtime/src/atn/LL1Analyzer.cpp:187
#19 0x00007fd733c1ba64 in antlr4::atn::LL1Analyzer::LOOK (this=0x7ffda80e2838, s=0x561d4521cdc0, 
    ctx=0x0)
    at .../antlr_4_10_1_build/runtime/src/atn/LL1Analyzer.cpp:180
#20 0x00007fd733bf69af in antlr4::atn::ATN::nextTokens (this=0x561d450d1970, s=0x561d4521cdc0, 
    ctx=0x0) at .../antlr_4_10_1_build/runtime/src/atn/ATN.cpp:35
#21 0x00007fd733bf6a2c in antlr4::atn::ATN::nextTokens (this=0x561d450d1970, s=0x561d4521cdc0)
    at .../antlr_4_10_1_build/runtime/src/atn/ATN.cpp:43
#22 0x00007fd733bbe816 in antlr4::DefaultErrorStrategy::sync (this=0x561d44fcec50, 
    recognizer=0x7ffda80e2ec0)

问题分析

从调用栈和报错信息来看,崩溃的根源是Antlr4 C++运行时的LL1AnalyzerImpl::LOOK方法中,使用的std::bitset大小(1048)小于要设置的token位置(1138),导致越界访问。这并非DefaultErrorStrategy的逻辑错误,而是运行时内部处理token集合时的容器容量不足问题——当语法定义的token类型数量超过bitset的预设大小时,就会触发这个崩溃。

DefaultErrorStrategy确实应该处理token缺失的情况,但它在执行sync逻辑时需要调用LL1分析器计算后续可能的token,而这个计算过程因为bitset容量不足直接崩溃了,根本没走到错误恢复的逻辑。

解决方案

  1. 优先升级Antlr4版本:当前使用的4.10.1存在bitset容量不足的已知问题,后续版本(比如4.13.1及以上)已修复该问题,将LL1分析器中的bitset替换为动态容器或增大了默认容量,升级后可直接解决崩溃问题。

  2. 临时修改运行时源码(若无法升级):

    • 找到antlr_4_10_1_build/runtime/src/atn/LL1Analyzer.cpp中的LL1AnalyzerImpl类,将其中的std::bitset<2048>替换为更大容量(比如std::bitset<4096>),或改用std::unordered_set<size_t>存储token集合,避免固定容量限制。
    • 修改后重新编译Antlr4运行时库,替换项目中的依赖库即可。
  3. 临时规避方案(不修改运行时):

    • 重写DefaultErrorStrategy的sync方法,跳过导致崩溃的LL1分析步骤,直接执行简单错误恢复,示例代码如下:
      class CustomErrorStrategy : public antlr4::DefaultErrorStrategy {
      public:
          void sync(antlr4::Parser *recognizer) override {
              antlr4::TokenStream *tokens = recognizer->getInputStream();
              if (tokens->LA(1) == antlr4::Token::EOF) {
                  return;
              }
              // 跳过当前token,直到遇到tokens_statement_2的token或EOF
              size_t expected[] = {TOK_04, TOK_05, TOK_06, antlr4::Token::EOF};
              skipUntil(recognizer, expected, sizeof(expected)/sizeof(size_t));
          }
      };
      
    • 在解析器初始化时设置自定义错误策略:parser.setErrorStrategy(std::make_shared<CustomErrorStrategy>());
    • 此方案仅为过渡方案,无法覆盖所有错误场景,建议尽快升级版本。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 17:55:23