Просмотр исходного кода

Parsing for while, break, and continue statements, following #340.

Richard Smith 5 лет назад
Родитель
Сommit
153d92b390
4 измененных файлов с 91 добавлено и 0 удалено
  1. 4 0
      parser/parse_node_kind.def
  2. 36 0
      parser/parse_tree_test.cpp
  3. 45 0
      parser/parser_impl.cpp
  4. 6 0
      parser/parser_impl.h

+ 4 - 0
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)

+ 36 - 0
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);

+ 45 - 0
parser/parser_impl.cpp

@@ -86,6 +86,17 @@ struct ExpectedSemiAfterExpression
       "Expected `;` after expression.";
 };
 
+struct ExpectedSemiAfter : SimpleDiagnostic<ExpectedSemiAfter> {
+  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<ExpectedIdentifierAfterDot> {
   static constexpr llvm::StringLiteral ShortName = "syntax-error";
@@ -758,6 +769,31 @@ auto ParseTree::Parser::ParseIfStatement() -> llvm::Optional<Node> {
                  /*has_errors=*/!cond || !then_case || else_has_errors);
 }
 
+auto ParseTree::Parser::ParseWhileStatement() -> llvm::Optional<Node> {
+  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<Node> {
+  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<ExpectedSemiAfter>(*position,
+                                         {.preceding = keyword_kind});
+  }
+  return AddNode(kind, keyword, start, /*has_errors=*/!semi);
+}
+
 auto ParseTree::Parser::ParseStatement() -> llvm::Optional<Node> {
   switch (tokens.GetKind(*position)) {
     case TokenKind::VarKeyword():
@@ -766,6 +802,15 @@ auto ParseTree::Parser::ParseStatement() -> llvm::Optional<Node> {
     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();
 

+ 6 - 0
parser/parser_impl.h

@@ -183,6 +183,12 @@ class ParseTree::Parser {
   // Parses an if-statement.
   auto ParseIfStatement() -> llvm::Optional<Node>;
 
+  // Parses a while-statement.
+  auto ParseWhileStatement() -> llvm::Optional<Node>;
+
+  // Parses a statement of the form `keyword;` such as `break;` or `continue;`.
+  auto ParseKeywordStatement(ParseNodeKind kind) -> llvm::Optional<Node>;
+
   // Parses a statement.
   auto ParseStatement() -> llvm::Optional<Node>;