mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 22:02:23 +01:00
Add semantic analysis and semantics IR building for `if` expressions, and add the first parts of control flow handling to semantics IR. After discussion with @chandlerc, use [block arguments](https://en.wikipedia.org/wiki/Static_single-assignment_form#Block_arguments) to convey values from the two arms of the `if` to the result. For now, only a single block argument is supported, but we should revisit this as we explore more of the requirements of the Semantics IR form. Functions can now contain multiple code blocks, so grab the entry block up-front instead of assuming the entry block will be at the top of the block stack when we reach the end of function emission. Add trivial support for `bool` type literal, because without it we can't write testcases.
307 lines
11 KiB
C++
307 lines
11 KiB
C++
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
// Exceptions. See /LICENSE for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#include "toolchain/parser/parser_context.h"
|
|
|
|
namespace Carbon {
|
|
|
|
CARBON_DIAGNOSTIC(
|
|
OperatorRequiresParentheses, Error,
|
|
"Parentheses are required to disambiguate operator precedence.");
|
|
|
|
auto ParserHandleExpression(ParserContext& context) -> void {
|
|
auto state = context.PopState();
|
|
|
|
// Check for a prefix operator.
|
|
if (auto operator_precedence =
|
|
PrecedenceGroup::ForLeading(context.PositionKind())) {
|
|
if (PrecedenceGroup::GetPriority(state.ambient_precedence,
|
|
*operator_precedence) !=
|
|
OperatorPriority::RightFirst) {
|
|
// The precedence rules don't permit this prefix operator in this
|
|
// context. Diagnose this, but carry on and parse it anyway.
|
|
context.emitter().Emit(*context.position(), OperatorRequiresParentheses);
|
|
} else {
|
|
// Check that this operator follows the proper whitespace rules.
|
|
context.DiagnoseOperatorFixity(ParserContext::OperatorFixity::Prefix);
|
|
}
|
|
|
|
if (context.PositionIs(TokenKind::If)) {
|
|
context.PushState(ParserState::IfExpressionFinish);
|
|
context.PushState(ParserState::IfExpressionFinishCondition);
|
|
} else {
|
|
context.PushStateForExpressionLoop(ParserState::ExpressionLoopForPrefix,
|
|
state.ambient_precedence,
|
|
*operator_precedence);
|
|
}
|
|
|
|
++context.position();
|
|
context.PushStateForExpression(*operator_precedence);
|
|
} else {
|
|
context.PushStateForExpressionLoop(ParserState::ExpressionLoop,
|
|
state.ambient_precedence,
|
|
PrecedenceGroup::ForPostfixExpression());
|
|
context.PushState(ParserState::ExpressionInPostfix);
|
|
}
|
|
}
|
|
|
|
auto ParserHandleExpressionInPostfix(ParserContext& context) -> void {
|
|
auto state = context.PopState();
|
|
|
|
// Continue to the loop state.
|
|
state.state = ParserState::ExpressionInPostfixLoop;
|
|
|
|
// Parses a primary expression, which is either a terminal portion of an
|
|
// expression tree, such as an identifier or literal, or a parenthesized
|
|
// expression.
|
|
switch (context.PositionKind()) {
|
|
case TokenKind::Identifier: {
|
|
context.AddLeafNode(ParseNodeKind::NameReference, context.Consume());
|
|
context.PushState(state);
|
|
break;
|
|
}
|
|
case TokenKind::IntegerLiteral:
|
|
case TokenKind::RealLiteral:
|
|
case TokenKind::StringLiteral:
|
|
case TokenKind::Bool:
|
|
case TokenKind::IntegerTypeLiteral:
|
|
case TokenKind::UnsignedIntegerTypeLiteral:
|
|
case TokenKind::FloatingPointTypeLiteral:
|
|
case TokenKind::StringTypeLiteral: {
|
|
context.AddLeafNode(ParseNodeKind::Literal, context.Consume());
|
|
context.PushState(state);
|
|
break;
|
|
}
|
|
case TokenKind::OpenCurlyBrace: {
|
|
context.PushState(state);
|
|
context.PushState(ParserState::BraceExpression);
|
|
break;
|
|
}
|
|
case TokenKind::OpenParen: {
|
|
context.PushState(state);
|
|
context.PushState(ParserState::ParenExpression);
|
|
break;
|
|
}
|
|
case TokenKind::SelfValueIdentifier: {
|
|
context.AddLeafNode(ParseNodeKind::SelfValueIdentifier,
|
|
context.Consume());
|
|
context.PushState(state);
|
|
break;
|
|
}
|
|
case TokenKind::SelfTypeIdentifier: {
|
|
context.AddLeafNode(ParseNodeKind::SelfTypeIdentifier, context.Consume());
|
|
context.PushState(state);
|
|
break;
|
|
}
|
|
default: {
|
|
// Add a node to keep the parse tree balanced.
|
|
context.AddLeafNode(ParseNodeKind::InvalidParse, *context.position(),
|
|
/*has_error=*/true);
|
|
CARBON_DIAGNOSTIC(ExpectedExpression, Error, "Expected expression.");
|
|
context.emitter().Emit(*context.position(), ExpectedExpression);
|
|
context.ReturnErrorOnState();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
auto ParserHandleExpressionInPostfixLoop(ParserContext& context) -> void {
|
|
// This is a cyclic state that repeats, so this state is typically pushed back
|
|
// on.
|
|
auto state = context.PopState();
|
|
|
|
state.token = *context.position();
|
|
|
|
switch (context.PositionKind()) {
|
|
case TokenKind::Period: {
|
|
context.PushState(state);
|
|
state.state = ParserState::DesignatorAsExpression;
|
|
context.PushState(state);
|
|
break;
|
|
}
|
|
case TokenKind::OpenParen: {
|
|
context.PushState(state);
|
|
state.state = ParserState::CallExpression;
|
|
context.PushState(state);
|
|
break;
|
|
}
|
|
default: {
|
|
if (state.has_error) {
|
|
context.ReturnErrorOnState();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
auto ParserHandleExpressionLoop(ParserContext& context) -> void {
|
|
auto state = context.PopState();
|
|
|
|
auto trailing_operator = PrecedenceGroup::ForTrailing(
|
|
context.PositionKind(), context.IsTrailingOperatorInfix());
|
|
if (!trailing_operator) {
|
|
if (state.has_error) {
|
|
context.ReturnErrorOnState();
|
|
}
|
|
return;
|
|
}
|
|
auto [operator_precedence, is_binary] = *trailing_operator;
|
|
|
|
// TODO: If this operator is ambiguous with either the ambient precedence
|
|
// or the LHS precedence, and there's a variant with a different fixity
|
|
// that would work, use that one instead for error recovery.
|
|
if (PrecedenceGroup::GetPriority(state.ambient_precedence,
|
|
operator_precedence) !=
|
|
OperatorPriority::RightFirst) {
|
|
// The precedence rules don't permit this operator in this context. Try
|
|
// again in the enclosing expression context.
|
|
if (state.has_error) {
|
|
context.ReturnErrorOnState();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (PrecedenceGroup::GetPriority(state.lhs_precedence, operator_precedence) !=
|
|
OperatorPriority::LeftFirst) {
|
|
// Either the LHS operator and this operator are ambiguous, or the
|
|
// LHS operator is a unary operator that can't be nested within
|
|
// this operator. Either way, parentheses are required.
|
|
context.emitter().Emit(*context.position(), OperatorRequiresParentheses);
|
|
state.has_error = true;
|
|
} else {
|
|
context.DiagnoseOperatorFixity(
|
|
is_binary ? ParserContext::OperatorFixity::Infix
|
|
: ParserContext::OperatorFixity::Postfix);
|
|
}
|
|
|
|
state.token = context.Consume();
|
|
state.lhs_precedence = operator_precedence;
|
|
|
|
if (is_binary) {
|
|
state.state = ParserState::ExpressionLoopForBinary;
|
|
context.PushState(state);
|
|
context.PushStateForExpression(operator_precedence);
|
|
} else {
|
|
context.AddNode(ParseNodeKind::PostfixOperator, state.token,
|
|
state.subtree_start, state.has_error);
|
|
state.has_error = false;
|
|
context.PushState(state);
|
|
}
|
|
}
|
|
|
|
auto ParserHandleExpressionLoopForBinary(ParserContext& context) -> void {
|
|
auto state = context.PopState();
|
|
|
|
context.AddNode(ParseNodeKind::InfixOperator, state.token,
|
|
state.subtree_start, state.has_error);
|
|
state.state = ParserState::ExpressionLoop;
|
|
state.has_error = false;
|
|
context.PushState(state);
|
|
}
|
|
|
|
auto ParserHandleExpressionLoopForPrefix(ParserContext& context) -> void {
|
|
auto state = context.PopState();
|
|
|
|
context.AddNode(ParseNodeKind::PrefixOperator, state.token,
|
|
state.subtree_start, state.has_error);
|
|
state.state = ParserState::ExpressionLoop;
|
|
state.has_error = false;
|
|
context.PushState(state);
|
|
}
|
|
|
|
auto ParserHandleIfExpressionFinishCondition(ParserContext& context) -> void {
|
|
auto state = context.PopState();
|
|
|
|
context.AddNode(ParseNodeKind::IfExpressionIf, state.token,
|
|
state.subtree_start, state.has_error);
|
|
|
|
if (context.PositionIs(TokenKind::Then)) {
|
|
context.PushState(ParserState::IfExpressionFinishThen);
|
|
context.ConsumeChecked(TokenKind::Then);
|
|
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 `IfExpressionThen` and final `Expression`.
|
|
context.AddLeafNode(ParseNodeKind::InvalidParse, *context.position(),
|
|
/*has_error=*/true);
|
|
context.AddLeafNode(ParseNodeKind::InvalidParse, *context.position(),
|
|
/*has_error=*/true);
|
|
context.ReturnErrorOnState();
|
|
}
|
|
}
|
|
|
|
auto ParserHandleIfExpressionFinishThen(ParserContext& context) -> void {
|
|
auto state = context.PopState();
|
|
|
|
context.AddNode(ParseNodeKind::IfExpressionThen, state.token,
|
|
state.subtree_start, state.has_error);
|
|
|
|
if (context.PositionIs(TokenKind::Else)) {
|
|
context.PushState(ParserState::IfExpressionFinishElse);
|
|
context.ConsumeChecked(TokenKind::Else);
|
|
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 placeholder for the final `Expression`.
|
|
context.AddLeafNode(ParseNodeKind::InvalidParse, *context.position(),
|
|
/*has_error=*/true);
|
|
context.ReturnErrorOnState();
|
|
}
|
|
}
|
|
|
|
auto ParserHandleIfExpressionFinishElse(ParserContext& context) -> void {
|
|
auto else_state = context.PopState();
|
|
|
|
// Propagate the location of `else`.
|
|
auto if_state = context.PopState();
|
|
if_state.token = else_state.token;
|
|
if_state.has_error |= else_state.has_error;
|
|
context.PushState(if_state);
|
|
}
|
|
|
|
auto ParserHandleIfExpressionFinish(ParserContext& context) -> void {
|
|
auto state = context.PopState();
|
|
|
|
context.AddNode(ParseNodeKind::IfExpressionElse, state.token,
|
|
state.subtree_start, state.has_error);
|
|
}
|
|
|
|
auto ParserHandleExpressionStatementFinish(ParserContext& context) -> void {
|
|
auto state = context.PopState();
|
|
|
|
if (auto semi = context.ConsumeIf(TokenKind::Semi)) {
|
|
context.AddNode(ParseNodeKind::ExpressionStatement, *semi,
|
|
state.subtree_start, state.has_error);
|
|
return;
|
|
}
|
|
|
|
if (!state.has_error) {
|
|
CARBON_DIAGNOSTIC(ExpectedSemiAfterExpression, Error,
|
|
"Expected `;` after expression.");
|
|
context.emitter().Emit(*context.position(), ExpectedSemiAfterExpression);
|
|
}
|
|
|
|
if (auto semi_token = context.SkipPastLikelyEnd(state.token)) {
|
|
context.AddNode(ParseNodeKind::ExpressionStatement, *semi_token,
|
|
state.subtree_start,
|
|
/*has_error=*/true);
|
|
return;
|
|
}
|
|
|
|
// Found junk not even followed by a `;`, no node to add.
|
|
context.ReturnErrorOnState();
|
|
}
|
|
|
|
} // namespace Carbon
|