Skip to main content

harn_vm/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        if let Some(fixture_name) = fixture_name {
21            let fixture = self.string_constant(fixture_name);
22            self.chunk.emit_u16(Op::GetVar, fixture, self.line);
23        }
24        let target = program.iter().find(
25            |node| matches!(peel_node(node), Node::Pipeline { name, .. } if name == pipeline_name),
26        );
27        let Some(target) = target else {
28            return Err(CompileError {
29                message: format!("Unknown pipeline: {pipeline_name}"),
30                line: self.line,
31            });
32        };
33        let Node::Pipeline {
34            name,
35            body,
36            extends,
37            params,
38            ..
39        } = peel_node(target)
40        else {
41            unreachable!("pipeline target was matched above");
42        };
43        let callable =
44            self.compile_pipeline_callable(program, name, params, body, extends.as_deref())?;
45        let function_index = self.chunk.functions.len();
46        self.chunk.functions.push(Arc::new(callable));
47        self.chunk
48            .emit_u16(Op::Closure, function_index as u16, self.line);
49        if fixture_name.is_some() {
50            self.chunk.emit_u16(Op::BuildList, 2, self.line);
51        }
52        self.chunk.emit(Op::Return, self.line);
53        ensure_chunk_addressable(&self.chunk, "the callable entry", self.line)?;
54        Ok(CompiledCallableEntry {
55            bootstrap: self.chunk,
56            has_fixture: fixture_name.is_some(),
57        })
58    }
59
60    /// Compile a named function as a top-level callable entry.
61    pub fn compile_named_function_entry(
62        mut self,
63        program: &[SNode],
64        function_name: &str,
65    ) -> Result<CompiledCallableEntry, CompileError> {
66        self.prepare_module_context(program);
67        self.compile_entry_imports(program)?;
68        self.compile_top_level_declarations(program)?;
69        let target = program.iter().any(
70            |node| matches!(peel_node(node), Node::FnDecl { name, .. } if name == function_name),
71        );
72        if !target {
73            return Err(CompileError {
74                message: format!("Unknown function: {function_name}"),
75                line: self.line,
76            });
77        }
78        let function = self.string_constant(function_name);
79        self.chunk.emit_u16(Op::GetVar, function, self.line);
80        self.chunk.emit(Op::Return, self.line);
81        ensure_chunk_addressable(&self.chunk, "the callable entry", self.line)?;
82        Ok(CompiledCallableEntry {
83            bootstrap: self.chunk,
84            has_fixture: false,
85        })
86    }
87
88    fn compile_entry_imports(&mut self, program: &[SNode]) -> Result<(), CompileError> {
89        for node in program {
90            if matches!(
91                &node.node,
92                Node::ImportDecl { .. }
93                    | Node::SelectiveImport { .. }
94                    | Node::NamespaceImport { .. }
95            ) {
96                self.compile_node(node)?;
97            }
98        }
99        Ok(())
100    }
101}