use clap::Parser; use myc::ast::environment::Environment; use myc::utils::tester; use std::fs; use std::path::PathBuf; #[derive(Parser)] #[command(author, version, about = "MYC AST Compiler & Benchmarker", long_about = None)] struct Cli { /// The script file to run or benchmark #[arg(value_name = "FILE")] file: Option, /// Run a script string directly #[arg(short, long)] eval: Option, /// Run benchmarks (Only allowed in Release mode) #[arg(short, long)] bench: bool, /// Update the benchmark baseline (Only allowed in Release mode) #[arg(short, long)] update_bench: bool, /// Dump the compiled AST #[arg(short, long)] dump: bool, /// Run with TracingObserver enabled #[arg(short, long)] trace: bool, /// Library directories to load before execution #[arg(short, long)] lib: Vec, /// Disable optimization #[arg(long)] no_opt: bool, } fn main() { let cli = Cli::parse(); let mut env = Environment::new().with_cwd(); env.optimization = !cli.no_opt; // Load libraries (Now just search paths) for lib_path in &cli.lib { env.add_search_path(lib_path); } if cli.bench || cli.update_bench { if cfg!(debug_assertions) { eprintln!("❌ ERROR: Benchmarks must be run in Release mode!"); eprintln!(" Use: cargo run --release --bin ast -- --bench"); std::process::exit(1); } println!("🚀 Running benchmarks in RELEASE mode...\n"); let filter = cli .file .as_ref() .and_then(|p| p.file_name()) .map(|n| n.to_string_lossy().to_string()); let results = tester::run_benchmarks(cli.update_bench, filter.as_deref()); for res in results { let diff = res .diff_pct .map_or(String::new(), |d| format!(" ({:+.1}%)", d)); println!("{}: {} - {:?}{}", res.status, res.name, res.median, diff); } return; } if let Some(script_str) = cli.eval { if let Err(e) = env.preload_dependencies(&script_str, None) { eprintln!("❌ Error resolving dependencies: {}", e); std::process::exit(1); } if cli.dump { match env.dump_ast(&script_str) { Ok(dump) => println!("{}", dump), Err(e) => eprintln!("Error dumping AST: {}", e), } } else if cli.trace { execute_trace(&env, &script_str); } else { execute(&env, &script_str); } } else if let Some(file_path) = cli.file { match fs::read_to_string(&file_path) { Ok(content) => { if let Err(e) = env.preload_dependencies(&content, Some(&file_path)) { eprintln!("❌ Error resolving dependencies for {:?}:\n{}", file_path, e); std::process::exit(1); } if cli.dump { match env.dump_ast(&content) { Ok(dump) => println!("{}", dump), Err(e) => eprintln!("Error dumping AST: {}", e), } } else if cli.trace { execute_trace(&env, &content); } else { execute(&env, &content); } } Err(e) => { eprintln!("Error reading file {:?}: {}", file_path, e); std::process::exit(1); } } } else { println!("MYC AST Compiler CLI. Use --help for usage."); } } fn execute(env: &Environment, source: &str) { let result = env.compile(source); for diag in &result.diagnostics.items { let level = format!("{:?}", diag.level).to_uppercase(); let loc = diag .identity .as_ref() .and_then(|id| id.location) .map(|l| format!(" at {}:{}", l.line, l.col)) .unwrap_or_default(); eprintln!("{}{} : {}", level, loc, diag.message); } if result.diagnostics.has_errors() { std::process::exit(1); } match env.run_script_compiled(result.ast.unwrap()) { Ok(res) => println!("{}", res), Err(e) => { eprintln!("Runtime Error: {}", e); std::process::exit(1); } } } fn execute_trace(env: &Environment, source: &str) { match env.run_debug(source) { Ok((Ok(result), logs)) => { for line in logs { println!("{}", line); } println!("Result: {}", result); } Ok((Err(e), logs)) => { for line in logs { println!("{}", line); } eprintln!("Runtime Error: {}", e); std::process::exit(1); } Err(e) => { eprintln!("Compilation Error: {}", e); std::process::exit(1); } } }