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

时间:2016-12-16 16:35:02

标签: parsing compilation grammar bison yacc

我有以下语法(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个移位/减少冲突)。

我使用-v标记作为建议here的输出文件,并检查this问题,但它与我的语法不太相似......

我如何找到冲突?

1 个答案:

答案 0 :(得分:2)

你的语法包括制作:

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)。

  3. 第一个策略更灵巧但更灵活;如果您正在尝试学习LR解析的工作方式,那可能会更好,因为它是明确的。第二种策略更紧凑,更容易阅读(因为许多休闲读者比无上下文语法更好地理解运算符优先级列表),但理解它的工作原理是更多的工作。

    在任何情况下,你都不能把所有运算符都归结为像MATH_OP这样的单个非终结符,因为具有不同优先级的运算符在语法上是不同的。

    您还可以在本网站找到许多相关的问题和答案。

    顺便说一下,将纯+这样的纯语法标记的字符串值传递给解析器是非常有用的。解析器不需要该值,因为它已经知道了令牌类型,因此strdup()是不必要的开销,而相应的free()使语法操作变得混乱(同样,在哪里放置它也不明显) 。如果您认为需要字符串来跟踪语法操作,请查看bison's debug facilities,它比在整个解析器中使用printfs更容易使用和更可靠。如果您根本没有使用运算符的语义值,那么您显然不需要去复制字符串然后释放或泄漏内存。