mod frontend; mod ast; mod ir; mod backend; mod utils; mod diagnostic; mod err; mod sema; use std::{fs::File, io::BufRead}; use clap::Parser as ArgParser; use crate::{frontend::{lexer::Lexer, parser::Parser}, ir::generator::Generator, sema::analyzer::Analyzer}; use crate::backend::generator::Generator as ASMGerenerator; /// Simple minic compiler built by Rust #[derive(ArgParser, Debug)] #[command(version, about, long_about = None)] struct Args { /// Output the generated IR code #[arg(short = 'I', long = "ir")] output_ir: bool, #[arg(skip)] output_asm: bool, #[arg(short = 't', long = "target", default_value = "ARM32")] target: String, /// Use recursive descent parsing #[arg(short = 'D', long = "recursive-descent")] recursive_descent: bool, /// Useless paramter #[arg(short = 'S', long = "symbol")] _useless: bool, /// Output file for the generated code (default: stdout) #[arg(short = 'o', long = "output")] output: Option, /// Source file to compile source: String, } fn main() { let mut args = Args::parse(); if !args.output_ir { args.output_asm = true; } if !args.recursive_descent { eprintln!("Currently only recursive descent parsing is supported. Use -D to enable it."); return; } if args.target != "ARM32" { eprintln!("Currently only ARM32 assembly output is supported. Use -t ARM32 to specify the target architecture."); return; } let source_path = std::path::Path::new(&args.source); let file = match File::open(&args.source) { Ok(f) => f, Err(e) => { eprintln!("Failed to open source file {}: {}", args.source, e); return; } }; let mut buf_reader = std::io::BufReader::new(file); let mut lexer = Lexer::new(); let mut full_text = String::new(); loop { let mut line = String::new(); let bytes_read = buf_reader.read_line(&mut line).unwrap(); if bytes_read == 0 { break; } full_text.push_str(&line); lexer.parse_next_str(&line); } let (tokens, diagnostics) = lexer.finish(); if !diagnostics.is_empty() { diagnostics.print(&format!("{}", source_path.display()), &full_text); } let mut parser = Parser::new(tokens, diagnostics); let compile_unit = parser.parse(); if !parser.diagnostics.is_empty() { parser.diagnostics.print(&format!("{}", source_path.display()), &full_text); } let mut analyzer = Analyzer::new(); let hir = analyzer.analyze(compile_unit); if !analyzer.get_diagnostics().is_empty() { analyzer.get_diagnostics().print(&format!("{}", source_path.display()), &full_text); } let mut generator = Generator::new(&analyzer); let ir = generator.emit(hir); if args.output_ir { if let Some(output_path) = args.output { match std::fs::write(&output_path, ir.iter().map(|instr| instr.to_string()).collect::>().join("\n")) { Ok(_) => println!("IR code written to {}", output_path), Err(e) => eprintln!("Failed to write IR code to {}: {}", output_path, e), } } else { for instr in ir { println!("{}", instr); } } } else if args.output_asm { let mut asm_generator = ASMGerenerator::new(); asm_generator.emit(ir); let asm_text = asm_generator.to_text(); if let Some(output_path) = args.output { match std::fs::write(&output_path, asm_text) { Ok(_) => println!("Assembly code written to {}", output_path), Err(e) => eprintln!("Failed to write assembly code to {}: {}", output_path, e), } } else { println!("{}", asm_text); } } }