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 { .. } | harn_parser::Node::LetBinding { .. }
78 )
79 })
80 .cloned()
81 .collect();
82 let init_chunk = if init_nodes.is_empty() {
83 None
84 } else {
85 Some(
86 crate::Compiler::new()
87 .compile(&init_nodes)
88 .map_err(|e| VmError::Runtime(format!("Import init compile error: {e}")))?
89 .freeze_for_cache(),
90 )
91 };
92
93 let mut functions = BTreeMap::new();
94 let mut public_names = HashSet::new();
95 for node in program {
96 let inner = match &node.node {
97 harn_parser::Node::AttributedDecl { inner, .. } => inner.as_ref(),
98 _ => node,
99 };
100 let harn_parser::Node::FnDecl {
101 name,
102 type_params,
103 params,
104 body,
105 is_pub,
106 ..
107 } = &inner.node
108 else {
109 continue;
110 };
111
112 let mut compiler = crate::Compiler::new();
113 let func_chunk = compiler
114 .compile_fn_body(type_params, params, body, module_source_file.clone())
115 .map_err(|e| VmError::Runtime(format!("Import compile error: {e}")))?;
116 functions.insert(name.clone(), func_chunk.freeze_for_cache());
117 if *is_pub {
118 public_names.insert(name.clone());
119 }
120 }
121
122 Ok(ModuleArtifact {
123 imports,
124 init_chunk,
125 functions,
126 public_names,
127 })
128}
129
130pub fn compile_module_artifact_from_source(
134 source_path: &Path,
135 source: &str,
136) -> Result<ModuleArtifact, VmError> {
137 let mut lexer = harn_lexer::Lexer::new(source);
138 let tokens = lexer.tokenize().map_err(|e| {
139 VmError::Runtime(format!(
140 "Import lex error in {}: {e}",
141 source_path.display()
142 ))
143 })?;
144 let mut parser = harn_parser::Parser::new(tokens);
145 let program = parser.parse().map_err(|e| {
146 VmError::Runtime(format!(
147 "Import parse error in {}: {e}",
148 source_path.display()
149 ))
150 })?;
151 compile_module_artifact(&program, Some(source_path.display().to_string()))
152}