use std::default::Default; use std::io::Write as _; use std::path::PathBuf; use clap::{Parser as ClapParser, Subcommand}; use lila::report::ToReport; use lila::source::SourceCache; use lila_ast::Module; use lila_checking::TypeCheckModule; use lila_jit as jit; use lila_parsing::{DefaultParser as LilaParser, Parser as _}; /// Experimental compiler for lila #[derive(ClapParser, Debug)] #[command(author = "Romain P. ")] #[command(version, about, long_about = None)] struct Cli { #[command(subcommand)] command: Commands, } #[derive(Subcommand, Debug)] enum Commands { Parse { /// Paths to the source files files: Vec, /// Dump the AST to stdout #[arg(long)] dump_ast: bool, }, Check { /// Paths to the source files files: Vec, /// Dump the AST to stdout #[arg(long)] dump_ast: bool, }, Compile { /// Paths to the source files files: Vec, /// Dump the CLIR to stdout #[arg(long)] dump_clir: bool, }, Run { /// Paths to the source files files: Vec, #[arg(long)] dump_clir: bool, }, } fn parse(files: &[String]) -> Vec { let mut parser = LilaParser::default(); let paths = files.iter().map(std::path::Path::new); paths .enumerate() .map(|(i, path)| match parser.parse_file(path, i as u32) { Ok(module) => module, Err(e) => panic!("Parsing error: {e:#?}"), }) .collect() } fn check(modules: &mut Vec, source_cache: &mut SourceCache) -> usize { let mut failed_count = 0; for module in modules { if let Err(errors) = module.type_check() { failed_count += 1; for error in errors { error .to_report(module) .eprint(&mut *source_cache) .expect("cannot write error to stderr"); std::io::stderr().write_all("\n".as_bytes()).unwrap(); } } } failed_count } fn main() -> anyhow::Result<()> { let cli = Cli::parse(); match &cli.command { Commands::Parse { files, dump_ast } => { let modules = parse(files); if *dump_ast { for module in &modules { println!("{:#?}", &module); } } println!("Parsing OK"); Ok(()) } Commands::Check { files, dump_ast } => { let mut source_cache = SourceCache { paths: files.iter().map(PathBuf::from).collect(), file_cache: ariadne::FileCache::default(), }; let mut modules = parse(files); let failed_count = check(&mut modules, &mut source_cache); if *dump_ast { for module in &modules { println!("{:#?}", &module); } } if failed_count > 0 { anyhow::bail!("{}/{} files failed to compile", failed_count, files.len()); } Ok(()) } Commands::Compile { files, dump_clir } | Commands::Run { files, dump_clir } => { if files.is_empty() { println!("No input files"); return Ok(()); } let mut source_cache = SourceCache { paths: files.iter().map(PathBuf::from).collect(), file_cache: ariadne::FileCache::default(), }; let mut jit = jit::JIT::default(); jit.dump_clir = *dump_clir; for (id, file) in files.iter().enumerate() { println!("Compiling {file}"); match jit.compile_file(file, id as u32, &mut source_cache) { Err(e) => eprintln!("{e}"), Ok(code) => { if let Commands::Run { .. } = cli.command { let ret = unsafe { let code_fn: unsafe extern "sysv64" fn() -> i32 = std::mem::transmute(code); code_fn() }; println!("Main returned {ret}"); } } } } Ok(()) } } }