It's the ancient dangling if (if without an else) problem. if you write some code like
if (a == b) then
if (b == c) then
do_something;
else
do_something_else;
a parser will have problems deciding to which "if" the "else" belongs.
One solution is to add a delimiter, like "endif". Example:
if (a == b) then
if (b == c) then
do_something;
endif;
else
do_something_else;
endif;
Not confused by indentation any more (which I did wrong deliberately), it is clear what to do now.
That's why a grammar like the one outlined below doesn't work (it produces a lot of conflicts):
stmtlist: stmt
| stmtlist stmt
stmt: ifequal
| something_else
ifequal: IFEQUAL '(' expr ',' expr ')' stmtlist opt_lt_gt
opt_lt_gt:
| SMALLER stmtlist
| LARGER stmtlist
| SMALLER stmtlist LARGER stmtlist
But as soon as the statement list is somehow separated from the IFEQUAL statement, e.g. by using braces, the problems vanish.
stmtlist: '{' stmtseq '}'
stmtseq: stmt
| stmtseq stmt
The other possibility is to prohibit incomplete statements in the stmtlist. This will more or less double your grammar. It could look like
fullifequal: IFEQUAL '(' expr ',' expr ')' fstmtlist SMALLER fstmtlist LARGER fstmtlist
fstmtlist: fullstmt
| fstmtlist fullstmt
fullstmt: fullifequal
| some_other
ifequal: IFEQUAL '(' expr ',' expr ')' fstmtlist opt_lt_gt
opt_lt_gt:
| SMALLER fstmtlist
| LARGER fstmtlist
| SMALLER fstmtlist LARGER fstmtlist
Personally I prefer the braces solution because it's easy to read, doesn't constrain the statements within an "ifequal" statement and the parser code will be shorter. But I guess you'll need the other solution.