Skip to main content

harn_kernel/compiler/
callable_entry.rs

1use std::sync::Arc;
2
3use harn_parser::{Node, SNode};
4
5use super::{ensure_chunk_addressable, peel_node, CompileError, CompiledCallableEntry, Compiler};
6use crate::chunk::Op;
7
8impl Compiler {
9    /// Compile a named pipeline for invocation with explicit host values.
10    pub fn compile_named_pipeline_entry(
11        mut self,
12        program: &[SNode],
13        pipeline_name: &str,
14        fixture_name: Option<&str>,
15    ) -> Result<CompiledCallableEntry, CompileError> {
16        self.prepare_module_context(program);
17        self.compile_entry_imports(program)?;
18        self.compile_top_level_declarations(program)?;
19
20        let fixture_expects_harness = fixture_name.is_some_and(|fixture_name| {
21            program.iter().any(|node| {
22                matches!(
23                    peel_node(node),
24                    Node::FnDecl { name, params, .. }
25                        if name == fixture_name
26                            && params.first().is_some_and(|param| matches!(
27                                param.type_expr.as_ref(),
28                                Some(harn_parser::TypeExpr::Named(name)) if name == "Harness"
29                            ))
30                )
31            })
32        });
33        if let Some(fixture_name) = fixture_name {
34            let fixture = self.string_constant(fixture_name);
35            self.chunk.emit_u16(Op::GetVar, fixture, self.line);
36        }
37        let target = program.iter().find(
38            |node| matches!(peel_node(node), Node::Pipeline { name, .. } if name == pipeline_name),
39        );
40        let Some(target) = target else {
41            return Err(CompileError {
42                message: format!("Unknown pipeline: {pipeline_name}"),
43                line: self.line,
44            });
45        };
46        let Node::Pipeline {
47            name,
48            body,
49            extends,
50            params,
51            ..
52        } = peel_node(target)
53        else {
54            unreachable!("pipeline target was matched above");
55        };
56        let expects_harness = params.first().is_some_and(|param| {
57            matches!(
58                param.type_expr.as_ref(),
59                Some(harn_parser::TypeExpr::Named(name)) if name == "Harness"
60            )
61        });
62        let callable =
63            self.compile_pipeline_callable(program, name, params, body, extends.as_deref())?;
64        let function_index = self.chunk.functions.len();
65        self.chunk.functions.push(Arc::new(callable));
66        self.chunk
67            .emit_u16(Op::Closure, function_index as u16, self.line);
68        if fixture_name.is_some() {
69            self.chunk.emit_u16(Op::BuildList, 2, self.line);
70        }
71        self.chunk.emit(Op::Return, self.line);
72        ensure_chunk_addressable(&self.chunk, "the callable entry", self.line)?;
73        Ok(CompiledCallableEntry {
74            bootstrap: self.chunk,
75            has_fixture: fixture_name.is_some(),
76            fixture_expects_harness,
77            expects_harness,
78        })
79    }
80
81    /// Compile a named function as a top-level callable entry.
82    pub fn compile_named_function_entry(
83        mut self,
84        program: &[SNode],
85        function_name: &str,
86    ) -> Result<CompiledCallableEntry, CompileError> {
87        self.prepare_module_context(program);
88        self.compile_entry_imports(program)?;
89        self.compile_top_level_declarations(program)?;
90        let target = program.iter().find(
91            |node| matches!(peel_node(node), Node::FnDecl { name, .. } if name == function_name),
92        );
93        let Some(target) = target else {
94            return Err(CompileError {
95                message: format!("Unknown function: {function_name}"),
96                line: self.line,
97            });
98        };
99        let expects_harness = matches!(
100            peel_node(target),
101            Node::FnDecl { params, .. }
102                if params.first().is_some_and(|param| matches!(
103                    param.type_expr.as_ref(),
104                    Some(harn_parser::TypeExpr::Named(name)) if name == "Harness"
105                ))
106        );
107        let function = self.string_constant(function_name);
108        self.chunk.emit_u16(Op::GetVar, function, self.line);
109        self.chunk.emit(Op::Return, self.line);
110        ensure_chunk_addressable(&self.chunk, "the callable entry", self.line)?;
111        Ok(CompiledCallableEntry {
112            bootstrap: self.chunk,
113            has_fixture: false,
114            fixture_expects_harness: false,
115            expects_harness,
116        })
117    }
118
119    fn compile_entry_imports(&mut self, program: &[SNode]) -> Result<(), CompileError> {
120        for node in program {
121            if matches!(
122                &node.node,
123                Node::ImportDecl { .. }
124                    | Node::SelectiveImport { .. }
125                    | Node::NamespaceImport { .. }
126            ) {
127                self.compile_node(node)?;
128            }
129        }
130        Ok(())
131    }
132}