use std::{collections::BTreeMap, path::Path};
use crate::ffi::sys::{self, PreprocessorDefine};
use cxx::UniquePtr;
pub type SleighCompilerResponse = sys::CompileResponse;
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("non-zero exit code: {exit_code}", exit_code = .0.exit_code)]
NonZeroExitCode(SleighCompilerResponse),
#[error("internal compiler error: {0}")]
InternalCompilerError(Box<dyn std::error::Error + Send + Sync>),
}
pub type Result<T> = std::result::Result<T, Error>;
pub struct SleighCompiler {
compiler: UniquePtr<sys::SleighCompileProxy>,
}
pub struct SleighCompilerOptions {
pub defines: BTreeMap<String, String>,
pub unnecessary_pcode_warnings: bool,
pub lenient_conflict: bool,
pub all_collision_warning: bool,
pub all_nop_warning: bool,
pub dead_temp_warning: bool,
pub enforce_local_keyword: bool,
pub large_temporary_warning: bool,
pub case_sensitive_register_names: bool,
pub debug_output: bool,
}
impl Default for SleighCompilerOptions {
fn default() -> Self {
Self {
defines: Default::default(),
unnecessary_pcode_warnings: false,
lenient_conflict: true,
all_collision_warning: false,
all_nop_warning: false,
dead_temp_warning: false,
enforce_local_keyword: false,
large_temporary_warning: false,
case_sensitive_register_names: false,
debug_output: false,
}
}
}
impl SleighCompiler {
pub fn new(options: SleighCompilerOptions) -> Self {
let mut compiler = sys::new_sleigh_compiler();
let mut defines = cxx::CxxVector::new();
for (name, value) in options.defines {
defines.pin_mut().push(PreprocessorDefine { name, value });
}
compiler.pin_mut().set_all_options(
defines.as_ref().unwrap(),
options.unnecessary_pcode_warnings,
options.lenient_conflict,
options.all_collision_warning,
options.all_nop_warning,
options.dead_temp_warning,
options.enforce_local_keyword,
options.large_temporary_warning,
options.case_sensitive_register_names,
options.debug_output,
);
Self { compiler }
}
pub fn compile(
&mut self,
input_slaspec_path: impl AsRef<Path>,
output_sla_path: impl AsRef<Path>,
) -> Result<SleighCompilerResponse> {
cxx::let_cxx_string!(filein = input_slaspec_path.as_ref().as_os_str().as_encoded_bytes());
cxx::let_cxx_string!(fileout = output_sla_path.as_ref().as_os_str().as_encoded_bytes());
let response = self
.compiler
.pin_mut()
.run_compilation(filein.as_ref(), fileout.as_ref())
.map_err(|err| Error::InternalCompilerError(Box::new(err)))?;
if response.exit_code == 0 {
Ok(response)
} else {
Err(Error::NonZeroExitCode(response))
}
}
}
impl Default for SleighCompiler {
fn default() -> Self {
Self::new(SleighCompilerOptions::default())
}
}