diff --git a/compiler/parser/src/parser/context.rs b/compiler/parser/src/parser/context.rs index 32ae516462..5c1059b7f4 100644 --- a/compiler/parser/src/parser/context.rs +++ b/compiler/parser/src/parser/context.rs @@ -240,6 +240,14 @@ impl<'a> ParserContext<'a> { self.parse_list(Delimiter::Parenthesis, Some(Token::Comma), f) } + /// Parse a list separated by `,` and delimited by brackets. + pub(super) fn parse_bracket_comma_list( + &mut self, + f: impl FnMut(&mut Self) -> Result>, + ) -> Result<(Vec, bool, Span)> { + self.parse_list(Delimiter::Bracket, Some(Token::Comma), f) + } + /// Returns true if the current token is `(`. pub(super) fn peek_is_left_par(&self) -> bool { matches!(self.token.token, Token::LeftParen) diff --git a/compiler/parser/src/parser/expression.rs b/compiler/parser/src/parser/expression.rs index 6bd5ae5619..b01cd78166 100644 --- a/compiler/parser/src/parser/expression.rs +++ b/compiler/parser/src/parser/expression.rs @@ -543,6 +543,19 @@ impl ParserContext<'_> { } } + /// Returns an [`Expression`] AST node if the next tokens represent an array initialization expression. + fn parse_array_expression(&mut self) -> Result { + let (elements, _, span) = self.parse_bracket_comma_list(|p| p.parse_expression().map(Some))?; + + match elements.is_empty() { + // If the array expression is empty, return an error. + true => Err(ParserError::array_must_have_at_least_one_element("expression", span).into()), + // Otherwise, return an array expression. + // Note: This is the only place where `ArrayExpression` is constructed in the parser. + false => Ok(Expression::Array(ArrayExpression { elements, span, id: self.node_builder.next_id() })), + } + } + /// Returns a reference to the next token if it is a [`GroupCoordinate`], or [None] if /// the next token is not a [`GroupCoordinate`]. fn peek_group_coordinate(&self, dist: &mut usize) -> Option { @@ -651,6 +664,8 @@ impl ParserContext<'_> { fn parse_primary_expression(&mut self) -> Result { if let Token::LeftParen = self.token.token { return self.parse_tuple_expression(); + } else if let Token::LeftSquare = self.token.token { + return self.parse_array_expression(); } let SpannedToken { token, span } = self.token.clone(); diff --git a/compiler/parser/src/tokenizer/token.rs b/compiler/parser/src/tokenizer/token.rs index 853d8ef3d8..b79967f6c9 100644 --- a/compiler/parser/src/tokenizer/token.rs +++ b/compiler/parser/src/tokenizer/token.rs @@ -380,6 +380,8 @@ pub enum Delimiter { Parenthesis, /// `{ ... }` Brace, + /// `[ ... ]` + Bracket, } impl Delimiter { @@ -388,6 +390,7 @@ impl Delimiter { match self { Self::Parenthesis => (Token::LeftParen, Token::RightParen), Self::Brace => (Token::LeftCurly, Token::RightCurly), + Self::Bracket => (Token::LeftSquare, Token::RightSquare), } } } diff --git a/errors/src/errors/parser/parser_errors.rs b/errors/src/errors/parser/parser_errors.rs index fa39d284b4..734c2f1fd6 100644 --- a/errors/src/errors/parser/parser_errors.rs +++ b/errors/src/errors/parser/parser_errors.rs @@ -291,4 +291,11 @@ create_messages!( msg: format!("expected no underscores or leading zeros -- found '{found}'"), help: None, } + + @formatted + array_must_have_at_least_one_element { + args: (kind: impl Display), + msg: format!("An array {kind} must have at least one element."), + help: None, + } );