Files
carbon-lang/toolchain/parser/parser.cpp
T
Jon Ross-PerkinsandChandler Carruth 84deb62aef Work on ParseTree structure to use more bracketed structures. (#2416)
This works on multiple statements to make them better for the bracketing model. Stub nodes are added in more cases of invalid syntax, simply so that the semantics has reliably structured input. Comments in parse_node_kind.def now try to show the expected parse tree structure in postorder form.

This labels If, While, and For a little differently in parse nodes so that at the start of the postorder traversal, it'll already be available to semantics which structure is being processed. I need to do a little more with If in particular, but this felt like a reasonable stopping point.

While this makes significant parser changes, the changes to parser_state.def are minimal, mostly naming-related. The actual flow isn't substantively changed, just a couple minor names and the new As(If|While) state which allows distinguishing IfCondition and WhileCondition.

Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
2022-11-28 15:02:59 -08:00

1721 lines
55 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.h"
#include <cstdlib>
#include <memory>
#include "common/check.h"
#include "llvm/ADT/Optional.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "toolchain/lexer/token_kind.h"
#include "toolchain/lexer/tokenized_buffer.h"
#include "toolchain/parser/parse_node_kind.h"
#include "toolchain/parser/parse_tree.h"
namespace Carbon {
// May be emitted a couple different ways as part of operator parsing.
CARBON_DIAGNOSTIC(
OperatorRequiresParentheses, Error,
"Parentheses are required to disambiguate operator precedence.");
CARBON_DIAGNOSTIC(ExpectedParenAfter, Error, "Expected `(` after `{0}`.",
TokenKind);
CARBON_DIAGNOSTIC(ExpectedSemiAfterExpression, Error,
"Expected `;` after expression.");
// A relative location for characters in errors.
enum class RelativeLocation : int8_t {
Around,
After,
Before,
};
// Adapts RelativeLocation for use with formatv.
static auto operator<<(llvm::raw_ostream& out, RelativeLocation loc)
-> llvm::raw_ostream& {
switch (loc) {
case RelativeLocation::Around:
out << "around";
break;
case RelativeLocation::After:
out << "after";
break;
case RelativeLocation::Before:
out << "before";
break;
}
return out;
}
class Parser::PrettyStackTraceParseState : public llvm::PrettyStackTraceEntry {
public:
explicit PrettyStackTraceParseState(const Parser* parser) : parser_(parser) {}
~PrettyStackTraceParseState() override = default;
auto print(llvm::raw_ostream& output) const -> void override {
output << "Parser stack:\n";
for (int i = 0; i < static_cast<int>(parser_->state_stack_.size()); ++i) {
const auto& entry = parser_->state_stack_[i];
output << "\t" << i << ".\t" << entry.state;
Print(output, entry.token);
}
output << "\tcursor\tposition_";
Print(output, *parser_->position_);
}
private:
auto Print(llvm::raw_ostream& output, TokenizedBuffer::Token token) const
-> void {
auto line = parser_->tokens_->GetLine(token);
output << " @ " << parser_->tokens_->GetLineNumber(line) << ":"
<< parser_->tokens_->GetColumnNumber(token) << ":"
<< " token " << token << " : "
<< parser_->tokens_->GetKind(token).Name() << "\n";
}
const Parser* parser_;
};
Parser::Parser(ParseTree& tree, TokenizedBuffer& tokens,
TokenDiagnosticEmitter& emitter)
: tree_(&tree),
tokens_(&tokens),
emitter_(&emitter),
position_(tokens_->tokens().begin()),
end_(tokens_->tokens().end()) {
CARBON_CHECK(position_ != end_) << "Empty TokenizedBuffer";
--end_;
CARBON_CHECK(tokens_->GetKind(*end_) == TokenKind::EndOfFile())
<< "TokenizedBuffer should end with EndOfFile, ended with "
<< tokens_->GetKind(*end_).Name();
}
auto Parser::AddLeafNode(ParseNodeKind kind, TokenizedBuffer::Token token,
bool has_error) -> void {
tree_->node_impls_.push_back(
ParseTree::NodeImpl(kind, has_error, token, /*subtree_size=*/1));
if (has_error) {
tree_->has_errors_ = true;
}
}
auto Parser::AddNode(ParseNodeKind kind, TokenizedBuffer::Token token,
int subtree_start, bool has_error) -> void {
int subtree_size = tree_->size() - subtree_start + 1;
tree_->node_impls_.push_back(
ParseTree::NodeImpl(kind, has_error, token, subtree_size));
if (has_error) {
tree_->has_errors_ = true;
}
}
auto Parser::ConsumeAndAddOpenParen(TokenizedBuffer::Token default_token,
ParseNodeKind start_kind) -> void {
if (auto open_paren = ConsumeIf(TokenKind::OpenParen())) {
AddLeafNode(start_kind, *open_paren, /*has_error=*/false);
} else {
emitter_->Emit(*position_, ExpectedParenAfter,
tokens_->GetKind(default_token));
AddLeafNode(start_kind, default_token, /*has_error=*/true);
}
}
auto Parser::ConsumeAndAddCloseParen(StateStackEntry state,
ParseNodeKind close_kind) -> void {
// state.token should point at the introducer, with the paren one after the
// introducer.
auto expected_paren = *(TokenizedBuffer::TokenIterator(state.token) + 1);
if (tokens_->GetKind(expected_paren) != TokenKind::OpenParen()) {
AddNode(close_kind, state.token, state.subtree_start, /*has_error=*/true);
} else if (auto close_token = ConsumeIf(TokenKind::CloseParen())) {
AddNode(close_kind, *close_token, state.subtree_start, state.has_error);
} else {
// TODO: Include the location of the matching open_paren in the diagnostic.
CARBON_DIAGNOSTIC(ExpectedCloseParen, Error,
"Unexpected tokens before `)`.");
emitter_->Emit(*position_, ExpectedCloseParen);
SkipTo(tokens_->GetMatchedClosingToken(expected_paren));
AddNode(close_kind, Consume(), state.subtree_start, /*has_error=*/true);
}
}
auto Parser::ConsumeAndAddLeafNodeIf(TokenKind token_kind,
ParseNodeKind node_kind) -> bool {
auto token = ConsumeIf(token_kind);
if (!token) {
return false;
}
AddLeafNode(node_kind, *token);
return true;
}
auto Parser::ConsumeIf(TokenKind kind)
-> llvm::Optional<TokenizedBuffer::Token> {
if (!PositionIs(kind)) {
return llvm::None;
}
return Consume();
}
auto Parser::FindNextOf(std::initializer_list<TokenKind> desired_kinds)
-> llvm::Optional<TokenizedBuffer::Token> {
auto new_position = position_;
while (true) {
TokenizedBuffer::Token token = *new_position;
TokenKind kind = tokens_->GetKind(token);
if (kind.IsOneOf(desired_kinds)) {
return token;
}
// Step to the next token at the current bracketing level.
if (kind.IsClosingSymbol() || kind == TokenKind::EndOfFile()) {
// There are no more tokens at this level.
return llvm::None;
} else if (kind.IsOpeningSymbol()) {
new_position = TokenizedBuffer::TokenIterator(
tokens_->GetMatchedClosingToken(token));
// Advance past the closing token.
++new_position;
} else {
++new_position;
}
}
}
auto Parser::SkipMatchingGroup() -> bool {
if (!PositionKind().IsOpeningSymbol()) {
return false;
}
SkipTo(tokens_->GetMatchedClosingToken(*position_));
++position_;
return true;
}
auto Parser::SkipPastLikelyEnd(TokenizedBuffer::Token skip_root)
-> llvm::Optional<TokenizedBuffer::Token> {
if (position_ == end_) {
return llvm::None;
}
TokenizedBuffer::Line root_line = tokens_->GetLine(skip_root);
int root_line_indent = tokens_->GetIndentColumnNumber(root_line);
// We will keep scanning through tokens on the same line as the root or
// lines with greater indentation than root's line.
auto is_same_line_or_indent_greater_than_root =
[&](TokenizedBuffer::Token t) {
TokenizedBuffer::Line l = tokens_->GetLine(t);
if (l == root_line) {
return true;
}
return tokens_->GetIndentColumnNumber(l) > root_line_indent;
};
do {
if (PositionIs(TokenKind::CloseCurlyBrace())) {
// Immediately bail out if we hit an unmatched close curly, this will
// pop us up a level of the syntax grouping.
return llvm::None;
}
// We assume that a semicolon is always intended to be the end of the
// current construct.
if (auto semi = ConsumeIf(TokenKind::Semi())) {
return semi;
}
// Skip over any matching group of tokens_->
if (SkipMatchingGroup()) {
continue;
}
// Otherwise just step forward one token.
++position_;
} while (position_ != end_ &&
is_same_line_or_indent_greater_than_root(*position_));
return llvm::None;
}
auto Parser::SkipTo(TokenizedBuffer::Token t) -> void {
CARBON_CHECK(t >= *position_) << "Tried to skip backwards from " << position_
<< " to " << TokenizedBuffer::TokenIterator(t);
position_ = TokenizedBuffer::TokenIterator(t);
CARBON_CHECK(position_ != end_) << "Skipped past EOF.";
}
auto Parser::HandleCodeBlockState() -> void {
PopAndDiscardState();
PushState(ParserState::CodeBlockFinish());
if (ConsumeAndAddLeafNodeIf(TokenKind::OpenCurlyBrace(),
ParseNodeKind::CodeBlockStart())) {
PushState(ParserState::StatementScopeLoop());
} else {
AddLeafNode(ParseNodeKind::CodeBlockStart(), *position_,
/*has_error=*/true);
// Recover by parsing a single statement.
CARBON_DIAGNOSTIC(ExpectedCodeBlock, Error, "Expected braced code block.");
emitter_->Emit(*position_, ExpectedCodeBlock);
PushState(ParserState::Statement());
}
}
// Determines whether the given token is considered to be the start of an
// operand according to the rules for infix operator parsing.
static auto IsAssumedStartOfOperand(TokenKind kind) -> bool {
return kind.IsOneOf({TokenKind::OpenParen(), TokenKind::Identifier(),
TokenKind::IntegerLiteral(), TokenKind::RealLiteral(),
TokenKind::StringLiteral()});
}
// Determines whether the given token is considered to be the end of an
// operand according to the rules for infix operator parsing.
static auto IsAssumedEndOfOperand(TokenKind kind) -> bool {
return kind.IsOneOf({TokenKind::CloseParen(), TokenKind::CloseCurlyBrace(),
TokenKind::CloseSquareBracket(), TokenKind::Identifier(),
TokenKind::IntegerLiteral(), TokenKind::RealLiteral(),
TokenKind::StringLiteral()});
}
// Determines whether the given token could possibly be the start of an
// operand. This is conservatively correct, and will never incorrectly return
// `false`, but can incorrectly return `true`.
static auto IsPossibleStartOfOperand(TokenKind kind) -> bool {
return !kind.IsOneOf({TokenKind::CloseParen(), TokenKind::CloseCurlyBrace(),
TokenKind::CloseSquareBracket(), TokenKind::Comma(),
TokenKind::Semi(), TokenKind::Colon()});
}
auto Parser::IsLexicallyValidInfixOperator() -> bool {
CARBON_CHECK(position_ != end_) << "Expected an operator token.";
bool leading_space = tokens_->HasLeadingWhitespace(*position_);
bool trailing_space = tokens_->HasTrailingWhitespace(*position_);
// If there's whitespace on both sides, it's an infix operator.
if (leading_space && trailing_space) {
return true;
}
// If there's whitespace on exactly one side, it's not an infix operator.
if (leading_space || trailing_space) {
return false;
}
// Otherwise, for an infix operator, the preceding token must be any close
// bracket, identifier, or literal and the next token must be an open paren,
// identifier, or literal.
if (position_ == tokens_->tokens().begin() ||
!IsAssumedEndOfOperand(tokens_->GetKind(*(position_ - 1))) ||
!IsAssumedStartOfOperand(tokens_->GetKind(*(position_ + 1)))) {
return false;
}
return true;
}
auto Parser::IsTrailingOperatorInfix() -> bool {
if (position_ == end_) {
return false;
}
// An operator that follows the infix operator rules is parsed as
// infix, unless the next token means that it can't possibly be.
if (IsLexicallyValidInfixOperator() &&
IsPossibleStartOfOperand(tokens_->GetKind(*(position_ + 1)))) {
return true;
}
// A trailing operator with leading whitespace that's not valid as infix is
// not valid at all. If the next token looks like the start of an operand,
// then parse as infix, otherwise as postfix. Either way we'll produce a
// diagnostic later on.
if (tokens_->HasLeadingWhitespace(*position_) &&
IsAssumedStartOfOperand(tokens_->GetKind(*(position_ + 1)))) {
return true;
}
return false;
}
auto Parser::DiagnoseOperatorFixity(OperatorFixity fixity) -> void {
if (fixity == OperatorFixity::Infix) {
// Infix operators must satisfy the infix operator rules.
if (!IsLexicallyValidInfixOperator()) {
CARBON_DIAGNOSTIC(BinaryOperatorRequiresWhitespace, Error,
"Whitespace missing {0} binary operator.",
RelativeLocation);
emitter_->Emit(*position_, BinaryOperatorRequiresWhitespace,
tokens_->HasLeadingWhitespace(*position_)
? RelativeLocation::After
: (tokens_->HasTrailingWhitespace(*position_)
? RelativeLocation::Before
: RelativeLocation::Around));
}
} else {
bool prefix = fixity == OperatorFixity::Prefix;
// Whitespace is not permitted between a symbolic pre/postfix operator and
// its operand.
if (PositionKind().IsSymbol() &&
(prefix ? tokens_->HasTrailingWhitespace(*position_)
: tokens_->HasLeadingWhitespace(*position_))) {
CARBON_DIAGNOSTIC(UnaryOperatorHasWhitespace, Error,
"Whitespace is not allowed {0} this unary operator.",
RelativeLocation);
emitter_->Emit(
*position_, UnaryOperatorHasWhitespace,
prefix ? RelativeLocation::After : RelativeLocation::Before);
}
// Pre/postfix operators must not satisfy the infix operator rules.
if (IsLexicallyValidInfixOperator()) {
CARBON_DIAGNOSTIC(UnaryOperatorRequiresWhitespace, Error,
"Whitespace is required {0} this unary operator.",
RelativeLocation);
emitter_->Emit(
*position_, UnaryOperatorRequiresWhitespace,
prefix ? RelativeLocation::Before : RelativeLocation::After);
}
}
}
auto Parser::ConsumeListToken(ParseNodeKind comma_kind, TokenKind close_kind,
bool already_has_error) -> ListTokenKind {
if (!PositionIs(TokenKind::Comma()) && !PositionIs(close_kind)) {
// Don't error a second time on the same element.
if (!already_has_error) {
CARBON_DIAGNOSTIC(UnexpectedTokenAfterListElement, Error,
"Expected `,` or `{0}`.", TokenKind);
emitter_->Emit(*position_, UnexpectedTokenAfterListElement, close_kind);
ReturnErrorOnState();
}
// Recover from the invalid token.
auto end_of_element = FindNextOf({TokenKind::Comma(), close_kind});
// The lexer guarantees that parentheses are balanced.
CARBON_CHECK(end_of_element)
<< "missing matching `" << close_kind.GetOpeningSymbol() << "` for `"
<< close_kind << "`";
SkipTo(*end_of_element);
}
if (PositionIs(close_kind)) {
return ListTokenKind::Close;
} else {
AddLeafNode(comma_kind, Consume());
return PositionIs(close_kind) ? ListTokenKind::CommaClose
: ListTokenKind::Comma;
}
}
auto Parser::Parse() -> void {
// Traces state_stack_. This runs even in opt because it's low overhead.
PrettyStackTraceParseState pretty_stack(this);
PushState(ParserState::DeclarationLoop());
while (!state_stack_.empty()) {
switch (state_stack_.back().state) {
#define CARBON_PARSER_STATE(Name) \
case ParserState::Name(): \
Handle##Name##State(); \
break;
#include "toolchain/parser/parser_state.def"
}
}
AddLeafNode(ParseNodeKind::FileEnd(), *position_);
}
auto Parser::HandleBraceExpressionState() -> void {
auto state = PopState();
state.state = ParserState::BraceExpressionFinishAsUnknown();
PushState(state);
CARBON_CHECK(ConsumeAndAddLeafNodeIf(
TokenKind::OpenCurlyBrace(),
ParseNodeKind::StructLiteralOrStructTypeLiteralStart()));
if (!PositionIs(TokenKind::CloseCurlyBrace())) {
PushState(ParserState::BraceExpressionParameterAsUnknown());
}
}
auto Parser::BraceExpressionKindToParserState(BraceExpressionKind kind,
ParserState type,
ParserState value,
ParserState unknown)
-> ParserState {
switch (kind) {
case BraceExpressionKind::Type: {
return type;
}
case BraceExpressionKind::Value: {
return value;
}
case BraceExpressionKind::Unknown: {
return unknown;
}
}
}
auto Parser::HandleBraceExpressionParameterError(StateStackEntry state,
BraceExpressionKind kind)
-> void {
CARBON_DIAGNOSTIC(ExpectedStructLiteralField, Error, "Expected {0}{1}{2}.",
llvm::StringRef, llvm::StringRef, llvm::StringRef);
bool can_be_type = kind != BraceExpressionKind::Value;
bool can_be_value = kind != BraceExpressionKind::Type;
emitter_->Emit(*position_, ExpectedStructLiteralField,
can_be_type ? "`.field: type`" : "",
(can_be_type && can_be_value) ? " or " : "",
can_be_value ? "`.field = value`" : "");
state.state = BraceExpressionKindToParserState(
kind, ParserState::BraceExpressionParameterFinishAsType(),
ParserState::BraceExpressionParameterFinishAsValue(),
ParserState::BraceExpressionParameterFinishAsUnknown());
state.has_error = true;
PushState(state);
}
auto Parser::HandleBraceExpressionParameter(BraceExpressionKind kind) -> void {
auto state = PopState();
if (!PositionIs(TokenKind::Period())) {
HandleBraceExpressionParameterError(state, kind);
return;
}
state.state = BraceExpressionKindToParserState(
kind, ParserState::BraceExpressionParameterAfterDesignatorAsType(),
ParserState::BraceExpressionParameterAfterDesignatorAsValue(),
ParserState::BraceExpressionParameterAfterDesignatorAsUnknown());
PushState(state);
PushState(ParserState::DesignatorAsStruct());
}
auto Parser::HandleBraceExpressionParameterAsTypeState() -> void {
HandleBraceExpressionParameter(BraceExpressionKind::Type);
}
auto Parser::HandleBraceExpressionParameterAsValueState() -> void {
HandleBraceExpressionParameter(BraceExpressionKind::Value);
}
auto Parser::HandleBraceExpressionParameterAsUnknownState() -> void {
HandleBraceExpressionParameter(BraceExpressionKind::Unknown);
}
auto Parser::HandleBraceExpressionParameterAfterDesignator(
BraceExpressionKind kind) -> void {
auto state = PopState();
if (state.has_error) {
auto recovery_pos = FindNextOf(
{TokenKind::Equal(), TokenKind::Colon(), TokenKind::Comma()});
if (!recovery_pos ||
tokens_->GetKind(*recovery_pos) == TokenKind::Comma()) {
state.state = BraceExpressionKindToParserState(
kind, ParserState::BraceExpressionParameterFinishAsType(),
ParserState::BraceExpressionParameterFinishAsValue(),
ParserState::BraceExpressionParameterFinishAsUnknown());
PushState(state);
return;
}
SkipTo(*recovery_pos);
}
// Work out the kind of this element.
auto elem_kind = BraceExpressionKind::Unknown;
if (PositionIs(TokenKind::Colon())) {
elem_kind = BraceExpressionKind::Type;
} else if (PositionIs(TokenKind::Equal())) {
elem_kind = BraceExpressionKind::Value;
}
// Unknown kinds and changes between type and value are errors.
if (elem_kind == BraceExpressionKind::Unknown ||
(kind != BraceExpressionKind::Unknown && elem_kind != kind)) {
HandleBraceExpressionParameterError(state, kind);
return;
}
// If we're setting the kind, update the BraceExpressionFinish state.
if (kind == BraceExpressionKind::Unknown) {
kind = elem_kind;
auto finish_state = PopState();
CARBON_CHECK(finish_state.state ==
ParserState::BraceExpressionFinishAsUnknown());
finish_state.state = BraceExpressionKindToParserState(
kind, ParserState::BraceExpressionFinishAsType(),
ParserState::BraceExpressionFinishAsValue(),
ParserState::BraceExpressionFinishAsUnknown());
PushState(finish_state);
}
state.state = BraceExpressionKindToParserState(
kind, ParserState::BraceExpressionParameterFinishAsType(),
ParserState::BraceExpressionParameterFinishAsValue(),
ParserState::BraceExpressionParameterFinishAsUnknown());
state.token = Consume();
// Struct type fields and value fields use the same grammar except
// that one has a `:` separator and the other has an `=` separator.
PushState(state);
PushState(ParserState::Expression());
}
auto Parser::HandleBraceExpressionParameterAfterDesignatorAsTypeState()
-> void {
HandleBraceExpressionParameterAfterDesignator(BraceExpressionKind::Type);
}
auto Parser::HandleBraceExpressionParameterAfterDesignatorAsValueState()
-> void {
HandleBraceExpressionParameterAfterDesignator(BraceExpressionKind::Value);
}
auto Parser::HandleBraceExpressionParameterAfterDesignatorAsUnknownState()
-> void {
HandleBraceExpressionParameterAfterDesignator(BraceExpressionKind::Unknown);
}
auto Parser::HandleBraceExpressionParameterFinish(BraceExpressionKind kind)
-> void {
auto state = PopState();
AddNode(kind == BraceExpressionKind::Type ? ParseNodeKind::StructFieldType()
: ParseNodeKind::StructFieldValue(),
state.token, state.subtree_start, state.has_error);
if (ConsumeListToken(ParseNodeKind::StructComma(),
TokenKind::CloseCurlyBrace(),
state.has_error) == ListTokenKind::Comma) {
PushState(BraceExpressionKindToParserState(
kind, ParserState::BraceExpressionParameterAsType(),
ParserState::BraceExpressionParameterAsValue(),
ParserState::BraceExpressionParameterAsUnknown()));
}
}
auto Parser::HandleBraceExpressionParameterFinishAsTypeState() -> void {
HandleBraceExpressionParameterFinish(BraceExpressionKind::Type);
}
auto Parser::HandleBraceExpressionParameterFinishAsValueState() -> void {
HandleBraceExpressionParameterFinish(BraceExpressionKind::Value);
}
auto Parser::HandleBraceExpressionParameterFinishAsUnknownState() -> void {
HandleBraceExpressionParameterFinish(BraceExpressionKind::Unknown);
}
auto Parser::HandleBraceExpressionFinish(BraceExpressionKind kind) -> void {
auto state = PopState();
AddNode(kind == BraceExpressionKind::Type ? ParseNodeKind::StructTypeLiteral()
: ParseNodeKind::StructLiteral(),
Consume(), state.subtree_start, state.has_error);
}
auto Parser::HandleBraceExpressionFinishAsTypeState() -> void {
HandleBraceExpressionFinish(BraceExpressionKind::Type);
}
auto Parser::HandleBraceExpressionFinishAsValueState() -> void {
HandleBraceExpressionFinish(BraceExpressionKind::Value);
}
auto Parser::HandleBraceExpressionFinishAsUnknownState() -> void {
HandleBraceExpressionFinish(BraceExpressionKind::Unknown);
}
auto Parser::HandleCallExpressionState() -> void {
auto state = PopState();
// TODO: When swapping () start/end, this should AddLeafNode the open before
// continuing.
state.state = ParserState::CallExpressionFinish();
PushState(state);
// Advance past the open paren.
++position_;
if (!PositionIs(TokenKind::CloseParen())) {
PushState(ParserState::CallExpressionParameterFinish());
PushState(ParserState::Expression());
}
}
auto Parser::HandleCallExpressionParameterFinishState() -> void {
auto state = PopState();
if (state.has_error) {
ReturnErrorOnState();
}
if (ConsumeListToken(ParseNodeKind::CallExpressionComma(),
TokenKind::CloseParen(),
state.has_error) == ListTokenKind::Comma) {
PushState(ParserState::CallExpressionParameterFinish());
PushState(ParserState::Expression());
}
}
auto Parser::HandleCallExpressionFinishState() -> void {
auto state = PopState();
AddLeafNode(ParseNodeKind::CallExpressionEnd(), Consume());
AddNode(ParseNodeKind::CallExpression(), state.token, state.subtree_start,
state.has_error);
}
auto Parser::HandleCodeBlockFinishState() -> void {
auto state = PopState();
// If the block started with an open curly, this is a close curly.
if (tokens_->GetKind(state.token) == TokenKind::OpenCurlyBrace()) {
AddNode(ParseNodeKind::CodeBlock(), Consume(), state.subtree_start,
state.has_error);
} else {
AddNode(ParseNodeKind::CodeBlock(), state.token, state.subtree_start,
/*has_error=*/true);
}
}
auto Parser::HandleDeclarationLoopState() -> void {
// This maintains the current state unless we're at the end of the file.
switch (PositionKind()) {
case TokenKind::EndOfFile(): {
PopAndDiscardState();
break;
}
case TokenKind::Fn(): {
PushState(ParserState::FunctionIntroducer());
AddLeafNode(ParseNodeKind::FunctionIntroducer(), Consume());
break;
}
case TokenKind::Package(): {
PushState(ParserState::Package());
break;
}
case TokenKind::Semi(): {
AddLeafNode(ParseNodeKind::EmptyDeclaration(), Consume());
break;
}
case TokenKind::Var(): {
PushState(ParserState::VarAsSemicolon());
break;
}
case TokenKind::Interface(): {
PushState(ParserState::InterfaceIntroducer());
++position_;
break;
}
default: {
CARBON_DIAGNOSTIC(UnrecognizedDeclaration, Error,
"Unrecognized declaration introducer.");
emitter_->Emit(*position_, UnrecognizedDeclaration);
auto cursor = *position_;
auto semi = SkipPastLikelyEnd(cursor);
// Locate the EmptyDeclaration at the semi when found, but use the
// original cursor location for an error when not.
AddLeafNode(ParseNodeKind::EmptyDeclaration(), semi ? *semi : cursor,
/*has_error=*/true);
break;
}
}
}
auto Parser::HandleDesignator(bool as_struct) -> void {
auto state = PopState();
// `.` identifier
auto dot = ConsumeIf(TokenKind::Period());
CARBON_CHECK(dot);
if (!ConsumeAndAddLeafNodeIf(TokenKind::Identifier(),
ParseNodeKind::DesignatedName())) {
CARBON_DIAGNOSTIC(ExpectedIdentifierAfterDot, Error,
"Expected identifier after `.`.");
emitter_->Emit(*position_, ExpectedIdentifierAfterDot);
// If we see a keyword, assume it was intended to be the designated name.
// TODO: Should keywords be valid in designators?
if (PositionKind().IsKeyword()) {
AddLeafNode(ParseNodeKind::DesignatedName(), Consume(),
/*has_error=*/true);
} else {
state.has_error = true;
ReturnErrorOnState();
}
}
AddNode(as_struct ? ParseNodeKind::StructFieldDesignator()
: ParseNodeKind::DesignatorExpression(),
*dot, state.subtree_start, state.has_error);
}
auto Parser::HandleDesignatorAsExpressionState() -> void {
HandleDesignator(/*as_struct=*/false);
}
auto Parser::HandleDesignatorAsStructState() -> void {
HandleDesignator(/*as_struct=*/true);
}
auto Parser::HandleExpressionState() -> void {
auto state = PopState();
// Check for a prefix operator.
if (auto operator_precedence = PrecedenceGroup::ForLeading(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.
emitter_->Emit(*position_, OperatorRequiresParentheses);
} else {
// Check that this operator follows the proper whitespace rules.
DiagnoseOperatorFixity(OperatorFixity::Prefix);
}
PushStateForExpressionLoop(ParserState::ExpressionLoopForPrefix(),
state.ambient_precedence, *operator_precedence);
++position_;
PushStateForExpression(*operator_precedence);
} else {
PushStateForExpressionLoop(ParserState::ExpressionLoop(),
state.ambient_precedence,
PrecedenceGroup::ForPostfixExpression());
PushState(ParserState::ExpressionInPostfix());
}
}
auto Parser::HandleExpressionInPostfixState() -> void {
auto state = 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 (PositionKind()) {
case TokenKind::Identifier(): {
AddLeafNode(ParseNodeKind::NameReference(), Consume());
PushState(state);
break;
}
case TokenKind::IntegerLiteral():
case TokenKind::RealLiteral():
case TokenKind::StringLiteral():
case TokenKind::IntegerTypeLiteral():
case TokenKind::UnsignedIntegerTypeLiteral():
case TokenKind::FloatingPointTypeLiteral(): {
AddLeafNode(ParseNodeKind::Literal(), Consume());
PushState(state);
break;
}
case TokenKind::OpenCurlyBrace(): {
PushState(state);
PushState(ParserState::BraceExpression());
break;
}
case TokenKind::OpenParen(): {
PushState(state);
PushState(ParserState::ParenExpression());
break;
}
default: {
CARBON_DIAGNOSTIC(ExpectedExpression, Error, "Expected expression.");
emitter_->Emit(*position_, ExpectedExpression);
ReturnErrorOnState();
break;
}
}
}
auto Parser::HandleExpressionInPostfixLoopState() -> void {
// This is a cyclic state that repeats, so this state is typically pushed back
// on.
auto state = PopState();
state.token = *position_;
switch (PositionKind()) {
case TokenKind::Period(): {
PushState(state);
state.state = ParserState::DesignatorAsExpression();
PushState(state);
break;
}
case TokenKind::OpenParen(): {
PushState(state);
state.state = ParserState::CallExpression();
PushState(state);
break;
}
default: {
if (state.has_error) {
ReturnErrorOnState();
}
break;
}
}
}
auto Parser::HandleExpressionLoopState() -> void {
auto state = PopState();
auto trailing_operator =
PrecedenceGroup::ForTrailing(PositionKind(), IsTrailingOperatorInfix());
if (!trailing_operator) {
if (state.has_error) {
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) {
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.
emitter_->Emit(*position_, OperatorRequiresParentheses);
state.has_error = true;
} else {
DiagnoseOperatorFixity(is_binary ? OperatorFixity::Infix
: OperatorFixity::Postfix);
}
state.token = Consume();
state.lhs_precedence = operator_precedence;
if (is_binary) {
state.state = ParserState::ExpressionLoopForBinary();
PushState(state);
PushStateForExpression(operator_precedence);
} else {
AddNode(ParseNodeKind::PostfixOperator(), state.token, state.subtree_start,
state.has_error);
state.has_error = false;
PushState(state);
}
}
auto Parser::HandleExpressionLoopForBinaryState() -> void {
auto state = PopState();
AddNode(ParseNodeKind::InfixOperator(), state.token, state.subtree_start,
state.has_error);
state.state = ParserState::ExpressionLoop();
state.has_error = false;
PushState(state);
}
auto Parser::HandleExpressionLoopForPrefixState() -> void {
auto state = PopState();
AddNode(ParseNodeKind::PrefixOperator(), state.token, state.subtree_start,
state.has_error);
state.state = ParserState::ExpressionLoop();
state.has_error = false;
PushState(state);
}
auto Parser::HandleExpressionStatementFinishState() -> void {
auto state = PopState();
if (auto semi = ConsumeIf(TokenKind::Semi())) {
AddNode(ParseNodeKind::ExpressionStatement(), *semi, state.subtree_start,
state.has_error);
return;
}
if (!state.has_error) {
emitter_->Emit(*position_, ExpectedSemiAfterExpression);
}
if (auto semi_token = SkipPastLikelyEnd(state.token)) {
AddNode(ParseNodeKind::ExpressionStatement(), *semi_token,
state.subtree_start,
/*has_error=*/true);
return;
}
// Found junk not even followed by a `;`, no node to add.
ReturnErrorOnState();
}
auto Parser::HandleFunctionError(StateStackEntry state,
bool skip_past_likely_end) -> void {
auto token = state.token;
if (skip_past_likely_end) {
if (auto semi = SkipPastLikelyEnd(token)) {
token = *semi;
}
}
AddNode(ParseNodeKind::FunctionDeclaration(), token, state.subtree_start,
/*has_error=*/true);
}
auto Parser::HandleFunctionIntroducerState() -> void {
auto state = PopState();
if (!ConsumeAndAddLeafNodeIf(TokenKind::Identifier(),
ParseNodeKind::DeclaredName())) {
CARBON_DIAGNOSTIC(ExpectedFunctionName, Error,
"Expected function name after `fn` keyword.");
emitter_->Emit(*position_, ExpectedFunctionName);
// TODO: We could change the lexer to allow us to synthesize certain
// kinds of tokens and try to "recover" here, but unclear that this is
// really useful.
HandleFunctionError(state, true);
return;
}
if (!PositionIs(TokenKind::OpenParen())) {
CARBON_DIAGNOSTIC(ExpectedFunctionParams, Error,
"Expected `(` after function name.");
emitter_->Emit(*position_, ExpectedFunctionParams);
HandleFunctionError(state, true);
return;
}
// Parse the parameter list as its own subtree; once that pops, resume
// function parsing.
state.state = ParserState::FunctionAfterParameterList();
PushState(state);
PushState(ParserState::FunctionParameterListFinish());
AddLeafNode(ParseNodeKind::ParameterListStart(), Consume());
if (!PositionIs(TokenKind::CloseParen())) {
PushState(ParserState::FunctionParameter());
}
}
auto Parser::HandleFunctionParameterState() -> void {
PopAndDiscardState();
PushState(ParserState::FunctionParameterFinish());
PushState(ParserState::PatternAsFunctionParameter());
}
auto Parser::HandleFunctionParameterFinishState() -> void {
auto state = PopState();
if (state.has_error) {
ReturnErrorOnState();
}
if (ConsumeListToken(ParseNodeKind::ParameterListComma(),
TokenKind::CloseParen(),
state.has_error) == ListTokenKind::Comma) {
PushState(ParserState::PatternAsFunctionParameter());
}
}
auto Parser::HandleFunctionParameterListFinishState() -> void {
auto state = PopState();
CARBON_CHECK(PositionIs(TokenKind::CloseParen())) << PositionKind().Name();
AddNode(ParseNodeKind::ParameterList(), Consume(), state.subtree_start,
state.has_error);
}
auto Parser::HandleFunctionAfterParameterListState() -> void {
auto state = PopState();
// Regardless of whether there's a return type, we'll finish the signature.
state.state = ParserState::FunctionSignatureFinish();
PushState(state);
// If there is a return type, parse the expression before adding the return
// type nod.e
if (PositionIs(TokenKind::MinusGreater())) {
PushState(ParserState::FunctionReturnTypeFinish());
++position_;
PushStateForExpression(PrecedenceGroup::ForType());
}
}
auto Parser::HandleFunctionReturnTypeFinishState() -> void {
auto state = PopState();
AddNode(ParseNodeKind::ReturnType(), state.token, state.subtree_start,
state.has_error);
}
auto Parser::HandleFunctionSignatureFinishState() -> void {
auto state = PopState();
switch (PositionKind()) {
case TokenKind::Semi(): {
AddNode(ParseNodeKind::FunctionDeclaration(), Consume(),
state.subtree_start, state.has_error);
break;
}
case TokenKind::OpenCurlyBrace(): {
AddNode(ParseNodeKind::FunctionDefinitionStart(), Consume(),
state.subtree_start, state.has_error);
// Any error is recorded on the FunctionDefinitionStart.
state.has_error = false;
state.state = ParserState::FunctionDefinitionFinish();
PushState(state);
PushState(ParserState::StatementScopeLoop());
break;
}
default: {
CARBON_DIAGNOSTIC(
ExpectedFunctionBodyOrSemi, Error,
"Expected function definition or `;` after function declaration.");
emitter_->Emit(*position_, ExpectedFunctionBodyOrSemi);
// Only need to skip if we've not already found a new line.
bool skip_past_likely_end =
tokens_->GetLine(*position_) == tokens_->GetLine(state.token);
HandleFunctionError(state, skip_past_likely_end);
break;
}
}
}
auto Parser::HandleFunctionDefinitionFinishState() -> void {
auto state = PopState();
AddNode(ParseNodeKind::FunctionDefinition(), Consume(), state.subtree_start,
state.has_error);
}
auto Parser::HandlePackageState() -> void {
auto state = PopState();
AddLeafNode(ParseNodeKind::PackageIntroducer(), Consume());
auto exit_on_parse_error = [&]() {
auto semi_token = SkipPastLikelyEnd(state.token);
return AddNode(ParseNodeKind::PackageDirective(),
semi_token ? *semi_token : state.token, state.subtree_start,
/*has_error=*/true);
};
if (!ConsumeAndAddLeafNodeIf(TokenKind::Identifier(),
ParseNodeKind::DeclaredName())) {
CARBON_DIAGNOSTIC(ExpectedIdentifierAfterPackage, Error,
"Expected identifier after `package`.");
emitter_->Emit(*position_, ExpectedIdentifierAfterPackage);
exit_on_parse_error();
return;
}
bool library_parsed = false;
if (auto library_token = ConsumeIf(TokenKind::Library())) {
auto library_start = tree_->size();
if (!ConsumeAndAddLeafNodeIf(TokenKind::StringLiteral(),
ParseNodeKind::Literal())) {
CARBON_DIAGNOSTIC(
ExpectedLibraryName, Error,
"Expected a string literal to specify the library name.");
emitter_->Emit(*position_, ExpectedLibraryName);
exit_on_parse_error();
return;
}
AddNode(ParseNodeKind::PackageLibrary(), *library_token, library_start,
/*has_error=*/false);
library_parsed = true;
}
switch (auto api_or_impl_token = tokens_->GetKind(*(position_))) {
case TokenKind::Api(): {
AddLeafNode(ParseNodeKind::PackageApi(), Consume());
break;
}
case TokenKind::Impl(): {
AddLeafNode(ParseNodeKind::PackageImpl(), Consume());
break;
}
default: {
if (!library_parsed && api_or_impl_token == TokenKind::StringLiteral()) {
// If we come acroess a string literal and we didn't parse `library
// "..."` yet, then most probably the user forgot to add `library`
// before the library name.
CARBON_DIAGNOSTIC(MissingLibraryKeyword, Error,
"Missing `library` keyword.");
emitter_->Emit(*position_, MissingLibraryKeyword);
} else {
CARBON_DIAGNOSTIC(ExpectedApiOrImpl, Error,
"Expected a `api` or `impl`.");
emitter_->Emit(*position_, ExpectedApiOrImpl);
}
exit_on_parse_error();
return;
}
}
if (!PositionIs(TokenKind::Semi())) {
CARBON_DIAGNOSTIC(ExpectedSemiToEndPackageDirective, Error,
"Expected `;` to end package directive.");
emitter_->Emit(*position_, ExpectedSemiToEndPackageDirective);
exit_on_parse_error();
return;
}
AddNode(ParseNodeKind::PackageDirective(), Consume(), state.subtree_start,
/*has_error=*/false);
}
auto Parser::HandleParenCondition(ParseNodeKind start_kind,
ParserState finish_state) -> void {
auto state = PopState();
ConsumeAndAddOpenParen(state.token, start_kind);
state.state = finish_state;
PushState(state);
PushState(ParserState::Expression());
}
auto Parser::HandleParenConditionAsIfState() -> void {
HandleParenCondition(ParseNodeKind::IfConditionStart(),
ParserState::ParenConditionFinishAsIf());
}
auto Parser::HandleParenConditionAsWhileState() -> void {
HandleParenCondition(ParseNodeKind::WhileConditionStart(),
ParserState::ParenConditionFinishAsWhile());
}
auto Parser::HandleParenConditionFinishAsIfState() -> void {
auto state = PopState();
ConsumeAndAddCloseParen(state, ParseNodeKind::IfCondition());
}
auto Parser::HandleParenConditionFinishAsWhileState() -> void {
auto state = PopState();
ConsumeAndAddCloseParen(state, ParseNodeKind::WhileCondition());
}
auto Parser::HandleParenExpressionState() -> void {
auto state = PopState();
// TODO: When swapping () start/end, this should AddLeafNode the open before
// continuing.
// Advance past the open paren.
CARBON_CHECK(PositionIs(TokenKind::OpenParen()));
++position_;
if (PositionIs(TokenKind::CloseParen())) {
state.state = ParserState::ParenExpressionFinishAsTuple();
PushState(state);
} else {
state.state = ParserState::ParenExpressionFinish();
PushState(state);
PushState(ParserState::ParenExpressionParameterFinishAsUnknown());
PushState(ParserState::Expression());
}
}
auto Parser::HandleParenExpressionParameterFinish(bool as_tuple) -> void {
auto state = PopState();
auto list_token_kind =
ConsumeListToken(ParseNodeKind::TupleLiteralComma(),
TokenKind::CloseParen(), state.has_error);
if (list_token_kind == ListTokenKind::Close) {
return;
}
// If this is the first item and a comma was found, switch to tuple handling.
// Note this could be `(expr,)` so we may not reuse the current state, but
// it's still necessary to switch the parent.
if (!as_tuple) {
state.state = ParserState::ParenExpressionParameterFinishAsTuple();
auto finish_state = PopState();
CARBON_CHECK(finish_state.state == ParserState::ParenExpressionFinish())
<< "Unexpected parent state, found: " << finish_state.state;
finish_state.state = ParserState::ParenExpressionFinishAsTuple();
PushState(finish_state);
}
// On a comma, push another expression handler.
if (list_token_kind == ListTokenKind::Comma) {
PushState(state);
PushState(ParserState::Expression());
}
}
auto Parser::HandleParenExpressionParameterFinishAsUnknownState() -> void {
HandleParenExpressionParameterFinish(/*as_tuple=*/false);
}
auto Parser::HandleParenExpressionParameterFinishAsTupleState() -> void {
HandleParenExpressionParameterFinish(/*as_tuple=*/true);
}
auto Parser::HandleParenExpressionFinishState() -> void {
auto state = PopState();
AddLeafNode(ParseNodeKind::ParenExpressionEnd(), Consume());
AddNode(ParseNodeKind::ParenExpression(), state.token, state.subtree_start,
state.has_error);
}
auto Parser::HandleParenExpressionFinishAsTupleState() -> void {
auto state = PopState();
AddLeafNode(ParseNodeKind::TupleLiteralEnd(), Consume());
AddNode(ParseNodeKind::TupleLiteral(), state.token, state.subtree_start,
state.has_error);
}
auto Parser::HandlePattern(PatternKind pattern_kind) -> void {
auto state = PopState();
// Ensure the finish state always follows.
state.state = ParserState::PatternFinish();
// Handle an invalid pattern introducer.
if (!PositionIs(TokenKind::Identifier()) ||
tokens_->GetKind(*(position_ + 1)) != TokenKind::Colon()) {
switch (pattern_kind) {
case PatternKind::Parameter: {
CARBON_DIAGNOSTIC(ExpectedParameterName, Error,
"Expected parameter declaration.");
emitter_->Emit(*position_, ExpectedParameterName);
break;
}
case PatternKind::Variable: {
CARBON_DIAGNOSTIC(ExpectedVariableName, Error,
"Expected pattern in `var` declaration.");
emitter_->Emit(*position_, ExpectedVariableName);
break;
}
}
state.has_error = true;
PushState(state);
return;
}
// Switch the context token to the colon, so that it'll be used for the root
// node.
state.token = *(position_ + 1);
PushState(state);
PushStateForExpression(PrecedenceGroup::ForType());
AddLeafNode(ParseNodeKind::DeclaredName(), *position_);
position_ += 2;
}
auto Parser::HandlePatternAsFunctionParameterState() -> void {
HandlePattern(PatternKind::Parameter);
}
auto Parser::HandlePatternAsVariableState() -> void {
HandlePattern(PatternKind::Variable);
}
auto Parser::HandlePatternFinishState() -> void {
auto state = PopState();
// If an error was encountered, propagate it without adding a node.
if (state.has_error) {
ReturnErrorOnState();
return;
}
// TODO: may need to mark has_error if !type.
AddNode(ParseNodeKind::PatternBinding(), state.token, state.subtree_start,
/*has_error=*/false);
}
auto Parser::HandleStatementState() -> void {
PopAndDiscardState();
switch (PositionKind()) {
case TokenKind::Break(): {
PushState(ParserState::StatementBreakFinish());
AddLeafNode(ParseNodeKind::BreakStatementStart(), Consume());
break;
}
case TokenKind::Continue(): {
PushState(ParserState::StatementContinueFinish());
AddLeafNode(ParseNodeKind::ContinueStatementStart(), Consume());
break;
}
case TokenKind::For(): {
PushState(ParserState::StatementForFinish());
PushState(ParserState::StatementForHeader());
++position_;
break;
}
case TokenKind::If(): {
PushState(ParserState::StatementIf());
break;
}
case TokenKind::Return(): {
PushState(ParserState::StatementReturn());
break;
}
case TokenKind::Var(): {
PushState(ParserState::VarAsSemicolon());
break;
}
case TokenKind::While(): {
PushState(ParserState::StatementWhile());
break;
}
default: {
PushState(ParserState::ExpressionStatementFinish());
PushState(ParserState::Expression());
break;
}
}
}
auto Parser::HandleStatementBreakFinishState() -> void {
HandleStatementKeywordFinish(ParseNodeKind::BreakStatement());
}
auto Parser::HandleStatementContinueFinishState() -> void {
HandleStatementKeywordFinish(ParseNodeKind::ContinueStatement());
}
auto Parser::HandleStatementForHeaderState() -> void {
auto state = PopState();
ConsumeAndAddOpenParen(state.token, ParseNodeKind::ForHeaderStart());
state.state = ParserState::StatementForHeaderIn();
if (PositionIs(TokenKind::Var())) {
PushState(state);
PushState(ParserState::VarAsFor());
} else {
CARBON_DIAGNOSTIC(ExpectedVariableDeclaration, Error,
"Expected `var` declaration.");
emitter_->Emit(*position_, ExpectedVariableDeclaration);
if (auto next_in = FindNextOf({TokenKind::In()})) {
SkipTo(*next_in);
++position_;
}
state.has_error = true;
PushState(state);
}
}
auto Parser::HandleStatementForHeaderInState() -> void {
auto state = PopState();
state.state = ParserState::StatementForHeaderFinish();
PushState(state);
PushState(ParserState::Expression());
}
auto Parser::HandleStatementForHeaderFinishState() -> void {
auto state = PopState();
ConsumeAndAddCloseParen(state, ParseNodeKind::ForHeader());
PushState(ParserState::CodeBlock());
}
auto Parser::HandleStatementForFinishState() -> void {
auto state = PopState();
AddNode(ParseNodeKind::ForStatement(), state.token, state.subtree_start,
state.has_error);
}
auto Parser::HandleStatementIfState() -> void {
PopAndDiscardState();
PushState(ParserState::StatementIfConditionFinish());
PushState(ParserState::ParenConditionAsIf());
++position_;
}
auto Parser::HandleStatementIfConditionFinishState() -> void {
auto state = PopState();
state.state = ParserState::StatementIfThenBlockFinish();
PushState(state);
PushState(ParserState::CodeBlock());
}
auto Parser::HandleStatementIfThenBlockFinishState() -> void {
auto state = PopState();
if (ConsumeAndAddLeafNodeIf(TokenKind::Else(),
ParseNodeKind::IfStatementElse())) {
state.state = ParserState::StatementIfElseBlockFinish();
PushState(state);
// `else if` is permitted as a special case.
PushState(PositionIs(TokenKind::If()) ? ParserState::StatementIf()
: ParserState::CodeBlock());
} else {
AddNode(ParseNodeKind::IfStatement(), state.token, state.subtree_start,
state.has_error);
}
}
auto Parser::HandleStatementIfElseBlockFinishState() -> void {
auto state = PopState();
AddNode(ParseNodeKind::IfStatement(), state.token, state.subtree_start,
state.has_error);
}
auto Parser::HandleStatementKeywordFinish(ParseNodeKind node_kind) -> void {
auto state = PopState();
auto semi = ConsumeIf(TokenKind::Semi());
if (!semi) {
CARBON_DIAGNOSTIC(ExpectedSemiAfter, Error, "Expected `;` after `{0}`.",
TokenKind);
emitter_->Emit(*position_, ExpectedSemiAfter,
tokens_->GetKind(state.token));
state.has_error = true;
// Recover to the next semicolon if possible, otherwise indicate the
// keyword for the error.
semi = SkipPastLikelyEnd(state.token);
if (!semi) {
semi = state.token;
}
}
AddNode(node_kind, *semi, state.subtree_start, state.has_error);
}
auto Parser::HandleStatementReturnState() -> void {
auto state = PopState();
state.state = ParserState::StatementReturnFinish();
PushState(state);
AddLeafNode(ParseNodeKind::ReturnStatementStart(), Consume());
if (!PositionIs(TokenKind::Semi())) {
PushState(ParserState::Expression());
}
}
auto Parser::HandleStatementReturnFinishState() -> void {
HandleStatementKeywordFinish(ParseNodeKind::ReturnStatement());
}
auto Parser::HandleStatementScopeLoopState() -> void {
// This maintains the current state until we're at the end of the scope.
auto token_kind = PositionKind();
if (token_kind == TokenKind::CloseCurlyBrace()) {
auto state = PopState();
if (state.has_error) {
ReturnErrorOnState();
}
} else {
PushState(ParserState::Statement());
}
}
auto Parser::HandleStatementWhileState() -> void {
PopAndDiscardState();
PushState(ParserState::StatementWhileConditionFinish());
PushState(ParserState::ParenConditionAsWhile());
++position_;
}
auto Parser::HandleStatementWhileConditionFinishState() -> void {
auto state = PopState();
state.state = ParserState::StatementWhileBlockFinish();
PushState(state);
PushState(ParserState::CodeBlock());
}
auto Parser::HandleStatementWhileBlockFinishState() -> void {
auto state = PopState();
AddNode(ParseNodeKind::WhileStatement(), state.token, state.subtree_start,
state.has_error);
}
auto Parser::HandleVar(ParserState finish_state) -> void {
PopAndDiscardState();
// These will start at the `var`.
PushState(finish_state);
PushState(ParserState::VarAfterPattern());
AddLeafNode(ParseNodeKind::VariableIntroducer(), Consume());
// This will start at the pattern.
PushState(ParserState::PatternAsVariable());
}
auto Parser::HandleVarAsSemicolonState() -> void {
HandleVar(ParserState::VarFinishAsSemicolon());
}
auto Parser::HandleVarAsForState() -> void {
HandleVar(ParserState::VarFinishAsFor());
}
auto Parser::HandleVarAfterPatternState() -> void {
auto state = PopState();
if (state.has_error) {
if (auto after_pattern =
FindNextOf({TokenKind::Equal(), TokenKind::Semi()})) {
SkipTo(*after_pattern);
}
}
if (PositionIs(TokenKind::Equal())) {
PushState(ParserState::VarAfterInitializer());
++position_;
PushState(ParserState::Expression());
return;
}
}
auto Parser::HandleVarAfterInitializerState() -> void {
auto state = PopState();
AddNode(ParseNodeKind::VariableInitializer(), state.token,
state.subtree_start, state.has_error);
}
auto Parser::HandleVarFinishAsSemicolonState() -> void {
auto state = PopState();
auto end_token = state.token;
if (PositionIs(TokenKind::Semi())) {
end_token = Consume();
} else {
emitter_->Emit(*position_, ExpectedSemiAfterExpression);
state.has_error = true;
if (auto semi_token = SkipPastLikelyEnd(state.token)) {
end_token = *semi_token;
}
}
AddNode(ParseNodeKind::VariableDeclaration(), end_token, state.subtree_start,
state.has_error);
}
auto Parser::HandleVarFinishAsForState() -> void {
auto state = PopState();
auto end_token = state.token;
if (PositionIs(TokenKind::In())) {
end_token = Consume();
} else if (PositionIs(TokenKind::Colon())) {
CARBON_DIAGNOSTIC(ExpectedIn, Error, "`:` should be replaced by `in`.");
emitter_->Emit(*position_, ExpectedIn);
state.has_error = true;
end_token = Consume();
} else {
CARBON_DIAGNOSTIC(ExpectedIn, Error,
"Expected `in` after loop `var` declaration.");
emitter_->Emit(*position_, ExpectedIn);
state.has_error = true;
}
AddNode(ParseNodeKind::ForIn(), end_token, state.subtree_start,
state.has_error);
}
auto Parser::HandleInterfaceIntroducerState() -> void {
auto state = PopState();
if (!ConsumeAndAddLeafNodeIf(TokenKind::Identifier(),
ParseNodeKind::DeclaredName())) {
CARBON_DIAGNOSTIC(ExpectedInterfaceName, Error,
"Expected interface name after `interface` keyword.");
emitter_->Emit(*position_, ExpectedInterfaceName);
state.has_error = true;
}
bool parse_body = true;
if (!PositionIs(TokenKind::OpenCurlyBrace())) {
CARBON_DIAGNOSTIC(ExpectedInterfaceOpenCurlyBrace, Error,
"Expected `{{` to start interface definition.");
emitter_->Emit(*position_, ExpectedInterfaceOpenCurlyBrace);
state.has_error = true;
SkipPastLikelyEnd(state.token);
parse_body = false;
}
state.state = ParserState::InterfaceDefinitionFinish();
PushState(state);
if (parse_body) {
PushState(ParserState::InterfaceDefinitionLoop());
AddLeafNode(ParseNodeKind::InterfaceBodyStart(), Consume());
}
}
auto Parser::HandleInterfaceDefinitionLoopState() -> void {
// This maintains the current state unless we're at the end of the interface
// definition.
switch (PositionKind()) {
case TokenKind::CloseCurlyBrace(): {
auto state = PopState();
AddNode(ParseNodeKind::InterfaceBody(), Consume(), state.subtree_start,
state.has_error);
break;
}
// TODO: Handle possible declarations inside interface body.
default: {
CARBON_DIAGNOSTIC(UnrecognizedDeclaration, Error,
"Unrecognized declaration introducer.");
emitter_->Emit(*position_, UnrecognizedDeclaration);
if (auto semi = SkipPastLikelyEnd(*position_)) {
AddLeafNode(ParseNodeKind::EmptyDeclaration(), *semi,
/*has_error=*/true);
} else {
ReturnErrorOnState();
}
break;
}
}
}
auto Parser::HandleInterfaceDefinitionFinishState() -> void {
auto state = PopState();
AddNode(ParseNodeKind::InterfaceDefinition(), state.token,
state.subtree_start, state.has_error);
}
} // namespace Carbon