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

[parser] Start re-implementing interfaces using the stack parser. (#2412)

First change towards re-implementing interfaces using the new parser. I kept it small to make sure we are on the same page regarding stack states and how the parse tree should look like.

Co-authored-by: ergawy <kareem.ergawy@guardsquare.com>
Kareem Ergawy 3 лет назад
Родитель
Сommit
a508390423

+ 4 - 0
toolchain/diagnostics/diagnostic_registry.def

@@ -78,6 +78,10 @@ CARBON_DIAGNOSTIC_KIND(ExpectedSemiToEndPackageDirective)
 // For-specific diagnostics
 CARBON_DIAGNOSTIC_KIND(ExpectedIn)
 
+// Interface-specific diagnostics
+CARBON_DIAGNOSTIC_KIND(ExpectedInterfaceName)
+CARBON_DIAGNOSTIC_KIND(ExpectedInterfaceOpenCurlyBrace)
+
 // ============================================================================
 // Semantics diagnostics
 // ============================================================================

+ 5 - 0
toolchain/parser/parse_node_kind.def

@@ -113,6 +113,11 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(StructFieldValue, TodoFixParseNode)
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(StructFieldType, TodoFixParseNode)
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(StructComma, 0)
 
+// Interfaces
+CARBON_PARSE_NODE_KIND_CHILD_COUNT(InterfaceDefinition, TodoFixParseNode)
+CARBON_PARSE_NODE_KIND_CHILD_COUNT(InterfaceBodyStart, 0)
+CARBON_PARSE_NODE_KIND_BRACKET(InterfaceBody, InterfaceBodyStart)
+
 #undef CARBON_PARSE_NODE_KIND
 #undef CARBON_PARSE_NODE_KIND_BRACKET
 #undef CARBON_PARSE_NODE_KIND_CHILD_COUNT

+ 72 - 0
toolchain/parser/parser.cpp

@@ -701,6 +701,11 @@ auto Parser::HandleDeclarationLoopState() -> void {
       PushState(ParserState::VarAsRequireSemicolon());
       break;
     }
+    case TokenKind::Interface(): {
+      PushState(ParserState::InterfaceIntroducer());
+      ++position_;
+      break;
+    }
     default: {
       CARBON_DIAGNOSTIC(UnrecognizedDeclaration, Error,
                         "Unrecognized declaration introducer.");
@@ -1658,4 +1663,71 @@ auto Parser::HandleVarFinishAsNoSemicolonState() -> void {
   HandleVarFinish(/*require_semicolon=*/false);
 }
 
+auto Parser::HandleInterfaceIntroducerState() -> void {
+  auto state = PopState();
+
+  if (!ConsumeAndAddLeafNodeIf(TokenKind::Identifier(),
+                               ParseNodeKind::DeclaredName())) {
+    CARBON_DIAGNOSTIC(ExpectedInterfaceName, Error,
+                      "Expected interface name after `interface` keyword.");
+    emitter_->Emit(*position_, ExpectedInterfaceName);
+    state.has_error = true;
+  }
+
+  bool parse_body = true;
+
+  if (!PositionIs(TokenKind::OpenCurlyBrace())) {
+    CARBON_DIAGNOSTIC(ExpectedInterfaceOpenCurlyBrace, Error,
+                      "Expected `{{` to start interface definition.");
+    emitter_->Emit(*position_, ExpectedInterfaceOpenCurlyBrace);
+    state.has_error = true;
+
+    SkipPastLikelyEnd(state.token);
+    parse_body = false;
+  }
+
+  state.state = ParserState::InterfaceDefinitionFinish();
+  PushState(state);
+
+  if (parse_body) {
+    PushState(ParserState::InterfaceDefinitionLoop());
+    AddLeafNode(ParseNodeKind::InterfaceBodyStart(), Consume());
+  }
+}
+
+auto Parser::HandleInterfaceDefinitionLoopState() -> void {
+  // This maintains the current state unless we're at the end of the interface
+  // definition.
+
+  switch (PositionKind()) {
+    case TokenKind::CloseCurlyBrace(): {
+      auto state = PopState();
+
+      AddNode(ParseNodeKind::InterfaceBody(), Consume(), state.subtree_start,
+              state.has_error);
+
+      break;
+    }
+      // TODO: Handle possible declarations inside interface body.
+    default: {
+      CARBON_DIAGNOSTIC(UnrecognizedDeclaration, Error,
+                        "Unrecognized declaration introducer.");
+      emitter_->Emit(*position_, UnrecognizedDeclaration);
+      if (auto semi = SkipPastLikelyEnd(*position_)) {
+        AddLeafNode(ParseNodeKind::EmptyDeclaration(), *semi,
+                    /*has_error=*/true);
+      } else {
+        ReturnErrorOnState();
+      }
+      break;
+    }
+  }
+}
+
+auto Parser::HandleInterfaceDefinitionFinishState() -> void {
+  auto state = PopState();
+  AddNode(ParseNodeKind::InterfaceDefinition(), state.token,
+          state.subtree_start, state.has_error);
+}
+
 }  // namespace Carbon

+ 28 - 0
toolchain/parser/parser_state.def

@@ -123,6 +123,9 @@ CARBON_PARSER_STATE(CodeBlockFinish)
 // If `Var`:
 //   1. Var
 //   2. DeclarationLoop
+// If `interface`:
+//   1. InterfaceIntroducer
+//   2. DeclarationLoop
 // Else:
 //   1. DeclarationLoop
 CARBON_PARSER_STATE(DeclarationLoop)
@@ -276,6 +279,31 @@ CARBON_PARSER_STATE(FunctionSignatureFinish)
 //   (state done)
 CARBON_PARSER_STATE(FunctionDefinitionFinish)
 
+// Finishes an interface definition.
+//
+// Always:
+//   (state done)
+CARBON_PARSER_STATE(InterfaceDefinitionFinish)
+
+// Handles parsing the body of an interface.
+//
+// If `}`:
+//   (state done)
+// Else:
+//   1. InterfaceDefinitionLoop
+CARBON_PARSER_STATE(InterfaceDefinitionLoop)
+
+// Handles processing of a intefaces's `interface <name> {`.
+//
+// If invalid:
+//   1. InterfaceDefinitionFinish
+// If `{` is missing:
+//   1. InterfaceDefinitionFinish
+// Else:
+//   1. InterfaceDefinitionLoop
+//   2. InterfaceDefinitionFinish
+CARBON_PARSER_STATE(InterfaceIntroducer)
+
 // Handles `package`.
 //
 // Always:

+ 15 - 0
toolchain/parser/testdata/generics/interface/basic.carbon

@@ -0,0 +1,15 @@
+// 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
+//
+// AUTOUPDATE
+// RUN: %{carbon-run-parser}
+// CHECK:STDOUT: [
+// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'foo'},
+// CHECK:STDOUT:     {kind: 'InterfaceBodyStart', text: '{'},
+// CHECK:STDOUT:   {kind: 'InterfaceBody', text: '}', subtree_size: 2},
+// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: 'interface', subtree_size: 4},
+// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT: ]
+interface foo {
+}

+ 16 - 0
toolchain/parser/testdata/generics/interface/fail_missing_name.carbon

@@ -0,0 +1,16 @@
+// 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
+//
+// AUTOUPDATE
+// RUN: %{not} %{carbon-run-parser}
+// CHECK:STDOUT: [
+// CHECK:STDOUT:     {kind: 'InterfaceBodyStart', text: '{'},
+// CHECK:STDOUT:   {kind: 'InterfaceBody', text: '}', subtree_size: 2},
+// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: 'interface', has_error: yes, subtree_size: 3},
+// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT: ]
+
+// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/generics/interface/fail_missing_name.carbon:[[@LINE+1]]:11: Expected interface name after `interface` keyword.
+interface {
+}

+ 19 - 0
toolchain/parser/testdata/generics/interface/fail_missing_open_curly.carbon

@@ -0,0 +1,19 @@
+// 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
+//
+// AUTOUPDATE
+// RUN: %{not} %{carbon-run-parser}
+// CHECK:STDOUT: [
+// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'bar'},
+// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: 'interface', has_error: yes, subtree_size: 2},
+// CHECK:STDOUT:   {kind: 'DeclaredName', text: 'foo'},
+// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: 'interface', has_error: yes, subtree_size: 2},
+// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT: ]
+
+// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/generics/interface/fail_missing_open_curly.carbon:[[@LINE+1]]:15: Expected `{` to start interface definition.
+interface bar baz {}
+
+// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/generics/interface/fail_missing_open_curly.carbon:[[@LINE+1]]:14: Expected `{` to start interface definition.
+interface foo