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::LetBinding { .. } | harn_parser::Node::ConstBinding { .. }
89 )
90 })
91 .cloned()
92 .collect();
93 let init_chunk = if init_nodes.is_empty() {
94 None
95 } else {
96 Some(
97 crate::Compiler::new()
98 .compile(&init_nodes)
99 .map_err(|e| VmError::Runtime(format!("Import init compile error: {e}")))?
100 .freeze_for_cache(),
101 )
102 };
103
104 let mut functions = BTreeMap::new();
105 let mut public_names = HashSet::new();
106 let mut public_type_names = HashSet::new();
107 for node in program {
108 let inner = match &node.node {
109 harn_parser::Node::AttributedDecl { inner, .. } => inner.as_ref(),
110 _ => node,
111 };
112 if let harn_parser::Node::TypeDecl {
113 name, is_pub: true, ..
114 } = &inner.node
115 {
116 public_type_names.insert(name.clone());
117 continue;
118 }
119 let harn_parser::Node::FnDecl {
120 name,
121 type_params,
122 params,
123 body,
124 is_pub,
125 ..
126 } = &inner.node
127 else {
128 continue;
129 };
130
131 let mut compiler = crate::Compiler::new();
132 let func_chunk = compiler
133 .compile_fn_body(type_params, params, body, module_source_file.clone())
134 .map_err(|e| VmError::Runtime(format!("Import compile error: {e}")))?;
135 functions.insert(name.clone(), func_chunk.freeze_for_cache());
136 if *is_pub {
137 public_names.insert(name.clone());
138 }
139 }
140
141 let public_type_schemas = crate::Compiler::lower_public_type_schemas(program)
142 .into_iter()
143 .map(|(name, schema)| (name, crate::stdlib::json::vm_value_to_json(&schema)))
144 .collect();
145
146 Ok(ModuleArtifact {
147 imports,
148 init_chunk,
149 functions,
150 public_names,
151 public_type_names,
152 public_type_schemas,
153 })
154}
155
156pub fn compile_module_artifact_from_source(
160 source_path: &Path,
161 source: &str,
162) -> Result<ModuleArtifact, VmError> {
163 let mut lexer = harn_lexer::Lexer::new(source);
164 let tokens = lexer.tokenize().map_err(|e| {
165 VmError::Runtime(format!(
166 "Import lex error in {}: {e}",
167 source_path.display()
168 ))
169 })?;
170 let mut parser = harn_parser::Parser::new(tokens);
171 let program = parser.parse().map_err(|e| {
172 VmError::Runtime(format!(
173 "Import parse error in {}: {e}",
174 source_path.display()
175 ))
176 })?;
177 compile_module_artifact(&program, Some(source_path.display().to_string()))
178}