Refactor module loading to improve clarity
The module loading logic has been refactored to enhance readability and
maintainability. Key changes include:
* **Path Handling**: Simplified path construction using `join` for
better clarity and consistency.
* **Error Reporting**: Improved error messages during parsing by
utilizing `parser.diagnostics.format_errors()`.
* **Module Loading Logic**:
* The `loaded_modules` set now uses `insert()` to both add new
modules and efficiently check for duplicates, returning `false`
if the module was already loaded.
* The recursive call to `collect_dependencies` is now placed
before processing the current file's AST, ensuring a topological
order of dependency loading.
* **Helper Function Scope**: `extract_use_directives` is now a static
method as it doesn't depend on the `ModuleLoader` instance.
This commit is contained in:
@@ -48,28 +48,26 @@ impl ModuleLoader {
|
|||||||
let file_name = format!("{}.myc", relative_path);
|
let file_name = format!("{}.myc", relative_path);
|
||||||
|
|
||||||
let check_path = |base: &Path| -> Option<Vec<PathBuf>> {
|
let check_path = |base: &Path| -> Option<Vec<PathBuf>> {
|
||||||
// Check for file first
|
let file_p = base.join(&file_name);
|
||||||
let mut file_p = base.to_path_buf();
|
|
||||||
file_p.push(&file_name);
|
|
||||||
if file_p.is_file() && let Ok(canon) = file_p.canonicalize() {
|
if file_p.is_file() && let Ok(canon) = file_p.canonicalize() {
|
||||||
return Some(vec![canon]);
|
return Some(vec![canon]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for directory
|
let dir_p = base.join(&relative_path);
|
||||||
let mut dir_p = base.to_path_buf();
|
|
||||||
dir_p.push(&relative_path);
|
|
||||||
if dir_p.is_dir() {
|
if dir_p.is_dir() {
|
||||||
let mut files = Vec::new();
|
let mut files = Vec::new();
|
||||||
if let Ok(entries) = std::fs::read_dir(&dir_p) {
|
if let Ok(entries) = std::fs::read_dir(&dir_p) {
|
||||||
let mut valid_entries: Vec<_> = entries.filter_map(|e| e.ok()).collect();
|
let mut valid_entries: Vec<_> = entries.filter_map(|e| e.ok()).collect();
|
||||||
valid_entries.sort_by_key(|e| e.path());
|
valid_entries.sort_by_key(|e| e.path());
|
||||||
for entry in valid_entries {
|
for entry in valid_entries {
|
||||||
let p = entry.path();
|
let Ok(file_type) = entry.file_type() else { continue };
|
||||||
if p.is_file()
|
if file_type.is_file() {
|
||||||
&& p.extension().is_some_and(|ext| ext == "myc")
|
let p = entry.path();
|
||||||
&& let Ok(canon) = p.canonicalize()
|
if p.extension().is_some_and(|ext| ext == "myc")
|
||||||
{
|
&& let Ok(canon) = p.canonicalize()
|
||||||
files.push(canon);
|
{
|
||||||
|
files.push(canon);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,12 +78,10 @@ impl ModuleLoader {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
// 1. Try relative to base_path
|
|
||||||
if let Some(paths) = check_path(base_path) {
|
if let Some(paths) = check_path(base_path) {
|
||||||
return Ok(paths);
|
return Ok(paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Try configured search paths
|
|
||||||
for sp in self.search_paths.borrow().iter() {
|
for sp in self.search_paths.borrow().iter() {
|
||||||
if let Some(paths) = check_path(sp) {
|
if let Some(paths) = check_path(sp) {
|
||||||
return Ok(paths);
|
return Ok(paths);
|
||||||
@@ -105,22 +101,20 @@ impl ModuleLoader {
|
|||||||
base_path: &Path,
|
base_path: &Path,
|
||||||
all_files: &mut Vec<(PathBuf, SyntaxNode)>,
|
all_files: &mut Vec<(PathBuf, SyntaxNode)>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let directives = self.extract_use_directives(source);
|
let directives = Self::extract_use_directives(source);
|
||||||
|
|
||||||
for module_path in directives {
|
for module_path in directives {
|
||||||
let abs_paths = self.resolve_module_paths(&module_path, base_path)?;
|
let abs_paths = self.resolve_module_paths(&module_path, base_path)?;
|
||||||
|
|
||||||
for abs_path in abs_paths {
|
for abs_path in abs_paths {
|
||||||
if self.loaded_modules.borrow().contains(&abs_path) {
|
// insert() returns false if the path was already present — skip in that case.
|
||||||
|
if !self.loaded_modules.borrow_mut().insert(abs_path.clone()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.loaded_modules.borrow_mut().insert(abs_path.clone());
|
|
||||||
|
|
||||||
let lib_source = std::fs::read_to_string(&abs_path)
|
let lib_source = std::fs::read_to_string(&abs_path)
|
||||||
.map_err(|e| format!("Failed to read module {:?}: {}", abs_path, e))?;
|
.map_err(|e| format!("Failed to read module {:?}: {}", abs_path, e))?;
|
||||||
|
|
||||||
// Recurse: load transitive dependencies before the current file (topological order)
|
|
||||||
let lib_base = abs_path.parent().unwrap_or(Path::new("."));
|
let lib_base = abs_path.parent().unwrap_or(Path::new("."));
|
||||||
self.collect_dependencies(&lib_source, lib_base, all_files)?;
|
self.collect_dependencies(&lib_source, lib_base, all_files)?;
|
||||||
|
|
||||||
@@ -130,7 +124,8 @@ impl ModuleLoader {
|
|||||||
if parser.diagnostics.has_errors() {
|
if parser.diagnostics.has_errors() {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Parser error in module {:?}:\n{}",
|
"Parser error in module {:?}:\n{}",
|
||||||
abs_path, parser.diagnostics.items[0].message
|
abs_path,
|
||||||
|
parser.diagnostics.format_errors()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +137,7 @@ impl ModuleLoader {
|
|||||||
|
|
||||||
/// Extracts `#use <path>` directives from the leading lines of `source`.
|
/// Extracts `#use <path>` directives from the leading lines of `source`.
|
||||||
/// Stops at the first non-directive, non-comment line.
|
/// Stops at the first non-directive, non-comment line.
|
||||||
fn extract_use_directives(&self, source: &str) -> Vec<String> {
|
fn extract_use_directives(source: &str) -> Vec<String> {
|
||||||
let mut paths = Vec::new();
|
let mut paths = Vec::new();
|
||||||
for line in source.lines() {
|
for line in source.lines() {
|
||||||
let trimmed = line.trim();
|
let trimmed = line.trim();
|
||||||
|
|||||||
Reference in New Issue
Block a user