The problem is that it is not only if
statements which can be "matched". An iteration statement which ends with an unmatched statement is just as unmatched as an if
statement which ends with an unmatched statement. That's what "matched" means.
So you have to divide your iterative statements into matched
and unmatched
variants. (This is why most people use precedence or expect declarations to deal with dangling else.)
Here's a simple example in case it becomes useful in the future. (Many of the non-terminals haven't been changed, so their definitions have been omitted. I also expanded a lot of abbreviations.)
statement: matched
| unmatched
/* Statements which could have been extended with an else clause,
* but haven't been. An unmatched statement can be the body of an
* unmatched compound statement, which includes an `if` statement
* whose `else` clause is unmatched. Unmatched also includes `if`
* statements without an `else` clause.
*/
unmatched: "if" '(' simpleExp ')' statement
| "if" '(' simpleExp ')' matched "else" unmatched
| "while" '(' simpleExp ')' "do" unmatched
| "for" IDENT '=' iterRange "do" unmatched
/* Statements which cannot be extended with an else clause.
* In other words, in a `matched` every `else` matches some `if`.
* Only `matched` statements can come between `if` and `else`, because
* an unmatched statement would grab the `else` for itself.
*/
matched : "if" '(' simpleExp ')' matched "else" matched
| "while" '(' simpleExp ')' "do" matched
| "for" IDENT '=' iterRange "do" matched
| expStatement
| returnStatement
| breakStatement
| '{' localDeclarations statementList '}'
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…