Parsing support for if expressions. (#2883)

We model `if a then b else` as a prefix operator for parsing precedence purposes. The rule that a statement starting with `if` is never an `if` expression is handled implicitly because the statement parser never invokes the expression parser for a statement starting with `if`.

This exposed a bug in our diagnosis of the whitespace rule for prefix operators, which was incorrectly being applied to non-symbolic operators in some cases, and was producing a bogus second diagnostic in some cases, which is also fixed here.
This commit is contained in:
Richard Smith
2023-06-09 16:41:10 -07:00
committed by GitHub
parent 4e1adcf4c7
commit 2c45cb3be8
18 changed files with 400 additions and 22 deletions
+61 -3
View File
@@ -27,9 +27,15 @@ auto ParserHandleExpression(ParserContext& context) -> void {
context.DiagnoseOperatorFixity(ParserContext::OperatorFixity::Prefix);
}
context.PushStateForExpressionLoop(ParserState::ExpressionLoopForPrefix,
state.ambient_precedence,
*operator_precedence);
if (context.PositionIs(TokenKind::If)) {
context.PushState(ParserState::ExpressionIfFinish);
context.PushState(ParserState::ExpressionThen);
} else {
context.PushStateForExpressionLoop(ParserState::ExpressionLoopForPrefix,
state.ambient_precedence,
*operator_precedence);
}
++context.position();
context.PushStateForExpression(*operator_precedence);
} else {
@@ -203,6 +209,58 @@ auto ParserHandleExpressionLoopForPrefix(ParserContext& context) -> void {
context.PushState(state);
}
auto ParserHandleExpressionThen(ParserContext& context) -> void {
auto state = context.PopState();
if (context.ConsumeAndAddLeafNodeIf(TokenKind::Then,
ParseNodeKind::IfExpressionThen)) {
context.PushState(ParserState::ExpressionElse);
context.PushStateForExpression(*PrecedenceGroup::ForLeading(TokenKind::If));
} else {
// TODO: Include the location of the `if` token.
CARBON_DIAGNOSTIC(ExpectedThenAfterIf, Error,
"Expected `then` after `if` condition.");
if (!state.has_error) {
context.emitter().Emit(*context.position(), ExpectedThenAfterIf);
}
// Add placeholders for `then expression else expression`.
for (int i = 0; i != 4; ++i) {
context.AddLeafNode(ParseNodeKind::InvalidParse, *context.position(),
/*has_error=*/true);
}
context.ReturnErrorOnState();
}
}
auto ParserHandleExpressionElse(ParserContext& context) -> void {
auto state = context.PopState();
if (context.ConsumeAndAddLeafNodeIf(TokenKind::Else,
ParseNodeKind::IfExpressionElse)) {
context.PushStateForExpression(*PrecedenceGroup::ForLeading(TokenKind::If));
} else {
// TODO: Include the location of the `if` token.
CARBON_DIAGNOSTIC(ExpectedElseAfterIf, Error,
"Expected `else` after `if ... then ...`.");
if (!state.has_error) {
context.emitter().Emit(*context.position(), ExpectedElseAfterIf);
}
// Add placeholders for `else expression`.
for (int i = 0; i != 2; ++i) {
context.AddLeafNode(ParseNodeKind::InvalidParse, *context.position(),
/*has_error=*/true);
}
context.ReturnErrorOnState();
}
}
auto ParserHandleExpressionIfFinish(ParserContext& context) -> void {
auto state = context.PopState();
context.AddNode(ParseNodeKind::IfExpression, state.token, state.subtree_start,
state.has_error);
}
auto ParserHandleExpressionStatementFinish(ParserContext& context) -> void {
auto state = context.PopState();