use std::sync::Arc;
use harn_parser::{Node, SNode, TypedParam};
use crate::chunk::{CompiledFunction, Op};
use super::error::CompileError;
use super::{peel_node, Compiler};
impl Compiler {
pub(crate) fn compile_pipeline_callable(
&self,
program: &[SNode],
name: &str,
params: &[TypedParam],
body: &[SNode],
extends: Option<&str>,
) -> Result<CompiledFunction, CompileError> {
let mut pipeline_compiler = self.nested_body();
pipeline_compiler.collect_module_enum_catalog(program);
if pipeline_compiler.enum_names.insert("Result".to_string()) {
Self::seed_builtin_variant_owners(&mut pipeline_compiler.enum_variant_owners);
}
Self::collect_struct_layouts(program, &mut pipeline_compiler.struct_layouts);
Self::collect_interface_methods(program, &mut pipeline_compiler.interface_methods);
pipeline_compiler.collect_type_aliases(program);
pipeline_compiler.declare_param_slots(params);
pipeline_compiler.record_param_types(params);
pipeline_compiler.emit_default_preamble(params)?;
pipeline_compiler.emit_type_checks(params);
pipeline_compiler.compile_with_pipeline_captures(program, body, extends, |compiler| {
if let Some(parent_name) = extends {
compiler.compile_parent_pipeline(program, parent_name)?;
}
compiler.compile_block(body)
})?;
pipeline_compiler.drain_finallys_to_floor(0)?;
pipeline_compiler.chunk.emit(Op::Nil, self.line);
pipeline_compiler.chunk.emit(Op::Return, self.line);
let param_slots = pipeline_compiler.compile_param_slots(params);
let has_runtime_type_checks =
CompiledFunction::has_runtime_type_checks_for_params(¶m_slots);
super::ensure_chunk_addressable(
&pipeline_compiler.chunk,
&format!("pipeline `{name}`"),
self.line,
)?;
Ok(CompiledFunction {
name: name.to_string(),
type_params: Vec::new(),
nominal_type_names: pipeline_compiler.nominal_type_names(),
params: param_slots,
default_start: TypedParam::default_start(params),
chunk: Arc::new(pipeline_compiler.chunk),
is_generator: false,
is_stream: false,
has_rest_param: params.last().is_some_and(|param| param.rest),
has_runtime_type_checks,
})
}
pub(super) fn compile_with_pipeline_captures<'a, T>(
&mut self,
program: &'a [SNode],
body: &'a [SNode],
extends: Option<&str>,
compile: impl FnOnce(&mut Self) -> Result<T, CompileError>,
) -> Result<T, CompileError> {
let mut lineage = Vec::new();
Self::append_parent_pipeline_bodies(program, extends, &mut lineage);
lineage.push(body);
let match_patterns = self.lexical_match_pattern_catalog();
let captured =
harn_parser::lexical::captured_bindings_in_pipeline_lineage(&lineage, &match_patterns);
let saved = std::mem::replace(&mut self.captured_bindings, captured);
let result = compile(self);
self.captured_bindings = saved;
result
}
pub(super) fn compile_parent_pipeline(
&mut self,
program: &[SNode],
parent_name: &str,
) -> Result<(), CompileError> {
let parent = program.iter().find(
|node| matches!(peel_node(node), Node::Pipeline { name, .. } if name == parent_name),
);
let Some(parent) = parent else {
return Ok(());
};
let Node::Pipeline { body, extends, .. } = peel_node(parent) else {
return Ok(());
};
let saved_catalog = self.enum_catalog_snapshot();
let result: Result<(), CompileError> = (|| {
if let Some(grandparent) = extends {
self.compile_parent_pipeline(program, grandparent)?;
}
for statement in body {
self.compile_discarded_stmt(statement)?;
}
Ok(())
})();
self.restore_enum_catalog(saved_catalog);
result
}
fn append_parent_pipeline_bodies<'a>(
program: &'a [SNode],
parent_name: Option<&str>,
lineage: &mut Vec<&'a [SNode]>,
) {
let Some(parent_name) = parent_name else {
return;
};
let Some(parent) = program.iter().find(
|node| matches!(peel_node(node), Node::Pipeline { name, .. } if name == parent_name),
) else {
return;
};
let Node::Pipeline { body, extends, .. } = peel_node(parent) else {
return;
};
Self::append_parent_pipeline_bodies(program, extends.as_deref(), lineage);
lineage.push(body);
}
}