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

求解Bison中do块引发的shift/reduce冲突:while循环与函数调用歧义

问题根源

你遇到的是典型的悬挂do二义性:当解析器处理到while call()后的do关键字时,存在两个合法分支:

  • 选择移进do,将其作为call后续do_block的开头
  • 选择归约当前的call()为完整表达式,再将do作为while循环的分隔符
    Yacc系解析器默认优先执行移进操作,所以do永远会被绑定给函数调用,导致语法解析不符合预期。
解决方案

方案1:文法拆分(推荐,无隐式依赖)

通过拆分表达式规则,强制while后的条件表达式不能直接跟裸do块,只有被括号包裹的表达式才允许绑定do块,完全符合你要的语法规则:

%token IDENT DO END WHILE

%%

program:
       %empty
       | stmts
       ;

stmts:
     stmt
     | stmts ';' stmt
     | stmts ';'
     ;

opt_stmts:
         %empty
         | stmts
         ;

opt_semi:
        %empty
        | ';'
        ;

term:
    ';'
    | DO opt_semi
    ;

while_loop:
          WHILE expr term opt_stmts END
          | WHILE term opt_stmts END
          ;

stmt:
    expr
    | while_loop
    ;

/* while条件位使用的表达式:不允许直接尾随do块 */
expr:
    IDENT
    | '(' inner_expr ')'
    | call_no_do
    ;

/* 完整表达式:允许包含带do块的函数调用 */
inner_expr:
    expr
    | call_with_do
    ;

do_block:
        DO '|' args '|' opt_stmts END
        | DO opt_stmts END
        ;

/* 拆分call为不带do块、带do块两个分支 */
call_no_do:
    IDENT '(' args ')'
    ;

call_with_do:
    IDENT '(' args ')' do_block
    | IDENT do_block
    ;

/* 其他场景使用的call可以同时支持两种形式 */
call:
    call_no_do
    | call_with_do
    ;

args:
    %empty
    | expr
    | args ',' expr
    | args ','
    ;

%%

该方案下:

  • while call() do stuff() end会正确将do绑定给while
  • 要给函数调用绑定do块必须加括号:while (call() do stuff() end) do more_stuff() end,完全符合预期。

方案2:优先级标记(改动最小)

如果不想调整文法结构,可以通过优先级声明强制归约无do块的函数调用优先级高于移进do的优先级:

%token IDENT END WHILE
%nonassoc DO
%nonassoc CALL_NO_DO /* 虚拟优先级标记,优先级高于DO */

%%

/* 原有规则不变,仅修改call的第一个产生式 */
call:
    IDENT '(' args ')' %prec CALL_NO_DO
    | IDENT '(' args ')' do_block
    | IDENT do_block
    ;

遇到冲突时,解析器会优先把call()归约为完整表达式,后续的do就会被识别为while的分隔符。

Ruby的实现逻辑

Ruby的官方解析器就是采用文法拆分的方案,它明确区分了可出现在条件位的表达式和普通表达式,条件位的表达式默认不允许直接跟裸do块,必须用括号包裹才能给内部的函数调用绑定do块,和你想要的行为完全一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 10:36:04