diff --git a/toolchain/parser/BUILD b/toolchain/parser/BUILD index e863f87bee7a..a06d333be86f 100644 --- a/toolchain/parser/BUILD +++ b/toolchain/parser/BUILD @@ -31,9 +31,13 @@ cc_library( name = "parse_tree", srcs = [ "parse_tree.cpp", - "parser.cpp", - "parser.h", - ], + "parser_context.cpp", + "parser_context.h", + ] + + # Glob handler files to avoid missing anyway. + glob([ + "parser_handle_*.cpp", + ]), hdrs = ["parse_tree.h"], deps = [ ":parse_node_kind", diff --git a/toolchain/parser/parse_tree.cpp b/toolchain/parser/parse_tree.cpp index f6d5cfefb7af..721b2d65c195 100644 --- a/toolchain/parser/parse_tree.cpp +++ b/toolchain/parser/parse_tree.cpp @@ -11,12 +11,43 @@ #include "common/error.h" #include "llvm/ADT/Sequence.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/Support/PrettyStackTrace.h" #include "toolchain/lexer/tokenized_buffer.h" #include "toolchain/parser/parse_node_kind.h" -#include "toolchain/parser/parser.h" +#include "toolchain/parser/parser_context.h" namespace Carbon { +class PrettyStackTraceParserContext : public llvm::PrettyStackTraceEntry { + public: + explicit PrettyStackTraceParserContext(const ParserContext* context) + : context_(context) {} + ~PrettyStackTraceParserContext() override = default; + + auto print(llvm::raw_ostream& output) const -> void override { + output << "Parser stack:\n"; + for (int i = 0; i < static_cast(context_->state_stack().size()); ++i) { + const auto& entry = context_->state_stack()[i]; + output << "\t" << i << ".\t" << entry.state; + Print(output, entry.token); + } + output << "\tcursor\tposition_"; + Print(output, *context_->position()); + } + + private: + auto Print(llvm::raw_ostream& output, TokenizedBuffer::Token token) const + -> void { + auto line = context_->tokens().GetLine(token); + output << " @ " << context_->tokens().GetLineNumber(line) << ":" + << context_->tokens().GetColumnNumber(token) << ":" + << " token " << token << " : " << context_->tokens().GetKind(token) + << "\n"; + } + + const ParserContext* context_; +}; + auto ParseTree::Parse(TokenizedBuffer& tokens, DiagnosticConsumer& consumer, llvm::raw_ostream* vlog_stream) -> ParseTree { TokenizedBuffer::TokenLocationTranslator translator( @@ -24,7 +55,30 @@ auto ParseTree::Parse(TokenizedBuffer& tokens, DiagnosticConsumer& consumer, TokenDiagnosticEmitter emitter(translator, consumer); // Delegate to the parser. - auto tree = Parser::Parse(tokens, emitter, vlog_stream); + ParseTree tree(tokens); + ParserContext context(tree, tokens, emitter, vlog_stream); + PrettyStackTraceParserContext pretty_context(&context); + + context.PushState(ParserState::DeclarationScopeLoop); + + // The package should always be the first token, if it's present. Any other + // use is invalid. + if (context.PositionIs(TokenKind::Package)) { + context.PushState(ParserState::Package); + } + + while (!context.state_stack().empty()) { + switch (context.state_stack().back().state) { +#define CARBON_PARSER_STATE(Name) \ + case ParserState::Name: \ + ParserHandle##Name(context); \ + break; +#include "toolchain/parser/parser_state.def" + } + } + + context.AddLeafNode(ParseNodeKind::FileEnd, *context.position()); + if (auto verify = tree.Verify(); !verify.ok()) { if (vlog_stream) { tree.Print(*vlog_stream); diff --git a/toolchain/parser/parse_tree.h b/toolchain/parser/parse_tree.h index c340b0155317..b9934c3cc930 100644 --- a/toolchain/parser/parse_tree.h +++ b/toolchain/parser/parse_tree.h @@ -149,7 +149,7 @@ class ParseTree { [[nodiscard]] auto Verify() const -> ErrorOr; private: - friend class Parser; + friend class ParserContext; // The in-memory representation of data used for a particular node in the // tree. @@ -205,8 +205,8 @@ class ParseTree { static_assert(sizeof(NodeImpl) == 12, "Unexpected size of node implementation!"); - // Wires up the reference to the tokenized buffer. The global `parse` routine - // should be used to actually parse the tokens into a tree. + // Wires up the reference to the tokenized buffer. The `Parse` function should + // be used to actually parse the tokens into a tree. explicit ParseTree(TokenizedBuffer& tokens_arg) : tokens_(&tokens_arg) { // If the tree is valid, there will be one node per token, so reserve once. node_impls_.reserve(tokens_->size()); diff --git a/toolchain/parser/parser.cpp b/toolchain/parser/parser.cpp deleted file mode 100644 index 4e5d6be83963..000000000000 --- a/toolchain/parser/parser.cpp +++ /dev/null @@ -1,1968 +0,0 @@ -// 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 -#include -#include - -#include "common/check.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(ExpectedSemiAfterExpression, Error, - "Expected `;` after expression."); - -CARBON_DIAGNOSTIC(ExpectedDeclarationName, Error, - "`{0}` introducer should be followed by a name.", TokenKind); -CARBON_DIAGNOSTIC(ExpectedDeclarationSemiOrDefinition, Error, - "`{0}` should either end with a `;` for a declaration or " - "have a `{{ ... }` block for a definition.", - TokenKind); - -// 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(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) - << "\n"; - } - - const Parser* parser_; -}; - -Parser::Parser(ParseTree& tree, TokenizedBuffer& tokens, - TokenDiagnosticEmitter& emitter, llvm::raw_ostream* vlog_stream) - : tree_(&tree), - tokens_(&tokens), - emitter_(&emitter), - vlog_stream_(vlog_stream), - 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_); -} - -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 { - CARBON_DIAGNOSTIC(ExpectedParenAfter, Error, "Expected `(` after `{0}`.", - TokenKind); - 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::ConsumeChecked(TokenKind kind) -> TokenizedBuffer::Token { - CARBON_CHECK(PositionIs(kind)) - << "Required " << kind << ", found " << PositionKind(); - return Consume(); -} - -auto Parser::ConsumeIf(TokenKind kind) - -> std::optional { - if (!PositionIs(kind)) { - return std::nullopt; - } - return Consume(); -} - -auto Parser::FindNextOf(std::initializer_list desired_kinds) - -> std::optional { - 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.is_closing_symbol() || kind == TokenKind::EndOfFile) { - // There are no more tokens at this level. - return std::nullopt; - } else if (kind.is_opening_symbol()) { - new_position = TokenizedBuffer::TokenIterator( - tokens_->GetMatchedClosingToken(token)); - // Advance past the closing token. - ++new_position; - } else { - ++new_position; - } - } -} - -auto Parser::SkipMatchingGroup() -> bool { - if (!PositionKind().is_opening_symbol()) { - return false; - } - - SkipTo(tokens_->GetMatchedClosingToken(*position_)); - ++position_; - return true; -} - -auto Parser::SkipPastLikelyEnd(TokenizedBuffer::Token skip_root) - -> std::optional { - if (position_ == end_) { - return std::nullopt; - } - - 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 std::nullopt; - } - - // 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 std::nullopt; -} - -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().is_symbol() && - (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.opening_symbol() << "` 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::DeclarationScopeLoop); - - // The package should always be the first token, if it's present. Any other - // use is invalid. - if (PositionIs(TokenKind::Package)) { - PushState(ParserState::Package); - } - - 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::GetDeclarationContext() -> DeclarationContext { - // i == 0 is the file-level DeclarationScopeLoop. Additionally, i == 1 can be - // skipped because it will never be a DeclarationScopeLoop. - for (int i = state_stack_.size() - 1; i > 1; --i) { - // The declaration context is always the state _above_ a - // DeclarationScopeLoop. - if (state_stack_[i].state == ParserState::DeclarationScopeLoop) { - switch (state_stack_[i - 1].state) { - case ParserState::TypeDefinitionFinishAsClass: - return DeclarationContext::Class; - case ParserState::TypeDefinitionFinishAsInterface: - return DeclarationContext::Interface; - case ParserState::TypeDefinitionFinishAsNamedConstraint: - return DeclarationContext::NamedConstraint; - default: - llvm_unreachable("Missing handling for a declaration scope"); - } - } - } - CARBON_CHECK(!state_stack_.empty() && - state_stack_[0].state == ParserState::DeclarationScopeLoop); - return DeclarationContext::File; -} - -auto Parser::HandleDeclarationError(StateStackEntry state, - ParseNodeKind parse_node_kind, - bool skip_past_likely_end) -> void { - auto token = state.token; - if (skip_past_likely_end) { - if (auto semi = SkipPastLikelyEnd(token)) { - token = *semi; - } - } - AddNode(parse_node_kind, token, state.subtree_start, - /*has_error=*/true); -} - -auto Parser::HandleUnrecognizedDeclaration() -> void { - 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); -} - -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::HandleBraceExpressionParameterError(StateStackEntry state, - ParserState param_finish_state) - -> void { - bool is_type = - param_finish_state == ParserState::BraceExpressionParameterFinishAsType; - bool is_value = - param_finish_state == ParserState::BraceExpressionParameterFinishAsValue; - bool is_unknown = param_finish_state == - ParserState::BraceExpressionParameterFinishAsUnknown; - CARBON_CHECK(is_type || is_value || is_unknown); - CARBON_DIAGNOSTIC(ExpectedStructLiteralField, Error, "Expected {0}{1}{2}.", - llvm::StringRef, llvm::StringRef, llvm::StringRef); - emitter_->Emit(*position_, ExpectedStructLiteralField, - (is_type || is_unknown) ? "`.field: field_type`" : "", - is_unknown ? " or " : "", - (is_value || is_unknown) ? "`.field = value`" : ""); - - state.state = param_finish_state; - state.has_error = true; - PushState(state); -} - -auto Parser::HandleBraceExpressionParameter(ParserState after_designator_state, - ParserState param_finish_state) - -> void { - auto state = PopState(); - - if (!PositionIs(TokenKind::Period)) { - HandleBraceExpressionParameterError(state, param_finish_state); - return; - } - - state.state = after_designator_state; - PushState(state); - PushState(ParserState::DesignatorAsStruct); -} - -auto Parser::HandleBraceExpressionParameterAsTypeState() -> void { - HandleBraceExpressionParameter( - ParserState::BraceExpressionParameterAfterDesignatorAsType, - ParserState::BraceExpressionParameterFinishAsType); -} - -auto Parser::HandleBraceExpressionParameterAsValueState() -> void { - HandleBraceExpressionParameter( - ParserState::BraceExpressionParameterAfterDesignatorAsValue, - ParserState::BraceExpressionParameterFinishAsValue); -} - -auto Parser::HandleBraceExpressionParameterAsUnknownState() -> void { - HandleBraceExpressionParameter( - ParserState::BraceExpressionParameterAfterDesignatorAsUnknown, - ParserState::BraceExpressionParameterFinishAsUnknown); -} - -auto Parser::HandleBraceExpressionParameterAfterDesignator( - ParserState param_finish_state) -> 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 = param_finish_state; - PushState(state); - return; - } - SkipTo(*recovery_pos); - } - - // Work out the kind of this element. - bool is_type; - if (PositionIs(TokenKind::Colon)) { - is_type = true; - } else if (PositionIs(TokenKind::Equal)) { - is_type = false; - } else { - HandleBraceExpressionParameterError(state, param_finish_state); - return; - } - - // If we're changing from unknown, update the related finish states. - if (param_finish_state == - ParserState::BraceExpressionParameterFinishAsUnknown) { - auto finish_state = PopState(); - CARBON_CHECK(finish_state.state == - ParserState::BraceExpressionFinishAsUnknown); - if (is_type) { - finish_state.state = ParserState::BraceExpressionFinishAsType; - param_finish_state = ParserState::BraceExpressionParameterFinishAsType; - } else { - finish_state.state = ParserState::BraceExpressionFinishAsValue; - param_finish_state = ParserState::BraceExpressionParameterFinishAsValue; - } - PushState(finish_state); - } - - auto want_param_finish_state = - is_type ? ParserState::BraceExpressionParameterFinishAsType - : ParserState::BraceExpressionParameterFinishAsValue; - if (param_finish_state != want_param_finish_state) { - HandleBraceExpressionParameterError(state, param_finish_state); - return; - } - - // Struct type fields and value fields use the same grammar except - // that one has a `:` separator and the other has an `=` separator. - state.state = param_finish_state; - state.token = Consume(); - PushState(state); - PushState(ParserState::Expression); -} - -auto Parser::HandleBraceExpressionParameterAfterDesignatorAsTypeState() - -> void { - HandleBraceExpressionParameterAfterDesignator( - ParserState::BraceExpressionParameterFinishAsType); -} - -auto Parser::HandleBraceExpressionParameterAfterDesignatorAsValueState() - -> void { - HandleBraceExpressionParameterAfterDesignator( - ParserState::BraceExpressionParameterFinishAsValue); -} - -auto Parser::HandleBraceExpressionParameterAfterDesignatorAsUnknownState() - -> void { - HandleBraceExpressionParameterAfterDesignator( - ParserState::BraceExpressionParameterFinishAsUnknown); -} - -auto Parser::HandleBraceExpressionParameterFinish(ParseNodeKind node_kind, - ParserState param_state) - -> void { - auto state = PopState(); - - if (state.has_error) { - AddLeafNode(ParseNodeKind::StructFieldUnknown, state.token, - /*has_error=*/true); - } else { - AddNode(node_kind, state.token, state.subtree_start, /*has_error=*/false); - } - - if (ConsumeListToken(ParseNodeKind::StructComma, TokenKind::CloseCurlyBrace, - state.has_error) == ListTokenKind::Comma) { - PushState(param_state); - } -} - -auto Parser::HandleBraceExpressionParameterFinishAsTypeState() -> void { - HandleBraceExpressionParameterFinish( - ParseNodeKind::StructFieldType, - ParserState::BraceExpressionParameterAsType); -} - -auto Parser::HandleBraceExpressionParameterFinishAsValueState() -> void { - HandleBraceExpressionParameterFinish( - ParseNodeKind::StructFieldValue, - ParserState::BraceExpressionParameterAsValue); -} - -auto Parser::HandleBraceExpressionParameterFinishAsUnknownState() -> void { - HandleBraceExpressionParameterFinish( - ParseNodeKind::StructFieldUnknown, - ParserState::BraceExpressionParameterAsUnknown); -} - -auto Parser::HandleBraceExpressionFinish(ParseNodeKind node_kind) -> void { - auto state = PopState(); - - AddNode(node_kind, Consume(), state.subtree_start, state.has_error); -} - -auto Parser::HandleBraceExpressionFinishAsTypeState() -> void { - HandleBraceExpressionFinish(ParseNodeKind::StructTypeLiteral); -} - -auto Parser::HandleBraceExpressionFinishAsValueState() -> void { - HandleBraceExpressionFinish(ParseNodeKind::StructLiteral); -} - -auto Parser::HandleBraceExpressionFinishAsUnknownState() -> void { - HandleBraceExpressionFinish(ParseNodeKind::StructLiteral); -} - -auto Parser::HandleCallExpressionState() -> void { - auto state = PopState(); - - state.state = ParserState::CallExpressionFinish; - PushState(state); - - AddNode(ParseNodeKind::CallExpressionStart, Consume(), state.subtree_start, - state.has_error); - 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(); - - AddNode(ParseNodeKind::CallExpression, Consume(), 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::HandleDeclarationNameAndParams(bool params_required) -> void { - auto state = PopState(); - - if (!ConsumeAndAddLeafNodeIf(TokenKind::Identifier, - ParseNodeKind::DeclaredName)) { - emitter_->Emit(*position_, ExpectedDeclarationName, - tokens_->GetKind(state.token)); - ReturnErrorOnState(); - return; - } - - if (PositionIs(TokenKind::OpenSquareBracket)) { - PushState(ParserState::DeclarationNameAndParamsAfterDeduced); - PushState(ParserState::ParameterListAsDeduced); - } else if (PositionIs(TokenKind::OpenParen)) { - PushState(ParserState::ParameterListAsRegular); - } else if (params_required) { - CARBON_DIAGNOSTIC(ParametersRequiredByIntroducer, Error, - "`{0}` requires a `(` for parameters.", TokenKind); - emitter_->Emit(*position_, ParametersRequiredByIntroducer, - tokens_->GetKind(state.token)); - ReturnErrorOnState(); - } -} - -auto Parser::HandleDeclarationNameAndParamsAsOptionalState() -> void { - HandleDeclarationNameAndParams(/*params_required=*/false); -} - -auto Parser::HandleDeclarationNameAndParamsAsRequiredState() -> void { - HandleDeclarationNameAndParams(/*params_required=*/true); -} - -auto Parser::HandleDeclarationNameAndParamsAfterDeducedState() -> void { - PopAndDiscardState(); - - if (PositionIs(TokenKind::OpenParen)) { - PushState(ParserState::ParameterListAsRegular); - } else { - CARBON_DIAGNOSTIC( - ParametersRequiredByDeduced, Error, - "A `(` for parameters is required after deduced parameters."); - emitter_->Emit(*position_, ParametersRequiredByDeduced); - ReturnErrorOnState(); - } -} - -auto Parser::HandleDeclarationScopeLoopState() -> void { - // This maintains the current state unless we're at the end of the scope. - - switch (PositionKind()) { - case TokenKind::CloseCurlyBrace: - case TokenKind::EndOfFile: { - // This is the end of the scope, so the loop state ends. - PopAndDiscardState(); - break; - } - case TokenKind::Class: { - PushState(ParserState::TypeIntroducerAsClass); - break; - } - case TokenKind::Constraint: { - PushState(ParserState::TypeIntroducerAsNamedConstraint); - break; - } - case TokenKind::Fn: { - PushState(ParserState::FunctionIntroducer); - break; - } - case TokenKind::Interface: { - PushState(ParserState::TypeIntroducerAsInterface); - break; - } - case TokenKind::Semi: { - AddLeafNode(ParseNodeKind::EmptyDeclaration, Consume()); - break; - } - case TokenKind::Var: { - PushState(ParserState::VarAsSemicolon); - break; - } - default: { - HandleUnrecognizedDeclaration(); - break; - } - } -} - -auto Parser::HandleDesignator(bool as_struct) -> void { - auto state = PopState(); - - // `.` identifier - auto dot = ConsumeChecked(TokenKind::Period); - - 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().is_keyword()) { - AddLeafNode(ParseNodeKind::DesignatedName, Consume(), - /*has_error=*/true); - } else { - AddLeafNode(ParseNodeKind::DesignatedName, *position_, - /*has_error=*/true); - // Indicate the error to the parent state so that it can avoid producing - // more errors. - 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: - case TokenKind::StringTypeLiteral: { - 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; - } - case TokenKind::SelfValueIdentifier: { - AddLeafNode(ParseNodeKind::SelfValueIdentifier, Consume()); - PushState(state); - break; - } - case TokenKind::SelfTypeIdentifier: { - AddLeafNode(ParseNodeKind::SelfTypeIdentifier, Consume()); - PushState(state); - break; - } - default: { - // Add a node to keep the parse tree balanced. - AddLeafNode(ParseNodeKind::InvalidParse, *position_, /*has_error=*/true); - 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::HandleFunctionIntroducerState() -> void { - auto state = PopState(); - - AddLeafNode(ParseNodeKind::FunctionIntroducer, Consume()); - - state.state = ParserState::FunctionAfterParameters; - PushState(state); - state.state = ParserState::DeclarationNameAndParamsAsRequired; - PushState(state); -} - -auto Parser::HandleFunctionAfterParametersState() -> 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: { - if (auto context = GetDeclarationContext(); - context == DeclarationContext::Interface || - context == DeclarationContext::NamedConstraint) { - CARBON_DIAGNOSTIC( - MethodImplNotAllowed, Error, - "Method implementations are not allowed in interfaces."); - emitter_->Emit(*position_, MethodImplNotAllowed); - HandleDeclarationError(state, ParseNodeKind::FunctionDeclaration, - /*skip_past_likely_end=*/true); - break; - } - - 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: { - if (!state.has_error) { - emitter_->Emit(*position_, ExpectedDeclarationSemiOrDefinition, - TokenKind::Fn); - } - // 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); - HandleDeclarationError(state, ParseNodeKind::FunctionDeclaration, - 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::HandleParameter(ParserState pattern_state, - ParserState finish_state) -> void { - PopAndDiscardState(); - - PushState(finish_state); - PushState(pattern_state); -} - -auto Parser::HandleParameterAsDeducedState() -> void { - HandleParameter(ParserState::PatternAsDeducedParameter, - ParserState::ParameterFinishAsDeduced); -} - -auto Parser::HandleParameterAsRegularState() -> void { - HandleParameter(ParserState::PatternAsParameter, - ParserState::ParameterFinishAsRegular); -} - -auto Parser::HandleParameterFinish(TokenKind close_token, - ParserState param_state) -> void { - auto state = PopState(); - - if (state.has_error) { - ReturnErrorOnState(); - } - - if (ConsumeListToken(ParseNodeKind::ParameterListComma, close_token, - state.has_error) == ListTokenKind::Comma) { - PushState(param_state); - } -} - -auto Parser::HandleParameterFinishAsDeducedState() -> void { - HandleParameterFinish(TokenKind::CloseSquareBracket, - ParserState::ParameterAsDeduced); -} - -auto Parser::HandleParameterFinishAsRegularState() -> void { - HandleParameterFinish(TokenKind::CloseParen, ParserState::ParameterAsRegular); -} - -auto Parser::HandleParameterList(ParseNodeKind parse_node_kind, - TokenKind open_token_kind, - TokenKind close_token_kind, - ParserState param_state, - ParserState finish_state) -> void { - PopAndDiscardState(); - - PushState(finish_state); - AddLeafNode(parse_node_kind, ConsumeChecked(open_token_kind)); - - if (!PositionIs(close_token_kind)) { - PushState(param_state); - } -} - -auto Parser::HandleParameterListAsDeducedState() -> void { - HandleParameterList( - ParseNodeKind::DeducedParameterListStart, TokenKind::OpenSquareBracket, - TokenKind::CloseSquareBracket, ParserState::ParameterAsDeduced, - ParserState::ParameterListFinishAsDeduced); -} - -auto Parser::HandleParameterListAsRegularState() -> void { - HandleParameterList(ParseNodeKind::ParameterListStart, TokenKind::OpenParen, - TokenKind::CloseParen, ParserState::ParameterAsRegular, - ParserState::ParameterListFinishAsRegular); -} - -auto Parser::HandleParameterListFinish(ParseNodeKind parse_node_kind, - TokenKind token_kind) -> void { - auto state = PopState(); - - AddNode(parse_node_kind, ConsumeChecked(token_kind), state.subtree_start, - state.has_error); -} - -auto Parser::HandleParameterListFinishAsDeducedState() -> void { - HandleParameterListFinish(ParseNodeKind::DeducedParameterList, - TokenKind::CloseSquareBracket); -} - -auto Parser::HandleParameterListFinishAsRegularState() -> void { - HandleParameterListFinish(ParseNodeKind::ParameterList, - TokenKind::CloseParen); -} - -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(); - - // Advance past the open paren. - AddLeafNode(ParseNodeKind::ParenExpressionOrTupleLiteralStart, - ConsumeChecked(TokenKind::OpenParen)); - - if (PositionIs(TokenKind::CloseParen)) { - state.state = ParserState::ParenExpressionFinishAsTuple; - PushState(state); - } else { - state.state = ParserState::ParenExpressionFinishAsNormal; - 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::ParenExpressionFinishAsNormal) - << "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::HandleParenExpressionFinishAsNormalState() -> void { - auto state = PopState(); - - AddNode(ParseNodeKind::ParenExpression, Consume(), state.subtree_start, - state.has_error); -} - -auto Parser::HandleParenExpressionFinishAsTupleState() -> void { - auto state = PopState(); - - AddNode(ParseNodeKind::TupleLiteral, Consume(), state.subtree_start, - state.has_error); -} - -auto Parser::ConsumeIfPatternKeyword(TokenKind keyword_token, - ParserState keyword_state, - int subtree_start) -> void { - if (auto token = ConsumeIf(keyword_token)) { - PushState(StateStackEntry( - keyword_state, PrecedenceGroup::ForTopLevelExpression(), - PrecedenceGroup::ForTopLevelExpression(), *token, subtree_start)); - } -} - -auto Parser::HandlePattern(PatternKind pattern_kind) -> void { - auto state = PopState(); - - // Parameters may have keywords prefixing the pattern. They become the parent - // for the full PatternBinding. - if (pattern_kind != PatternKind::Variable) { - ConsumeIfPatternKeyword(TokenKind::Template, ParserState::PatternTemplate, - state.subtree_start); - ConsumeIfPatternKeyword(TokenKind::Addr, ParserState::PatternAddress, - state.subtree_start); - } - - // Handle an invalid pattern introducer for parameters and variables. - auto on_error = [&]() { - switch (pattern_kind) { - case PatternKind::DeducedParameter: - 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; - } - } - // Add a placeholder for the type. - AddLeafNode(ParseNodeKind::InvalidParse, *position_, /*has_error=*/true); - state.state = ParserState::PatternFinishAsRegular; - state.has_error = true; - PushState(state); - }; - - // The first item should be an identifier or, for deduced parameters, `self`. - bool has_name = false; - if (auto identifier = ConsumeIf(TokenKind::Identifier)) { - AddLeafNode(ParseNodeKind::DeclaredName, *identifier); - has_name = true; - } else if (pattern_kind == PatternKind::DeducedParameter) { - if (auto self = ConsumeIf(TokenKind::SelfValueIdentifier)) { - AddLeafNode(ParseNodeKind::SelfValueIdentifier, *self); - has_name = true; - } - } - if (!has_name) { - // Add a placeholder for the name. - AddLeafNode(ParseNodeKind::DeclaredName, *position_, /*has_error=*/true); - on_error(); - return; - } - - if (auto kind = PositionKind(); - kind == TokenKind::Colon || kind == TokenKind::ColonExclaim) { - state.state = kind == TokenKind::Colon - ? ParserState::PatternFinishAsRegular - : ParserState::PatternFinishAsGeneric; - // Use the `:` or `:!` for the root node. - state.token = Consume(); - PushState(state); - PushStateForExpression(PrecedenceGroup::ForType()); - } else { - on_error(); - return; - } -} - -auto Parser::HandlePatternAsDeducedParameterState() -> void { - HandlePattern(PatternKind::DeducedParameter); -} - -auto Parser::HandlePatternAsParameterState() -> void { - HandlePattern(PatternKind::Parameter); -} - -auto Parser::HandlePatternAsVariableState() -> void { - HandlePattern(PatternKind::Variable); -} - -auto Parser::HandlePatternFinish(ParseNodeKind node_kind) -> void { - auto state = PopState(); - - AddNode(node_kind, state.token, state.subtree_start, state.has_error); - - // Propagate errors to the parent state so that they can take different - // actions on invalid patterns. - if (state.has_error) { - ReturnErrorOnState(); - } -} - -auto Parser::HandlePatternFinishAsGenericState() -> void { - HandlePatternFinish(ParseNodeKind::GenericPatternBinding); -} - -auto Parser::HandlePatternFinishAsRegularState() -> void { - HandlePatternFinish(ParseNodeKind::PatternBinding); -} - -auto Parser::HandlePatternAddressState() -> void { - auto state = PopState(); - - AddNode(ParseNodeKind::Address, state.token, state.subtree_start, - state.has_error); - - // If an error was encountered, propagate it while adding a node. - if (state.has_error) { - ReturnErrorOnState(); - } -} - -auto Parser::HandlePatternTemplateState() -> void { - auto state = PopState(); - - AddNode(ParseNodeKind::Template, state.token, state.subtree_start, - state.has_error); - - // If an error was encountered, propagate it while adding a node. - if (state.has_error) { - ReturnErrorOnState(); - } -} - -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::HandleTypeIntroducer(ParseNodeKind introducer_kind, - ParserState after_params_state) -> void { - auto state = PopState(); - - AddLeafNode(introducer_kind, Consume()); - - state.state = after_params_state; - PushState(state); - state.state = ParserState::DeclarationNameAndParamsAsOptional; - PushState(state); -} - -auto Parser::HandleTypeIntroducerAsClassState() -> void { - HandleTypeIntroducer(ParseNodeKind::ClassIntroducer, - ParserState::TypeAfterParamsAsClass); -} - -auto Parser::HandleTypeIntroducerAsInterfaceState() -> void { - HandleTypeIntroducer(ParseNodeKind::InterfaceIntroducer, - ParserState::TypeAfterParamsAsInterface); -} - -auto Parser::HandleTypeIntroducerAsNamedConstraintState() -> void { - HandleTypeIntroducer(ParseNodeKind::NamedConstraintIntroducer, - ParserState::TypeAfterParamsAsNamedConstraint); -} - -auto Parser::HandleTypeAfterParams(ParseNodeKind declaration_kind, - ParseNodeKind definition_start_kind, - ParserState definition_finish_state) - -> void { - auto state = PopState(); - - if (state.has_error) { - HandleDeclarationError(state, declaration_kind, - /*skip_past_likely_end=*/true); - return; - } - - if (auto semi = ConsumeIf(TokenKind::Semi)) { - AddNode(declaration_kind, *semi, state.subtree_start, state.has_error); - return; - } - - if (!PositionIs(TokenKind::OpenCurlyBrace)) { - emitter_->Emit(*position_, ExpectedDeclarationSemiOrDefinition, - tokens_->GetKind(state.token)); - HandleDeclarationError(state, declaration_kind, - /*skip_past_likely_end=*/true); - return; - } - - state.state = definition_finish_state; - PushState(state); - PushState(ParserState::DeclarationScopeLoop); - AddNode(definition_start_kind, Consume(), state.subtree_start, - state.has_error); -} - -auto Parser::HandleTypeAfterParamsAsClassState() -> void { - HandleTypeAfterParams(ParseNodeKind::ClassDeclaration, - ParseNodeKind::ClassDefinitionStart, - ParserState::TypeDefinitionFinishAsClass); -} - -auto Parser::HandleTypeAfterParamsAsInterfaceState() -> void { - HandleTypeAfterParams(ParseNodeKind::InterfaceDeclaration, - ParseNodeKind::InterfaceDefinitionStart, - ParserState::TypeDefinitionFinishAsInterface); -} - -auto Parser::HandleTypeAfterParamsAsNamedConstraintState() -> void { - HandleTypeAfterParams(ParseNodeKind::NamedConstraintDeclaration, - ParseNodeKind::NamedConstraintDefinitionStart, - ParserState::TypeDefinitionFinishAsNamedConstraint); -} - -auto Parser::HandleTypeDefinitionFinish(ParseNodeKind definition_kind) -> void { - auto state = PopState(); - - AddNode(definition_kind, Consume(), state.subtree_start, state.has_error); -} - -auto Parser::HandleTypeDefinitionFinishAsClassState() -> void { - HandleTypeDefinitionFinish(ParseNodeKind::ClassDefinition); -} - -auto Parser::HandleTypeDefinitionFinishAsInterfaceState() -> void { - HandleTypeDefinitionFinish(ParseNodeKind::InterfaceDefinition); -} - -auto Parser::HandleTypeDefinitionFinishAsNamedConstraintState() -> void { - HandleTypeDefinitionFinish(ParseNodeKind::NamedConstraintDefinition); -} - -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 (auto equals = ConsumeIf(TokenKind::Equal)) { - AddLeafNode(ParseNodeKind::VariableInitializer, *equals); - PushState(ParserState::Expression); - } -} - -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(ExpectedInNotColon, Error, - "`:` should be replaced by `in`."); - emitter_->Emit(*position_, ExpectedInNotColon); - 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); -} - -} // namespace Carbon diff --git a/toolchain/parser/parser_context.cpp b/toolchain/parser/parser_context.cpp new file mode 100644 index 000000000000..117452bbffd4 --- /dev/null +++ b/toolchain/parser/parser_context.cpp @@ -0,0 +1,425 @@ +// 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" + +#include +#include +#include + +#include "common/check.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 { + +// 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; +} + +ParserContext::ParserContext(ParseTree& tree, TokenizedBuffer& tokens, + TokenDiagnosticEmitter& emitter, + llvm::raw_ostream* vlog_stream) + : tree_(&tree), + tokens_(&tokens), + emitter_(&emitter), + vlog_stream_(vlog_stream), + 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_); +} + +auto ParserContext::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 ParserContext::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 ParserContext::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 { + CARBON_DIAGNOSTIC(ExpectedParenAfter, Error, "Expected `(` after `{0}`.", + TokenKind); + emitter_->Emit(*position_, ExpectedParenAfter, + tokens().GetKind(default_token)); + AddLeafNode(start_kind, default_token, /*has_error=*/true); + } +} + +auto ParserContext::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 ParserContext::ConsumeAndAddLeafNodeIf(TokenKind token_kind, + ParseNodeKind node_kind) -> bool { + auto token = ConsumeIf(token_kind); + if (!token) { + return false; + } + + AddLeafNode(node_kind, *token); + return true; +} + +auto ParserContext::ConsumeChecked(TokenKind kind) -> TokenizedBuffer::Token { + CARBON_CHECK(PositionIs(kind)) + << "Required " << kind << ", found " << PositionKind(); + return Consume(); +} + +auto ParserContext::ConsumeIf(TokenKind kind) + -> std::optional { + if (!PositionIs(kind)) { + return std::nullopt; + } + return Consume(); +} + +auto ParserContext::ConsumeIfPatternKeyword(TokenKind keyword_token, + ParserState keyword_state, + int subtree_start) -> void { + if (auto token = ConsumeIf(keyword_token)) { + PushState(ParserContext::StateStackEntry( + keyword_state, PrecedenceGroup::ForTopLevelExpression(), + PrecedenceGroup::ForTopLevelExpression(), *token, subtree_start)); + } +} + +auto ParserContext::FindNextOf(std::initializer_list desired_kinds) + -> std::optional { + 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.is_closing_symbol() || kind == TokenKind::EndOfFile) { + // There are no more tokens at this level. + return std::nullopt; + } else if (kind.is_opening_symbol()) { + new_position = TokenizedBuffer::TokenIterator( + tokens().GetMatchedClosingToken(token)); + // Advance past the closing token. + ++new_position; + } else { + ++new_position; + } + } +} + +auto ParserContext::SkipMatchingGroup() -> bool { + if (!PositionKind().is_opening_symbol()) { + return false; + } + + SkipTo(tokens().GetMatchedClosingToken(*position_)); + ++position_; + return true; +} + +auto ParserContext::SkipPastLikelyEnd(TokenizedBuffer::Token skip_root) + -> std::optional { + if (position_ == end_) { + return std::nullopt; + } + + 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 std::nullopt; + } + + // 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 std::nullopt; +} + +auto ParserContext::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."; +} + +// 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 ParserContext::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 ParserContext::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 ParserContext::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().is_symbol() && + (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 ParserContext::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.opening_symbol() << "` 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 ParserContext::GetDeclarationContext() -> DeclarationContext { + // i == 0 is the file-level DeclarationScopeLoop. Additionally, i == 1 can be + // skipped because it will never be a DeclarationScopeLoop. + for (int i = state_stack_.size() - 1; i > 1; --i) { + // The declaration context is always the state _above_ a + // DeclarationScopeLoop. + if (state_stack_[i].state == ParserState::DeclarationScopeLoop) { + switch (state_stack_[i - 1].state) { + case ParserState::TypeDefinitionFinishAsClass: + return DeclarationContext::Class; + case ParserState::TypeDefinitionFinishAsInterface: + return DeclarationContext::Interface; + case ParserState::TypeDefinitionFinishAsNamedConstraint: + return DeclarationContext::NamedConstraint; + default: + llvm_unreachable("Missing handling for a declaration scope"); + } + } + } + CARBON_CHECK(!state_stack_.empty() && + state_stack_[0].state == ParserState::DeclarationScopeLoop); + return DeclarationContext::File; +} + +auto ParserContext::RecoverFromDeclarationError(StateStackEntry state, + ParseNodeKind parse_node_kind, + bool skip_past_likely_end) + -> void { + auto token = state.token; + if (skip_past_likely_end) { + if (auto semi = SkipPastLikelyEnd(token)) { + token = *semi; + } + } + AddNode(parse_node_kind, token, state.subtree_start, + /*has_error=*/true); +} + +} // namespace Carbon diff --git a/toolchain/parser/parser.h b/toolchain/parser/parser_context.h similarity index 70% rename from toolchain/parser/parser.h rename to toolchain/parser/parser_context.h index 6e3196c18c73..f19be90ec143 100644 --- a/toolchain/parser/parser.h +++ b/toolchain/parser/parser_context.h @@ -2,8 +2,8 @@ // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -#ifndef CARBON_TOOLCHAIN_PARSER_PARSER_H_ -#define CARBON_TOOLCHAIN_PARSER_PARSER_H_ +#ifndef CARBON_TOOLCHAIN_PARSER_PARSER_CONTEXT_H_ +#define CARBON_TOOLCHAIN_PARSER_PARSER_CONTEXT_H_ #include @@ -18,22 +18,10 @@ namespace Carbon { -// This parser uses a stack for state transitions. See parser_state.def for -// state documentation. -class Parser { +// Context and shared functionality for parser handlers. See parser_state.def +// for state documentation. +class ParserContext { public: - // Parses the tokens into a parse tree, emitting any errors encountered. - // - // This is the entry point to the parser implementation. - static auto Parse(TokenizedBuffer& tokens, TokenDiagnosticEmitter& emitter, - llvm::raw_ostream* vlog_stream) -> ParseTree { - ParseTree tree(tokens); - Parser parser(tree, tokens, emitter, vlog_stream); - parser.Parse(); - return tree; - } - - private: // Possible operator fixities for errors. enum class OperatorFixity { Prefix, Infix, Postfix }; @@ -51,9 +39,6 @@ class Parser { NamedConstraint, }; - // Helper class for tracing state_stack_ on crashes. - class PrettyStackTraceParseState; - // Used to track state on state_stack_. struct StateStackEntry { explicit StateStackEntry(ParserState state, @@ -106,11 +91,9 @@ class Parser { static_assert(sizeof(StateStackEntry) == 12, "StateStackEntry has unexpected size!"); - explicit Parser(ParseTree& tree, TokenizedBuffer& tokens, - TokenDiagnosticEmitter& emitter, - llvm::raw_ostream* vlog_stream); - - auto Parse() -> void; + explicit ParserContext(ParseTree& tree, TokenizedBuffer& tokens, + TokenDiagnosticEmitter& emitter, + llvm::raw_ostream* vlog_stream); // Adds a node to the parse tree that has no children (a leaf). auto AddLeafNode(ParseNodeKind kind, TokenizedBuffer::Token token, @@ -264,106 +247,40 @@ class Parser { // DeclarationScopeLoop. auto GetDeclarationContext() -> DeclarationContext; - // Handles error recovery in a declaration, particularly before any possible - // definition has started (although one could be present). Recover to a - // semicolon when it makes sense as a possible end, otherwise use the - // introducer token for the error. - auto HandleDeclarationError(StateStackEntry state, - ParseNodeKind parse_node_kind, - bool skip_past_likely_end) -> void; - - // Handles an unrecognized declaration, adding an error node. - auto HandleUnrecognizedDeclaration() -> void; - // Propagates an error up the state stack, to the parent state. auto ReturnErrorOnState() -> void { state_stack_.back().has_error = true; } - // Prints a diagnostic for brace expression syntax errors. - auto HandleBraceExpressionParameterError(StateStackEntry state, - ParserState param_finish_state) - -> void; - - // Handles BraceExpressionParameterAs(Type|Value|Unknown). - auto HandleBraceExpressionParameter(ParserState after_designator_state, - ParserState param_finish_state) -> void; - - // Handles BraceExpressionParameterAfterDesignatorAs(Type|Value|Unknown). - auto HandleBraceExpressionParameterAfterDesignator( - ParserState param_finish_state) -> void; - - // Handles BraceExpressionParameterFinishAs(Type|Value|Unknown). - auto HandleBraceExpressionParameterFinish(ParseNodeKind node_kind, - ParserState param_state) -> void; - - // Handles BraceExpressionFinishAs(Type|Value|Unknown). - auto HandleBraceExpressionFinish(ParseNodeKind node_kind) -> void; - - // Handles DeclarationNameAndParamsAs(Optional|Required). - auto HandleDeclarationNameAndParams(bool params_required) -> void; - - // Handles DesignatorAs. - auto HandleDesignator(bool as_struct) -> void; - - // Handles ParameterAs(Deduced|Regular). - auto HandleParameter(ParserState pattern_state, ParserState finish_state) - -> void; - - // Handles ParameterFinishAs(Deduced|Regular). - auto HandleParameterFinish(TokenKind close_token, ParserState param_state) - -> void; - - // Handles ParameterListAs(Deduced|Regular). - auto HandleParameterList(ParseNodeKind parse_node_kind, - TokenKind open_token_kind, - TokenKind close_token_kind, ParserState param_state, - ParserState finish_state) -> void; - - // Handles ParameterListFinishAs(Deduced|Regular). - auto HandleParameterListFinish(ParseNodeKind parse_node_kind, - TokenKind token_kind) -> void; - - // Handles ParenConditionAs(If|While) - auto HandleParenCondition(ParseNodeKind start_kind, ParserState finish_state) - -> void; - - // Handles ParenExpressionParameterFinishAs(Unknown|Tuple). - auto HandleParenExpressionParameterFinish(bool as_tuple) -> void; - - // Handles PatternAs(DeducedParameter|FunctionParameter|Variable). - auto HandlePattern(PatternKind pattern_kind) -> void; - - // Handles PatternFinishAs(Generic|Regular). - auto HandlePatternFinish(ParseNodeKind node_kind) -> void; - - // For HandlePattern, tries to consume a wrapping keyword. + // For ParserHandlePattern, tries to consume a wrapping keyword. auto ConsumeIfPatternKeyword(TokenKind keyword_token, ParserState keyword_state, int subtree_start) -> void; - // Handles the `;` after a keyword statement. - auto HandleStatementKeywordFinish(ParseNodeKind node_kind) -> void; + // Handles error recovery in a declaration, particularly before any possible + // definition has started (although one could be present). Recover to a + // semicolon when it makes sense as a possible end, otherwise use the + // introducer token for the error. + auto RecoverFromDeclarationError(StateStackEntry state, + ParseNodeKind parse_node_kind, + bool skip_past_likely_end) -> void; - // Handles processing of a type's introducer. - auto HandleTypeIntroducer(ParseNodeKind introducer_kind, - ParserState after_params_state) -> void; + auto tree() const -> const ParseTree& { return *tree_; } - // Handles processing after params, deciding whether it's a declaration or - // definition. - auto HandleTypeAfterParams(ParseNodeKind declaration_kind, - ParseNodeKind definition_start_kind, - ParserState definition_finish_state) -> void; + auto tokens() const -> const TokenizedBuffer& { return *tokens_; } - // Handles parsing after the declaration scope of a type. - auto HandleTypeDefinitionFinish(ParseNodeKind definition_kind) -> void; + auto emitter() -> TokenDiagnosticEmitter& { return *emitter_; } - // Handles VarAs(Semicolon|For). - auto HandleVar(ParserState finish_state) -> void; + auto position() -> TokenizedBuffer::TokenIterator& { return position_; } + auto position() const -> TokenizedBuffer::TokenIterator { return position_; } - // `clang-format` has a bug with spacing around `->` returns in macros. See - // https://bugs.llvm.org/show_bug.cgi?id=48320 for details. -#define CARBON_PARSER_STATE(Name) auto Handle##Name##State()->void; -#include "toolchain/parser/parser_state.def" + auto state_stack() -> llvm::SmallVector& { + return state_stack_; + } + auto state_stack() const -> const llvm::SmallVector& { + return state_stack_; + } + + private: ParseTree* tree_; TokenizedBuffer* tokens_; TokenDiagnosticEmitter* emitter_; @@ -379,6 +296,30 @@ class Parser { llvm::SmallVector state_stack_; }; +// `clang-format` has a bug with spacing around `->` returns in macros. See +// https://bugs.llvm.org/show_bug.cgi?id=48320 for details. +#define CARBON_PARSER_STATE(Name) \ + auto ParserHandle##Name(ParserContext& context)->void; +#include "toolchain/parser/parser_state.def" + +// The diagnostics below may be emitted a couple different ways as part of +// operator parsing. +// TODO: Clean these up, maybe as context functions? + +CARBON_DIAGNOSTIC( + OperatorRequiresParentheses, Error, + "Parentheses are required to disambiguate operator precedence."); + +CARBON_DIAGNOSTIC(ExpectedSemiAfterExpression, Error, + "Expected `;` after expression."); + +CARBON_DIAGNOSTIC(ExpectedDeclarationName, Error, + "`{0}` introducer should be followed by a name.", TokenKind); +CARBON_DIAGNOSTIC(ExpectedDeclarationSemiOrDefinition, Error, + "`{0}` should either end with a `;` for a declaration or " + "have a `{{ ... }` block for a definition.", + TokenKind); + } // namespace Carbon -#endif // CARBON_TOOLCHAIN_PARSER_PARSER_H_ +#endif // CARBON_TOOLCHAIN_PARSER_PARSER_CONTEXT_H_ diff --git a/toolchain/parser/parser_handle_brace_expression.cpp b/toolchain/parser/parser_handle_brace_expression.cpp new file mode 100644 index 000000000000..f3761219ec08 --- /dev/null +++ b/toolchain/parser/parser_handle_brace_expression.cpp @@ -0,0 +1,229 @@ +// 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 { + +auto ParserHandleBraceExpression(ParserContext& context) -> void { + auto state = context.PopState(); + + state.state = ParserState::BraceExpressionFinishAsUnknown; + context.PushState(state); + + CARBON_CHECK(context.ConsumeAndAddLeafNodeIf( + TokenKind::OpenCurlyBrace, + ParseNodeKind::StructLiteralOrStructTypeLiteralStart)); + if (!context.PositionIs(TokenKind::CloseCurlyBrace)) { + context.PushState(ParserState::BraceExpressionParameterAsUnknown); + } +} + +// Prints a diagnostic for brace expression syntax errors. +static auto ParserHandleBraceExpressionParameterError( + ParserContext& context, ParserContext::StateStackEntry state, + ParserState param_finish_state) -> void { + bool is_type = + param_finish_state == ParserState::BraceExpressionParameterFinishAsType; + bool is_value = + param_finish_state == ParserState::BraceExpressionParameterFinishAsValue; + bool is_unknown = param_finish_state == + ParserState::BraceExpressionParameterFinishAsUnknown; + CARBON_CHECK(is_type || is_value || is_unknown); + CARBON_DIAGNOSTIC(ExpectedStructLiteralField, Error, "Expected {0}{1}{2}.", + llvm::StringRef, llvm::StringRef, llvm::StringRef); + context.emitter().Emit(*context.position(), ExpectedStructLiteralField, + (is_type || is_unknown) ? "`.field: field_type`" : "", + is_unknown ? " or " : "", + (is_value || is_unknown) ? "`.field = value`" : ""); + + state.state = param_finish_state; + state.has_error = true; + context.PushState(state); +} + +// Handles BraceExpressionParameterAs(Type|Value|Unknown). +static auto ParserHandleBraceExpressionParameter( + ParserContext& context, ParserState after_designator_state, + ParserState param_finish_state) -> void { + auto state = context.PopState(); + + if (!context.PositionIs(TokenKind::Period)) { + ParserHandleBraceExpressionParameterError(context, state, + param_finish_state); + return; + } + + state.state = after_designator_state; + context.PushState(state); + context.PushState(ParserState::DesignatorAsStruct); +} + +auto ParserHandleBraceExpressionParameterAsType(ParserContext& context) + -> void { + ParserHandleBraceExpressionParameter( + context, ParserState::BraceExpressionParameterAfterDesignatorAsType, + ParserState::BraceExpressionParameterFinishAsType); +} + +auto ParserHandleBraceExpressionParameterAsValue(ParserContext& context) + -> void { + ParserHandleBraceExpressionParameter( + context, ParserState::BraceExpressionParameterAfterDesignatorAsValue, + ParserState::BraceExpressionParameterFinishAsValue); +} + +auto ParserHandleBraceExpressionParameterAsUnknown(ParserContext& context) + -> void { + ParserHandleBraceExpressionParameter( + context, ParserState::BraceExpressionParameterAfterDesignatorAsUnknown, + ParserState::BraceExpressionParameterFinishAsUnknown); +} + +// Handles BraceExpressionParameterAfterDesignatorAs(Type|Value|Unknown). +static auto ParserHandleBraceExpressionParameterAfterDesignator( + ParserContext& context, ParserState param_finish_state) -> void { + auto state = context.PopState(); + + if (state.has_error) { + auto recovery_pos = context.FindNextOf( + {TokenKind::Equal, TokenKind::Colon, TokenKind::Comma}); + if (!recovery_pos || + context.tokens().GetKind(*recovery_pos) == TokenKind::Comma) { + state.state = param_finish_state; + context.PushState(state); + return; + } + context.SkipTo(*recovery_pos); + } + + // Work out the kind of this element. + bool is_type; + if (context.PositionIs(TokenKind::Colon)) { + is_type = true; + } else if (context.PositionIs(TokenKind::Equal)) { + is_type = false; + } else { + ParserHandleBraceExpressionParameterError(context, state, + param_finish_state); + return; + } + + // If we're changing from unknown, update the related finish states. + if (param_finish_state == + ParserState::BraceExpressionParameterFinishAsUnknown) { + auto finish_state = context.PopState(); + CARBON_CHECK(finish_state.state == + ParserState::BraceExpressionFinishAsUnknown); + if (is_type) { + finish_state.state = ParserState::BraceExpressionFinishAsType; + param_finish_state = ParserState::BraceExpressionParameterFinishAsType; + } else { + finish_state.state = ParserState::BraceExpressionFinishAsValue; + param_finish_state = ParserState::BraceExpressionParameterFinishAsValue; + } + context.PushState(finish_state); + } + + auto want_param_finish_state = + is_type ? ParserState::BraceExpressionParameterFinishAsType + : ParserState::BraceExpressionParameterFinishAsValue; + if (param_finish_state != want_param_finish_state) { + ParserHandleBraceExpressionParameterError(context, state, + param_finish_state); + return; + } + + // Struct type fields and value fields use the same grammar except + // that one has a `:` separator and the other has an `=` separator. + state.state = param_finish_state; + state.token = context.Consume(); + context.PushState(state); + context.PushState(ParserState::Expression); +} + +auto ParserHandleBraceExpressionParameterAfterDesignatorAsType( + ParserContext& context) -> void { + ParserHandleBraceExpressionParameterAfterDesignator( + context, ParserState::BraceExpressionParameterFinishAsType); +} + +auto ParserHandleBraceExpressionParameterAfterDesignatorAsValue( + ParserContext& context) -> void { + ParserHandleBraceExpressionParameterAfterDesignator( + context, ParserState::BraceExpressionParameterFinishAsValue); +} + +auto ParserHandleBraceExpressionParameterAfterDesignatorAsUnknown( + ParserContext& context) -> void { + ParserHandleBraceExpressionParameterAfterDesignator( + context, ParserState::BraceExpressionParameterFinishAsUnknown); +} + +// Handles BraceExpressionParameterFinishAs(Type|Value|Unknown). +static auto ParserHandleBraceExpressionParameterFinish(ParserContext& context, + ParseNodeKind node_kind, + ParserState param_state) + -> void { + auto state = context.PopState(); + + if (state.has_error) { + context.AddLeafNode(ParseNodeKind::StructFieldUnknown, state.token, + /*has_error=*/true); + } else { + context.AddNode(node_kind, state.token, state.subtree_start, + /*has_error=*/false); + } + + if (context.ConsumeListToken(ParseNodeKind::StructComma, + TokenKind::CloseCurlyBrace, state.has_error) == + ParserContext::ListTokenKind::Comma) { + context.PushState(param_state); + } +} + +auto ParserHandleBraceExpressionParameterFinishAsType(ParserContext& context) + -> void { + ParserHandleBraceExpressionParameterFinish( + context, ParseNodeKind::StructFieldType, + ParserState::BraceExpressionParameterAsType); +} + +auto ParserHandleBraceExpressionParameterFinishAsValue(ParserContext& context) + -> void { + ParserHandleBraceExpressionParameterFinish( + context, ParseNodeKind::StructFieldValue, + ParserState::BraceExpressionParameterAsValue); +} + +auto ParserHandleBraceExpressionParameterFinishAsUnknown(ParserContext& context) + -> void { + ParserHandleBraceExpressionParameterFinish( + context, ParseNodeKind::StructFieldUnknown, + ParserState::BraceExpressionParameterAsUnknown); +} + +// Handles BraceExpressionFinishAs(Type|Value|Unknown). +static auto ParserHandleBraceExpressionFinish(ParserContext& context, + ParseNodeKind node_kind) -> void { + auto state = context.PopState(); + + context.AddNode(node_kind, context.Consume(), state.subtree_start, + state.has_error); +} + +auto ParserHandleBraceExpressionFinishAsType(ParserContext& context) -> void { + ParserHandleBraceExpressionFinish(context, ParseNodeKind::StructTypeLiteral); +} + +auto ParserHandleBraceExpressionFinishAsValue(ParserContext& context) -> void { + ParserHandleBraceExpressionFinish(context, ParseNodeKind::StructLiteral); +} + +auto ParserHandleBraceExpressionFinishAsUnknown(ParserContext& context) + -> void { + ParserHandleBraceExpressionFinish(context, ParseNodeKind::StructLiteral); +} + +} // namespace Carbon diff --git a/toolchain/parser/parser_handle_call_expression.cpp b/toolchain/parser/parser_handle_call_expression.cpp new file mode 100644 index 000000000000..976c85c31c6e --- /dev/null +++ b/toolchain/parser/parser_handle_call_expression.cpp @@ -0,0 +1,45 @@ +// 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 { + +auto ParserHandleCallExpression(ParserContext& context) -> void { + auto state = context.PopState(); + + state.state = ParserState::CallExpressionFinish; + context.PushState(state); + + context.AddNode(ParseNodeKind::CallExpressionStart, context.Consume(), + state.subtree_start, state.has_error); + if (!context.PositionIs(TokenKind::CloseParen)) { + context.PushState(ParserState::CallExpressionParameterFinish); + context.PushState(ParserState::Expression); + } +} + +auto ParserHandleCallExpressionParameterFinish(ParserContext& context) -> void { + auto state = context.PopState(); + + if (state.has_error) { + context.ReturnErrorOnState(); + } + + if (context.ConsumeListToken(ParseNodeKind::CallExpressionComma, + TokenKind::CloseParen, state.has_error) == + ParserContext::ListTokenKind::Comma) { + context.PushState(ParserState::CallExpressionParameterFinish); + context.PushState(ParserState::Expression); + } +} + +auto ParserHandleCallExpressionFinish(ParserContext& context) -> void { + auto state = context.PopState(); + + context.AddNode(ParseNodeKind::CallExpression, context.Consume(), + state.subtree_start, state.has_error); +} + +} // namespace Carbon diff --git a/toolchain/parser/parser_handle_code_block.cpp b/toolchain/parser/parser_handle_code_block.cpp new file mode 100644 index 000000000000..df48807d00c1 --- /dev/null +++ b/toolchain/parser/parser_handle_code_block.cpp @@ -0,0 +1,41 @@ +// 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 { + +auto ParserHandleCodeBlock(ParserContext& context) -> void { + context.PopAndDiscardState(); + + context.PushState(ParserState::CodeBlockFinish); + if (context.ConsumeAndAddLeafNodeIf(TokenKind::OpenCurlyBrace, + ParseNodeKind::CodeBlockStart)) { + context.PushState(ParserState::StatementScopeLoop); + } else { + context.AddLeafNode(ParseNodeKind::CodeBlockStart, *context.position(), + /*has_error=*/true); + + // Recover by parsing a single statement. + CARBON_DIAGNOSTIC(ExpectedCodeBlock, Error, "Expected braced code block."); + context.emitter().Emit(*context.position(), ExpectedCodeBlock); + + context.PushState(ParserState::Statement); + } +} + +auto ParserHandleCodeBlockFinish(ParserContext& context) -> void { + auto state = context.PopState(); + + // If the block started with an open curly, this is a close curly. + if (context.tokens().GetKind(state.token) == TokenKind::OpenCurlyBrace) { + context.AddNode(ParseNodeKind::CodeBlock, context.Consume(), + state.subtree_start, state.has_error); + } else { + context.AddNode(ParseNodeKind::CodeBlock, state.token, state.subtree_start, + /*has_error=*/true); + } +} + +} // namespace Carbon diff --git a/toolchain/parser/parser_handle_declaration_name_and_params.cpp b/toolchain/parser/parser_handle_declaration_name_and_params.cpp new file mode 100644 index 000000000000..c75929b79709 --- /dev/null +++ b/toolchain/parser/parser_handle_declaration_name_and_params.cpp @@ -0,0 +1,61 @@ +// 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 { + +// Handles DeclarationNameAndParamsAs(Optional|Required). +static auto ParserHandleDeclarationNameAndParams(ParserContext& context, + bool params_required) -> void { + auto state = context.PopState(); + + if (!context.ConsumeAndAddLeafNodeIf(TokenKind::Identifier, + ParseNodeKind::DeclaredName)) { + context.emitter().Emit(*context.position(), ExpectedDeclarationName, + context.tokens().GetKind(state.token)); + context.ReturnErrorOnState(); + return; + } + + if (context.PositionIs(TokenKind::OpenSquareBracket)) { + context.PushState(ParserState::DeclarationNameAndParamsAfterDeduced); + context.PushState(ParserState::ParameterListAsDeduced); + } else if (context.PositionIs(TokenKind::OpenParen)) { + context.PushState(ParserState::ParameterListAsRegular); + } else if (params_required) { + CARBON_DIAGNOSTIC(ParametersRequiredByIntroducer, Error, + "`{0}` requires a `(` for parameters.", TokenKind); + context.emitter().Emit(*context.position(), ParametersRequiredByIntroducer, + context.tokens().GetKind(state.token)); + context.ReturnErrorOnState(); + } +} + +auto ParserHandleDeclarationNameAndParamsAsOptional(ParserContext& context) + -> void { + ParserHandleDeclarationNameAndParams(context, /*params_required=*/false); +} + +auto ParserHandleDeclarationNameAndParamsAsRequired(ParserContext& context) + -> void { + ParserHandleDeclarationNameAndParams(context, /*params_required=*/true); +} + +auto ParserHandleDeclarationNameAndParamsAfterDeduced(ParserContext& context) + -> void { + context.PopAndDiscardState(); + + if (context.PositionIs(TokenKind::OpenParen)) { + context.PushState(ParserState::ParameterListAsRegular); + } else { + CARBON_DIAGNOSTIC( + ParametersRequiredByDeduced, Error, + "A `(` for parameters is required after deduced parameters."); + context.emitter().Emit(*context.position(), ParametersRequiredByDeduced); + context.ReturnErrorOnState(); + } +} + +} // namespace Carbon diff --git a/toolchain/parser/parser_handle_declaration_scope_loop.cpp b/toolchain/parser/parser_handle_declaration_scope_loop.cpp new file mode 100644 index 000000000000..23a91de938b8 --- /dev/null +++ b/toolchain/parser/parser_handle_declaration_scope_loop.cpp @@ -0,0 +1,64 @@ +// 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 { + +// Handles an unrecognized declaration, adding an error node. +static auto ParserHandleUnrecognizedDeclaration(ParserContext& context) + -> void { + CARBON_DIAGNOSTIC(UnrecognizedDeclaration, Error, + "Unrecognized declaration introducer."); + context.emitter().Emit(*context.position(), UnrecognizedDeclaration); + auto cursor = *context.position(); + auto semi = context.SkipPastLikelyEnd(cursor); + // Locate the EmptyDeclaration at the semi when found, but use the + // original cursor location for an error when not. + context.AddLeafNode(ParseNodeKind::EmptyDeclaration, semi ? *semi : cursor, + /*has_error=*/true); +} + +auto ParserHandleDeclarationScopeLoop(ParserContext& context) -> void { + // This maintains the current state unless we're at the end of the scope. + + switch (context.PositionKind()) { + case TokenKind::CloseCurlyBrace: + case TokenKind::EndOfFile: { + // This is the end of the scope, so the loop state ends. + context.PopAndDiscardState(); + break; + } + case TokenKind::Class: { + context.PushState(ParserState::TypeIntroducerAsClass); + break; + } + case TokenKind::Constraint: { + context.PushState(ParserState::TypeIntroducerAsNamedConstraint); + break; + } + case TokenKind::Fn: { + context.PushState(ParserState::FunctionIntroducer); + break; + } + case TokenKind::Interface: { + context.PushState(ParserState::TypeIntroducerAsInterface); + break; + } + case TokenKind::Semi: { + context.AddLeafNode(ParseNodeKind::EmptyDeclaration, context.Consume()); + break; + } + case TokenKind::Var: { + context.PushState(ParserState::VarAsSemicolon); + break; + } + default: { + ParserHandleUnrecognizedDeclaration(context); + break; + } + } +} + +} // namespace Carbon diff --git a/toolchain/parser/parser_handle_designator.cpp b/toolchain/parser/parser_handle_designator.cpp new file mode 100644 index 000000000000..857534405fc0 --- /dev/null +++ b/toolchain/parser/parser_handle_designator.cpp @@ -0,0 +1,48 @@ +// 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 { + +// Handles DesignatorAs. +auto ParserHandleDesignator(ParserContext& context, bool as_struct) -> void { + auto state = context.PopState(); + + // `.` identifier + auto dot = context.ConsumeChecked(TokenKind::Period); + + if (!context.ConsumeAndAddLeafNodeIf(TokenKind::Identifier, + ParseNodeKind::DesignatedName)) { + CARBON_DIAGNOSTIC(ExpectedIdentifierAfterDot, Error, + "Expected identifier after `.`."); + context.emitter().Emit(*context.position(), ExpectedIdentifierAfterDot); + // If we see a keyword, assume it was intended to be the designated name. + // TODO: Should keywords be valid in designators? + if (context.PositionKind().is_keyword()) { + context.AddLeafNode(ParseNodeKind::DesignatedName, context.Consume(), + /*has_error=*/true); + } else { + context.AddLeafNode(ParseNodeKind::DesignatedName, *context.position(), + /*has_error=*/true); + // Indicate the error to the parent state so that it can avoid producing + // more errors. + context.ReturnErrorOnState(); + } + } + + context.AddNode(as_struct ? ParseNodeKind::StructFieldDesignator + : ParseNodeKind::DesignatorExpression, + dot, state.subtree_start, state.has_error); +} + +auto ParserHandleDesignatorAsExpression(ParserContext& context) -> void { + ParserHandleDesignator(context, /*as_struct=*/false); +} + +auto ParserHandleDesignatorAsStruct(ParserContext& context) -> void { + ParserHandleDesignator(context, /*as_struct=*/true); +} + +} // namespace Carbon diff --git a/toolchain/parser/parser_handle_expression.cpp b/toolchain/parser/parser_handle_expression.cpp new file mode 100644 index 000000000000..8ee4b8ccba9a --- /dev/null +++ b/toolchain/parser/parser_handle_expression.cpp @@ -0,0 +1,226 @@ +// 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 { + +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); + } + + 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::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 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) { + 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 diff --git a/toolchain/parser/parser_handle_function.cpp b/toolchain/parser/parser_handle_function.cpp new file mode 100644 index 000000000000..bbc8944fc82f --- /dev/null +++ b/toolchain/parser/parser_handle_function.cpp @@ -0,0 +1,98 @@ +// 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 { + +auto ParserHandleFunctionIntroducer(ParserContext& context) -> void { + auto state = context.PopState(); + + context.AddLeafNode(ParseNodeKind::FunctionIntroducer, context.Consume()); + + state.state = ParserState::FunctionAfterParameters; + context.PushState(state); + state.state = ParserState::DeclarationNameAndParamsAsRequired; + context.PushState(state); +} + +auto ParserHandleFunctionAfterParameters(ParserContext& context) -> void { + auto state = context.PopState(); + + // Regardless of whether there's a return type, we'll finish the signature. + state.state = ParserState::FunctionSignatureFinish; + context.PushState(state); + + // If there is a return type, parse the expression before adding the return + // type nod.e + if (context.PositionIs(TokenKind::MinusGreater)) { + context.PushState(ParserState::FunctionReturnTypeFinish); + ++context.position(); + context.PushStateForExpression(PrecedenceGroup::ForType()); + } +} + +auto ParserHandleFunctionReturnTypeFinish(ParserContext& context) -> void { + auto state = context.PopState(); + + context.AddNode(ParseNodeKind::ReturnType, state.token, state.subtree_start, + state.has_error); +} + +auto ParserHandleFunctionSignatureFinish(ParserContext& context) -> void { + auto state = context.PopState(); + + switch (context.PositionKind()) { + case TokenKind::Semi: { + context.AddNode(ParseNodeKind::FunctionDeclaration, context.Consume(), + state.subtree_start, state.has_error); + break; + } + case TokenKind::OpenCurlyBrace: { + if (auto decl_context = context.GetDeclarationContext(); + decl_context == ParserContext::DeclarationContext::Interface || + decl_context == ParserContext::DeclarationContext::NamedConstraint) { + CARBON_DIAGNOSTIC( + MethodImplNotAllowed, Error, + "Method implementations are not allowed in interfaces."); + context.emitter().Emit(*context.position(), MethodImplNotAllowed); + context.RecoverFromDeclarationError(state, + ParseNodeKind::FunctionDeclaration, + /*skip_past_likely_end=*/true); + break; + } + + context.AddNode(ParseNodeKind::FunctionDefinitionStart, context.Consume(), + state.subtree_start, state.has_error); + // Any error is recorded on the FunctionDefinitionStart. + state.has_error = false; + state.state = ParserState::FunctionDefinitionFinish; + context.PushState(state); + context.PushState(ParserState::StatementScopeLoop); + break; + } + default: { + if (!state.has_error) { + context.emitter().Emit(*context.position(), + ExpectedDeclarationSemiOrDefinition, + TokenKind::Fn); + } + // Only need to skip if we've not already found a new line. + bool skip_past_likely_end = + context.tokens().GetLine(*context.position()) == + context.tokens().GetLine(state.token); + context.RecoverFromDeclarationError( + state, ParseNodeKind::FunctionDeclaration, skip_past_likely_end); + break; + } + } +} + +auto ParserHandleFunctionDefinitionFinish(ParserContext& context) -> void { + auto state = context.PopState(); + context.AddNode(ParseNodeKind::FunctionDefinition, context.Consume(), + state.subtree_start, state.has_error); +} + +} // namespace Carbon diff --git a/toolchain/parser/parser_handle_package.cpp b/toolchain/parser/parser_handle_package.cpp new file mode 100644 index 000000000000..a1265069662b --- /dev/null +++ b/toolchain/parser/parser_handle_package.cpp @@ -0,0 +1,93 @@ +// 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 { + +auto ParserHandlePackage(ParserContext& context) -> void { + auto state = context.PopState(); + + context.AddLeafNode(ParseNodeKind::PackageIntroducer, context.Consume()); + + auto exit_on_parse_error = [&]() { + auto semi_token = context.SkipPastLikelyEnd(state.token); + return context.AddNode(ParseNodeKind::PackageDirective, + semi_token ? *semi_token : state.token, + state.subtree_start, + /*has_error=*/true); + }; + + if (!context.ConsumeAndAddLeafNodeIf(TokenKind::Identifier, + ParseNodeKind::DeclaredName)) { + CARBON_DIAGNOSTIC(ExpectedIdentifierAfterPackage, Error, + "Expected identifier after `package`."); + context.emitter().Emit(*context.position(), ExpectedIdentifierAfterPackage); + exit_on_parse_error(); + return; + } + + bool library_parsed = false; + if (auto library_token = context.ConsumeIf(TokenKind::Library)) { + auto library_start = context.tree().size(); + + if (!context.ConsumeAndAddLeafNodeIf(TokenKind::StringLiteral, + ParseNodeKind::Literal)) { + CARBON_DIAGNOSTIC( + ExpectedLibraryName, Error, + "Expected a string literal to specify the library name."); + context.emitter().Emit(*context.position(), ExpectedLibraryName); + exit_on_parse_error(); + return; + } + + context.AddNode(ParseNodeKind::PackageLibrary, *library_token, + library_start, + /*has_error=*/false); + library_parsed = true; + } + + switch (auto api_or_impl_token = + context.tokens().GetKind(*(context.position()))) { + case TokenKind::Api: { + context.AddLeafNode(ParseNodeKind::PackageApi, context.Consume()); + break; + } + case TokenKind::Impl: { + context.AddLeafNode(ParseNodeKind::PackageImpl, context.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."); + context.emitter().Emit(*context.position(), MissingLibraryKeyword); + } else { + CARBON_DIAGNOSTIC(ExpectedApiOrImpl, Error, + "Expected a `api` or `impl`."); + context.emitter().Emit(*context.position(), ExpectedApiOrImpl); + } + exit_on_parse_error(); + return; + } + } + + if (!context.PositionIs(TokenKind::Semi)) { + CARBON_DIAGNOSTIC(ExpectedSemiToEndPackageDirective, Error, + "Expected `;` to end package directive."); + context.emitter().Emit(*context.position(), + ExpectedSemiToEndPackageDirective); + exit_on_parse_error(); + return; + } + + context.AddNode(ParseNodeKind::PackageDirective, context.Consume(), + state.subtree_start, + /*has_error=*/false); +} + +} // namespace Carbon diff --git a/toolchain/parser/parser_handle_parameter.cpp b/toolchain/parser/parser_handle_parameter.cpp new file mode 100644 index 000000000000..3eb1e6c7190e --- /dev/null +++ b/toolchain/parser/parser_handle_parameter.cpp @@ -0,0 +1,108 @@ +// 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 { + +// Handles ParameterAs(Deduced|Regular). +static auto ParserHandleParameter(ParserContext& context, + ParserState pattern_state, + ParserState finish_state) -> void { + context.PopAndDiscardState(); + + context.PushState(finish_state); + context.PushState(pattern_state); +} + +auto ParserHandleParameterAsDeduced(ParserContext& context) -> void { + ParserHandleParameter(context, ParserState::PatternAsDeducedParameter, + ParserState::ParameterFinishAsDeduced); +} + +auto ParserHandleParameterAsRegular(ParserContext& context) -> void { + ParserHandleParameter(context, ParserState::PatternAsParameter, + ParserState::ParameterFinishAsRegular); +} + +// Handles ParameterFinishAs(Deduced|Regular). +static auto ParserHandleParameterFinish(ParserContext& context, + TokenKind close_token, + ParserState param_state) -> void { + auto state = context.PopState(); + + if (state.has_error) { + context.ReturnErrorOnState(); + } + + if (context.ConsumeListToken(ParseNodeKind::ParameterListComma, close_token, + state.has_error) == + ParserContext::ListTokenKind::Comma) { + context.PushState(param_state); + } +} + +auto ParserHandleParameterFinishAsDeduced(ParserContext& context) -> void { + ParserHandleParameterFinish(context, TokenKind::CloseSquareBracket, + ParserState::ParameterAsDeduced); +} + +auto ParserHandleParameterFinishAsRegular(ParserContext& context) -> void { + ParserHandleParameterFinish(context, TokenKind::CloseParen, + ParserState::ParameterAsRegular); +} + +// Handles ParameterListAs(Deduced|Regular). +static auto ParserHandleParameterList(ParserContext& context, + ParseNodeKind parse_node_kind, + TokenKind open_token_kind, + TokenKind close_token_kind, + ParserState param_state, + ParserState finish_state) -> void { + context.PopAndDiscardState(); + + context.PushState(finish_state); + context.AddLeafNode(parse_node_kind, context.ConsumeChecked(open_token_kind)); + + if (!context.PositionIs(close_token_kind)) { + context.PushState(param_state); + } +} + +auto ParserHandleParameterListAsDeduced(ParserContext& context) -> void { + ParserHandleParameterList(context, ParseNodeKind::DeducedParameterListStart, + TokenKind::OpenSquareBracket, + TokenKind::CloseSquareBracket, + ParserState::ParameterAsDeduced, + ParserState::ParameterListFinishAsDeduced); +} + +auto ParserHandleParameterListAsRegular(ParserContext& context) -> void { + ParserHandleParameterList(context, ParseNodeKind::ParameterListStart, + TokenKind::OpenParen, TokenKind::CloseParen, + ParserState::ParameterAsRegular, + ParserState::ParameterListFinishAsRegular); +} + +// Handles ParameterListFinishAs(Deduced|Regular). +static auto ParserHandleParameterListFinish(ParserContext& context, + ParseNodeKind parse_node_kind, + TokenKind token_kind) -> void { + auto state = context.PopState(); + + context.AddNode(parse_node_kind, context.ConsumeChecked(token_kind), + state.subtree_start, state.has_error); +} + +auto ParserHandleParameterListFinishAsDeduced(ParserContext& context) -> void { + ParserHandleParameterListFinish(context, ParseNodeKind::DeducedParameterList, + TokenKind::CloseSquareBracket); +} + +auto ParserHandleParameterListFinishAsRegular(ParserContext& context) -> void { + ParserHandleParameterListFinish(context, ParseNodeKind::ParameterList, + TokenKind::CloseParen); +} + +} // namespace Carbon diff --git a/toolchain/parser/parser_handle_paren_condition.cpp b/toolchain/parser/parser_handle_paren_condition.cpp new file mode 100644 index 000000000000..1802174c858b --- /dev/null +++ b/toolchain/parser/parser_handle_paren_condition.cpp @@ -0,0 +1,44 @@ +// 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 { + +// Handles ParenConditionAs(If|While). +static auto ParserHandleParenCondition(ParserContext& context, + ParseNodeKind start_kind, + ParserState finish_state) -> void { + auto state = context.PopState(); + + context.ConsumeAndAddOpenParen(state.token, start_kind); + + state.state = finish_state; + context.PushState(state); + context.PushState(ParserState::Expression); +} + +auto ParserHandleParenConditionAsIf(ParserContext& context) -> void { + ParserHandleParenCondition(context, ParseNodeKind::IfConditionStart, + ParserState::ParenConditionFinishAsIf); +} + +auto ParserHandleParenConditionAsWhile(ParserContext& context) -> void { + ParserHandleParenCondition(context, ParseNodeKind::WhileConditionStart, + ParserState::ParenConditionFinishAsWhile); +} + +auto ParserHandleParenConditionFinishAsIf(ParserContext& context) -> void { + auto state = context.PopState(); + + context.ConsumeAndAddCloseParen(state, ParseNodeKind::IfCondition); +} + +auto ParserHandleParenConditionFinishAsWhile(ParserContext& context) -> void { + auto state = context.PopState(); + + context.ConsumeAndAddCloseParen(state, ParseNodeKind::WhileCondition); +} + +} // namespace Carbon diff --git a/toolchain/parser/parser_handle_paren_expression.cpp b/toolchain/parser/parser_handle_paren_expression.cpp new file mode 100644 index 000000000000..d6b21732c888 --- /dev/null +++ b/toolchain/parser/parser_handle_paren_expression.cpp @@ -0,0 +1,83 @@ +// 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 { + +auto ParserHandleParenExpression(ParserContext& context) -> void { + auto state = context.PopState(); + + // Advance past the open paren. + context.AddLeafNode(ParseNodeKind::ParenExpressionOrTupleLiteralStart, + context.ConsumeChecked(TokenKind::OpenParen)); + + if (context.PositionIs(TokenKind::CloseParen)) { + state.state = ParserState::ParenExpressionFinishAsTuple; + context.PushState(state); + } else { + state.state = ParserState::ParenExpressionFinishAsNormal; + context.PushState(state); + context.PushState(ParserState::ParenExpressionParameterFinishAsUnknown); + context.PushState(ParserState::Expression); + } +} + +// Handles ParenExpressionParameterFinishAs(Unknown|Tuple). +static auto ParserHandleParenExpressionParameterFinish(ParserContext& context, + bool as_tuple) -> void { + auto state = context.PopState(); + + auto list_token_kind = context.ConsumeListToken( + ParseNodeKind::TupleLiteralComma, TokenKind::CloseParen, state.has_error); + if (list_token_kind == ParserContext::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 = context.PopState(); + CARBON_CHECK(finish_state.state == + ParserState::ParenExpressionFinishAsNormal) + << "Unexpected parent state, found: " << finish_state.state; + finish_state.state = ParserState::ParenExpressionFinishAsTuple; + context.PushState(finish_state); + } + + // On a comma, push another expression handler. + if (list_token_kind == ParserContext::ListTokenKind::Comma) { + context.PushState(state); + context.PushState(ParserState::Expression); + } +} + +auto ParserHandleParenExpressionParameterFinishAsUnknown(ParserContext& context) + -> void { + ParserHandleParenExpressionParameterFinish(context, /*as_tuple=*/false); +} + +auto ParserHandleParenExpressionParameterFinishAsTuple(ParserContext& context) + -> void { + ParserHandleParenExpressionParameterFinish(context, /*as_tuple=*/true); +} + +auto ParserHandleParenExpressionFinishAsNormal(ParserContext& context) -> void { + auto state = context.PopState(); + + context.AddNode(ParseNodeKind::ParenExpression, context.Consume(), + state.subtree_start, state.has_error); +} + +auto ParserHandleParenExpressionFinishAsTuple(ParserContext& context) -> void { + auto state = context.PopState(); + + context.AddNode(ParseNodeKind::TupleLiteral, context.Consume(), + state.subtree_start, state.has_error); +} + +} // namespace Carbon diff --git a/toolchain/parser/parser_handle_pattern.cpp b/toolchain/parser/parser_handle_pattern.cpp new file mode 100644 index 000000000000..2252c9f487cf --- /dev/null +++ b/toolchain/parser/parser_handle_pattern.cpp @@ -0,0 +1,141 @@ +// 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 { + +// Handles PatternAs(DeducedParameter|FunctionParameter|Variable). +static auto ParserHandlePattern(ParserContext& context, + ParserContext::PatternKind pattern_kind) + -> void { + auto state = context.PopState(); + + // Parameters may have keywords prefixing the pattern. They become the parent + // for the full PatternBinding. + if (pattern_kind != ParserContext::PatternKind::Variable) { + context.ConsumeIfPatternKeyword( + TokenKind::Template, ParserState::PatternTemplate, state.subtree_start); + context.ConsumeIfPatternKeyword( + TokenKind::Addr, ParserState::PatternAddress, state.subtree_start); + } + + // ParserHandle an invalid pattern introducer for parameters and variables. + auto on_error = [&]() { + switch (pattern_kind) { + case ParserContext::PatternKind::DeducedParameter: + case ParserContext::PatternKind::Parameter: { + CARBON_DIAGNOSTIC(ExpectedParameterName, Error, + "Expected parameter declaration."); + context.emitter().Emit(*context.position(), ExpectedParameterName); + break; + } + case ParserContext::PatternKind::Variable: { + CARBON_DIAGNOSTIC(ExpectedVariableName, Error, + "Expected pattern in `var` declaration."); + context.emitter().Emit(*context.position(), ExpectedVariableName); + break; + } + } + // Add a placeholder for the type. + context.AddLeafNode(ParseNodeKind::InvalidParse, *context.position(), + /*has_error=*/true); + state.state = ParserState::PatternFinishAsRegular; + state.has_error = true; + context.PushState(state); + }; + + // The first item should be an identifier or, for deduced parameters, `self`. + bool has_name = false; + if (auto identifier = context.ConsumeIf(TokenKind::Identifier)) { + context.AddLeafNode(ParseNodeKind::DeclaredName, *identifier); + has_name = true; + } else if (pattern_kind == ParserContext::PatternKind::DeducedParameter) { + if (auto self = context.ConsumeIf(TokenKind::SelfValueIdentifier)) { + context.AddLeafNode(ParseNodeKind::SelfValueIdentifier, *self); + has_name = true; + } + } + if (!has_name) { + // Add a placeholder for the name. + context.AddLeafNode(ParseNodeKind::DeclaredName, *context.position(), + /*has_error=*/true); + on_error(); + return; + } + + if (auto kind = context.PositionKind(); + kind == TokenKind::Colon || kind == TokenKind::ColonExclaim) { + state.state = kind == TokenKind::Colon + ? ParserState::PatternFinishAsRegular + : ParserState::PatternFinishAsGeneric; + // Use the `:` or `:!` for the root node. + state.token = context.Consume(); + context.PushState(state); + context.PushStateForExpression(PrecedenceGroup::ForType()); + } else { + on_error(); + return; + } +} + +auto ParserHandlePatternAsDeducedParameter(ParserContext& context) -> void { + ParserHandlePattern(context, ParserContext::PatternKind::DeducedParameter); +} + +auto ParserHandlePatternAsParameter(ParserContext& context) -> void { + ParserHandlePattern(context, ParserContext::PatternKind::Parameter); +} + +auto ParserHandlePatternAsVariable(ParserContext& context) -> void { + ParserHandlePattern(context, ParserContext::PatternKind::Variable); +} + +// Handles PatternFinishAs(Generic|Regular). +static auto ParserHandlePatternFinish(ParserContext& context, + ParseNodeKind node_kind) -> void { + auto state = context.PopState(); + + context.AddNode(node_kind, state.token, state.subtree_start, state.has_error); + + // Propagate errors to the parent state so that they can take different + // actions on invalid patterns. + if (state.has_error) { + context.ReturnErrorOnState(); + } +} + +auto ParserHandlePatternFinishAsGeneric(ParserContext& context) -> void { + ParserHandlePatternFinish(context, ParseNodeKind::GenericPatternBinding); +} + +auto ParserHandlePatternFinishAsRegular(ParserContext& context) -> void { + ParserHandlePatternFinish(context, ParseNodeKind::PatternBinding); +} + +auto ParserHandlePatternAddress(ParserContext& context) -> void { + auto state = context.PopState(); + + context.AddNode(ParseNodeKind::Address, state.token, state.subtree_start, + state.has_error); + + // If an error was encountered, propagate it while adding a node. + if (state.has_error) { + context.ReturnErrorOnState(); + } +} + +auto ParserHandlePatternTemplate(ParserContext& context) -> void { + auto state = context.PopState(); + + context.AddNode(ParseNodeKind::Template, state.token, state.subtree_start, + state.has_error); + + // If an error was encountered, propagate it while adding a node. + if (state.has_error) { + context.ReturnErrorOnState(); + } +} + +} // namespace Carbon diff --git a/toolchain/parser/parser_handle_statement.cpp b/toolchain/parser/parser_handle_statement.cpp new file mode 100644 index 000000000000..4063614f9401 --- /dev/null +++ b/toolchain/parser/parser_handle_statement.cpp @@ -0,0 +1,224 @@ +// 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 { + +auto ParserHandleStatement(ParserContext& context) -> void { + context.PopAndDiscardState(); + + switch (context.PositionKind()) { + case TokenKind::Break: { + context.PushState(ParserState::StatementBreakFinish); + context.AddLeafNode(ParseNodeKind::BreakStatementStart, + context.Consume()); + break; + } + case TokenKind::Continue: { + context.PushState(ParserState::StatementContinueFinish); + context.AddLeafNode(ParseNodeKind::ContinueStatementStart, + context.Consume()); + break; + } + case TokenKind::For: { + context.PushState(ParserState::StatementForFinish); + context.PushState(ParserState::StatementForHeader); + ++context.position(); + break; + } + case TokenKind::If: { + context.PushState(ParserState::StatementIf); + break; + } + case TokenKind::Return: { + context.PushState(ParserState::StatementReturn); + break; + } + case TokenKind::Var: { + context.PushState(ParserState::VarAsSemicolon); + break; + } + case TokenKind::While: { + context.PushState(ParserState::StatementWhile); + break; + } + default: { + context.PushState(ParserState::ExpressionStatementFinish); + context.PushState(ParserState::Expression); + break; + } + } +} + +// Handles the `;` after a keyword statement. +static auto ParserHandleStatementKeywordFinish(ParserContext& context, + ParseNodeKind node_kind) + -> void { + auto state = context.PopState(); + + auto semi = context.ConsumeIf(TokenKind::Semi); + if (!semi) { + CARBON_DIAGNOSTIC(ExpectedSemiAfter, Error, "Expected `;` after `{0}`.", + TokenKind); + context.emitter().Emit(*context.position(), ExpectedSemiAfter, + context.tokens().GetKind(state.token)); + state.has_error = true; + // Recover to the next semicolon if possible, otherwise indicate the + // keyword for the error. + semi = context.SkipPastLikelyEnd(state.token); + if (!semi) { + semi = state.token; + } + } + context.AddNode(node_kind, *semi, state.subtree_start, state.has_error); +} + +auto ParserHandleStatementBreakFinish(ParserContext& context) -> void { + ParserHandleStatementKeywordFinish(context, ParseNodeKind::BreakStatement); +} + +auto ParserHandleStatementContinueFinish(ParserContext& context) -> void { + ParserHandleStatementKeywordFinish(context, ParseNodeKind::ContinueStatement); +} + +auto ParserHandleStatementForHeader(ParserContext& context) -> void { + auto state = context.PopState(); + + context.ConsumeAndAddOpenParen(state.token, ParseNodeKind::ForHeaderStart); + + state.state = ParserState::StatementForHeaderIn; + + if (context.PositionIs(TokenKind::Var)) { + context.PushState(state); + context.PushState(ParserState::VarAsFor); + } else { + CARBON_DIAGNOSTIC(ExpectedVariableDeclaration, Error, + "Expected `var` declaration."); + context.emitter().Emit(*context.position(), ExpectedVariableDeclaration); + + if (auto next_in = context.FindNextOf({TokenKind::In})) { + context.SkipTo(*next_in); + ++context.position(); + } + state.has_error = true; + context.PushState(state); + } +} + +auto ParserHandleStatementForHeaderIn(ParserContext& context) -> void { + auto state = context.PopState(); + + state.state = ParserState::StatementForHeaderFinish; + context.PushState(state); + context.PushState(ParserState::Expression); +} + +auto ParserHandleStatementForHeaderFinish(ParserContext& context) -> void { + auto state = context.PopState(); + + context.ConsumeAndAddCloseParen(state, ParseNodeKind::ForHeader); + + context.PushState(ParserState::CodeBlock); +} + +auto ParserHandleStatementForFinish(ParserContext& context) -> void { + auto state = context.PopState(); + + context.AddNode(ParseNodeKind::ForStatement, state.token, state.subtree_start, + state.has_error); +} + +auto ParserHandleStatementIf(ParserContext& context) -> void { + context.PopAndDiscardState(); + + context.PushState(ParserState::StatementIfConditionFinish); + context.PushState(ParserState::ParenConditionAsIf); + ++context.position(); +} + +auto ParserHandleStatementIfConditionFinish(ParserContext& context) -> void { + auto state = context.PopState(); + + state.state = ParserState::StatementIfThenBlockFinish; + context.PushState(state); + context.PushState(ParserState::CodeBlock); +} + +auto ParserHandleStatementIfThenBlockFinish(ParserContext& context) -> void { + auto state = context.PopState(); + + if (context.ConsumeAndAddLeafNodeIf(TokenKind::Else, + ParseNodeKind::IfStatementElse)) { + state.state = ParserState::StatementIfElseBlockFinish; + context.PushState(state); + // `else if` is permitted as a special case. + context.PushState(context.PositionIs(TokenKind::If) + ? ParserState::StatementIf + : ParserState::CodeBlock); + } else { + context.AddNode(ParseNodeKind::IfStatement, state.token, + state.subtree_start, state.has_error); + } +} + +auto ParserHandleStatementIfElseBlockFinish(ParserContext& context) -> void { + auto state = context.PopState(); + context.AddNode(ParseNodeKind::IfStatement, state.token, state.subtree_start, + state.has_error); +} + +auto ParserHandleStatementReturn(ParserContext& context) -> void { + auto state = context.PopState(); + state.state = ParserState::StatementReturnFinish; + context.PushState(state); + + context.AddLeafNode(ParseNodeKind::ReturnStatementStart, context.Consume()); + if (!context.PositionIs(TokenKind::Semi)) { + context.PushState(ParserState::Expression); + } +} + +auto ParserHandleStatementReturnFinish(ParserContext& context) -> void { + ParserHandleStatementKeywordFinish(context, ParseNodeKind::ReturnStatement); +} + +auto ParserHandleStatementScopeLoop(ParserContext& context) -> void { + // This maintains the current state until we're at the end of the scope. + + auto token_kind = context.PositionKind(); + if (token_kind == TokenKind::CloseCurlyBrace) { + auto state = context.PopState(); + if (state.has_error) { + context.ReturnErrorOnState(); + } + } else { + context.PushState(ParserState::Statement); + } +} + +auto ParserHandleStatementWhile(ParserContext& context) -> void { + context.PopAndDiscardState(); + + context.PushState(ParserState::StatementWhileConditionFinish); + context.PushState(ParserState::ParenConditionAsWhile); + ++context.position(); +} + +auto ParserHandleStatementWhileConditionFinish(ParserContext& context) -> void { + auto state = context.PopState(); + + state.state = ParserState::StatementWhileBlockFinish; + context.PushState(state); + context.PushState(ParserState::CodeBlock); +} + +auto ParserHandleStatementWhileBlockFinish(ParserContext& context) -> void { + auto state = context.PopState(); + + context.AddNode(ParseNodeKind::WhileStatement, state.token, + state.subtree_start, state.has_error); +} + +} // namespace Carbon diff --git a/toolchain/parser/parser_handle_type.cpp b/toolchain/parser/parser_handle_type.cpp new file mode 100644 index 000000000000..8a9f3ccb2fc7 --- /dev/null +++ b/toolchain/parser/parser_handle_type.cpp @@ -0,0 +1,121 @@ +// 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 { + +// Handles processing of a type's introducer. +static auto ParserHandleTypeIntroducer(ParserContext& context, + ParseNodeKind introducer_kind, + ParserState after_params_state) -> void { + auto state = context.PopState(); + + context.AddLeafNode(introducer_kind, context.Consume()); + + state.state = after_params_state; + context.PushState(state); + state.state = ParserState::DeclarationNameAndParamsAsOptional; + context.PushState(state); +} + +auto ParserHandleTypeIntroducerAsClass(ParserContext& context) -> void { + ParserHandleTypeIntroducer(context, ParseNodeKind::ClassIntroducer, + ParserState::TypeAfterParamsAsClass); +} + +auto ParserHandleTypeIntroducerAsInterface(ParserContext& context) -> void { + ParserHandleTypeIntroducer(context, ParseNodeKind::InterfaceIntroducer, + ParserState::TypeAfterParamsAsInterface); +} + +auto ParserHandleTypeIntroducerAsNamedConstraint(ParserContext& context) + -> void { + ParserHandleTypeIntroducer(context, ParseNodeKind::NamedConstraintIntroducer, + ParserState::TypeAfterParamsAsNamedConstraint); +} + +// Handles processing after params, deciding whether it's a declaration or +// definition. +static auto ParserHandleTypeAfterParams(ParserContext& context, + ParseNodeKind declaration_kind, + ParseNodeKind definition_start_kind, + ParserState definition_finish_state) + -> void { + auto state = context.PopState(); + + if (state.has_error) { + context.RecoverFromDeclarationError(state, declaration_kind, + /*skip_past_likely_end=*/true); + return; + } + + if (auto semi = context.ConsumeIf(TokenKind::Semi)) { + context.AddNode(declaration_kind, *semi, state.subtree_start, + state.has_error); + return; + } + + if (!context.PositionIs(TokenKind::OpenCurlyBrace)) { + context.emitter().Emit(*context.position(), + ExpectedDeclarationSemiOrDefinition, + context.tokens().GetKind(state.token)); + context.RecoverFromDeclarationError(state, declaration_kind, + /*skip_past_likely_end=*/true); + return; + } + + state.state = definition_finish_state; + context.PushState(state); + context.PushState(ParserState::DeclarationScopeLoop); + context.AddNode(definition_start_kind, context.Consume(), state.subtree_start, + state.has_error); +} + +auto ParserHandleTypeAfterParamsAsClass(ParserContext& context) -> void { + ParserHandleTypeAfterParams(context, ParseNodeKind::ClassDeclaration, + ParseNodeKind::ClassDefinitionStart, + ParserState::TypeDefinitionFinishAsClass); +} + +auto ParserHandleTypeAfterParamsAsInterface(ParserContext& context) -> void { + ParserHandleTypeAfterParams(context, ParseNodeKind::InterfaceDeclaration, + ParseNodeKind::InterfaceDefinitionStart, + ParserState::TypeDefinitionFinishAsInterface); +} + +auto ParserHandleTypeAfterParamsAsNamedConstraint(ParserContext& context) + -> void { + ParserHandleTypeAfterParams( + context, ParseNodeKind::NamedConstraintDeclaration, + ParseNodeKind::NamedConstraintDefinitionStart, + ParserState::TypeDefinitionFinishAsNamedConstraint); +} + +// Handles parsing after the declaration scope of a type. +static auto ParserHandleTypeDefinitionFinish(ParserContext& context, + ParseNodeKind definition_kind) + -> void { + auto state = context.PopState(); + + context.AddNode(definition_kind, context.Consume(), state.subtree_start, + state.has_error); +} + +auto ParserHandleTypeDefinitionFinishAsClass(ParserContext& context) -> void { + ParserHandleTypeDefinitionFinish(context, ParseNodeKind::ClassDefinition); +} + +auto ParserHandleTypeDefinitionFinishAsInterface(ParserContext& context) + -> void { + ParserHandleTypeDefinitionFinish(context, ParseNodeKind::InterfaceDefinition); +} + +auto ParserHandleTypeDefinitionFinishAsNamedConstraint(ParserContext& context) + -> void { + ParserHandleTypeDefinitionFinish(context, + ParseNodeKind::NamedConstraintDefinition); +} + +} // namespace Carbon diff --git a/toolchain/parser/parser_handle_var.cpp b/toolchain/parser/parser_handle_var.cpp new file mode 100644 index 000000000000..c1a0fc4fd88c --- /dev/null +++ b/toolchain/parser/parser_handle_var.cpp @@ -0,0 +1,88 @@ +// 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 { + +// Handles VarAs(Semicolon|For). +static auto ParserHandleVar(ParserContext& context, ParserState finish_state) + -> void { + context.PopAndDiscardState(); + + // These will start at the `var`. + context.PushState(finish_state); + context.PushState(ParserState::VarAfterPattern); + + context.AddLeafNode(ParseNodeKind::VariableIntroducer, context.Consume()); + + // This will start at the pattern. + context.PushState(ParserState::PatternAsVariable); +} + +auto ParserHandleVarAsSemicolon(ParserContext& context) -> void { + ParserHandleVar(context, ParserState::VarFinishAsSemicolon); +} + +auto ParserHandleVarAsFor(ParserContext& context) -> void { + ParserHandleVar(context, ParserState::VarFinishAsFor); +} + +auto ParserHandleVarAfterPattern(ParserContext& context) -> void { + auto state = context.PopState(); + + if (state.has_error) { + if (auto after_pattern = + context.FindNextOf({TokenKind::Equal, TokenKind::Semi})) { + context.SkipTo(*after_pattern); + } + } + + if (auto equals = context.ConsumeIf(TokenKind::Equal)) { + context.AddLeafNode(ParseNodeKind::VariableInitializer, *equals); + context.PushState(ParserState::Expression); + } +} + +auto ParserHandleVarFinishAsSemicolon(ParserContext& context) -> void { + auto state = context.PopState(); + + auto end_token = state.token; + if (context.PositionIs(TokenKind::Semi)) { + end_token = context.Consume(); + } else { + context.emitter().Emit(*context.position(), ExpectedSemiAfterExpression); + state.has_error = true; + if (auto semi_token = context.SkipPastLikelyEnd(state.token)) { + end_token = *semi_token; + } + } + context.AddNode(ParseNodeKind::VariableDeclaration, end_token, + state.subtree_start, state.has_error); +} + +auto ParserHandleVarFinishAsFor(ParserContext& context) -> void { + auto state = context.PopState(); + + auto end_token = state.token; + if (context.PositionIs(TokenKind::In)) { + end_token = context.Consume(); + } else if (context.PositionIs(TokenKind::Colon)) { + CARBON_DIAGNOSTIC(ExpectedInNotColon, Error, + "`:` should be replaced by `in`."); + context.emitter().Emit(*context.position(), ExpectedInNotColon); + state.has_error = true; + end_token = context.Consume(); + } else { + CARBON_DIAGNOSTIC(ExpectedIn, Error, + "Expected `in` after loop `var` declaration."); + context.emitter().Emit(*context.position(), ExpectedIn); + state.has_error = true; + } + + context.AddNode(ParseNodeKind::ForIn, end_token, state.subtree_start, + state.has_error); +} + +} // namespace Carbon