#!/usr/bin/env rust-script
//! # Thepg: A Parser Generator in Rust
//!
//! Thepg is a parser generator that takes an Extended Backus–Naur Form (ENBF) grammar
//! and produces compile-ready Rust code for parsing according to that grammar.
//!
//! ## Features
//! - Supports LL(1) and LL(k) parsing strategies
//! - Generates lexing-free parsers
//! - Outputs all code in a single Rust file
//!
//! ## Usage
//! ```bash
//! cargo run --release -- grammar.enbf -o parser.rs --grammar-type LL1
//! ```

use std::env;
use std::fs;
use std::io::{self, Write};
use std::collections::HashSet;

/// Represents a grammar rule
#[derive(Debug, Clone)]
struct Rule {
    head: String,
    bodies: Vec<Vec<String>>,
}

/// Represents the parsed grammar
#[derive(Debug)]
struct Grammar {
    rules: Vec<Rule>,
    terminals: HashSet<String>,
}

impl Grammar {
    /// Parses the ENBF grammar from a string
    fn parse(enbf: &str) -> Result<Self, String> {
        let mut rules = Vec::new();
        let mut non_terminals = HashSet::new(); // Kept for potential future use
        let mut terminals = HashSet::new();

        for (line_no, line) in enbf.lines().enumerate() {
            let line = line.trim();
            if line.is_empty() || line.starts_with("#") {
                continue;
            }
            let parts: Vec<&str> = line.split("::=").collect();
            if parts.len() != 2 {
                return Err(format!("Invalid rule at line {}", line_no + 1));
            }
            let head = parts[0].trim().to_string();
            non_terminals.insert(head.clone());
            let bodies_str = parts[1];
            let bodies: Vec<Vec<String>> = bodies_str.split('|').map(|body| {
                body.trim().split_whitespace().map(|s| s.to_string()).collect()
            }).collect();
            rules.push(Rule { head, bodies });
        }

        // Identify terminals
        for rule in &rules {
            for body in &rule.bodies {
                for symbol in body {
                    if !symbol.starts_with("<") && !symbol.ends_with(">") && !symbol.starts_with("\"") {
                        terminals.insert(symbol.clone());
                    }
                }
            }
        }

        // Remove non-terminals from terminals set
        for nt in &non_terminals {
            terminals.remove(nt);
        }

        Ok(Grammar {
            rules,
            terminals,
        })
    }

    /// Generates Rust code for the parser
    fn generate_rust(&self, grammar_type: &str) -> String {
        let mut code = String::new();

        // Header
        code.push_str("// Generated by Thepg\n");
        code.push_str("use std::str::Chars;\n");
        code.push_str("use std::iter::Peekable;\n\n");

        // Token definitions
        code.push_str("#[derive(Debug, PartialEq, Clone)]\n");
        code.push_str("pub enum Token {\n");
        for terminal in &self.terminals {
            code.push_str(&format!("    {},\n", Self::sanitize_token(terminal)));
        }
        code.push_str("    EOF,\n");
        code.push_str("}\n\n");

        // Lexer (if needed)
        // Since parsers are lexer-free, tokens are determined by the parser directly

        // Parser struct
        code.push_str("pub struct Parser<'a> {\n");
        code.push_str("    input: Peekable<Chars<'a>>,\n");
        code.push_str("    current_token: Token,\n");
        code.push_str("}\n\n");

        // Implement Parser
        code.push_str("impl<'a> Parser<'a> {\n");
        code.push_str("    pub fn new(input: &'a str) -> Self {\n");
        code.push_str("        let mut chars = input.chars().peekable();\n");
        code.push_str("        let first_token = Parser::next_token(&mut chars);\n");
        code.push_str("        Parser {\n");
        code.push_str("            input: chars,\n");
        code.push_str("            current_token: first_token,\n");
        code.push_str("        }\n");
        code.push_str("    }\n\n");

        // Tokenizer
        code.push_str("    fn next_token(chars: &mut Peekable<Chars>) -> Token {\n");
        code.push_str("        while let Some(&c) = chars.peek() {\n");
        code.push_str("            match c {\n");
        for terminal in &self.terminals {
            if terminal.starts_with("\"") && terminal.ends_with("\"") {
                let symbol = &terminal[1..terminal.len()-1];
                code.push_str(&format!("                '{}' => {{ chars.next(); Token::{} }},\n",
                                       symbol, Self::sanitize_token(terminal)));
            } else {
                // For simplicity, treat as identifier or number
                code.push_str(&format!("                _ if c.is_alphabetic() => Token::{},\n",
                                       Self::sanitize_token(terminal)));
            }
        }
        code.push_str("                _ => { chars.next(); Token::EOF },\n");
        code.push_str("            }\n");
        code.push_str("        }\n");
        code.push_str("        Token::EOF\n");
        code.push_str("    }\n\n");

        // Match and consume functions
        code.push_str("    fn eat(&mut self, token: Token) -> Result<(), String> {\n");
        code.push_str("        if self.current_token == token {\n");
        code.push_str("            self.current_token = Parser::next_token(&mut self.input);\n");
        code.push_str("            Ok(())\n");
        code.push_str("        } else {\n");
        code.push_str("            Err(format!(\"Expected {:?}, found {:?}\", token, self.current_token))\n");
        code.push_str("        }\n");
        code.push_str("    }\n\n");

        // Generate parsing functions
        for rule in &self.rules {
            let func_name = Self::sanitize_func(&rule.head);
            code.push_str(&format!("    pub fn {}(&mut self) -> Result<(), String> {{\n", func_name));
            if grammar_type.to_uppercase() == "LL1" {
                code.push_str("        match self.current_token {\n");
                // TODO: Compute FIRST sets for LL(1)
                // For simplicity, assume first symbol determines the production
                for body in &rule.bodies {
                    if let Some(first) = body.first() {
                        let token = if first.starts_with("\"") && first.ends_with("\"") {
                            Self::sanitize_token(first)
                        } else {
                            Self::sanitize_token(&format!("\"{}\"", first))
                        };
                        code.push_str(&format!("            Token::{} => {{\n", token));
                        for symbol in body {
                            if symbol.starts_with("<") && symbol.ends_with(">") {
                                let sub_func = Self::sanitize_func(&symbol[1..symbol.len()-1]);
                                code.push_str(&format!("                self.{}()?;\n", sub_func));
                            } else {
                                let tok = Self::sanitize_token(symbol);
                                code.push_str(&format!("                self.eat(Token::{})?;\n", tok));
                            }
                        }
                        code.push_str("                Ok(())\n");
                        code.push_str("            },\n");
                    }
                }
                // Error case
                code.push_str("            _ => Err(format!(\"Unexpected token {:?} in parsing {}\", self.current_token, \"");
                code.push_str(&rule.head);
                code.push_str("\")),\n");
                code.push_str("        }\n");
            } else {
                // LL(k) not implemented in this example
                code.push_str("        // LL(k) parsing not implemented yet\n");
                code.push_str("        Ok(())\n");
            }
            code.push_str("    }\n\n");
        }

        // Start symbol
        if let Some(start_rule) = self.rules.first() {
            let func_name = Self::sanitize_func(&start_rule.head);
            code.push_str("    pub fn parse(&mut self) -> Result<(), String> {\n");
            code.push_str(&format!("        self.{}()?\n", func_name));
            code.push_str("            .and_then(|_| {\n");
            code.push_str("                if self.current_token == Token::EOF {\n");
            code.push_str("                    Ok(())\n");
            code.push_str("                } else {\n");
            code.push_str("                    Err(\"Unexpected tokens after parsing complete\".to_string())\n");
            code.push_str("                }\n");
            code.push_str("            })\n");
            code.push_str("    }\n");
        }

        // End of impl
        code.push_str("}\n\n");

        // Example usage
        code.push_str("#[cfg(test)]\n");
        code.push_str("mod tests {\n");
        code.push_str("    use super::*;\n\n");
        code.push_str("    #[test]\n");
        code.push_str("    fn test_parser() {\n");
        code.push_str("        let input = \"your_input_here\";\n");
        code.push_str("        let mut parser = Parser::new(input);\n");
        code.push_str("        assert!(parser.parse().is_ok());\n");
        code.push_str("    }\n");
        code.push_str("}\n");

        code
    }

    /// Sanitizes token names to be Rust-compatible
    fn sanitize_token(token: &str) -> String {
        token.trim_matches('"').replace("+", "Plus")
                                 .replace("-", "Minus")
                                 .replace("*", "Star")
                                 .replace("/", "Slash")
                                 .replace("(", "LParen")
                                 .replace(")", "RParen")
                                 .to_uppercase()
    }

    /// Sanitizes function names to be Rust-compatible
    fn sanitize_func(func: &str) -> String {
        func.to_lowercase().replace("<", "").replace(">", "").replace("-", "_")
    }
}

/// Entry point for Thepg
fn main() -> Result<(), String> {
    let args: Vec<String> = env::args().collect();
    if args.len() < 3 {
        return Err("Usage: thepg <input.enbf> -o <output.rs> --grammar-type <LL1|LLk>".to_string());
    }

    let input_file = &args[1];
    let mut output_file = String::from("parser.rs");
    let mut grammar_type = "LL1".to_string();

    let mut i = 2;
    while i < args.len() {
        match args[i].as_str() {
            "-o" => {
                if i + 1 < args.len() {
                    output_file = args[i + 1].clone();
                    i += 2;
                } else {
                    return Err("Expected output file after -o".to_string());
                }
            },
            "--grammar-type" => {
                if i + 1 < args.len() {
                    grammar_type = args[i + 1].clone();
                    i += 2;
                } else {
                    return Err("Expected grammar type after --grammar-type".to_string());
                }
            },
            _ => i += 1,
        }
    }

    let enbf = fs::read_to_string(input_file)
        .map_err(|e| format!("Failed to read input file: {}", e))?;
    
    let grammar = Grammar::parse(&enbf)?;

    let rust_code = grammar.generate_rust(&grammar_type);

    fs::write(&output_file, rust_code)
        .map_err(|e| format!("Failed to write output file: {}", e))?;

    println!("Parser generated successfully in {}", output_file);
    Ok(())
}

continue writing this parser generator