我有以下语法(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问题,但它与我的语法不太相似......
我如何找到冲突?
答案 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语法有两种常规策略:
明确指出如何解析表达式。 (Example from Wikipedia)。
使用优先级声明来隐式限制解析。 (Example from bison manual)。
第一个策略更灵巧但更灵活;如果您正在尝试学习LR解析的工作方式,那可能会更好,因为它是明确的。第二种策略更紧凑,更容易阅读(因为许多休闲读者比无上下文语法更好地理解运算符优先级列表),但理解它的工作原理是更多的工作。
在任何情况下,你都不能把所有运算符都归结为像MATH_OP
这样的单个非终结符,因为具有不同优先级的运算符在语法上是不同的。
您还可以在本网站找到许多相关的问题和答案。
顺便说一下,将纯+
这样的纯语法标记的字符串值传递给解析器是非常有用的。解析器不需要该值,因为它已经知道了令牌类型,因此strdup()
是不必要的开销,而相应的free()
使语法操作变得混乱(同样,在哪里放置它也不明显) 。如果您认为需要字符串来跟踪语法操作,请查看bison's debug facilities,它比在整个解析器中使用printfs更容易使用和更可靠。如果您根本没有使用运算符的语义值,那么您显然不需要去复制字符串然后释放或泄漏内存。