[英]How to overcome shift-reduce conflict in LALR grammar
我正在嘗試解析正和負小數。
number(N) ::= pnumber(N1).
number(N) ::= nnumber(N1).
number(N) ::= pnumber(N1) DOT pnumber(N2).
number(N) ::= nnumber(N1) DOT pnumber(N2).
pnumber(N) ::= NUMBER(N1).
nnumber(N) ::= MINUS NUMBER(N1).
包含前兩個規則會產生移位/減少沖突,但我不知道如何編寫語法以使沖突永遠不會發生。 我正在使用Lemon解析器。
編輯:與.out文件沖突
State 79:
(56) number ::= nnumber *
number ::= nnumber * DOT pnumber
DOT shift 39
DOT reduce 56 ** Parsing conflict **
{default} reduce 56 number ::= nnumber
State 80:
(55) number ::= pnumber *
number ::= pnumber * DOT pnumber
DOT shift 40
DOT reduce 55 ** Parsing conflict **
{default} reduce 55 number ::= pnumber
State 39:
number ::= nnumber DOT * pnumber
pnumber ::= * NUMBER
NUMBER shift-reduce 59 pnumber ::= NUMBER
pnumber shift-reduce 58 number ::= nnumber DOT pnumber
State 40:
number ::= pnumber DOT * pnumber
pnumber ::= * NUMBER
NUMBER shift-reduce 59 pnumber ::= NUMBER
pnumber shift-reduce 57 number ::= pnumber DOT pnumber
編輯2:導致問題的最小語法
start ::= prog.
prog ::= rule.
rule ::= REVERSE_IMPLICATION body DOT.
body ::= bodydef.
body ::= body CONJUNCTION bodydef.
bodydef ::= literal.
literal ::= variable.
variable ::= number.
number ::= pnumber.
number ::= nnumber.
number ::= pnumber DOT pnumber.
number ::= nnumber DOT pnumber.
pnumber ::= NUMBER.
nnumber ::= MINUS NUMBER.
告訴你的沖突指示與如何的問題number
的非終端,而不適用於number
本身。
的基本問題是,看到后pnumber
或nnumber
,當先行的下一個標記是DOT
,它不能決定是否應該是這樣的的端部number
(減少,所以DOT
是一些其它的非末端后的部分的number
),或者如果DOT
應作為的一部分被處理number
(移位,因此它可以減少后來在p中的一個/ n已接收DOT pnumber規則。)
因此,為了診斷問題,您需要在右側的任何位置顯示所有使用number
的規則(並在右側遞歸使用這些規則的非終結符的所有其他規則)。
請注意,僅發布語法片段很少有用,因為LR解析器的構造過程在很大程度上取決於在語法中其他地方使用規則的環境。
因此,這里的問題是,你需要兩個令牌一個先行區分DOT
的(真正) number
literal
和DOT
在結束rule
。
最簡單的解決方法是讓詞法分析器處理它-詞法分析器可以非常輕松地進行少量先行查找,因此您可以將REAL_NUMBER
識別為與NUMBER
截然不同的非REAL_NUMBER
(可能仍然沒有-
,所以最終得到
number ::= NUMBER | MINUS NUMBER | REAL_NUMBER | MINUS REAL_NUMBER
通過分解語法來消除沖突要困難得多,但是可以做到。
通常,要重構語法以消除前瞻性沖突,您需要找出表明沖突的規則(此處為rule
和number
),並進行重構以將它們組合到具有共同前綴的規則中,直到您足夠了解為止。消除歧義。
首先,我將假設除了number
以外,還有其他規則可以出現在這里,否則我們可以消除所有中間規則。
variable ::= number | name
我們想將number
規則“向上”移動到語法中,以使其與DOT
rule
位於同一位置。 因此,當包含規則以number
結尾時,我們需要將它們拆分為特殊情況。 我們添加后綴來表示與原始規則相對應的規則,所有版本均以number
結尾
variable ::= number | variable_n
variable_n ::= name
...並傳播“向上”
literal ::= number | literal_n
literal_n ::= variable_n
...然后再次
bodydef ::= number | bodydef_n
bodydef_n := literal_n
...然后再次
body ::= number | body_n
body := body CONJUNCTION number
body_n ::= bodydef_n
body_n ::= body CONJUNCTION bodydef_n
請注意,在向上移動時,您需要分解越來越多的規則,因此此過程可能會使語法大打折扣。 但是, 僅在重構的rhs末尾使用的規則最終僅需要_n
版本,因此您不必一定要加倍規則數。
...最后一步
rule ::= REVERSE_IMPLICATION body_n DOT
rule ::= REVERSE_IMPLICATION number DOT
rule ::= REVERSE_IMPLICATION body CONJUNCTION number DOT
現在,您在所有位置都擁有DOT,因此請擴展number
規則:
rule ::= REVERSE_IMPLICATION body_n DOT
rule ::= REVERSE_IMPLICATION integer DOT
rule ::= REVERSE_IMPLICATION integer DOT pnumber DOT
rule ::= REVERSE_IMPLICATION body CONJUNCTION integer DOT
rule ::= REVERSE_IMPLICATION body CONJUNCTION integer DOT pnumber DOT
移位-減少沖突消失了,因為規則具有共同的前綴,直到超過需要使用的前瞻性來確定要使用的前綴為止。 通過添加以下內容,我減少了此最終擴展中的規則數量
integer ::= pnumber | nnumber
您必須使用%left
或%right
聲明DOT
運算符的關聯性。
或者,另一個想法是放棄這種中間減少。 語法的明顯特征是數字隨DOT
增加而增加。 可以用一條規則來捕獲:
number : number DOT NUMBER
一個數字,一個DOT
,一個NUMBER
令牌仍然是一個數字。
該規則不需要DOT
聲明關聯性,因為沒有歧義。 規則純粹是左遞歸, DOT
的右手是終端令牌。 當狀態機在這一點上時,解析器必須將堆棧的頂部減小為number
,然后移動DOT
:
number : number DOT NUMBER
您在此處解析的語言是常規語言; 它可以由正則表達式解析,而無需任何遞歸。 這就是為什么其中同時具有左右遞歸並要求聲明關聯性的規則有些“大錘子”。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.