//! Parsing of declaration specifiers used in `var(...) ...` syntax for
//! Fermented `UnrealScript`.

use crate::arena::ArenaVec;
use crate::ast::{VarEditorSpecifier, VarEditorSpecifierRef, VarModifier};
use crate::lexer::Token;
use crate::parser::{ParseErrorKind, Parser, ResultRecoveryExt, SyncLevel};

impl<'src, 'arena> Parser<'src, 'arena> {
    /// Parses a consecutive run of variable declaration modifiers.
    ///
    /// This is used for declarations such as
    /// `var transient config editconst int X;`.
    ///
    /// Parsing stops when the next token is not a recognized [`VarModifier`].
    /// That token is left unconsumed for the caller.
    ///
    /// Returns the parsed modifiers in source order, or an empty vector if the
    /// current token does not begin a modifier list.
    #[must_use]
    pub(crate) fn parse_var_declaration_modifiers(&mut self) -> ArenaVec<'arena, VarModifier> {
        let mut modifiers = self.arena.vec();
        while let Some(current_token_and_position) = self.peek_token_and_position() {
            let Ok(parsed_modifier) = VarModifier::try_from(current_token_and_position) else {
                break;
            };
            self.advance();
            modifiers.push(parsed_modifier);
        }
        modifiers
    }

    /// Parses the optional parenthesized editor specifier list in `var(...)`.
    ///
    /// Assumes that `var` has already been consumed.
    ///
    /// Returns `None` if the current token is not `(`. Returns `Some(...)` once
    /// `(` is present, including for an empty list.
    ///
    /// Recovery is intentionally minimal because these specifier lists are not
    /// important enough to justify aggressive repair.
    #[must_use]
    pub(crate) fn parse_var_editor_specifier_list(
        &mut self,
    ) -> Option<ArenaVec<'arena, VarEditorSpecifierRef<'src, 'arena>>> {
        if !self.eat(Token::LeftParenthesis) {
            return None;
        }
        let mut editor_specifiers = self.arena.vec();
        while let Some((next_token, next_token_lexeme, next_token_position)) =
            self.peek_token_lexeme_and_position()
            && next_token != Token::RightParenthesis
        {
            if next_token == Token::StringLiteral {
                self.advance();
                let string_value = self.unescape_string_literal(next_token_lexeme);
                editor_specifiers.push(self.arena.alloc_node_at(
                    VarEditorSpecifier::String(string_value),
                    next_token_position,
                ));
            } else if let Some(specifier_identifier) =
                Self::identifier_token_from_token(next_token, next_token_position)
            {
                self.advance();
                editor_specifiers.push(self.arena.alloc_node_at(
                    VarEditorSpecifier::Identifier(specifier_identifier),
                    next_token_position,
                ));
            } else {
                self.make_error_here(ParseErrorKind::VarSpecNotIdentifier)
                    .sync_error_until(self, SyncLevel::ListSeparator)
                    .report_error(self);
            }
            // Detailed recovery is not worthwhile here;
            // stop once list structure becomes unclear.
            if !self.eat(Token::Comma) {
                break;
            }
            self.ensure_forward_progress(next_token_position);
        }
        self.expect(
            Token::RightParenthesis,
            ParseErrorKind::VarSpecsMissingClosingParenthesis,
        )
        .sync_error_at(self, SyncLevel::CloseParenthesis)
        .report_error(self);
        Some(editor_specifiers)
    }
}
