Ply shift/reduce conflicts: dangling else and empty productions

笑着哭i 提交于 2021-01-29 08:46:34

问题


I had lots of conflicts, most of them were due to operators and relational operators which had different precedences. But I still face some conflicts that I don't really know how to tackle them. some of them are below. I suspect that maybe I should do epsilon elimination for stmtlist but to be honest I'm not sure about it.

state 70:

state 70

    (27) block -> LCB varlist . stmtlist RCB
    (25) varlist -> varlist . vardec
    (28) stmtlist -> . stmt
    (29) stmtlist -> . stmtlist stmt
    (30) stmtlist -> .
    (15) vardec -> . type idlist SEMICOLON
    (33) stmt -> . RETURN exp SEMICOLON
    (34) stmt -> . exp SEMICOLON
    (35) stmt -> . WHILE LRB exp RRB stmt
    (36) stmt -> . FOR LRB exp SEMICOLON exp SEMICOLON exp RRB stmt
    (37) stmt -> . IF LRB exp RRB stmt elseiflist
    (38) stmt -> . IF LRB exp RRB stmt elseiflist ELSE stmt
    (39) stmt -> . PRINT LRB ID RRB SEMICOLON
    (40) stmt -> . block
    (7) type -> . INTEGER
    (8) type -> . FLOAT
    (9) type -> . BOOLEAN
    (44) exp -> . lvalue ASSIGN exp
    (45) exp -> . exp SUM exp
    (46) exp -> . exp MUL exp
    (47) exp -> . exp SUB exp
    (48) exp -> . exp DIV exp
    (49) exp -> . exp MOD exp
    (50) exp -> . exp AND exp
    (51) exp -> . exp OR exp
    (52) exp -> . exp LT exp
    (53) exp -> . exp LE exp
    (54) exp -> . exp GT exp
    (55) exp -> . exp GE exp
    (56) exp -> . exp NE exp
    (57) exp -> . exp EQ exp
    (58) exp -> . const
    (59) exp -> . lvalue
    (60) exp -> . ID LRB explist RRB
    (61) exp -> . LRB exp RRB
    (62) exp -> . ID LRB RRB
    (63) exp -> . SUB exp
    (64) exp -> . NOT exp
    (27) block -> . LCB varlist stmtlist RCB
    (31) lvalue -> . ID
    (32) lvalue -> . ID LSB exp RSB
    (72) const -> . INTEGERNUMBER
    (73) const -> . FLOATNUMBER
    (74) const -> . TRUE
    (75) const -> . FALSE

  ! shift/reduce conflict for RETURN resolved as shift
  ! shift/reduce conflict for WHILE resolved as shift
  ! shift/reduce conflict for FOR resolved as shift
  ! shift/reduce conflict for IF resolved as shift
  ! shift/reduce conflict for PRINT resolved as shift
  ! shift/reduce conflict for ID resolved as shift
  ! shift/reduce conflict for LRB resolved as shift
  ! shift/reduce conflict for SUB resolved as shift
  ! shift/reduce conflict for NOT resolved as shift
  ! shift/reduce conflict for LCB resolved as shift
  ! shift/reduce conflict for INTEGERNUMBER resolved as shift
  ! shift/reduce conflict for FLOATNUMBER resolved as shift
  ! shift/reduce conflict for TRUE resolved as shift
  ! shift/reduce conflict for FALSE resolved as shift
    RCB             reduce using rule 30 (stmtlist -> .)
    RETURN          shift and go to state 99
    WHILE           shift and go to state 101
    FOR             shift and go to state 102
    IF              shift and go to state 103
    PRINT           shift and go to state 104
    INTEGER         shift and go to state 8
    FLOAT           shift and go to state 9
    BOOLEAN         shift and go to state 10
    ID              shift and go to state 31
    LRB             shift and go to state 36
    SUB             shift and go to state 34
    NOT             shift and go to state 37
    LCB             shift and go to state 45
    INTEGERNUMBER   shift and go to state 38
    FLOATNUMBER     shift and go to state 39
    TRUE            shift and go to state 40
    FALSE           shift and go to state 41

  ! RETURN          [ reduce using rule 30 (stmtlist -> .) ]
  ! WHILE           [ reduce using rule 30 (stmtlist -> .) ]
  ! FOR             [ reduce using rule 30 (stmtlist -> .) ]
  ! IF              [ reduce using rule 30 (stmtlist -> .) ]
  ! PRINT           [ reduce using rule 30 (stmtlist -> .) ]
  ! ID              [ reduce using rule 30 (stmtlist -> .) ]
  ! LRB             [ reduce using rule 30 (stmtlist -> .) ]
  ! SUB             [ reduce using rule 30 (stmtlist -> .) ]
  ! NOT             [ reduce using rule 30 (stmtlist -> .) ]
  ! LCB             [ reduce using rule 30 (stmtlist -> .) ]
  ! INTEGERNUMBER   [ reduce using rule 30 (stmtlist -> .) ]
  ! FLOATNUMBER     [ reduce using rule 30 (stmtlist -> .) ]
  ! TRUE            [ reduce using rule 30 (stmtlist -> .) ]
  ! FALSE           [ reduce using rule 30 (stmtlist -> .) ]

    stmtlist                       shift and go to state 96
    vardec                         shift and go to state 97
    stmt                           shift and go to state 98
    type                           shift and go to state 72
    exp                            shift and go to state 100
    block                          shift and go to state 105
    lvalue                         shift and go to state 33
    const                          shift and go to state 35 

here is a list of all productions:

program → declist main ( ) block

declist → dec | declist dec | 𝜖

dec → vardec | funcdec

type → int | float | bool

iddec → id | id [ exp ] | id=exp

idlist → iddec | idlist , iddec

vardec → type idlist ;

funcdec → type id (paramdecs) block | void id (paramdecs) block

paramdecs → paramdecslist | 𝜖

paramdecslist → paramdec | paramdecslist , paramdec

paramdec → type id | type id []

Precedencevarlist → vardec | varlist vardec | 𝜖

block → { varlist stmtlist }

stmtlist → stmt | stmlist stmt | 𝜖

lvalue → id | id [exp]

stmt → return exp ; | exp ;| block |

while (exp) stmt |

for(exp ; exp ; exp) stmt |

if (exp) stmt elseiflist | if (exp) stmt elseiflist else stmt |

print ( id) ;

elseiflist → elif (exp) stmt | elseiflist elif (exp) stmt | 𝜖

exp → lvalue=exp | exp operator exp |exp relop exp|

const | lvalue | id(explist) | (exp) | id() | - exp | ! exp

operator → “||” | && | + | - | * | / | %

const → intnumber | floatnumber | true | false

relop → > | < | != | == | <= | >=

explist → exp | explist,exp

Another problem is the famous dangling else, I added ('nonassoc', 'IFP'), ('left', 'ELSE' , 'ELIF') to precedence tuple and change the grammar in this way:

def p_stmt_5(self, p):
    """stmt : IF LRB exp RRB stmt elseiflist %prec IFP """
    print("""stmt : IF LRB exp RRB stmt elseiflist """)

def p_stmt_6(self, p):
    """stmt : IF LRB exp RRB stmt elseiflist ELSE stmt"""
    print("""stmt : IF LRB exp RRB stmt elseiflist else stmt """)

But it didn't make it go away. below is the state where the shift/reduce conflict happens.

state 130

    (37) stmt -> IF LRB exp RRB stmt . elseiflist
    (38) stmt -> IF LRB exp RRB stmt . elseiflist ELSE stmt
    (41) elseiflist -> . ELIF LRB exp RRB stmt
    (42) elseiflist -> . elseiflist ELIF LRB exp RRB stmt
    (43) elseiflist -> .

  ! shift/reduce conflict for ELIF resolved as shift
    ELIF            shift and go to state 134
    RCB             reduce using rule 43 (elseiflist -> .)
    RETURN          reduce using rule 43 (elseiflist -> .)
    WHILE           reduce using rule 43 (elseiflist -> .)
    FOR             reduce using rule 43 (elseiflist -> .)
    IF              reduce using rule 43 (elseiflist -> .)
    PRINT           reduce using rule 43 (elseiflist -> .)
    ID              reduce using rule 43 (elseiflist -> .)
    LRB             reduce using rule 43 (elseiflist -> .)
    SUB             reduce using rule 43 (elseiflist -> .)
    NOT             reduce using rule 43 (elseiflist -> .)
    LCB             reduce using rule 43 (elseiflist -> .)
    INTEGERNUMBER   reduce using rule 43 (elseiflist -> .)
    FLOATNUMBER     reduce using rule 43 (elseiflist -> .)
    TRUE            reduce using rule 43 (elseiflist -> .)
    FALSE           reduce using rule 43 (elseiflist -> .)
    ELSE            reduce using rule 43 (elseiflist -> .)

  ! ELIF            [ reduce using rule 43 (elseiflist -> .) ]

    elseiflist                     shift and go to state 133

Finally there are two more states with shift/reduce errors which I list below:

state 45

    (27) block -> LCB . varlist stmtlist RCB
    (24) varlist -> . vardec
    (25) varlist -> . varlist vardec
    (26) varlist -> .
    (15) vardec -> . type idlist SEMICOLON
    (7) type -> . INTEGER
    (8) type -> . FLOAT
    (9) type -> . BOOLEAN

  ! shift/reduce conflict for INTEGER resolved as shift
  ! shift/reduce conflict for FLOAT resolved as shift
  ! shift/reduce conflict for BOOLEAN resolved as shift
    RETURN          reduce using rule 26 (varlist -> .)
    WHILE           reduce using rule 26 (varlist -> .)
    FOR             reduce using rule 26 (varlist -> .)
    IF              reduce using rule 26 (varlist -> .)
    PRINT           reduce using rule 26 (varlist -> .)
    ID              reduce using rule 26 (varlist -> .)
    LRB             reduce using rule 26 (varlist -> .)
    SUB             reduce using rule 26 (varlist -> .)
    NOT             reduce using rule 26 (varlist -> .)
    LCB             reduce using rule 26 (varlist -> .)
    INTEGERNUMBER   reduce using rule 26 (varlist -> .)
    FLOATNUMBER     reduce using rule 26 (varlist -> .)
    TRUE            reduce using rule 26 (varlist -> .)
    FALSE           reduce using rule 26 (varlist -> .)
    RCB             reduce using rule 26 (varlist -> .)
    INTEGER         shift and go to state 8
    FLOAT           shift and go to state 9
    BOOLEAN         shift and go to state 10

  ! INTEGER         [ reduce using rule 26 (varlist -> .) ]
  ! FLOAT           [ reduce using rule 26 (varlist -> .) ]
  ! BOOLEAN         [ reduce using rule 26 (varlist -> .) ]

    varlist                        shift and go to state 70
    vardec                         shift and go to state 71
    type                           shift and go to state 72

And:

state 0

    (0) S' -> . program
    (1) program -> . declist MAIN LRB RRB block
    (2) declist -> . dec
    (3) declist -> . declist dec
    (4) declist -> .
    (5) dec -> . vardec
    (6) dec -> . funcdec
    (15) vardec -> . type idlist SEMICOLON
    (16) funcdec -> . type ID LRB paramdecs RRB block
    (17) funcdec -> . VOID ID LRB paramdecs RRB block
    (7) type -> . INTEGER
    (8) type -> . FLOAT
    (9) type -> . BOOLEAN

  ! shift/reduce conflict for VOID resolved as shift
  ! shift/reduce conflict for INTEGER resolved as shift
  ! shift/reduce conflict for FLOAT resolved as shift
  ! shift/reduce conflict for BOOLEAN resolved as shift
    MAIN            reduce using rule 4 (declist -> .)
    VOID            shift and go to state 7
    INTEGER         shift and go to state 8
    FLOAT           shift and go to state 9
    BOOLEAN         shift and go to state 10

  ! VOID            [ reduce using rule 4 (declist -> .) ]
  ! INTEGER         [ reduce using rule 4 (declist -> .) ]
  ! FLOAT           [ reduce using rule 4 (declist -> .) ]
  ! BOOLEAN         [ reduce using rule 4 (declist -> .) ]

    program                        shift and go to state 1
    declist                        shift and go to state 2
    dec                            shift and go to state 3
    vardec                         shift and go to state 4
    funcdec                        shift and go to state 5
    type                           shift and go to state 6

Thank you so much in advance.


回答1:


There are actually two somewhat related problems here, both having to do with ambiguity induced by duplicate base cases in recursive productions:

1. Ambiguity in stmtlist

First, as you imply, there is a problem with stmtlist. Your grammar for stmtlist is:

stmtlist → stmt | stmlist stmt | 𝜖

which has two base cases: stmtlist → stmt and stmtlist → 𝜖. This duplication means that a single stmt can be parsed in two ways:

  1. stmtlist → stmt

  2. stmtlist → stmtlist stmt → 𝜖 stmt

Grammatical ambiguities always manifest as conflicts. To eliminate the conflict, eliminate the ambiguity. If you want stmtlist to be possibly empty, use:

stmtlist → stmlist stmt | 𝜖

If you want to insist that stmtlist contains at least one stmt, use:

stmtlist → stmlist stmt | stmt

Above all, try to understand the logic of the above suggestion.

In addition, you allow stmt to be empty. It should be obvious that this is going to lead to an ambiguity in stmtlist because it is impossible to know how many empty stmts there are in a list. It could be 3; it could be 42; it could be eight million. Empty is invisible.

The potential nothingness of stmt also creates an ambiguity with those compound statements which end with stmt, such as "while" '(' exp ')' stmt. If stmt could be nothing, then

while (x) while(y) c;

could be two statements: while(x) with an empty repeated statement, and then while(y) with a loop on c;. Or it could have the (probably expected) meaning of a while(x) loop whose repeated statement is a nested while(y) c;. I would suggest that no-one would expect the first interpretation and that the grammar should not allow it. If you wanted an empty while target, you would use ; as the repeated statement, not nothing.

I'm sure you didn't intend that a stmt can be nothing. It makes lots of sense to allow the empty statement written as ; (that is, an emptyness followed by a semicolon), but that's obviously a different syntax. (Inside {} you might want to allow nothing, rather than insisting on a semicolon. To achieve that, you need an empty stmtlist, not an empty stmt.)

2. Dangling else: actually an ambiguity in elseiflist

I think this is the grammar you are using:

(37) stmt -> "if" '(' exp ')' stmt elseiflist %prec IFP
(38) stmt -> "if" '(' exp ')' stmt elseiflist "else" stmt
(41) elseiflist -> "elif" '(' exp ')' stmt
(42) elseiflist -> elseiflist "elif" '(' exp ')' stmt
(43) elseiflist ->

Just as with the stmtlist production, elseiflist is a recursive production with two base cases, one of which is redundant. Again, it is necessary to decide whether or not elseiflist can really be empty (Hint: it can be), and then to remove one or the other of the base cases to avoid an ambiguous parse.

Having said that, I don't think that's the best way of writing the grammar for an if statement; the parse tree it builds might not be quite as you expect. But I guess it will work.



来源:https://stackoverflow.com/questions/61755329/ply-shift-reduce-conflicts-dangling-else-and-empty-productions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!