//! Frontend des Terminal-Basic-Compilers.
//!
//! Enthält Lexer, Parser, AST-Definitionen und die semantische Analyse
//! (Symboltabellen, Typprüfung, implizite Deklarationen, `DEFINT`-Regeln usw.).
//! Ausgabe des Frontends ist ein typgeprüfter AST, den `tb-vm` in Bytecode
//! übersetzt.

pub mod ast;
pub mod forms;
pub mod hir;
pub mod lexer;
pub mod parser;
pub mod sema;
pub mod source;

/// Quelltextposition für Diagnostik (1-basiert, wie im IDE-Vorbild).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct SourcePos {
    pub source: u32,
    pub line: u32,
    pub column: u32,
}

/// Eine Diagnosemeldung mit Position. Die Texte folgen den (englischen)
/// Meldungen des Vorbilds, wo es eine Entsprechung gibt.
#[derive(Debug, Clone)]
pub struct Diagnostic {
    pub file: Option<String>,
    pub pos: SourcePos,
    pub message: String,
}

impl std::fmt::Display for Diagnostic {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if let Some(file) = &self.file {
            write!(f, "{file}:")?;
        }
        write!(f, "{}:{}: {}", self.pos.line, self.pos.column, self.message)
    }
}

/// Ergebnis der Frontend-Pipeline für ein Modul.
pub struct Analysis {
    pub module: ast::Module,
    pub diagnostics: Vec<Diagnostic>,
    /// Typisiertes HIR (vollständig nur bei leeren Diagnosen).
    pub hir: Option<hir::HirModule>,
}

/// Komplette Pipeline: Lexen → Parsen → semantische Prüfung + Lowering.
pub fn analyze_source(module_name: &str, source: &str) -> Analysis {
    analyze_source_with_forms(module_name, source, &forms::FormCatalog::default())
}

pub fn analyze_source_with_forms(
    module_name: &str,
    source: &str,
    forms: &forms::FormCatalog,
) -> Analysis {
    let lexed = lexer::lex(source);
    let mut diagnostics = lexed.diagnostics;
    let parsed = parser::parse(module_name, &lexed.tokens);
    diagnostics.extend(parsed.diagnostics);
    let (hir, sema_diags) = sema::lower_with_forms(&parsed.module, forms);
    diagnostics.extend(sema_diags);
    diagnostics.sort_by_key(|d| (d.pos.line, d.pos.column));
    Analysis {
        module: parsed.module,
        diagnostics,
        hir,
    }
}

pub mod editing;
