簡體   English   中英

如何克服LALR語法中的移位減少沖突

[英]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本身。

的基本問題是,看到后pnumbernnumber ,當先行的下一個標記是DOT ,它不能決定是否應該是這樣的的端部number (減少,所以DOT是一些其它的非末端的部分的number ),或者如果DOT應作為的一部分被處理number (移位,因此它可以減少后來在p中的一個/ n已接收DOT pnumber規則。)

因此,為了診斷問題,您需要在右側的任何位置顯示所有使用number的規則(並在右側遞歸使用這些規則的非終結符的所有其他規則)。

請注意,僅發布語法片段很少有用,因為LR解析器的構造過程在很大程度上取決於在語法中其他地方使用規則的環境。


因此,這里的問題是,你需要兩個令牌一個先行區分DOT的(真正) number literalDOT在結束rule

最簡單的解決方法是讓詞法分析器處理它-詞法分析器可以非常輕松地進行少量先行查找,因此您可以將REAL_NUMBER識別為與NUMBER截然不同的非REAL_NUMBER (可能仍然沒有- ,所以最終得到

number ::= NUMBER | MINUS NUMBER | REAL_NUMBER | MINUS REAL_NUMBER

通過分解語法來消除沖突要困難得多,但是可以做到。


通常,要重構語法以消除前瞻性沖突,您需要找出表明沖突的規則(此處為rulenumber ),並進行重構以將它們組合到具有共同前綴的規則中,直到您足夠了解為止。消除歧義。

首先,我將假設除了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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM