shift/reduceとreduce/reduceのconflict
Shift (LR法)やReduce (LR法)の競合
特にHappyについて
shift/reduce conflict
日本語ではシフト還元競合とか
文法規則がshiftとreduceの両方が解釈できることを示す
つまりその入力の時点で、さらに長い非終端記号を選択することもできるし、そこで打ち切って非終端記号にしてしまうこともできてしまう
一概に文法規則が間違っているとは言えない
競合した場合、happyは自動的にshiftが選ぶ
ぶらさがりelseの例
code:bnf
S -> if E then S ①
S -> if E then S else S ②
S -> other ③
このとき、if a then if b then s1 else s2は以下の2通りに解釈可能
code:example
if a then (if b then s1) else s2
if a then (if b then s1 else s2) // 一般的にはこっち
https://gyazo.com/948646d84a2ef06dd28ec683d1c9a056
頭から順々にスタックに積んでいき、s1まで積み終えたあとで、2つの選択肢があり、コンフリクトしている
緑の部分を①を使ってreduceするか
exampleの前者
②を使うためにelseをshiftするか
exampleの後者
演算子の優先順位がない場合のa + b * cでも同じような問題に至る
a + bまでスタックに積んだ状態で、
a+bを還元するか、*をshiftするか
どうやって解消するか
タイガーブック p.61
reduce/reduce conflict
日本語では還元還元競合とか
同時にReduceできる文法規則が複数あることを示す
便宜上Happyは先に書かれた方の文法を優先するがこれは正しいとは限らない
人間がこのエラーを取り除く必要がある
解析手順によってプログラムの出力が異なる
入力の特定のトークン列が1つ以上の非終端記号を表してしまっている
どうやって解消するか
例を見てみる ref
他にも色々解説されているので詳しくは参照元を参照
code:エラー.bnf
sequence: /* 空 */
| maybeword
| sequence word
maybeword: /* 空 */
| word
wordがmaybewordにもなり得るパターンが複数あるのが問題
word→maybeword→sequence
空 word→sequence word→sequence
以下のように修正するなど
code:修正後.bnf
sequence: /* 空 */
| sequence word
maybeword: /* 空 */
| word
具体例
Hytlで実際に問題になっていたもの一部抜粋
code:.y
Exp :: { Exp }
: Exp '+' Exp
| var Factor
| Factor
Factor
: '(' Exp ')'
| int
| var
| list
code:info
state 44 contains 1 reduce/reduce conflicts.
// 中略
State 44
Factor -> list . (rule 25)
list -> Factor ':' list . (rule 27)
int reduce using rule 27
var reduce using rule 27
'+' reduce using rule 27
':' reduce using rule 27
(reduce using rule 25)
%eof reduce using rule 27
https://gyazo.com/c5df8376980d033a6a3edf8c9c5b7888
あってる?
なぜなっていたかというと以下のような構文をサポートしていたため
list : list
これはlistのレベルが必要なので、型などがないと単純にparseできない #??
[1]:[2]:[]はOKだが、1:[2]:[]はNGにしないといけないが、ここの差はhappy内では明示されていない
どう解決するか
list : listのサポートを止める
いったんこれにしたmrsekut.icon
code:bnf
Exp :: { Exp }
: Exp '+' Exp
| var Factor
| list
| Factor
Factor
: '(' Exp ')'
| int
| var
ルール内に配列のネストレベルを計算させる
わからんけど
型検査で解決する
わからんけど
Happyのエラー例
$ happy --info hoge.yによってinfoファイルを見るといい
一番上のブロックにどのStateでエラーが起きているかが表示されている
code:.info
state 31 contains 10 reduce/reduce conflicts.
これはState 31でreduce/reduce conflictsが10個起きていることを表す
実際にState31を見てみる
端折っているがこんなふうに書かれてある
code:.info
State 31
Factor -> list . (rule 20)
list -> Exp ':' list . (rule 22)
int reduce using rule 22
var reduce using rule 22
bool reduce using rule 22
'+' reduce using rule 22
(reduce using rule 20)
'-' reduce using rule 22
(reduce using rule 20)
'*' reduce using rule 22
(reduce using rule 20)
'/' reduce using rule 22
(reduce using rule 20)
ここでintやvarは正常にrule 22を用いてreduceされるが書かれてあるが、+や-などについては2つのruleが記載されている
これはrule 22とrule 20のどちらでreduceするべきかが決まらないという旨を告げている
それでもHappyはどちらかを決めないといけないので、とりあえずrule 22が選ばれているということだろう
上の引用は一部端折っているが、(reduce using rule 20)という記述はこのState 31の中に10箇所ある
これがstate 31 contains 10 reduce/reduce conflicts.の示す意味
最初の方の行の
Exp -> var . Factor (rule 13)の「.」の意味
現在、.の手前までスタックに積んでいる状態で、.の右のトークンが来たよという意味
↑では、varをスタックに積んだ状態で、Factorが来た際のことを問題としている
たぶんmrsekut.icon
参考
shift/reduce conflictのみ
タイガーブック p.61
sec-conflict-tips.html - Pac Learner
reduce/reduce conflictのみ
両方
Bison 1.28 - Bison構文解析器のアルゴリズム
詳しくて良いmrsekut.icon
構文解析の実際:yaccの使い方
https://mizunashi-mana.github.io/blog/posts/2021/01/how-to-use-happy/