Module Loading

Module loading handles use statements, resolving imports and making external functions available.

Location

compiler/oric/src/eval/module/import.rs (~240 lines)

Import Types

Relative Imports

use './math' { add, subtract }
use '../utils' { helper }
use './http/client' { get, post }

Module Imports

use std.math { sqrt, abs }
use std.time { Duration }

Private Imports

use './internal' { ::private_helper }

Import Resolution

pub fn resolve_import(
    current_file: &Path,
    import: &Import,
) -> Result<PathBuf, ImportError> {
    match &import.path {
        ImportPath::Relative(rel_path) => {
            // Relative to current file
            let base = current_file.parent().unwrap_or(Path::new("."));
            let resolved = base.join(rel_path).with_extension("si");

            if resolved.exists() {
                Ok(resolved.canonicalize()?)
            } else {
                Err(ImportError::FileNotFound(resolved))
            }
        }

        ImportPath::Module(module_path) => {
            // Look in standard library / packages
            resolve_module_path(module_path)
        }
    }
}

Module Loading Process

impl Evaluator {
    pub fn load_import(&mut self, import: &Import) -> Result<(), EvalError> {
        let path = resolve_import(&self.current_file, import)?;

        // Check cache first
        let module_result = if let Some(cached) = self.module_cache.get(&path) {
            cached.clone()
        } else {
            // Load and evaluate module
            let result = self.load_and_evaluate_module(&path)?;
            self.module_cache.insert(path.clone(), result.clone());
            result
        };

        // Import requested items into current scope
        for item in &import.items {
            match item {
                ImportItem::Named { name, alias } => {
                    let value = module_result.exports.get(name)
                        .ok_or(ImportError::ItemNotExported(*name))?;

                    let bind_name = alias.unwrap_or(*name);
                    self.env.bind(bind_name, value.clone());
                }

                ImportItem::Private { name } => {
                    // Private import - check for :: prefix
                    let value = module_result.all_items.get(name)
                        .ok_or(ImportError::ItemNotFound(*name))?;

                    self.env.bind(*name, value.clone());
                }
            }
        }

        Ok(())
    }
}

Module Evaluation

impl Evaluator {
    fn load_and_evaluate_module(&mut self, path: &Path) -> Result<ModuleEvalResult, EvalError> {
        // Read source
        let source = std::fs::read_to_string(path)
            .map_err(|e| EvalError::IoError(e))?;

        // Create new evaluator for module
        let mut module_eval = Evaluator::new_for_module(
            self.pattern_registry.clone(),
            self.type_registry.clone(),
        );

        module_eval.current_file = path.to_path_buf();

        // Compile and evaluate
        let tokens = lexer::tokenize(&source);
        let parsed = parser::parse(&tokens)?;
        let typed = typeck::type_check(&parsed)?;
        let result = module_eval.evaluate(&typed.module)?;

        // Collect exports
        let exports = module_eval.collect_exports(&parsed.module);
        let all_items = module_eval.collect_all_items(&parsed.module);

        Ok(ModuleEvalResult {
            value: result,
            exports,
            all_items,
            output: module_eval.output,
        })
    }
}

Export Collection

fn collect_exports(&self, module: &Module) -> HashMap<Name, Value> {
    let mut exports = HashMap::new();

    // Public functions
    for func in &module.functions {
        if func.is_public {
            let value = self.env.get(func.name).cloned().unwrap();
            exports.insert(func.name, value);
        }
    }

    // Public types
    for type_def in &module.types {
        if type_def.is_public {
            // Types are exported as constructors
            exports.insert(type_def.name, Value::TypeConstructor(type_def.name));
        }
    }

    // Public configs
    for config in &module.configs {
        if config.is_public {
            let value = self.env.get(config.name).cloned().unwrap();
            exports.insert(config.name, value);
        }
    }

    exports
}

Circular Import Detection

impl Evaluator {
    fn load_with_cycle_check(
        &mut self,
        path: &Path,
        loading_stack: &mut Vec<PathBuf>,
    ) -> Result<ModuleEvalResult, EvalError> {
        // Check for cycle
        if loading_stack.contains(path) {
            return Err(EvalError::CircularImport {
                path: path.to_path_buf(),
                stack: loading_stack.clone(),
            });
        }

        loading_stack.push(path.to_path_buf());
        let result = self.load_and_evaluate_module(path);
        loading_stack.pop();

        result
    }
}

Module Cache

pub struct ModuleCache {
    cache: HashMap<PathBuf, ModuleEvalResult>,
}

impl ModuleCache {
    pub fn get(&self, path: &Path) -> Option<&ModuleEvalResult> {
        self.cache.get(path)
    }

    pub fn insert(&mut self, path: PathBuf, result: ModuleEvalResult) {
        self.cache.insert(path, result);
    }

    pub fn invalidate(&mut self, path: &Path) {
        self.cache.remove(path);
        // Also invalidate dependents (for incremental compilation)
    }
}

Standard Library Resolution

fn resolve_module_path(module_path: &[Name]) -> Result<PathBuf, ImportError> {
    // std.math -> $ORI_STDLIB/std/math.ori
    let stdlib_path = std::env::var("ORI_STDLIB")
        .unwrap_or_else(|_| "/usr/local/lib/ori/stdlib".into());

    let mut path = PathBuf::from(stdlib_path);
    for component in module_path {
        path.push(component.as_str());
    }
    path.set_extension("si");

    if path.exists() {
        Ok(path)
    } else {
        Err(ImportError::ModuleNotFound(module_path.to_vec()))
    }
}

Re-exports

// In lib.ori
pub use './internal' { Widget }  // Re-export Widget
fn process_reexports(&mut self, module: &Module, exports: &mut HashMap<Name, Value>) {
    for import in &module.imports {
        if import.is_reexport {
            for item in &import.items {
                if let ImportItem::Named { name, alias } = item {
                    let value = self.env.get(*name).cloned().unwrap();
                    exports.insert(alias.unwrap_or(*name), value);
                }
            }
        }
    }
}

Error Handling

pub enum ImportError {
    FileNotFound(PathBuf),
    ModuleNotFound(Vec<Name>),
    ItemNotExported(Name),
    ItemNotFound(Name),
    CircularImport { path: PathBuf, stack: Vec<PathBuf> },
    ParseError(ParseError),
    TypeError(TypeError),
}