Huge dump of refactored code. Still in the middle of the changes that are to be squashed later in a one huge monster commit, because there is no value in anything atomic here.
173 lines
6.3 KiB
Rust
173 lines
6.3 KiB
Rust
//! 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())
|
|
}
|
|
}
|