harn_vm/
module_artifact.rs1use std::collections::{BTreeMap, HashSet};
13use std::path::Path;
14
15use serde::{Deserialize, Serialize};
16
17use crate::chunk::{CachedChunk, CachedCompiledFunction};
18use crate::value::VmError;
19
20#[derive(Clone, Debug, Serialize, Deserialize)]
24pub struct ModuleImportSpec {
25 pub path: String,
26 pub selected_names: Option<Vec<String>>,
27 pub is_pub: bool,
28}
29
30#[derive(Clone, Debug, Serialize, Deserialize)]
36pub struct ModuleArtifact {
37 pub imports: Vec<ModuleImportSpec>,
38 pub init_chunk: Option<CachedChunk>,
39 pub functions: BTreeMap<String, CachedCompiledFunction>,
40 pub public_names: HashSet<String>,
41}
42
43pub fn compile_module_artifact(
48 program: &[harn_parser::SNode],
49 module_source_file: Option<String>,
50) -> Result<ModuleArtifact, VmError> {
51 let imports = program
52 .iter()
53 .filter_map(|node| match &node.node {
54 harn_parser::Node::ImportDecl { path, is_pub } => Some(ModuleImportSpec {
55 path: path.clone(),
56 selected_names: None,
57 is_pub: *is_pub,
58 }),
59 harn_parser::Node::SelectiveImport {
60 names,
61 path,
62 is_pub,
63 } => Some(ModuleImportSpec {
64 path: path.clone(),
65 selected_names: Some(names.clone()),
66 is_pub: *is_pub,
67 }),
68 _ => None,
69 })
70 .collect();
71
72 let init_nodes: Vec<harn_parser::SNode> = program
73 .iter()
74 .filter(|sn| {
75 matches!(
76 &sn.node,
77 harn_parser::Node::VarBinding { .. }
78 | harn_parser::Node::LetBinding { .. }
79 | harn_parser::Node::ConstBinding { .. }
80 )
81 })
82 .cloned()
83 .collect();
84 let init_chunk = if init_nodes.is_empty() {
85 None
86 } else {
87 Some(
88 crate::Compiler::new()
89 .compile(&init_nodes)
90 .map_err(|e| VmError::Runtime(format!("Import init compile error: {e}")))?
91 .freeze_for_cache(),
92 )
93 };
94
95 let mut functions = BTreeMap::new();
96 let mut public_names = HashSet::new();
97 for node in program {
98 let inner = match &node.node {
99 harn_parser::Node::AttributedDecl { inner, .. } => inner.as_ref(),
100 _ => node,
101 };
102 let harn_parser::Node::FnDecl {
103 name,
104 type_params,
105 params,
106 body,
107 is_pub,
108 ..
109 } = &inner.node
110 else {
111 continue;
112 };
113
114 let mut compiler = crate::Compiler::new();
115 let func_chunk = compiler
116 .compile_fn_body(type_params, params, body, module_source_file.clone())
117 .map_err(|e| VmError::Runtime(format!("Import compile error: {e}")))?;
118 functions.insert(name.clone(), func_chunk.freeze_for_cache());
119 if *is_pub {
120 public_names.insert(name.clone());
121 }
122 }
123
124 Ok(ModuleArtifact {
125 imports,
126 init_chunk,
127 functions,
128 public_names,
129 })
130}
131
132pub fn compile_module_artifact_from_source(
136 source_path: &Path,
137 source: &str,
138) -> Result<ModuleArtifact, VmError> {
139 let mut lexer = harn_lexer::Lexer::new(source);
140 let tokens = lexer.tokenize().map_err(|e| {
141 VmError::Runtime(format!(
142 "Import lex error in {}: {e}",
143 source_path.display()
144 ))
145 })?;
146 let mut parser = harn_parser::Parser::new(tokens);
147 let program = parser.parse().map_err(|e| {
148 VmError::Runtime(format!(
149 "Import parse error in {}: {e}",
150 source_path.display()
151 ))
152 })?;
153 compile_module_artifact(&program, Some(source_path.display().to_string()))
154}