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

Yacc中布尔与算术表达式文法冲突问题求助

解决算术与布尔表达式文法的归约冲突

我在完成课程作业的编译器实现,目标语言需同时支持算术表达式与布尔表达式,但编写文法规则时遇到了冲突:两类表达式均可归约为变量访问操作,导致文法冲突,无法正常生成解析器。以下是复现问题的最简代码示例:

编译命令

  • 生成语法分析器:yacc -v -d -Wcounterexamples y.tab.y(-Wcounterexamples是GNU Bison新版本专属参数,比POSIX Yacc更推荐使用)
  • 生成词法分析器:lex -o lex.yy.c lex.yy.l(lex文件并非排查问题必需)

冲突的文法文件 y.tab.y

%{
    extern int yylex(void);
    extern int yyerror(char *msg);
%}
%token MINUS PLUS DIVIDED_BY TIMES OPEN_PARENTHESIS CLOSED_PARENTHESIS INTEGER_CONSTANT
%token OR AND FALSE TRUE NOT DIFFERENT EQUAL INFERIOR STRICT_INFERIOR SUPERIOR STRICT_SUPERIOR
%token IDENTIFIER
%%
expression:
    arithmetic_expression
    | boolean_expression
    ;
arithmetic_expression:
    arithmetic_term 
    | arithmetic_expression MINUS arithmetic_term
    | arithmetic_expression PLUS arithmetic_term 
    ;
arithmetic_term:
    arithmetic_factor 
    | arithmetic_term DIVIDED_BY arithmetic_factor 
    | arithmetic_term TIMES arithmetic_factor
    ;
arithmetic_factor:
    OPEN_PARENTHESIS arithmetic_expression CLOSED_PARENTHESIS
    | INTEGER_CONSTANT
    | variable 
    ;
boolean_expression:
    boolean_term
    | boolean_expression OR boolean_term
    ;
boolean_term:
    boolean_factor
    | boolean_term AND boolean_factor
    ;
boolean_factor:
    FALSE
    | TRUE
    | NOT boolean_factor
    | arithmetic_expression comparison arithmetic_expression
    | OPEN_PARENTHESIS boolean_expression CLOSED_PARENTHESIS 
    | variable
    ;
comparison:
    DIFFERENT
    | EQUAL
    | INFERIOR 
    | STRICT_INFERIOR
    | SUPERIOR
    | STRICT_SUPERIOR
    ;
variable:
    IDENTIFIER
    ;

词法文件 lex.yy.l

%{
    #include "y.tab.h"
%}
%%
- {return MINUS;}
\+ {return PLUS;}
\* {return TIMES;}
\( {return OPEN_PARENTHESIS;}
\) {return CLOSED_PARENTHESIS;}
\|\| {return OR;}
&& {return AND;}
false {return FALSE;}
true {return TRUE;}
! {return NOT;}
!= {return DIFFERENT;}
== {return EQUAL;}
<= {return INFERIOR;}
< {return STRICT_INFERIOR;}
>= {return SUPERIOR;}
> {return STRICT_SUPERIOR;}
[a-z][a-zA-Z0-9_]* {return IDENTIFIER;}
%%

问题根源

冲突核心在于variable同时属于arithmetic_factor和boolean_factor,当解析到IDENTIFIER时,Yacc/Bison无法确定该归约为算术表达式的一部分还是布尔表达式的一部分——因为顶层规则expression可以是两者之一,直接引发移进/归约冲突。

解决方法

方法1:合并表达式层级,用优先级消除歧义

将布尔表达式作为算术表达式的扩展,通过明确运算符优先级(布尔运算符 < 比较运算符 < 算术运算符),让解析器明确选择逻辑。修改后的文法如下:

%{
    extern int yylex(void);
    extern int yyerror(char *msg);
%}
// 优先级从低到高排列
%left OR
%left AND
%left NOT
%left EQUAL DIFFERENT
%left INFERIOR STRICT_INFERIOR SUPERIOR STRICT_SUPERIOR
%left PLUS MINUS
%left TIMES DIVIDED_BY
%token OPEN_PARENTHESIS CLOSED_PARENTHESIS INTEGER_CONSTANT
%token FALSE TRUE IDENTIFIER
%%
expression:
    expression OR expression
    | expression AND expression
    | NOT expression
    | expression EQUAL expression
    | expression DIFFERENT expression
    | expression INFERIOR expression
    | expression STRICT_INFERIOR expression
    | expression SUPERIOR expression
    | expression STRICT_SUPERIOR expression
    | expression PLUS expression
    | expression MINUS expression
    | expression TIMES expression
    | expression DIVIDED_BY expression
    | OPEN_PARENTHESIS expression CLOSED_PARENTHESIS
    | INTEGER_CONSTANT
    | TRUE
    | FALSE
    | IDENTIFIER
    ;
%%

这种方法直接合并所有表达式规则,通过优先级声明彻底消除冲突,是课程作业中最常用的解决方案。

方法2:保留分层结构,用上下文约束消除歧义

如果必须保留算术/布尔表达式的分层结构,可以通过明确上下文规则区分:比如仅在if条件、逻辑运算符操作数等布尔上下文解析布尔表达式,在赋值右侧等算术上下文解析算术表达式。示例如下:

// 添加语句规则,明确表达式的使用上下文
statement:
    IF OPEN_PARENTHESIS boolean_expression CLOSED_PARENTHESIS block
    | ASSIGNMENT IDENTIFIER EQUAL arithmetic_expression
    ;

这样解析器会根据上下文自动选择归约方向,避免冲突。

方法3:语义分析辅助(不推荐课程作业使用)

若文法层面无法完全消除冲突,可以让解析器接受冲突,在语义分析阶段检查表达式类型是否符合上下文要求。但这种方法会耦合语法与语义分析,课程作业中更推荐前两种方法。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 23:47:17