harn_vm/compiler/
callable_entry.rs1use 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 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 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}