我正在尝试为C编写语法,并且遇到了一个我不太了解的问题。语法的相关部分:
stmt :
types decl SEMI { marks (A.Declare ($1, $2)) (1, 2) }
| simp SEMI { marks $1 (1, 1) }
| RETURN exp SEMI { marks (A.Return $2) (1, 2) }
| control { $1 }
| block { marks $1 (1, 1) }
;
control :
if { $1 }
| WHILE RPAREN exp LPAREN stmt { marks (A.While ($3, $5)) (1, 5) }
| FOR LPAREN simpopt SEMI exp SEMI simpopt RPAREN stmt { marks (A.For ($3, $5, $7, $9)) (1, 9) }
;
if :
IF RPAREN exp LPAREN stmt { marks (A.If ($3, $5, None)) (1, 5) }
| IF RPAREN exp LPAREN stmt ELSE stmt { marks (A.If ($3, $5, $7)) (1, 7) }
;
这不起作用。我跑了ocamlyacc -v并得到了以下报告:
83: shift/reduce conflict (shift 86, reduce 14) on ELSE
state 83
if : IF RPAREN exp LPAREN stmt . (14)
if : IF RPAREN exp LPAREN stmt . ELSE stmt (15)
ELSE shift 86
IF reduce 14
WHILE reduce 14
FOR reduce 14
BOOL reduce 14
IDENT reduce 14
RETURN reduce 14
INT reduce 14
MAIN reduce 14
LBRACE reduce 14
RBRACE reduce 14
LPAREN reduce 14
我已经读过,转换/减少冲突是由于语法规范的模糊性,但是我不知道如何以一种不模糊的方式指定它?
答案 0 :(得分:3)
语法肯定是模棱两可的,虽然你知道每个字符串的含义,而且尽管ocamlyacc
报告了一个移位/减少冲突,但它生成的语法也会为每个有效输入生成正确的解析。 / p>
含糊不清来自
if ( exp1 ) if ( exp2) stmt1 else stmt2;
显然stmt1
仅在exp1
和exp2
都为真时才会执行。但如果stmt1
为false,或者exp1
为true且exp1
为false,exp2
会执行吗?那些代表不同的解析;第一个(无效)解析将else stmt2
附加到if (exp1)
,而您,我和ocamlyacc知道正确的解析会将else stmt2
附加到if (exp2)
。
语法可以重写,虽然这有点令人讨厌。基本思想是将语句分为两类:“匹配”(这意味着语句中的每个else
都匹配一些if
)和“不匹配”(这意味着跟随{{1会匹配语句中的一些else
。完整的语句可能是不匹配的,因为if
子句是可选的,但你永远不能在else
和{{{{}}之间有一个不匹配的语句1}},因为if
必须与不匹配语句中的else
匹配。
以下语法基本上就是你提供的语法,但重写为使用野牛式单引号,我发现它更具可读性。我不知道ocamlyacc是否会处理这些问题。 (顺便说一下,你的语法说 else
,用左右括号的共同定义,意思是if
。这就是我找到单引号字符终端的原因之一更具可读性。)
Bison处理这种语法而没有冲突。
IF RPAREN exp LPAREN...
就个人而言,我会稍微重构一下。例如:
if ) exp (
一个常见且更简单但不太严格的解决方案是使用bison manual中建议的优先声明。