knave 0.13.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::constructor_catalog::ConstructorCatalog;
use super::constructor_tag::ConstructorTag;
use super::cover_pattern::CoverPattern;
use super::pattern_matrix::PatternMatrix;
use super::query::Query;
use super::witness::Witness;
use crate::core::{BindingId, Literal};
use crate::types::Type;

pub(super) struct Usefulness<'c, 'a> {
    catalog: &'c ConstructorCatalog<'a>,
    expanding: Vec<BindingId>,
}

impl<'c, 'a> Usefulness<'c, 'a> {
    pub(super) fn new(catalog: &'c ConstructorCatalog<'a>) -> Self {
        Self {
            catalog,
            expanding: Vec::new(),
        }
    }

    pub(super) fn is_useful(&mut self, matrix: &PatternMatrix, query: Query) -> bool {
        if query.is_empty() {
            return matrix.is_empty();
        }
        if matrix.is_empty() {
            return true;
        }
        if matrix.has_wild_row() {
            return false;
        }
        match query.first_pattern() {
            CoverPattern::Wild => self.is_useful_wild(matrix, query),
            CoverPattern::Variant { owner, index, .. } => {
                let tag = ConstructorTag::Variant {
                    owner: *owner,
                    index: *index,
                };
                self.is_useful_variant(matrix, query, tag)
            }
            CoverPattern::Struct { owner, .. } => {
                self.is_useful_tagged(matrix, query, ConstructorTag::Struct(*owner))
            }
            CoverPattern::Literal(literal) => self.is_useful_literal(matrix, query, literal),
        }
    }

    fn is_useful_wild(&mut self, matrix: &PatternMatrix, query: Query) -> bool {
        let owner = self.catalog.value_owner(query.first_type());
        if owner.is_some_and(|owner| self.expanding.contains(&owner)) {
            return self.is_useful_recursive_wild(matrix, query);
        }
        self.while_expanding(owner, |usefulness| {
            match usefulness.catalog.constructors(query.first_type()) {
                Some(constructors) => constructors.iter().any(|constructor| {
                    usefulness.is_useful_constructor(matrix, query, constructor)
                }),
                None => usefulness.is_useful(&matrix.default_rows(), query.rest()),
            }
        })
    }

    fn is_useful_recursive_wild(&mut self, matrix: &PatternMatrix, query: Query) -> bool {
        let default = matrix.default_rows();
        if !default.is_empty() && !self.is_useful(&default, query.rest()) {
            return false;
        }
        let concrete = matrix.concrete_rows();
        self.catalog
            .constructors(query.first_type())
            .unwrap_or_default()
            .iter()
            .any(|constructor| self.is_useful_constructor(&concrete, query, constructor))
    }

    fn is_useful_constructor(
        &mut self,
        matrix: &PatternMatrix,
        query: Query,
        constructor: &Constructor,
    ) -> bool {
        let expanded = query.expanded(constructor, &constructor.wild_arguments());
        self.is_useful_specialized(matrix, constructor, expanded.query())
    }

    fn is_useful_variant(
        &mut self,
        matrix: &PatternMatrix,
        query: Query,
        tag: ConstructorTag,
    ) -> bool {
        let owner = self
            .catalog
            .value_owner(query.first_type())
            .filter(|owner| !self.expanding.contains(owner));
        self.while_expanding(owner, |usefulness| {
            usefulness.is_useful_tagged(matrix, query, tag)
        })
    }

    fn is_useful_tagged(
        &mut self,
        matrix: &PatternMatrix,
        query: Query,
        tag: ConstructorTag,
    ) -> bool {
        let constructor = self
            .catalog
            .constructors(query.first_type())
            .and_then(|constructors| {
                constructors
                    .into_iter()
                    .find(|constructor| constructor.tag() == tag)
            });
        match constructor {
            Some(constructor) => {
                let expanded = query.expanded(&constructor, query.first_pattern().arguments());
                self.is_useful_specialized(matrix, &constructor, expanded.query())
            }
            None => false,
        }
    }

    fn is_useful_specialized(
        &mut self,
        matrix: &PatternMatrix,
        constructor: &Constructor,
        query: Query,
    ) -> bool {
        self.is_useful(&matrix.specialize(constructor), query)
    }

    fn is_useful_literal(
        &mut self,
        matrix: &PatternMatrix,
        query: Query,
        literal: &Literal,
    ) -> bool {
        self.is_useful(&matrix.specialize_literal(literal), query.rest())
    }

    pub(super) fn witnesses(
        &mut self,
        matrix: &PatternMatrix,
        types: &[Type],
    ) -> Vec<Vec<Witness>> {
        if types.is_empty() {
            return match matrix.is_empty() {
                true => vec![Vec::new()],
                false => Vec::new(),
            };
        }
        let (owner, constructors) = self.expansion(&types[0]);
        self.while_expanding(owner, |usefulness| match constructors {
            Some(constructors) => constructors
                .iter()
                .flat_map(|constructor| {
                    usefulness.constructor_witnesses(matrix, types, constructor)
                })
                .collect(),
            None => usefulness.wild_witnesses(matrix, types),
        })
    }

    fn constructor_witnesses(
        &mut self,
        matrix: &PatternMatrix,
        types: &[Type],
        constructor: &Constructor,
    ) -> Vec<Vec<Witness>> {
        let columns = constructor.columns_with(&types[1..]);
        self.witnesses(&matrix.specialize(constructor), &columns)
            .into_iter()
            .map(|row| {
                let (arguments, rest) = row.split_at(constructor.arity());
                let mut witness = Vec::with_capacity(rest.len() + 1);
                witness.push(Witness::built_by(constructor, arguments));
                witness.extend_from_slice(rest);
                witness
            })
            .collect()
    }

    fn wild_witnesses(&mut self, matrix: &PatternMatrix, types: &[Type]) -> Vec<Vec<Witness>> {
        self.witnesses(&matrix.default_rows(), &types[1..])
            .into_iter()
            .map(|row| {
                let mut witness = Vec::with_capacity(row.len() + 1);
                witness.push(Witness::Wild);
                witness.extend(row);
                witness
            })
            .collect()
    }

    fn expansion(&self, ty: &Type) -> (Option<BindingId>, Option<Vec<Constructor>>) {
        match self.catalog.value_owner(ty) {
            Some(owner) if !self.expanding.contains(&owner) => {
                (Some(owner), self.catalog.constructors(ty))
            }
            Some(_) => (None, None),
            None => (None, self.catalog.constructors(ty)),
        }
    }

    fn while_expanding<T>(
        &mut self,
        owner: Option<BindingId>,
        work: impl FnOnce(&mut Self) -> T,
    ) -> T {
        if let Some(owner) = owner {
            self.expanding.push(owner);
        }
        let result = work(self);
        if owner.is_some() {
            self.expanding.pop();
        }
        result
    }
}