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

Parsing support for return statements.

Richard Smith 5 лет назад
Родитель
Сommit
e3b732e564
4 измененных файлов с 69 добавлено и 15 удалено
  1. 1 0
      parser/parse_node_kind.def
  2. 39 10
      parser/parse_tree_test.cpp
  3. 20 4
      parser/parser_impl.cpp
  4. 9 1
      parser/parser_impl.h

+ 1 - 0
parser/parse_node_kind.def

@@ -38,6 +38,7 @@ CARBON_PARSE_NODE_KIND(Condition)
 CARBON_PARSE_NODE_KIND(ConditionEnd)
 CARBON_PARSE_NODE_KIND(ContinueStatement)
 CARBON_PARSE_NODE_KIND(BreakStatement)
+CARBON_PARSE_NODE_KIND(ReturnStatement)
 CARBON_PARSE_NODE_KIND(StatementEnd)
 
 // Expressions.

+ 39 - 10
parser/parse_tree_test.cpp

@@ -212,19 +212,19 @@ TEST_F(ParseTreeTest, FunctionDeclarationWithReturnType) {
 TEST_F(ParseTreeTest, FunctionDefinitionWithReturnType) {
   TokenizedBuffer tokens = GetTokenizedBuffer(
       "fn foo() -> Int {\n"
-      "  // return 42;\n"
+      "  return 42;\n"
       "}");
   ParseTree tree = ParseTree::Parse(tokens, consumer);
   EXPECT_FALSE(tree.HasErrors());
-  EXPECT_THAT(
-      tree,
-      MatchParseTreeNodes(
-          {MatchFunctionDeclaration(MatchDeclaredName("foo"), MatchParameters(),
-                                    MatchReturnType(MatchNameReference("Int")),
-                                    MatchCodeBlock(
-                                        // TODO: Match a return statement.
-                                        MatchCodeBlockEnd())),
-           MatchFileEnd()}));
+  EXPECT_THAT(tree,
+              MatchParseTreeNodes(
+                  {MatchFunctionDeclaration(
+                       MatchDeclaredName("foo"), MatchParameters(),
+                       MatchReturnType(MatchNameReference("Int")),
+                       MatchCodeBlock(MatchReturnStatement(MatchLiteral("42"),
+                                                           MatchStatementEnd()),
+                                      MatchCodeBlockEnd())),
+                   MatchFileEnd()}));
 }
 
 TEST_F(ParseTreeTest, FunctionDeclarationWithSingleIdentifierParameterList) {
@@ -693,6 +693,35 @@ TEST_F(ParseTreeTest, WhileBreakContinue) {
            MatchFileEnd()}));
 }
 
+TEST_F(ParseTreeTest, Return) {
+  TokenizedBuffer tokens = GetTokenizedBuffer(
+      "fn F() {\n"
+      "  if (c)\n"
+      "    return;\n"
+      "}\n"
+      "fn G(Int x) -> Int {\n"
+      "  return x;\n"
+      "}");
+  ParseTree tree = ParseTree::Parse(tokens, consumer);
+  EXPECT_FALSE(tree.HasErrors());
+
+  EXPECT_THAT(
+      tree,
+      MatchParseTreeNodes(
+          {MatchFunctionWithBody(MatchIfStatement(
+               MatchCondition(MatchNameReference("c"), MatchConditionEnd()),
+               MatchReturnStatement(MatchStatementEnd()))),
+           MatchFunctionDeclaration(
+               MatchDeclaredName(),
+               MatchParameters(
+                   MatchParameterDeclaration(MatchNameReference("Int"), "x")),
+               MatchReturnType(MatchNameReference("Int")),
+               MatchCodeBlock(MatchReturnStatement(MatchNameReference("x"),
+                                                   MatchStatementEnd()),
+                              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);

+ 20 - 4
parser/parser_impl.cpp

@@ -827,20 +827,30 @@ auto ParseTree::Parser::ParseWhileStatement() -> llvm::Optional<Node> {
                  /*has_errors=*/!cond || !body);
 }
 
-auto ParseTree::Parser::ParseKeywordStatement(ParseNodeKind kind)
+auto ParseTree::Parser::ParseKeywordStatement(ParseNodeKind kind,
+                                              KeywordStatementArgument argument)
     -> llvm::Optional<Node> {
   auto keyword_kind = tokens.GetKind(*position);
   assert(keyword_kind.IsKeyword());
 
   auto start = StartSubtree();
   auto keyword = Consume(keyword_kind);
+
+  bool arg_error = false;
+  if ((argument == KeywordStatementArgument::Optional &&
+       tokens.GetKind(*position) != TokenKind::Semi()) ||
+      argument == KeywordStatementArgument::Mandatory) {
+    arg_error = !ParseExpression();
+  }
+
   auto semi =
       ConsumeAndAddLeafNodeIf(TokenKind::Semi(), ParseNodeKind::StatementEnd());
   if (!semi) {
     emitter.EmitError<ExpectedSemiAfter>(*position,
                                          {.preceding = keyword_kind});
+    // FIXME: Try to skip to a semicolon to recover.
   }
-  return AddNode(kind, keyword, start, /*has_errors=*/!semi);
+  return AddNode(kind, keyword, start, /*has_errors=*/!semi || arg_error);
 }
 
 auto ParseTree::Parser::ParseStatement() -> llvm::Optional<Node> {
@@ -855,10 +865,16 @@ auto ParseTree::Parser::ParseStatement() -> llvm::Optional<Node> {
       return ParseWhileStatement();
 
     case TokenKind::ContinueKeyword():
-      return ParseKeywordStatement(ParseNodeKind::ContinueStatement());
+      return ParseKeywordStatement(ParseNodeKind::ContinueStatement(),
+                                   KeywordStatementArgument::None);
 
     case TokenKind::BreakKeyword():
-      return ParseKeywordStatement(ParseNodeKind::BreakStatement());
+      return ParseKeywordStatement(ParseNodeKind::BreakStatement(),
+                                   KeywordStatementArgument::None);
+
+    case TokenKind::ReturnKeyword():
+      return ParseKeywordStatement(ParseNodeKind::ReturnStatement(),
+                                   KeywordStatementArgument::Optional);
 
     case TokenKind::OpenCurlyBrace():
       return ParseCodeBlock();

+ 9 - 1
parser/parser_impl.h

@@ -199,8 +199,16 @@ class ParseTree::Parser {
   // Parses a while-statement.
   auto ParseWhileStatement() -> llvm::Optional<Node>;
 
+  enum class KeywordStatementArgument {
+    None,
+    Optional,
+    Mandatory,
+  };
+
   // Parses a statement of the form `keyword;` such as `break;` or `continue;`.
-  auto ParseKeywordStatement(ParseNodeKind kind) -> llvm::Optional<Node>;
+  auto ParseKeywordStatement(ParseNodeKind kind,
+                             KeywordStatementArgument argument)
+      -> llvm::Optional<Node>;
 
   // Parses a statement.
   auto ParseStatement() -> llvm::Optional<Node>;