use std::collections::HashMap; use bpaf::{Bpaf, Parser}; use bumpalo::Bump; use kuht::{ analyze::symbol_table, compile::compile, diagnostics::{self, DiagnosticsContext}, parse::parse_document, tokenize::tokenize, }; #[derive(Debug, Clone, Bpaf)] struct Options { /// Show any errors without compiling #[bpaf(switch)] dry: bool, // The location to place the resulting HTML file #[bpaf(short)] out: String, /// The files to compile to an HTML #[bpaf(positional("FILES"))] files: Vec, } fn main() { let options = options().run(); let mut has_failed = false; let sources = options .files .into_iter() .map(|filename| { let source = match std::fs::read(&filename) { Ok(source) => source, Err(error) => { eprintln!("Error while reading {filename}: {error}"); std::process::exit(1); } }; (filename, source) }) .filter_map(|(filename, source)| { let source: &'static [u8] = Vec::leak(source); let Some(source) = diagnostics::run_character_diagnostics(source) else { has_failed = true; return None; }; Some((filename, source)) }) .collect::>(); let arena = Bump::new(); let tokens = sources .into_iter() .map(|(filename, source)| { ( filename, (DiagnosticsContext::new(source), tokenize(source, &arena)), ) }) .filter(|(_, (ctx, tokens))| { let failed = diagnostics::run_token_diagnostics(ctx, tokens).is_none(); has_failed |= failed; !failed }) .collect::>(); let asts = tokens .into_iter() .map(|(filename, (ctx, tokens))| (filename, (ctx, parse_document(tokens, &arena)))) .filter(|(_, (ctx, document))| { let failed = diagnostics::run_ast_diagnostics(ctx, document).is_none(); has_failed |= failed; !failed }) .collect::>(); if has_failed { std::process::exit(1); } let symbol_tables = asts .into_iter() .map(|(filename, (ctx, document))| (filename, (ctx, symbol_table(&arena, document)))) .collect::>(); if !options.dry { let output = compile( &symbol_tables .into_values() .map(|(_, document)| document) .collect::>(), ); if let Err(e) = std::fs::write(options.out, output.as_bytes()) { eprintln!("{e}"); std::process::exit(1); } } }