use std::{
ffi::OsString,
path::{Path, PathBuf},
};
use crate::{
parse::{FileSystemResolver, SourceResolver},
Diagnostic, GlyphMap, ParseTree,
};
use super::{
error::{CompilerError, DiagnosticSet},
Compilation, Opts,
};
#[derive(Debug)]
pub struct Compiler<'a> {
root_path: OsString,
project_root: Option<PathBuf>,
glyph_map: &'a GlyphMap,
verbose: bool,
opts: Opts,
resolver: Option<Box<dyn SourceResolver>>,
}
impl<'a> Compiler<'a> {
pub fn new(root_path: impl Into<OsString>, glyph_map: &'a GlyphMap) -> Self {
Compiler {
root_path: root_path.into(),
glyph_map,
opts: Default::default(),
verbose: false,
resolver: Default::default(),
project_root: Default::default(),
}
}
pub fn with_resolver(mut self, resolver: impl SourceResolver + 'static) -> Self {
self.resolver = Some(Box::new(resolver));
self
}
pub fn verbose(mut self, verbose: bool) -> Self {
self.verbose = verbose;
self
}
pub fn with_project_root(mut self, project_root: impl Into<PathBuf>) -> Self {
self.project_root = Some(project_root.into());
self
}
pub fn with_opts(mut self, opts: Opts) -> Self {
self.opts = opts;
self
}
pub fn compile(self) -> Result<Compilation, CompilerError> {
let resolver = self.resolver.unwrap_or_else(|| {
let project_root = self.project_root.unwrap_or_else(|| {
Path::new(&self.root_path)
.parent()
.map(PathBuf::from)
.unwrap_or_default()
});
Box::new(FileSystemResolver::new(project_root))
});
let (tree, diagnostics) =
crate::parse::ParseContext::parse(self.root_path, Some(self.glyph_map), resolver)?
.generate_parse_tree();
print_warnings_return_errors(diagnostics, &tree, self.verbose)
.map_err(CompilerError::ParseFail)?;
let diagnostics = super::validate(&tree, self.glyph_map);
print_warnings_return_errors(diagnostics, &tree, self.verbose)
.map_err(CompilerError::ValidationFail)?;
let mut ctx = super::CompilationCtx::new(self.glyph_map, tree.source_map());
ctx.compile(&tree.typed_root());
print_warnings_return_errors(std::mem::take(&mut ctx.errors), &tree, self.verbose)
.map_err(CompilerError::CompilationFail)?;
Ok(ctx.build().unwrap()) }
pub fn compile_binary(self) -> Result<Vec<u8>, CompilerError> {
let opts = self.opts.clone();
let glyph_map = self.glyph_map;
Ok(self.compile()?.assemble(glyph_map, opts)?.build())
}
}
fn print_warnings_return_errors(
mut diagnostics: Vec<Diagnostic>,
tree: &ParseTree,
verbose: bool,
) -> Result<(), DiagnosticSet> {
diagnostics.sort_unstable_by_key(|diag| diag.level);
let split_at = diagnostics
.iter()
.position(|x| !x.is_error())
.unwrap_or(diagnostics.len());
let warnings = diagnostics.split_off(split_at);
if verbose {
for w in warnings {
eprintln!("{}", tree.format_diagnostic(&w));
}
}
if diagnostics.is_empty() {
Ok(())
} else {
Err(DiagnosticSet {
messages: diagnostics,
sources: tree.sources.clone(),
})
}
}