试图在语法中找到 Shift/Reduce 冲突

Trying to find Shift/Reduce conflict in Grammar

我有以下语法 (Yacc),这是一个简单的 C 编译器的开始,我从一个简单的 if 语句开始:

S : E
  ;

E : COND_NO_ELSE
  ;

COND_NO_ELSE : IF BOOL_EXP BLOCK
;

BLOCK : LC EXP RC

BOOL_EXP : LP EXP BOOL_OP EXP RP
     ;

BOOL_OP : LT_OP
     | GT_OP
     | LE_OP
     | GE_OP
     | EQ_OP
     | NE_OP
     ;

MATH_OP : PLUS_OP
    ;

EXP : IDENTIFIER
    | EXP MATH_OP EXP
    ;

相关规则的词法分析器扫描器如下:

"="       { yylval.string = strdup(yytext); return ASSIGN;}
"+"       { yylval.string = strdup(yytext); return PLUS_OP;}
"-"       { yylval.string = strdup(yytext); return MINUS_OP;}
"*"       { yylval.string = strdup(yytext); return MULTIPLY_OP;}
"/"       { yylval.string = strdup(yytext); return DIV_OP;}
"%"       { yylval.string = strdup(yytext); return MOD_OP;}

"<"       { yylval.string = strdup(yytext); return LT_OP;}
">"       { yylval.string = strdup(yytext); return GT_OP; }
"<="       { yylval.string = strdup(yytext); return LE_OP; }
">="       { yylval.string = strdup(yytext); return GE_OP; }
"=="       { yylval.string = strdup(yytext); return EQ_OP; }
"!="       { yylval.string = strdup(yytext); return NE_OP; }
"("       { yylval.string = strdup(yytext); return LP; }
")"       { yylval.string = strdup(yytext); return RP; }
"{"       { yylval.string = strdup(yytext); return LC; }
"}"       { yylval.string = strdup(yytext); return RC; }
if { return IF; }

我知道当我添加 MATH_OP 时冲突开始了(我有所有数学运算符并且有 5 个冲突,除了 PLUS_OP 之外的所有都被删除并得到 1 shift/reduce冲突)。

我按照建议 here, and checked this 问题对输出文件使用了 -v 标志,但它不太像我的语法...

如何找到冲突?

你的语法包括产生式:

EXP : EXP MATH_OP EXP

这本质上是模棱两可的。假设您有两个运算符:

1 + 2 * 3

很明显,上面的是EXP MATH_OP EXP(因为没有别的选择),而是那个

[EXP: 1 + 2] [MATH_OP *] [EXP: 3]

或者是

[EXP: 1] [MATH_OP +] [EXP: 2 * 3]

这两个解析显然有不同的语义,语法允许两者。

即使您只有一个运算符,也会存在歧义(实际上,同样的歧义),尽管碰巧使用 + 的通常定义,评估将是相同。 (与单个运算符 - 不同,这会使歧义稍微清楚一些。)

创建算术表达式的 yacc/bison 文法有两种常规策略:

  1. 明确指出表达式的解析方式。 (Example from Wikipedia).

  2. 使用优先声明隐式限制解析。 (Example from bison manual.)

第一种策略比较冗长但更灵活;如果您正在尝试了解 LR 解析的工作原理,可能会更好,因为它是显式的。第二种策略更紧凑,可以说更容易阅读(因为许多临时读者比上下文无关语法更能理解运算符优先级列表),但要详细了解它是如何工作的。

在这两种情况下,您都不能像 MATH_OP 那样将所有运算符都集中到一个非终结符中,因为具有不同优先级的运算符在语法上是不同的。

您还将在此站点中找到许多相关问题和答案。


顺便说一句,将 + 之类的纯句法标记的字符串值传递给解析器的情况很少见。解析器不需要那个值,因为它已经知道标记类型,所以 strdup() 是不必要的开销,相应的 free() 使语法动作混乱(而且,把它放在哪里并不明显) .如果您认为您需要字符串来跟踪语法操作,请查看 bison's debug facilities,它比在整个解析器中散布 printfs 更容易使用和更可靠。如果您根本不使用运算符的语义值,那么您显然不需要去复制字符串然后释放或泄漏内存的麻烦。