dixscript 1.0.0

Config, code, and encryption in one file — a data interchange format with compile-time functions, AES-256/ChaCha20 built-in, and cross-platform FFI
Documentation
use crate::Compiler::Core::Tokenizer::{Token, TokenType, TokenizationResult};

/// Debug printer for DixScript tokens.
/// Shows exact token types and positions for debugging lexer output.
pub struct TokenDebugPrinter {
    output:         String,
    show_positions: bool,
    show_sections:  bool,
    group_by_line:  bool,
}

impl TokenDebugPrinter {
    pub fn new(show_positions: bool, show_sections: bool, group_by_line: bool) -> Self {
        TokenDebugPrinter {
            output: String::new(),
            show_positions,
            show_sections,
            group_by_line,
        }
    }

    pub fn print(&mut self, result: &TokenizationResult) -> String {
        self.output.clear();

        self.writeln("=== DIXSCRIPT TOKEN DEBUG OUTPUT ===");
        self.writeln(&format!("Generated: {}", chrono::Local::now().format("%Y-%m-%d %H:%M:%S")));
        self.writeln(&format!("Total Tokens: {}", result.tokens.len()));
        self.writeln(&format!("Total Lines: {}", result.metadata.total_lines));
        self.writeln("");

        if self.group_by_line {
            self.print_grouped_by_line(&result.tokens);
        } else {
            self.print_sequential(&result.tokens);
        }

        self.writeln("");
        self.writeln("=== METADATA ===");
        self.writeln(&format!("Version: {}", result.metadata.version));
        self.writeln(&format!("Sections Detected: {:?}", result.metadata.sections_detected));
        self.writeln(&format!("Prefixed Constructors: {}", result.metadata.prefixed_constructors_found));
        self.writeln(&format!("  - Blob: {}",  result.metadata.blob_constructors));
        self.writeln(&format!("  - Tuple: {}", result.metadata.tuple_constructors));
        self.writeln(&format!("  - Regex: {}", result.metadata.regex_constructors));
        self.writeln(&format!("Static Calls Found: {}", result.metadata.static_calls_found));
        self.writeln(&format!("Potential Builtin Calls: {}", result.metadata.potential_builtin_calls));

        if !result.prefixed_constructors.is_empty() {
            self.writeln("");
            self.writeln("=== PREFIXED CONSTRUCTORS ===");
            for pc in &result.prefixed_constructors {
                self.writeln(&format!(
                    "{}:{} at L{}:C{} ({})",
                    pc.prefix, pc.constructor_type, pc.line, pc.column, pc.section
                ));
            }
        }

        if !result.static_calls.is_empty() {
            self.writeln("");
            self.writeln("=== STATIC CALLS ===");
            for sc in &result.static_calls {
                self.writeln(&format!(
                    "{}.{} at L{}:C{} ({})",
                    sc.object_name, sc.method_name, sc.line, sc.column, sc.section
                ));
            }
        }

        self.output.clone()
    }

    pub fn print_to_file(
        &mut self,
        result:   &TokenizationResult,
        filename: &str,
    ) -> Result<String, String> {
        let content = self.print(result);

        let mut project_dir = std::env::current_dir()
            .map_err(|e| format!("Failed to get current directory: {}", e))?;

        loop {
            if project_dir.join("../../Cargo.toml").exists() { break; }
            match project_dir.parent() {
                Some(parent) => project_dir = parent.to_path_buf(),
                None         => break,
            }
        }

        let filepath = project_dir.join(filename);
        std::fs::write(&filepath, &content)
            .map_err(|e| format!("Failed to write file: {}", e))?;

        let filepath_str = filepath.to_string_lossy().to_string();
        println!("[TokenDebugPrinter] Tokens dumped to: {}", filepath_str);
        Ok(filepath_str)
    }

    // ── Internal helpers ──────────────────────────────────────────────────────

    fn print_sequential(&mut self, tokens: &[Token]) {
        for (idx, token) in tokens.iter().enumerate() {
            self.write_token(idx, token);
        }
    }

    fn print_grouped_by_line(&mut self, tokens: &[Token]) {
        if tokens.is_empty() { return; }

        let mut current_line  = 1;
        let mut line_tokens: Vec<(usize, &Token)> = Vec::new();

        for (idx, token) in tokens.iter().enumerate() {
            if token.line != current_line {
                self.print_line(current_line, &line_tokens);
                line_tokens.clear();
                current_line = token.line;
            }
            line_tokens.push((idx, token));
        }
        if !line_tokens.is_empty() {
            self.print_line(current_line, &line_tokens);
        }
    }

    fn print_line(&mut self, line_num: usize, tokens: &[(usize, &Token)]) {
        self.writeln(&format!("--- Line {} ---", line_num));
        for (idx, token) in tokens {
            self.write_token(*idx, token);
        }
        self.writeln("");
    }

    fn write_token(&mut self, idx: usize, token: &Token) {
        let mut parts = vec![format!("[{}]", idx)];

        parts.push(self.format_token_type(&token.token_type));

        if self.show_positions {
            parts.push(format!("@L{}:C{}", token.line, token.column));
        }
        if self.show_sections {
            if let Some(section) = token.section.to_option() {
                parts.push(format!("({})", section));
            }
        }

        self.writeln(&parts.join(" "));
    }

    /// Format a token type to its debug string representation.
    /// Only contains variants that the lexer actually emits.
    fn format_token_type(&self, token_type: &TokenType) -> String {
        match token_type {
            // ── Keywords / identifiers ────────────────────────────────────────
            TokenType::Keyword(k)    => format!("Keyword({})", k),
            TokenType::Identifier(i) => format!("Identifier({})", i),

            // ── Numerics ──────────────────────────────────────────────────────
            TokenType::Integer(i)            => format!("Integer({})", i),
            TokenType::Long(l)               => format!("Long({}L)", l),
            TokenType::Float(f)              => format!("Float({}f)", f),
            TokenType::Double(d)             => format!("Double({})", d),
            TokenType::ScientificNotation(s) => format!("ScientificNotation({})", s),

            // ── Strings ───────────────────────────────────────────────────────
            TokenType::String(s)               => format!("String(\"{}\")", Self::escape(s)),
            TokenType::StringSingle(s)         => format!("StringSingle('{}')", Self::escape(s)),
            TokenType::InterpolatedString(s)   => format!("InterpolatedString($\"{}\")", Self::escape(s)),

            // ── Bool / Symbol ─────────────────────────────────────────────────
            TokenType::Bool(b)  => format!("Bool({})", b),
            TokenType::Symbol(c) => format!("Symbol({})", c),

            // ── Special literals ──────────────────────────────────────────────
            TokenType::HexColor(h)  => format!("HexColor({})", h),
            TokenType::Date(d)      => format!("Date({})", d),
            TokenType::Timestamp(t) => format!("Timestamp({})", t),

            // ── Structural punctuation ────────────────────────────────────────
            TokenType::DoubleColon => "DoubleColon(::)".to_string(),
            TokenType::Arrow       => "Arrow(=>)".to_string(),
            TokenType::SwitchCase  => "SwitchCase(->)".to_string(),

            // ── Operators ─────────────────────────────────────────────────────
            TokenType::ArithmeticOp(op)       => format!("ArithmeticOp({})", op),
            TokenType::ArithmeticAssignOp(op) => format!("ArithmeticAssignOp({})", op),
            TokenType::ComparisonOp(op)       => format!("ComparisonOp({})", op),
            TokenType::LogicalOp(op)          => format!("LogicalOp({})", op),
            TokenType::BitwiseOp(op)          => format!("BitwiseOp({})", op),

            // ── Prefixed constructors ─────────────────────────────────────────
            TokenType::BlobConstructor(v)  => format!("BlobConstructor(b:{})", v),
            TokenType::TupleConstructor(v) => format!("TupleConstructor(t:{})", v),
            TokenType::RegexConstructor(v) => format!("RegexConstructor(r:{})", v),

            // ── Section keywords ──────────────────────────────────────────────
            TokenType::SectionConfig     => "SectionConfig(@CONFIG)".to_string(),
            TokenType::SectionDLM        => "SectionDLM(@DLM)".to_string(),
            TokenType::SectionEnums      => "SectionEnums(@ENUMS)".to_string(),
            TokenType::SectionImports    => "SectionImports(@IMPORTS)".to_string(),
            TokenType::SectionQuickFuncs => "SectionQuickFuncs(@QUICKFUNCS)".to_string(),
            TokenType::SectionData       => "SectionData(@DATA)".to_string(),
            TokenType::SectionSecurity   => "SectionSecurity(@SECURITY)".to_string(),

            // ── Diagnostic ───────────────────────────────────────────────────
            TokenType::Comment(c) => format!("Comment({})", Self::truncate(c, 50)),
            TokenType::Error(e)   => format!("Error({})", e),
            TokenType::EndOfFile  => "EndOfFile".to_string(),
        }
    }

    fn escape(s: &str) -> String {
        s.replace('\\', "\\\\")
            .replace('\n', "\\n")
            .replace('\t', "\\t")
            .replace('\r', "\\r")
            .replace('"',  "\\\"")
    }

    fn truncate(s: &str, max: usize) -> String {
        if s.len() <= max { s.to_string() } else { format!("{}...", &s[..max]) }
    }

    fn writeln(&mut self, text: &str) {
        self.output.push_str(text);
        self.output.push('\n');
    }
}

impl Default for TokenDebugPrinter {
    fn default() -> Self {
        Self::new(true, true, false)
    }
        }