knave 0.14.4

A Lisp programming language for humans and agents that infers types and ownership and compiles to readable Rust
Documentation
use super::constructor::Constructor;
use super::cover_pattern::CoverPattern;
use crate::types::Type;

#[derive(Clone, Copy)]
pub(super) struct Query<'q> {
    patterns: &'q [CoverPattern],
    types: &'q [Type],
}

pub(super) struct ExpandedQuery {
    patterns: Vec<CoverPattern>,
    types: Vec<Type>,
}

impl<'q> Query<'q> {
    pub(super) fn new(patterns: &'q [CoverPattern], types: &'q [Type]) -> Self {
        Self { patterns, types }
    }

    pub(super) fn is_empty(&self) -> bool {
        self.patterns.is_empty()
    }

    pub(super) fn first_pattern(&self) -> &'q CoverPattern {
        &self.patterns[0]
    }

    pub(super) fn first_type(&self) -> &'q Type {
        &self.types[0]
    }

    pub(super) fn rest(&self) -> Self {
        Self::new(&self.patterns[1..], &self.types[1..])
    }

    pub(super) fn expanded(
        &self,
        constructor: &Constructor,
        arguments: &[CoverPattern],
    ) -> ExpandedQuery {
        ExpandedQuery {
            patterns: [arguments, &self.patterns[1..]].concat(),
            types: constructor.columns_with(&self.types[1..]),
        }
    }
}

impl ExpandedQuery {
    pub(super) fn query(&self) -> Query<'_> {
        Query::new(&self.patterns, &self.types)
    }
}

#[cfg(test)]
mod tests {
    use super::Query;
    use crate::core::{Literal, PrimitiveType};
    use crate::types::Type;
    use crate::types::coverage::constructor::Constructor;
    use crate::types::coverage::constructor_tag::ConstructorTag;
    use crate::types::coverage::cover_pattern::CoverPattern;

    struct QueryFixture {
        patterns: Vec<CoverPattern>,
        types: Vec<Type>,
    }

    impl QueryFixture {
        fn new() -> Self {
            Self {
                patterns: vec![
                    CoverPattern::Wild,
                    CoverPattern::Literal(Literal::Bool(false)),
                ],
                types: vec![Self::text(), Self::boolean()],
            }
        }

        fn query(&self) -> Query<'_> {
            Query::new(&self.patterns, &self.types)
        }

        fn text() -> Type {
            Type::Primitive(PrimitiveType::String)
        }

        fn boolean() -> Type {
            Type::Primitive(PrimitiveType::Bool)
        }

        fn unit() -> Type {
            Type::Primitive(PrimitiveType::Unit)
        }
    }

    #[test]
    fn a_query_with_no_columns_is_empty() {
        assert!(Query::new(&[], &[]).is_empty());
    }

    #[test]
    fn its_first_column_gives_the_first_pattern_and_type() {
        let fixture = QueryFixture::new();
        assert!(matches!(
            fixture.query().first_pattern(),
            CoverPattern::Wild
        ));
        assert_eq!(fixture.query().first_type(), &QueryFixture::text());
    }

    #[test]
    fn its_rest_drops_the_first_column() {
        let fixture = QueryFixture::new();
        let rest = fixture.query().rest();
        assert!(matches!(
            rest.first_pattern(),
            CoverPattern::Literal(Literal::Bool(false))
        ));
        assert_eq!(rest.first_type(), &QueryFixture::boolean());
        assert!(rest.rest().is_empty());
    }

    #[test]
    fn expanding_puts_the_constructor_columns_in_place_of_the_first() {
        let fixture = QueryFixture::new();
        let constructor = Constructor::new(ConstructorTag::Bool(true), vec![QueryFixture::unit()]);
        let expanded = fixture
            .query()
            .expanded(&constructor, &[CoverPattern::Wild]);
        let query = expanded.query();
        assert_eq!(query.first_type(), &QueryFixture::unit());
        assert!(matches!(
            query.rest().first_pattern(),
            CoverPattern::Literal(Literal::Bool(false))
        ));
        assert_eq!(query.rest().first_type(), &QueryFixture::boolean());
    }
}