//! Parser for Fermented UnrealScript (`FerUS`).
//!
//! Consumes tokens from [`crate::lexer::TokenizedFile`] and allocates AST
//! nodes in [`crate::arena::Arena`]. Basic expressions use a Pratt parser;
//! the rest rely on recursive descent in [`crate::parser::grammar`].\
//! Non-fatal errors accumulate in `Parser::diagnostics` as
//! [`crate::diagnostics::Diagnostic`]; recovery skips to sync points defined by
//! [`crate::parser::recovery::SyncLevel`] and synthesizes error nodes while
//! keeping the parse going.
//!
//! Components:
//! - `cursor`: token I/O, `peek`/`advance`, and lazy trivia capture;
//! - `trivia`: trailing comments and newline counts keyed to
//!     the previous significant token and BOF;
//! - `recovery`: panic-mode skipping and recovery adapters for results;
//! - `pretty`: printable trees (`ExprTree`, `StmtTree`) for messages and dumps;
//! - `errors`: [`ParseError`] and [`ParseErrorKind`].
//!
//! Lifetimes: `'src` ties to lexer slices; `'arena` ties to AST allocation.
//!
//! Guarantees:
//!
//! - Parser does not abort on user input. It emits diagnostics and error nodes.
//! - Trivia is recorded as you scan and can be queried by formatters/linters.
//! - Public surface keeps [`Parser`] small;
//!     low-level plumbing lives in submodules.

use super::lexer;

pub use lexer::{TokenData, Tokens};

mod cursor;
mod errors;
mod grammar;
mod recovery;
mod trivia;

pub use errors::ParseError;
pub(crate) use errors::{ParseErrorKind, ParseResult};
pub(crate) use recovery::{ResultRecoveryExt, SyncLevel};
pub(crate) use trivia::{TriviaKind, TriviaToken};

pub type ParseExpressionResult<'src, 'arena> =
    ParseResult<'src, 'arena, crate::ast::ExpressionRef<'src, 'arena>>;

pub(crate) mod diagnostic_labels {
    pub(crate) const EXPRESSION_REQUIRED_BY: &str = "expression_required_by";
    pub(crate) const EXPRESSION_EXPECTED_AFTER: &str = "expression_expected_after";
    pub(crate) const QUALIFIED_IDENTIFIER_DOT: &str = "qualifier_dot";
}

// TODO: add some kind of bailing for infinite loops
// let remaining_steps = file.token_count().saturating_mul(256).saturating_add(1024);
/// A recursive-descent parser over token from [`crate::lexer::TokenizedFile`].
pub struct Parser<'src, 'arena> {
    file: &'src lexer::TokenizedFile<'src>,
    arena: &'arena crate::arena::Arena,
    pub diagnostics: Vec<crate::diagnostics::Diagnostic>,
    cursor: cursor::Cursor<'src, 'src>,
    trivia: trivia::TriviaIndexBuilder<'src>,
}

impl<'src, 'arena> Parser<'src, 'arena> {
    pub fn new(file: &'src lexer::TokenizedFile<'src>, arena: &'arena crate::arena::Arena) -> Self {
        Self {
            file,
            arena,
            diagnostics: Vec::new(),
            cursor: cursor::Cursor::new(file),
            trivia: trivia::TriviaIndexBuilder::default(),
        }
    }

    pub fn file(&self) -> &'src lexer::TokenizedFile<'src> {
        self.file
    }
}
