pub mod ast;
pub mod codegen;
pub mod diagnostic;
pub mod ir;
pub mod lexer;
pub mod parser;
pub mod semantic;
pub mod symbol;
use std::path::Path;
use diagnostic::Diagnostic;
pub type CompileResult = Result<CompileOutput, Vec<Diagnostic>>;
#[derive(Debug)]
pub struct CompileOutput {
pub code: String,
pub namespace: String,
pub warnings: Vec<Diagnostic>,
}
pub fn compile(source_path: impl AsRef<Path>, out_dir: impl AsRef<Path>) -> CompileResult {
let source_path = source_path.as_ref();
let out_dir = out_dir.as_ref();
let file_str = source_path.display().to_string();
println!("cargo:rerun-if-changed={file_str}");
let source = std::fs::read_to_string(source_path)
.map_err(|e| vec![Diagnostic::error(&file_str, format!("cannot read: {e}"))])?;
compile_source(&source, &file_str, Some(out_dir))
}
pub fn compile_source(source: &str, file_name: &str, out_dir: Option<&Path>) -> CompileResult {
let mut warnings = Vec::new();
let is_md = file_name.ends_with(".md");
let ast = if is_md {
parser::parse_md(source, file_name)?
} else {
parser::parse_pht(source, file_name)?
};
let (table, sym_diags) = symbol::build(&ast, file_name);
let (sym_errors, sym_warnings): (Vec<_>, Vec<_>) =
sym_diags.into_iter().partition(|d| d.is_error());
warnings.extend(sym_warnings);
if !sym_errors.is_empty() {
return Err(sym_errors);
}
let (module, ir_diags) = ir::lower(&ast, &table, file_name);
let (ir_errors, ir_warnings): (Vec<_>, Vec<_>) =
ir_diags.into_iter().partition(|d| d.is_error());
warnings.extend(ir_warnings);
if !ir_errors.is_empty() {
return Err(ir_errors);
}
let sem_diags = semantic::validate(&module, file_name);
let (sem_errors, sem_warnings): (Vec<_>, Vec<_>) =
sem_diags.into_iter().partition(|d| d.is_error());
warnings.extend(sem_warnings);
if !sem_errors.is_empty() {
return Err(sem_errors);
}
let code = codegen::generate(&module);
if let Some(out_dir) = out_dir {
let ns_path = module.namespace.replace('/', std::path::MAIN_SEPARATOR_STR);
let dir = out_dir.join(&ns_path);
let file = dir.join("mod.rs");
std::fs::create_dir_all(&dir).map_err(|e| {
vec![Diagnostic::error(
file_name,
format!("cannot create directory '{}': {e}", dir.display()),
)]
})?;
std::fs::write(&file, &code).map_err(|e| {
vec![Diagnostic::error(
file_name,
format!("cannot write '{}': {e}", file.display()),
)]
})?;
}
Ok(CompileOutput {
code,
namespace: module.namespace.clone(),
warnings,
})
}