diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index ede81a3e2334..80c49417b563 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -50,6 +50,7 @@ CARBON_DIAGNOSTIC_KIND(WrongRealLiteralExponent) // ============================================================================ CARBON_DIAGNOSTIC_KIND(BinaryOperatorRequiresWhitespace) +CARBON_DIAGNOSTIC_KIND(ExpectedArraySemi) CARBON_DIAGNOSTIC_KIND(ExpectedCloseSymbol) CARBON_DIAGNOSTIC_KIND(ExpectedCodeBlock) CARBON_DIAGNOSTIC_KIND(ExpectedExpression) diff --git a/toolchain/parser/parse_node_kind.def b/toolchain/parser/parse_node_kind.def index 0b8a78b536a7..5428ca046708 100644 --- a/toolchain/parser/parse_node_kind.def +++ b/toolchain/parser/parse_node_kind.def @@ -115,6 +115,18 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(ParameterListComma, 0) CARBON_PARSE_NODE_KIND_BRACKET(ParameterList, ParameterListStart) CARBON_PARSE_NODE_KIND_BRACKET(DeducedParameterList, DeducedParameterListStart) +// An array type, such as `[i32; 3]` or `[i32;]`: +// ArrayExpressionStart +// _external_: type expression +// ArrayExpressionSemi +// _external_: expression +// ArrayExpression +// +// The bound expression is optional. +CARBON_PARSE_NODE_KIND_CHILD_COUNT(ArrayExpressionStart, 0) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(ArrayExpressionSemi, 2) +CARBON_PARSE_NODE_KIND_BRACKET(ArrayExpression, ArrayExpressionSemi) + // A pattern binding, such as `name: Type`: // Name // _external_: type expression diff --git a/toolchain/parser/parser_handle_array_expression.cpp b/toolchain/parser/parser_handle_array_expression.cpp new file mode 100644 index 000000000000..9081836e9e38 --- /dev/null +++ b/toolchain/parser/parser_handle_array_expression.cpp @@ -0,0 +1,50 @@ +// 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 + +#include "toolchain/lexer/token_kind.h" +#include "toolchain/lexer/tokenized_buffer.h" +#include "toolchain/parser/parse_node_kind.h" +#include "toolchain/parser/parser_context.h" +#include "toolchain/parser/parser_state.h" + +namespace Carbon { + +auto ParserHandleArrayExpression(ParserContext& context) -> void { + auto state = context.PopState(); + state.state = ParserState::ArrayExpressionSemi; + context.AddLeafNode(ParseNodeKind::ArrayExpressionStart, + context.ConsumeChecked(TokenKind::OpenSquareBracket), + state.has_error); + context.PushState(state); + context.PushState(ParserState::Expression); +} + +auto ParserHandleArrayExpressionSemi(ParserContext& context) -> void { + auto state = context.PopState(); + auto semi = context.ConsumeIf(TokenKind::Semi); + if (!semi) { + context.AddNode(ParseNodeKind::ArrayExpressionSemi, *context.position(), + state.subtree_start, true); + CARBON_DIAGNOSTIC(ExpectedArraySemi, Error, "Expected `;` in array type."); + context.emitter().Emit(*context.position(), ExpectedArraySemi); + state.has_error = true; + } else { + context.AddNode(ParseNodeKind::ArrayExpressionSemi, *semi, + state.subtree_start, state.has_error); + } + state.state = ParserState::ArrayExpressionFinish; + context.PushState(state); + if (!context.PositionIs(TokenKind::CloseSquareBracket)) { + context.PushState(ParserState::Expression); + } +} + +auto ParserHandleArrayExpressionFinish(ParserContext& context) -> void { + auto state = context.PopState(); + context.ConsumeAndAddCloseSymbol( + *(TokenizedBuffer::TokenIterator(state.token)), state, + ParseNodeKind::ArrayExpression); +} + +} // namespace Carbon diff --git a/toolchain/parser/parser_handle_expression.cpp b/toolchain/parser/parser_handle_expression.cpp index 854877ce3afa..fb62e9ccacc3 100644 --- a/toolchain/parser/parser_handle_expression.cpp +++ b/toolchain/parser/parser_handle_expression.cpp @@ -88,6 +88,11 @@ auto ParserHandleExpressionInPostfix(ParserContext& context) -> void { context.PushState(ParserState::ParenExpression); break; } + case TokenKind::OpenSquareBracket: { + context.PushState(state); + context.PushState(ParserState::ArrayExpression); + break; + } case TokenKind::SelfValueIdentifier: { context.AddLeafNode(ParseNodeKind::SelfValueName, context.Consume()); context.PushState(state); @@ -115,9 +120,7 @@ auto ParserHandleExpressionInPostfixLoop(ParserContext& context) -> void { // This is a cyclic state that repeats, so this state is typically pushed back // on. auto state = context.PopState(); - state.token = *context.position(); - switch (context.PositionKind()) { case TokenKind::Period: { context.PushState(state); diff --git a/toolchain/parser/parser_state.def b/toolchain/parser/parser_state.def index d0e2da477518..942b9095c7a7 100644 --- a/toolchain/parser/parser_state.def +++ b/toolchain/parser/parser_state.def @@ -52,6 +52,28 @@ CARBON_PARSER_STATE(IndexExpression) // (state done) CARBON_PARSER_STATE(IndexExpressionFinish) +// Handles an array expression `[T; N]`. +// +// Always: +// 1. Expression +// 2. ArrayExpressionSemi +CARBON_PARSER_STATE(ArrayExpression) + +// Handles ';' in an array expression `[T; N]`. +// +// If `CloseSquareBracket`: +// 1. ArrayExpressionFinish +// Else: +// 1. Expression +// 2. ArrayExpressionFinish +CARBON_PARSER_STATE(ArrayExpressionSemi) + +// Handles finishing the array expression. +// +// Always: +// (state done) +CARBON_PARSER_STATE(ArrayExpressionFinish) + // Handles the `{` of a brace expression. // // If `CloseCurlyBrace`: @@ -260,6 +282,9 @@ CARBON_PARSER_STATE(Expression) // If `OpenParen`: // 1. ParenExpression // 2. ExpressionInPostfixLoop +// If `OpenSquareBracket`: +// 1. ArrayExpression +// 2. ExpressionInPostfixLoop // Else: // (state done) CARBON_PARSER_STATE(ExpressionInPostfix) diff --git a/toolchain/parser/testdata/array/fail_require_close_bracket.carbon b/toolchain/parser/testdata/array/fail_require_close_bracket.carbon new file mode 100644 index 000000000000..637b8f8afdf5 --- /dev/null +++ b/toolchain/parser/testdata/array/fail_require_close_bracket.carbon @@ -0,0 +1,33 @@ +// 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 +// TODO: It should emit only one error message. + +// CHECK:STDERR: fail_require_close_bracket.carbon:[[@LINE+9]]:8: Closing symbol does not match most recent opening symbol. +// CHECK:STDERR: var x: [i32;; +// CHECK:STDERR: ^ +// CHECK:STDERR: fail_require_close_bracket.carbon:[[@LINE+6]]:13: Expected expression. +// CHECK:STDERR: var x: [i32;; +// CHECK:STDERR: ^ +// CHECK:STDERR: fail_require_close_bracket.carbon:[[@LINE+3]]:13: Unexpected tokens before `]`. +// CHECK:STDERR: var x: [i32;; +// CHECK:STDERR: ^ +var x: [i32;; + +// CHECK:STDERR: fail_require_close_bracket.carbon:[[@LINE+14]]:19: `var` declarations must end with a `;`. +// CHECK:STDERR: // CHECK:STDOUT: ] +// CHECK:STDERR: ^ +// CHECK:STDOUT: [ +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'Name', text: 'x'}, +// CHECK:STDOUT: {kind: 'ArrayExpressionStart', text: '['}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'ArrayExpressionSemi', text: ';', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, +// CHECK:STDOUT: {kind: 'ArrayExpression', text: ']', has_error: yes, subtree_size: 5}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'VariableDeclaration', text: 'var', has_error: yes, subtree_size: 9}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parser/testdata/array/fail_require_semi.carbon b/toolchain/parser/testdata/array/fail_require_semi.carbon new file mode 100644 index 000000000000..4c00eb5c9156 --- /dev/null +++ b/toolchain/parser/testdata/array/fail_require_semi.carbon @@ -0,0 +1,22 @@ +// 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 + +// CHECK:STDERR: fail_require_semi.carbon:[[@LINE+3]]:12: Expected `;` in array type. +// CHECK:STDERR: var x: [i32]; +// CHECK:STDERR: ^ +var x: [i32]; + +// CHECK:STDOUT: [ +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'Name', text: 'x'}, +// CHECK:STDOUT: {kind: 'ArrayExpressionStart', text: '['}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'ArrayExpressionSemi', text: ']', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ArrayExpression', text: ']', has_error: yes, subtree_size: 4}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parser/testdata/array/with_length.carbon b/toolchain/parser/testdata/array/with_length.carbon new file mode 100644 index 000000000000..c56a4d13b4cf --- /dev/null +++ b/toolchain/parser/testdata/array/with_length.carbon @@ -0,0 +1,20 @@ +// 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 + +var x: [i32; 10]; + +// CHECK:STDOUT: [ +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'Name', text: 'x'}, +// CHECK:STDOUT: {kind: 'ArrayExpressionStart', text: '['}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'ArrayExpressionSemi', text: ';', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'Literal', text: '10'}, +// CHECK:STDOUT: {kind: 'ArrayExpression', text: ']', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parser/testdata/array/without_length.carbon b/toolchain/parser/testdata/array/without_length.carbon new file mode 100644 index 000000000000..a0807ad222fe --- /dev/null +++ b/toolchain/parser/testdata/array/without_length.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 + +var x: [i32;]; + +// CHECK:STDOUT: [ +// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'}, +// CHECK:STDOUT: {kind: 'Name', text: 'x'}, +// CHECK:STDOUT: {kind: 'ArrayExpressionStart', text: '['}, +// CHECK:STDOUT: {kind: 'Literal', text: 'i32'}, +// CHECK:STDOUT: {kind: 'ArrayExpressionSemi', text: ';', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ArrayExpression', text: ']', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'VariableDeclaration', text: ';', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/semantics/semantics_handle_array.cpp b/toolchain/semantics/semantics_handle_array.cpp new file mode 100644 index 000000000000..64938274a6c8 --- /dev/null +++ b/toolchain/semantics/semantics_handle_array.cpp @@ -0,0 +1,24 @@ +// 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 + +#include "toolchain/semantics/semantics_context.h" + +namespace Carbon { + +auto SemanticsHandleArrayExpressionStart(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleArrayExpressionStart"); +} + +auto SemanticsHandleArrayExpressionSemi(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleArrayExpressionSemi"); +} + +auto SemanticsHandleArrayExpression(SemanticsContext& context, + ParseTree::Node parse_node) -> bool { + return context.TODO(parse_node, "HandleArrayExpression"); +} + +} // namespace Carbon