knave-expand 0.13.2

The reading, scoping and expansion stages of knave
Documentation
use std::rc::Rc;

use rustc_hash::FxHashMap;

use super::module_scopes::ModuleScopes;
use crate::core::{BindingId, ModuleId, ModulePath, Namespace};
use crate::reader::Symbol;
use crate::scope::ScopeSet;

#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
enum LoadStatus {
    Loading,
    Loaded,
}

#[derive(serde::Serialize, serde::Deserialize)]
pub struct ModuleRecord {
    path: ModulePath,
    scopes: ModuleScopes,
    exports: FxHashMap<(Rc<str>, Namespace), BindingId>,
    status: LoadStatus,
}

impl ModuleRecord {
    pub fn path(&self) -> &ModulePath {
        &self.path
    }

    pub fn scopes(&self) -> ModuleScopes {
        self.scopes
    }

    pub fn symbol(&self) -> Symbol {
        Symbol::with_scopes(self.path.as_str(), self.scopes.set())
    }

    pub fn export(&mut self, name: Rc<str>, namespace: Namespace, binding: BindingId) {
        self.exports.insert((name, namespace), binding);
    }

    pub fn exported(&self, name: &str, namespace: Namespace) -> Option<BindingId> {
        self.exports.get(&(Rc::from(name), namespace)).copied()
    }

    pub fn exports(&self) -> impl Iterator<Item = (&Rc<str>, Namespace, BindingId)> {
        self.exports
            .iter()
            .map(|((name, namespace), binding)| (name, *namespace, *binding))
    }

    pub fn is_loading(&self) -> bool {
        self.status == LoadStatus::Loading
    }
}

#[derive(Default)]
pub struct ModuleRegistry {
    records: Vec<ModuleRecord>,
    by_path: FxHashMap<ModulePath, ModuleId>,
}

impl ModuleRegistry {
    pub fn register(&mut self, path: ModulePath, scopes: ModuleScopes) -> ModuleId {
        self.adopt(ModuleRecord {
            path,
            scopes,
            exports: FxHashMap::default(),
            status: LoadStatus::Loading,
        })
    }

    pub fn adopt(&mut self, record: ModuleRecord) -> ModuleId {
        let id = ModuleId::new(self.records.len());
        self.by_path.insert(record.path.clone(), id);
        self.records.push(record);
        id
    }

    pub fn into_records_after(mut self, id: ModuleId) -> Vec<ModuleRecord> {
        self.records.split_off(id.index() + 1)
    }

    pub fn find(&self, path: &ModulePath) -> Option<ModuleId> {
        self.by_path.get(path).copied()
    }

    pub fn finish_loading(&mut self, id: ModuleId) {
        self.records[id.index()].status = LoadStatus::Loaded;
    }

    pub fn loading_paths(&self) -> Vec<&ModulePath> {
        self.records
            .iter()
            .filter(|record| record.is_loading())
            .map(ModuleRecord::path)
            .collect()
    }

    pub fn with_body_scopes(&self, scopes: &ScopeSet) -> Option<ModuleId> {
        self.records
            .iter()
            .position(|record| record.scopes.set() == *scopes)
            .map(ModuleId::new)
    }

    pub fn records(&self) -> impl Iterator<Item = &ModuleRecord> {
        self.records.iter()
    }

    pub fn get(&self, id: ModuleId) -> &ModuleRecord {
        &self.records[id.index()]
    }

    pub fn get_mut(&mut self, id: ModuleId) -> &mut ModuleRecord {
        &mut self.records[id.index()]
    }
}