knave 0.12.7

A Lisp programming language for humans and agents that infers types and ownership and compiles to readable Rust
Documentation
use rustc_hash::FxHashSet;

use crate::core::BindingId;
use crate::expand::Program;
use crate::modules::ModuleSources;

pub struct DeferredDefinitions;

impl DeferredDefinitions {
    pub fn of(program: &Program) -> FxHashSet<BindingId> {
        let always_checked = Self::always_checked(program);
        program
            .modules
            .iter()
            .filter(|module| module.id != program.entry)
            .filter(|module| ModuleSources::is_embedded_prelude(&module.path))
            .flat_map(|module| module.definitions())
            .map(|(binding, _)| binding)
            .filter(|binding| !always_checked.contains(binding))
            .chain(program.pending_definitions.undecoded())
            .collect()
    }

    fn always_checked(program: &Program) -> FxHashSet<BindingId> {
        let methods = program
            .modules
            .iter()
            .flat_map(|module| &module.items)
            .flat_map(|item| item.kind.method_bodies());
        methods
            .chain(program.string_view)
            .chain(program.checked_roots.iter().copied())
            .collect()
    }
}

#[cfg(test)]
mod tests {
    use super::DeferredDefinitions;
    use crate::core::{BindingId, CoreModule, ModulePath};
    use crate::expand::Program;
    use crate::modules::ModuleSources;
    use crate::types::TypedSource;

    struct PreludeModuleFixture {
        source: TypedSource,
    }

    impl PreludeModuleFixture {
        const MODULE: &str = "
            (trait named
              (name (fn ((ref self)) i64) (self) 1)
              (rank (fn ((ref self)) i64)))
            (defstruct car :year i64)
            (impl named car
              (rank (c) (:year c)))
            (defn helper () 1)
            (defn main ())";
        const METHOD_BODIES: usize = 2;

        fn new() -> Self {
            Self::moved_to(ModuleSources::PRELUDE)
        }

        fn moved_to(path: &str) -> Self {
            let mut source = TypedSource::expand(Self::MODULE);
            source.move_entry_to(ModulePath::new(path));
            source.detach_entry();
            Self { source }
        }

        fn program(&self) -> &Program {
            self.source.program()
        }

        fn module(&self) -> &CoreModule {
            self.program()
                .modules
                .last()
                .expect("the module is expanded")
        }

        fn definition(&self, name: &str) -> BindingId {
            let bindings = &self.program().bindings;
            self.module()
                .definitions()
                .map(|(binding, _)| binding)
                .find(|binding| bindings.get(*binding).name() == name)
                .expect("the module defines the name")
        }

        fn defers(&self, name: &str) -> bool {
            self.defers_binding(self.definition(name))
        }

        fn defers_binding(&self, binding: BindingId) -> bool {
            DeferredDefinitions::of(self.program()).contains(&binding)
        }

        fn methods(&self) -> Vec<BindingId> {
            self.module()
                .items
                .iter()
                .flat_map(|item| item.kind.method_bodies())
                .collect()
        }
    }

    #[test]
    fn a_prelude_function_waits_for_a_reference() {
        assert!(PreludeModuleFixture::new().defers("helper"));
    }

    #[test]
    fn a_user_module_under_the_prelude_directory_never_waits() {
        assert!(!PreludeModuleFixture::moved_to("prelude/extra").defers("helper"));
    }

    #[test]
    fn an_entry_named_like_the_prelude_never_waits() {
        let mut source = TypedSource::expand(PreludeModuleFixture::MODULE);
        source.move_entry_to(ModulePath::new(ModuleSources::PRELUDE));
        let fixture = PreludeModuleFixture { source };
        assert!(!fixture.defers("helper"));
    }

    #[test]
    fn a_written_definition_never_waits() {
        let source = TypedSource::expand("(defn helper () 1) (defn main ())");
        let program = source.program();
        let helper = program
            .entry_module()
            .definitions()
            .map(|(binding, _)| binding)
            .next()
            .expect("the module defines a function");
        assert!(!DeferredDefinitions::of(program).contains(&helper));
    }

    #[test]
    fn every_method_body_never_waits() {
        let fixture = PreludeModuleFixture::new();
        let methods = fixture.methods();
        assert_eq!(methods.len(), PreludeModuleFixture::METHOD_BODIES);
        assert!(!methods.iter().any(|method| fixture.defers_binding(*method)));
    }

    #[test]
    fn a_checked_root_never_waits() {
        let mut fixture = PreludeModuleFixture::new();
        let helper = fixture.definition("helper");
        fixture.source.check_also(helper);
        assert!(!fixture.defers("helper"));
    }

    #[test]
    fn the_string_view_never_waits() {
        let source = TypedSource::expand("(defn main ())");
        let program = source.program();
        let view = program.string_view.expect("the prelude views strings");
        assert!(!DeferredDefinitions::of(program).contains(&view));
    }
}