//! Parsing of comma-separated variable declarator lists for //! Fermented `UnrealScript`. //! //! Extends original `UnrealScript` by allowing array-size expressions and //! declarator initializers. #![allow(clippy::option_if_let_else)] use std::ops::ControlFlow; use crate::arena::ArenaVec; use crate::ast::{AstSpan, OptionalExpression, VariableDeclarator, VariableDeclaratorRef}; use crate::lexer::{Token, TokenPosition}; use crate::parser::{ParseErrorKind, ParseResult, Parser, ResultRecoveryExt, SyncLevel}; #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] enum VariableDeclaratorParseState { ExpectingDeclarator, ExpectingSeparator, } impl<'src, 'arena> Parser<'src, 'arena> { /// Parses a comma-separated list of variable declarators. /// /// Accepts optional array-size expressions and `=` initializers. #[must_use] pub(crate) fn parse_variable_declarators( &mut self, ) -> ArenaVec<'arena, VariableDeclaratorRef<'src, 'arena>> { use VariableDeclaratorParseState::{ExpectingDeclarator, ExpectingSeparator}; let mut declarators = self.arena.vec(); let mut parser_state = ExpectingDeclarator; while let Some((next_token, next_token_position)) = self.peek_token_and_position() { match (parser_state, next_token) { (ExpectingDeclarator, Token::Semicolon) => { self.report_error_here(ParseErrorKind::DeclEmptyVariableDeclarations); return declarators; } (ExpectingDeclarator, Token::Comma) => { if self .recover_empty_variable_declarator(next_token_position) .is_break() { return declarators; } } (ExpectingDeclarator, _) => { if self .parse_variable_declarator_into(&mut declarators) .is_break() { // Breaking means we've failed to parse declarator self.report_error_here(ParseErrorKind::DeclEmptyVariableDeclarations); break; } parser_state = ExpectingSeparator; } (ExpectingSeparator, Token::Comma) => { self.advance(); parser_state = ExpectingDeclarator; } (ExpectingSeparator, Token::Semicolon) => break, (ExpectingSeparator, _) => { if self .recover_missing_variable_declarator_separator( next_token_position, &mut declarators, ) .is_break() { break; } } } self.ensure_forward_progress(next_token_position); } // In case of reaching EOF here, it does not matter if we emit // an additional diagnostic. // The caller is expected to report the more relevant enclosing error. declarators } fn recover_empty_variable_declarator( &mut self, error_start_position: TokenPosition, ) -> ControlFlow<()> { while self.peek_token() == Some(Token::Comma) { self.advance(); } self.make_error_here(ParseErrorKind::DeclEmptyVariableDeclarations) .widen_error_span_from(error_start_position) .report_error(self); if matches!(self.peek_token(), Some(Token::Semicolon) | None) { ControlFlow::Break(()) } else { ControlFlow::Continue(()) } } fn parse_variable_declarator_into( &mut self, declarators: &mut ArenaVec<'arena, VariableDeclaratorRef<'src, 'arena>>, ) -> ControlFlow<()> { if let Some(parsed_declarator) = self .parse_variable_declarator() .sync_error_until(self, SyncLevel::Statement) .ok_or_report(self) { declarators.push(parsed_declarator); ControlFlow::Continue(()) } else { ControlFlow::Break(()) } } fn recover_missing_variable_declarator_separator( &mut self, error_start_position: TokenPosition, declarators: &mut ArenaVec<'arena, VariableDeclaratorRef<'src, 'arena>>, ) -> ControlFlow<()> { if let Some(parsed_declarator) = self .parse_variable_declarator() .widen_error_span_from(error_start_position) .sync_error_until(self, SyncLevel::Statement) .ok_or_report(self) { self.make_error_here(ParseErrorKind::DeclNoSeparatorBetweenVariableDeclarations) .widen_error_span_from(error_start_position) .report_error(self); declarators.push(parsed_declarator); ControlFlow::Continue(()) } else { ControlFlow::Break(()) } } fn parse_variable_declarator( &mut self, ) -> ParseResult<'src, 'arena, VariableDeclaratorRef<'src, 'arena>> { let name = self.parse_identifier(ParseErrorKind::DeclBadVariableIdentifier)?; let array_size = self.parse_optional_array_size(); let initializer = self.parse_optional_variable_initializer(); let span = AstSpan::range(name.0, self.last_consumed_position_or_start()); Ok(self.arena.alloc_node( VariableDeclarator { name, initializer, array_size, }, span, )) } fn parse_optional_array_size(&mut self) -> OptionalExpression<'src, 'arena> { if !self.eat(Token::LeftBracket) { return None; } let array_size_expression = self.parse_expression(); self.expect( Token::RightBracket, ParseErrorKind::DeclExpectedRightBracketAfterArraySize, ) .sync_error_at(self, SyncLevel::CloseBracket) .report_error(self); Some(array_size_expression) } fn parse_optional_variable_initializer(&mut self) -> OptionalExpression<'src, 'arena> { self.eat(Token::Assign).then(|| self.parse_expression()) } }