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<String>,
}
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::<HashMap<_, _>>();
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::<HashMap<_, _>>();
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::<HashMap<_, _>>();
if has_failed {
std::process::exit(1);
}
let symbol_tables = asts
.into_iter()
.map(|(filename, (ctx, document))| (filename, (ctx, symbol_table(&arena, document))))
.collect::<HashMap<_, _>>();
if !options.dry {
let output = compile(
&symbol_tables
.into_values()
.map(|(_, document)| document)
.collect::<Box<_>>(),
);
if let Err(e) = std::fs::write(options.out, output.as_bytes()) {
eprintln!("{e}");
std::process::exit(1);
}
}
}
|