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 pub public_type_names: HashSet<String>,
46 pub public_type_schemas: BTreeMap<String, String>,
52}
53
54pub fn compile_module_artifact(
59 program: &[harn_parser::SNode],
60 module_source_file: Option<String>,
61) -> Result<ModuleArtifact, VmError> {
62 let imports = program
63 .iter()
64 .filter_map(|node| match &node.node {
65 harn_parser::Node::ImportDecl { path, is_pub } => Some(ModuleImportSpec {
66 path: path.clone(),
67 selected_names: None,
68 is_pub: *is_pub,
69 }),
70 harn_parser::Node::SelectiveImport {
71 names,
72 path,
73 is_pub,
74 } => Some(ModuleImportSpec {
75 path: path.clone(),
76 selected_names: Some(names.clone()),
77 is_pub: *is_pub,
78 }),
79 _ => None,
80 })
81 .collect();
82
83 let init_nodes: Vec<harn_parser::SNode> = program
84 .iter()
85 .filter(|sn| {
86 matches!(
87 &sn.node,
88 harn_parser::Node::VarBinding { .. }
89 | harn_parser::Node::LetBinding { .. }
90 | harn_parser::Node::ConstBinding { .. }
91 )
92 })
93 .cloned()
94 .collect();
95 let init_chunk = if init_nodes.is_empty() {
96 None
97 } else {
98 Some(
99 crate::Compiler::new()
100 .compile(&init_nodes)
101 .map_err(|e| VmError::Runtime(format!("Import init compile error: {e}")))?
102 .freeze_for_cache(),
103 )
104 };
105
106 let mut functions = BTreeMap::new();
107 let mut public_names = HashSet::new();
108 let mut public_type_names = HashSet::new();
109 for node in program {
110 let inner = match &node.node {
111 harn_parser::Node::AttributedDecl { inner, .. } => inner.as_ref(),
112 _ => node,
113 };
114 if let harn_parser::Node::TypeDecl {
115 name, is_pub: true, ..
116 } = &inner.node
117 {
118 public_type_names.insert(name.clone());
119 continue;
120 }
121 let harn_parser::Node::FnDecl {
122 name,
123 type_params,
124 params,
125 body,
126 is_pub,
127 ..
128 } = &inner.node
129 else {
130 continue;
131 };
132
133 let mut compiler = crate::Compiler::new();
134 let func_chunk = compiler
135 .compile_fn_body(type_params, params, body, module_source_file.clone())
136 .map_err(|e| VmError::Runtime(format!("Import compile error: {e}")))?;
137 functions.insert(name.clone(), func_chunk.freeze_for_cache());
138 if *is_pub {
139 public_names.insert(name.clone());
140 }
141 }
142
143 let public_type_schemas = crate::Compiler::lower_public_type_schemas(program)
144 .into_iter()
145 .map(|(name, schema)| (name, crate::stdlib::json::vm_value_to_json(&schema)))
146 .collect();
147
148 Ok(ModuleArtifact {
149 imports,
150 init_chunk,
151 functions,
152 public_names,
153 public_type_names,
154 public_type_schemas,
155 })
156}
157
158pub fn compile_module_artifact_from_source(
162 source_path: &Path,
163 source: &str,
164) -> Result<ModuleArtifact, VmError> {
165 let mut lexer = harn_lexer::Lexer::new(source);
166 let tokens = lexer.tokenize().map_err(|e| {
167 VmError::Runtime(format!(
168 "Import lex error in {}: {e}",
169 source_path.display()
170 ))
171 })?;
172 let mut parser = harn_parser::Parser::new(tokens);
173 let program = parser.parse().map_err(|e| {
174 VmError::Runtime(format!(
175 "Import parse error in {}: {e}",
176 source_path.display()
177 ))
178 })?;
179 compile_module_artifact(&program, Some(source_path.display().to_string()))
180}