One way I found helpful in giving precise parsing errors without causing excessive false errors is to have the parser auto-completing the missing tokens with placeholder error nodes. E.g.
if (cond1 And ) { ... }
The And operator is missing its right operand, but everything else is valid.
The parser knows the And operator needs two arms. It can complete the missing arm with an error node.
fn parseCondition() {
let leftNode = parseBoolExpr()
if (lookaheadToken() is And)
return parseAnd(leftNode)
if (lookaheadToken() is Or)
...
return leftNode
}
fn parseAnd(leftNode) {
consumeToken()
let rightNode = parseBoolExpr()
if (rightNode != null)
return AndNode(leftNode, rightNode)
else
return AndNode(leftNode, ErrorNode("missing operand", location))
}
In this way, the conditional expression is parsed to completion without interruption. Multiple errors can be handled with completion and recorded. At the end it's a matter of traversing the AST to print out all the ErrorNodes.
The parser knows the And operator needs two arms. It can complete the missing arm with an error node.
In this way, the conditional expression is parsed to completion without interruption. Multiple errors can be handled with completion and recorded. At the end it's a matter of traversing the AST to print out all the ErrorNodes.