https://www.gnu.org/software/bison/manual/bison.html#Algorithm
学习yacc后一直有一个疑问,reduce到底什么时候发生? 遇到匹配的规则立即执行reduce吗?还是在等一等看看后面的token,可能匹配上其他的规则?
bison行为:
具体步骤:
上面的步骤2并不是匹配上的都能reduce,lookahead token会影响一些规则,使其延迟reduce。
expr:
term '+' expr
| term
;
term:
'(' expr ')'
| term '!'
| "number"
;
当1+2
进入语法树时,如果不向前看一个token,会发生的问题:
1 + 2 )
\ /
1 + 2 reduce为:expr ')'
\ /
expr 和 右括号reduce为term
如果1+2后面跟的是!,上面的1+2已经被reduce为expr,叹号肯定是无法匹配上了。
所以要reduce'1+2'
时,必须向前看一个,再决定1+2要不要reduce。
lookahead=)
,可以直接reduce。lookahead=!
,需要延迟reduce,什么也不做。实例:其中"if"、“then”、"else"终结符
if_stmt:
"if" expr "then" stmt
| "if" expr "then" stmt "else" stmt
;
假设当前"if"、“then"都已经在解析栈中,lookahead是"then”。
现在发生了shift/reduce冲突。Bison会通过选择shift来解决这些冲突(除非运算符优先级声明)。
为了解其中的原因,下面与其他选择进行对比:
正例:如果bison更偏向于shift “else”,下面语句1就等价与语句2,符合预期。
-- 语句1
if x then
if y then win;
else lose;
-- 语句2:else和里面的If结合,符合预期
if x then do;
if y then win;
else lose; end;
反例:如果bison更偏向于reduce,下面语句1就等价与语句2,不符合预期。
能shift也能recude得时候,先recude了,
-- 语句1
if x then
if y then win; -- 栈:if then if then(lookahead=else),看到后一个if then直接recude了。
else lose; -- 剩下一个else,只能和外面的if一起recude了。
-- 语句2:else和外面的if结合,不符合预期。
if x then do;
if y then win; end;
else lose; -- 结果就是else给外面的if了,但预期是需要和里面的if结合。
这就是经典的“dangling else”冲突,悬挂的else。
文件
%%
stmt:
expr
| if_stmt
;
if_stmt:
"if" expr "then" stmt
| "if" expr "then" stmt "else" stmt
;
expr:
"identifier"
;
bison --report=counterexamples if.y
output文件
State 9
3 if_stmt: "if" expr "then" stmt •
4 | "if" expr "then" stmt • "else" stmt
"else" shift, and go to state 10
后面挂了一个else,是要等else进来shift,还是
"else" [reduce using rule 3 (if_stmt)]
直接reduce掉前面的if then?
$default reduce using rule 3 (if_stmt)
shift/reduce conflict on t0ken "else":
3 if_stmt: "if" expr "then" stmt •
4 if_stmt: "if" expr "then" stmt • "else" stmt
Example: "if" expr "then" "if" expr "then" stmt • "else" stmt
Shift derivation
if_stmt
↳ 3: "if" expr "then" stmt
↳ 2: if_stmt
↳ 4: "if" expr "then" stmt • "else" stmt
Reduce derivation
if_stmt
↳ 4: "if" expr "then" stmt "else" stmt
↳ 2: if_stmt
↳ 3: "if" expr "then" stmt •