use crate::boundary_ir::{BoundaryModule, CompileArtifact};
use crate::core_ir::{Decl, Stmt, Typ, UnifiedModule};
use std::path::Path;
pub fn parse_file_with<F>(path: &Path, parse_source: F) -> Result<UnifiedModule, String>
where
F: FnOnce(&str) -> Result<UnifiedModule, String>,
{
let src = std::fs::read_to_string(path).map_err(|e| format!("read {}: {e}", path.display()))?;
parse_source(&src)
}
pub fn parse_artifact_with<P, B>(
path: &Path,
parse_source: P,
extract_boundary: B,
) -> Result<CompileArtifact, String>
where
P: FnOnce(&str) -> Result<UnifiedModule, String>,
B: FnOnce(&str) -> Option<BoundaryModule>,
{
let src = std::fs::read_to_string(path).map_err(|e| format!("read {}: {e}", path.display()))?;
artifact_from_source(&src, parse_source, extract_boundary)
}
pub fn artifact_from_source<P, B>(
src: &str,
parse_source: P,
extract_boundary: B,
) -> Result<CompileArtifact, String>
where
P: FnOnce(&str) -> Result<UnifiedModule, String>,
B: FnOnce(&str) -> Option<BoundaryModule>,
{
let semantic = parse_source(src)?;
let boundary = extract_boundary(src);
Ok(match boundary {
Some(boundary) => CompileArtifact::with_boundary(semantic, boundary),
None => CompileArtifact::from_semantic(semantic),
})
}
pub fn extract_boundary_from_comment(src: &str, prefixes: &[&str]) -> Option<BoundaryModule> {
let line = src.lines().next()?;
let trimmed = line.trim();
let payload = prefixes
.iter()
.find_map(|prefix| trimmed.strip_prefix(prefix))?;
let module: BoundaryModule = serde_json::from_str(payload.trim()).ok()?;
Some(if module.layout_hash.is_empty() {
module.with_layout_hash()
} else {
module
})
}
pub fn ensure_main(decls: &mut Vec<Decl>) {
let has_main = decls
.iter()
.any(|d| matches!(d, Decl::Function { name, .. } if name == "main"));
if !has_main {
decls.push(Decl::Function {
name: "main".to_string(),
params: vec![],
ret: Typ::Void,
body: vec![Stmt::Return(None)],
type_params: vec![],
});
}
}