knave 0.14.11

A Lisp programming language for humans and agents that infers types and ownership and compiles to readable Rust
Documentation
use super::rust_types::RustTypeTable;
use super::structs::StructTable;
use super::ty::Type;
use super::view_coercion::ViewCoercion;
use crate::core::BindingId;

#[derive(Clone, Copy)]
pub(super) struct Declarations<'a> {
    structs: &'a StructTable,
    rust_types: &'a RustTypeTable,
}

impl<'a> Declarations<'a> {
    pub(super) fn new(structs: &'a StructTable, rust_types: &'a RustTypeTable) -> Self {
        Self {
            structs,
            rust_types,
        }
    }

    pub(super) fn field_type(
        &self,
        owner: BindingId,
        label: &str,
        arguments: &[Type],
    ) -> Option<Type> {
        self.structs
            .get(owner)
            .and_then(|definition| definition.instantiated_field(label, arguments))
    }

    pub(super) fn viewed_items(&self, coercion: ViewCoercion) -> Option<Vec<(Type, Type)>> {
        coercion.viewed_items(self.rust_types)
    }
}

#[cfg(test)]
mod tests {
    use super::Declarations;
    use crate::core::{BindingId, PrimitiveType};
    use crate::types::view_coercion::ViewCoercion;
    use crate::types::{Type, TypedProgram, TypedSource};

    struct DeclarationsFixture {
        program: TypedProgram,
    }

    impl DeclarationsFixture {
        const SOURCE: &str = "
            (struct reading (value i64))
            (defn readings ((items (vec i64))) items)
            (defn window ((items (ref (slice u8)))) (len items))
            (defn main () (println \"Leaf\"))";

        fn new() -> Self {
            Self {
                program: TypedSource::expand(Self::SOURCE).typed(),
            }
        }

        fn declarations(&self) -> Declarations<'_> {
            Declarations::new(&self.program.structs, &self.program.rust_types)
        }

        fn reading(&self) -> BindingId {
            self.program
                .structs
                .iter()
                .map(|(binding, _)| binding)
                .find(|binding| self.program.bindings.get(*binding).name() == "reading")
                .expect("the source declares the struct")
        }

        fn parameter_referent(&self, function: &str) -> Type {
            let definition = self
                .program
                .modules
                .iter()
                .flat_map(|module| &module.definitions)
                .find(|definition| self.program.bindings.get(definition.binding).name() == function)
                .expect("the source defines the function");
            let Type::Function(_, parameters, _) = &definition.scheme.body else {
                panic!("a defn has a function type");
            };
            parameters[0].referent().clone()
        }
    }

    #[test]
    fn a_declared_field_has_its_declared_type() {
        let fixture = DeclarationsFixture::new();
        let reading = fixture.reading();
        assert_eq!(
            fixture.declarations().field_type(reading, "value", &[]),
            Some(Type::Primitive(PrimitiveType::I64))
        );
    }

    #[test]
    fn an_undeclared_field_has_no_type() {
        let fixture = DeclarationsFixture::new();
        let reading = fixture.reading();
        assert_eq!(
            fixture.declarations().field_type(reading, "missing", &[]),
            None
        );
    }

    #[test]
    fn a_slice_relates_its_items_to_the_elements_of_a_vector() {
        let fixture = DeclarationsFixture::new();
        let slice = fixture.parameter_referent("window");
        let vector = fixture.parameter_referent("readings");
        assert_eq!(
            fixture
                .declarations()
                .viewed_items(ViewCoercion::new(&slice, &vector)),
            Some(vec![(
                Type::Primitive(PrimitiveType::U8),
                Type::Primitive(PrimitiveType::I64)
            )])
        );
    }
}