palladium/resolver/
mod.rs1use crate::ast::{Import, Program};
5use crate::errors::{CompileError, Result};
6use crate::lexer::Lexer;
7use crate::parser::Parser;
8use std::collections::{HashMap, HashSet};
9use std::fs;
10use std::path::PathBuf;
11
12#[derive(Debug, Clone)]
14pub struct ModuleInfo {
15 pub path: PathBuf,
16 pub ast: Program,
17 pub exports: HashSet<String>, }
19
20pub struct ModuleResolver {
22 search_paths: Vec<PathBuf>,
24 loaded_modules: HashMap<String, ModuleInfo>,
26}
27
28impl Default for ModuleResolver {
29 fn default() -> Self {
30 Self::new()
31 }
32}
33
34impl ModuleResolver {
35 pub fn new() -> Self {
36 let mut search_paths = vec![
37 PathBuf::from("."), PathBuf::from("examples"), ];
40
41 if let Ok(exe_path) = std::env::current_exe() {
43 if let Some(parent) = exe_path.parent() {
44 let std_lib = parent.join("std");
45 if std_lib.exists() {
46 search_paths.push(std_lib.clone());
47 }
48 }
49 }
50
51 if let Ok(pd_path) = std::env::var("PALLADIUM_PATH") {
53 for path in pd_path.split(':') {
54 search_paths.push(PathBuf::from(path));
55 }
56 }
57
58 Self {
59 search_paths,
60 loaded_modules: HashMap::new(),
61 }
62 }
63
64 pub fn add_search_path(&mut self, path: PathBuf) {
66 self.search_paths.push(path);
67 }
68
69 pub fn resolve_program(&mut self, program: &Program) -> Result<HashMap<String, ModuleInfo>> {
71 let mut resolved = HashMap::new();
72
73 for import in &program.imports {
74 let module_name = self.import_to_module_name(import);
75 if !self.loaded_modules.contains_key(&module_name) {
76 self.load_module(&module_name)?;
77 }
78
79 if let Some(module_info) = self.loaded_modules.get(&module_name) {
80 let filtered_info = if let Some(items) = &import.items {
82 self.filter_module_info(module_info, items)
83 } else {
84 module_info.clone()
85 };
86
87 let key = if let Some(alias) = &import.alias {
89 alias.clone()
90 } else {
91 module_name
92 };
93 resolved.insert(key, filtered_info);
94 }
95 }
96
97 Ok(resolved)
98 }
99
100 fn import_to_module_name(&self, import: &Import) -> String {
102 import.path.join("::")
103 }
104
105 fn filter_module_info(&self, module_info: &ModuleInfo, items: &[String]) -> ModuleInfo {
107 let item_set: HashSet<_> = items.iter().cloned().collect();
108
109 ModuleInfo {
110 path: module_info.path.clone(),
111 ast: module_info.ast.clone(),
112 exports: module_info
113 .exports
114 .iter()
115 .filter(|export_name| item_set.contains(*export_name))
116 .cloned()
117 .collect(),
118 }
119 }
120
121 fn load_module(&mut self, module_name: &str) -> Result<()> {
123 let module_path = module_name.replace("::", "/");
125 let file_name = format!("{}.pd", module_path);
126
127 let mut module_file = None;
129 for search_path in &self.search_paths {
130 let full_path = search_path.join(&file_name);
131 if full_path.exists() {
132 module_file = Some(full_path);
133 break;
134 }
135 }
136
137 let module_file = module_file
138 .ok_or_else(|| CompileError::Generic(format!("Module '{}' not found", module_name)))?;
139
140 let source = fs::read_to_string(&module_file).map_err(|e| {
142 CompileError::Generic(format!("Failed to read module '{}': {}", module_name, e))
143 })?;
144
145 let mut lexer = Lexer::new(&source);
146 let tokens = lexer.collect_tokens()?;
147 let mut parser = Parser::new(tokens);
148 let ast = parser.parse()?;
149
150 let mut exports = HashSet::new();
152 for item in &ast.items {
153 match item {
154 crate::ast::Item::Function(func) => {
155 if matches!(func.visibility, crate::ast::Visibility::Public) {
156 exports.insert(func.name.clone());
157 }
158 }
159 crate::ast::Item::Struct(struct_def) => {
160 if matches!(struct_def.visibility, crate::ast::Visibility::Public) {
161 exports.insert(struct_def.name.clone());
162 }
163 }
164 crate::ast::Item::Enum(enum_def) => {
165 exports.insert(enum_def.name.clone());
169 }
170 crate::ast::Item::Trait(trait_def) => {
171 if matches!(trait_def.visibility, crate::ast::Visibility::Public) {
172 exports.insert(trait_def.name.clone());
173 }
174 }
175 crate::ast::Item::Impl(_) => {
176 }
178 crate::ast::Item::TypeAlias(type_alias) => {
179 if matches!(type_alias.visibility, crate::ast::Visibility::Public) {
180 exports.insert(type_alias.name.clone());
181 }
182 }
183 crate::ast::Item::Macro(_) => {
184 }
186 }
187 }
188
189 let _sub_modules = self.resolve_program(&ast)?;
191
192 let module_info = ModuleInfo {
194 path: module_file,
195 ast,
196 exports,
197 };
198
199 self.loaded_modules
200 .insert(module_name.to_string(), module_info);
201
202 Ok(())
203 }
204
205 pub fn get_loaded_modules(&self) -> &HashMap<String, ModuleInfo> {
207 &self.loaded_modules
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214
215 #[test]
216 fn test_module_name_conversion() {
217 let resolver = ModuleResolver::new();
218 let import = Import {
219 path: vec!["std".to_string(), "io".to_string()],
220 items: None,
221 alias: None,
222 span: crate::errors::Span::dummy(),
223 };
224
225 assert_eq!(resolver.import_to_module_name(&import), "std::io");
226 }
227}