use std::collections::BTreeMap;
use harn_parser::{Node, SNode};
use serde::{Deserialize, Serialize};
use crate::{Chunk, CompiledFunction};
use super::{peel_node, CompileError, Compiler};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PortableImport {
pub path: String,
pub target: String,
pub selected_names: Option<Vec<String>>,
pub namespace_alias: Option<String>,
pub is_pub: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PortableExportKind {
Function,
Pipeline,
Tool,
Skill,
EvalPack,
Struct,
Enum,
Interface,
Type,
Variable,
}
impl PortableExportKind {
pub const fn has_runtime_value(self) -> bool {
!matches!(self, Self::Interface | Self::Type)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PortableSourceModule {
pub id: String,
pub source: String,
#[serde(default)]
pub imports: Vec<PortableImport>,
#[serde(default)]
pub exports: BTreeMap<String, PortableExportKind>,
#[serde(default)]
pub imported_enum_candidates: Vec<String>,
#[serde(default)]
pub source_file: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PortableSourcePackage {
pub root_source: String,
#[serde(default)]
pub root_imports: Vec<PortableImport>,
#[serde(default)]
pub modules: Vec<PortableSourceModule>,
}
#[derive(Debug, Clone)]
pub struct CompiledPortableModule {
pub id: String,
pub imports: Vec<PortableImport>,
pub init: Option<Chunk>,
pub functions: BTreeMap<String, CompiledFunction>,
pub exports: BTreeMap<String, PortableExportKind>,
}
impl Compiler {
pub fn compile_portable_module(
mut self,
id: impl Into<String>,
program: &[SNode],
imports: Vec<PortableImport>,
exports: BTreeMap<String, PortableExportKind>,
imported_enum_candidates: &[String],
source_file: Option<String>,
) -> Result<CompiledPortableModule, CompileError> {
self.prepare_module_context(program);
self.add_imported_enum_candidates(imported_enum_candidates.iter().cloned());
let init_nodes: Vec<SNode> = program
.iter()
.filter(|sn| {
let inner = peel_node(sn);
matches!(
inner,
Node::LetBinding { .. }
| Node::ConstBinding { .. }
| Node::EnumDecl { is_pub: true, .. }
| Node::ToolDecl { .. }
| Node::SkillDecl { .. }
| Node::EvalPackDecl { .. }
)
})
.cloned()
.collect();
let init = if init_nodes.is_empty() {
None
} else {
Some(Compiler::with_options(self.options).compile_module_init(
program,
&init_nodes,
imported_enum_candidates,
)?)
};
let mut functions = BTreeMap::new();
for node in program {
let inner = peel_node(node);
match inner {
Node::StructDecl { name, fields, .. } => {
let constructor = self.compile_struct_constructor(name, fields)?;
functions.insert(name.clone(), constructor);
}
Node::Pipeline {
name,
params,
body,
extends,
..
} => {
let function = self.compile_pipeline_callable(
program,
name,
params,
body,
extends.as_deref(),
)?;
functions.insert(name.clone(), function);
}
Node::FnDecl {
name,
type_params,
params,
body,
..
} => {
let mut compiler = Compiler::with_options(self.options);
compiler.prepare_module_context(program);
compiler.add_imported_enum_candidates(imported_enum_candidates.iter().cloned());
let mut function =
compiler.compile_fn_body(type_params, params, body, source_file.clone())?;
function.name = name.clone();
functions.insert(name.clone(), function);
}
_ => {}
}
}
Ok(CompiledPortableModule {
id: id.into(),
imports,
init,
functions,
exports,
})
}
}