summaryrefslogtreecommitdiff
path: root/src/main.rs
blob: 8a51b8ee1852011f2eaa9909112360956771d9ef (plain)
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);
		}
	}
}