From a508390423630bf837458015d593fe09d4e8e80b Mon Sep 17 00:00:00 2001 From: Kareem Ergawy Date: Mon, 28 Nov 2022 21:58:41 +0100 Subject: [PATCH] [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 --- toolchain/diagnostics/diagnostic_registry.def | 4 ++ toolchain/parser/parse_node_kind.def | 5 ++ toolchain/parser/parser.cpp | 72 +++++++++++++++++++ toolchain/parser/parser_state.def | 28 ++++++++ .../testdata/generics/interface/basic.carbon | 15 ++++ .../interface/fail_missing_name.carbon | 16 +++++ .../interface/fail_missing_open_curly.carbon | 19 +++++ 7 files changed, 159 insertions(+) create mode 100644 toolchain/parser/testdata/generics/interface/basic.carbon create mode 100644 toolchain/parser/testdata/generics/interface/fail_missing_name.carbon create mode 100644 toolchain/parser/testdata/generics/interface/fail_missing_open_curly.carbon diff --git a/toolchain/diagnostics/diagnostic_registry.def b/toolchain/diagnostics/diagnostic_registry.def index 3a209faacff9..43dfbb706550 100644 --- a/toolchain/diagnostics/diagnostic_registry.def +++ b/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 // ============================================================================ diff --git a/toolchain/parser/parse_node_kind.def b/toolchain/parser/parse_node_kind.def index 2586c62b0fca..5d4e88829641 100644 --- a/toolchain/parser/parse_node_kind.def +++ b/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 diff --git a/toolchain/parser/parser.cpp b/toolchain/parser/parser.cpp index 6fa60ebbbbff..f58eacb12c10 100644 --- a/toolchain/parser/parser.cpp +++ b/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 diff --git a/toolchain/parser/parser_state.def b/toolchain/parser/parser_state.def index 6b4be058b3f7..c302d5f3ced9 100644 --- a/toolchain/parser/parser_state.def +++ b/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 {`. +// +// If invalid: +// 1. InterfaceDefinitionFinish +// If `{` is missing: +// 1. InterfaceDefinitionFinish +// Else: +// 1. InterfaceDefinitionLoop +// 2. InterfaceDefinitionFinish +CARBON_PARSER_STATE(InterfaceIntroducer) + // Handles `package`. // // Always: diff --git a/toolchain/parser/testdata/generics/interface/basic.carbon b/toolchain/parser/testdata/generics/interface/basic.carbon new file mode 100644 index 000000000000..27beb7ca0d17 --- /dev/null +++ b/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 { +} diff --git a/toolchain/parser/testdata/generics/interface/fail_missing_name.carbon b/toolchain/parser/testdata/generics/interface/fail_missing_name.carbon new file mode 100644 index 000000000000..4ee53592029d --- /dev/null +++ b/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 { +} diff --git a/toolchain/parser/testdata/generics/interface/fail_missing_open_curly.carbon b/toolchain/parser/testdata/generics/interface/fail_missing_open_curly.carbon new file mode 100644 index 000000000000..0b807500e518 --- /dev/null +++ b/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