问题
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:
stmtlist → stmt
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 stmt
s 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