diff --git a/parser/parse_node_kind.def b/parser/parse_node_kind.def index 8653681f6577..c374f5866705 100644 --- a/parser/parse_node_kind.def +++ b/parser/parse_node_kind.def @@ -30,8 +30,12 @@ CARBON_PARSE_NODE_KIND(CodeBlock) CARBON_PARSE_NODE_KIND(ExpressionStatement) CARBON_PARSE_NODE_KIND(IfStatement) CARBON_PARSE_NODE_KIND(IfStatementElse) +CARBON_PARSE_NODE_KIND(WhileStatement) CARBON_PARSE_NODE_KIND(Condition) CARBON_PARSE_NODE_KIND(ConditionEnd) +CARBON_PARSE_NODE_KIND(ContinueStatement) +CARBON_PARSE_NODE_KIND(BreakStatement) +CARBON_PARSE_NODE_KIND(StatementEnd) // Expressions. CARBON_PARSE_NODE_KIND(Literal) diff --git a/parser/parse_tree_test.cpp b/parser/parse_tree_test.cpp index 8269ce6ea002..d2674a31aa8d 100644 --- a/parser/parse_tree_test.cpp +++ b/parser/parse_tree_test.cpp @@ -643,6 +643,42 @@ TEST_F(ParseTreeTest, IfError) { MatchFileEnd()})); } +TEST_F(ParseTreeTest, WhileBreakContinue) { + TokenizedBuffer tokens = GetTokenizedBuffer( + "fn F() {\n" + " while (a) {\n" + " if (b)\n" + " break;\n" + " if (c)\n" + " continue;\n" + "}"); + ParseTree tree = ParseTree::Parse(tokens, consumer); + EXPECT_FALSE(tree.HasErrors()); + + EXPECT_THAT( + tree, + MatchParseTreeNodes( + {MatchFunctionDeclaration( + MatchDeclaredName("F"), + MatchParameterList(MatchParameterListEnd()), + MatchCodeBlock( + MatchWhileStatement( + MatchCondition(MatchNameReference("a"), + MatchConditionEnd()), + MatchCodeBlock( + MatchIfStatement( + MatchCondition(MatchNameReference("b"), + MatchConditionEnd()), + MatchBreakStatement(MatchStatementEnd())), + MatchIfStatement( + MatchCondition(MatchNameReference("c"), + MatchConditionEnd()), + MatchContinueStatement(MatchStatementEnd())), + MatchCodeBlockEnd())), + MatchCodeBlockEnd())), + MatchFileEnd()})); +} + auto GetAndDropLine(llvm::StringRef& s) -> std::string { auto newline_offset = s.find_first_of('\n'); llvm::StringRef line = s.slice(0, newline_offset); diff --git a/parser/parser_impl.cpp b/parser/parser_impl.cpp index 72133d02f5ff..a5c9d3e30381 100644 --- a/parser/parser_impl.cpp +++ b/parser/parser_impl.cpp @@ -86,6 +86,17 @@ struct ExpectedSemiAfterExpression "Expected `;` after expression."; }; +struct ExpectedSemiAfter : SimpleDiagnostic { + static constexpr llvm::StringLiteral ShortName = "syntax-error"; + static constexpr const char* Message = "Expected `;` after `{0}`."; + + TokenKind preceding; + + auto Format() -> std::string { + return llvm::formatv(Message, preceding.GetFixedSpelling()).str(); + } +}; + struct ExpectedIdentifierAfterDot : SimpleDiagnostic { static constexpr llvm::StringLiteral ShortName = "syntax-error"; @@ -758,6 +769,31 @@ auto ParseTree::Parser::ParseIfStatement() -> llvm::Optional { /*has_errors=*/!cond || !then_case || else_has_errors); } +auto ParseTree::Parser::ParseWhileStatement() -> llvm::Optional { + auto start = StartSubtree(); + auto while_token = Consume(TokenKind::WhileKeyword()); + auto cond = ParseParenCondition(TokenKind::WhileKeyword()); + auto body = ParseStatement(); + return AddNode(ParseNodeKind::WhileStatement(), while_token, start, + /*has_errors=*/!cond || !body); +} + +auto ParseTree::Parser::ParseKeywordStatement(ParseNodeKind kind) + -> llvm::Optional { + auto keyword_kind = tokens.GetKind(*position); + assert(keyword_kind.IsKeyword()); + + auto start = StartSubtree(); + auto keyword = Consume(keyword_kind); + auto semi = + ConsumeAndAddLeafNodeIf(TokenKind::Semi(), ParseNodeKind::StatementEnd()); + if (!semi) { + emitter.EmitError(*position, + {.preceding = keyword_kind}); + } + return AddNode(kind, keyword, start, /*has_errors=*/!semi); +} + auto ParseTree::Parser::ParseStatement() -> llvm::Optional { switch (tokens.GetKind(*position)) { case TokenKind::VarKeyword(): @@ -766,6 +802,15 @@ auto ParseTree::Parser::ParseStatement() -> llvm::Optional { case TokenKind::IfKeyword(): return ParseIfStatement(); + case TokenKind::WhileKeyword(): + return ParseWhileStatement(); + + case TokenKind::ContinueKeyword(): + return ParseKeywordStatement(ParseNodeKind::ContinueStatement()); + + case TokenKind::BreakKeyword(): + return ParseKeywordStatement(ParseNodeKind::BreakStatement()); + case TokenKind::OpenCurlyBrace(): return ParseCodeBlock(); diff --git a/parser/parser_impl.h b/parser/parser_impl.h index 63728723867b..2cf7027b2eb0 100644 --- a/parser/parser_impl.h +++ b/parser/parser_impl.h @@ -183,6 +183,12 @@ class ParseTree::Parser { // Parses an if-statement. auto ParseIfStatement() -> llvm::Optional; + // Parses a while-statement. + auto ParseWhileStatement() -> llvm::Optional; + + // Parses a statement of the form `keyword;` such as `break;` or `continue;`. + auto ParseKeywordStatement(ParseNodeKind kind) -> llvm::Optional; + // Parses a statement. auto ParseStatement() -> llvm::Optional;