knave 0.13.3

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

use rustc_hash::FxHashMap;

use crate::core::BindingId;
use crate::source::Span;

#[derive(Clone)]
pub enum MoveSite {
    Whole(Span),
    Field { span: Span, place: Rc<str> },
}

impl MoveSite {
    pub fn span(&self) -> Span {
        match self {
            Self::Whole(span) | Self::Field { span, .. } => *span,
        }
    }

    pub fn moved_place<'a>(&'a self, name: &'a str) -> &'a str {
        match self {
            Self::Whole(_) => name,
            Self::Field { place, .. } => place,
        }
    }
}

pub type FieldPath = Vec<Rc<str>>;

#[derive(Clone)]
enum MovedValue {
    Whole(Span),
    Fields(Vec<MovedField>),
}

#[derive(Clone)]
struct MovedField {
    path: FieldPath,
    site: MoveSite,
}

#[derive(Clone, Default)]
pub struct MovedValues {
    values: FxHashMap<BindingId, MovedValue>,
}

impl MovedValues {
    pub fn site(&self, binding: BindingId) -> Option<MoveSite> {
        self.site_overlapping(binding, std::iter::empty())
    }

    pub fn site_overlapping<'p>(
        &self,
        binding: BindingId,
        read: impl Iterator<Item = &'p str> + Clone,
    ) -> Option<MoveSite> {
        self.values.get(&binding)?.site_overlapping(read)
    }

    pub fn move_whole(&mut self, binding: BindingId, span: Span) {
        self.values.insert(binding, MovedValue::Whole(span));
    }

    pub fn move_field(&mut self, binding: BindingId, path: FieldPath, site: MoveSite) {
        let moved = self
            .values
            .entry(binding)
            .or_insert_with(|| MovedValue::Fields(Vec::new()));
        moved.add_field(MovedField { path, site });
    }

    pub fn moved_since(&self, earlier: &MovedValues) -> MovedValues {
        let values = self
            .values
            .iter()
            .filter_map(|(binding, moved)| {
                let since = moved.moved_since(earlier.values.get(binding))?;
                Some((*binding, since))
            })
            .collect();
        MovedValues { values }
    }

    pub fn retain(&mut self, mut keep: impl FnMut(BindingId) -> bool) {
        self.values.retain(|binding, _| keep(*binding));
    }

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

    pub fn merge(&mut self, other: MovedValues) {
        for (binding, moved) in other.values {
            match self.values.get_mut(&binding) {
                Some(existing) => existing.absorb(moved),
                None => {
                    self.values.insert(binding, moved);
                }
            }
        }
    }
}

impl MovedValue {
    fn site_overlapping<'p>(
        &self,
        read: impl Iterator<Item = &'p str> + Clone,
    ) -> Option<MoveSite> {
        match self {
            Self::Whole(span) => Some(MoveSite::Whole(*span)),
            Self::Fields(fields) => fields
                .iter()
                .find(|field| field.overlaps(read.clone()))
                .map(|field| field.site.clone()),
        }
    }

    fn add_field(&mut self, field: MovedField) {
        if let Self::Fields(fields) = self
            && !fields.iter().any(|moved| moved.path == field.path)
        {
            fields.push(field);
        }
    }

    fn moved_since(&self, earlier: Option<&MovedValue>) -> Option<MovedValue> {
        match (self, earlier) {
            (_, None) | (Self::Whole(_), Some(Self::Fields(_))) => Some(self.clone()),
            (_, Some(Self::Whole(_))) => None,
            (Self::Fields(fields), Some(Self::Fields(earlier))) => {
                Self::fields_moved_since(fields, earlier)
            }
        }
    }

    fn fields_moved_since(fields: &[MovedField], earlier: &[MovedField]) -> Option<MovedValue> {
        let since: Vec<MovedField> = fields
            .iter()
            .filter(|field| !earlier.iter().any(|moved| moved.path == field.path))
            .cloned()
            .collect();
        (!since.is_empty()).then_some(Self::Fields(since))
    }

    fn absorb(&mut self, other: MovedValue) {
        match other {
            Self::Whole(_) => *self = other,
            Self::Fields(fields) => fields.into_iter().for_each(|field| self.add_field(field)),
        }
    }
}

impl MovedField {
    fn overlaps<'p>(&self, read: impl Iterator<Item = &'p str>) -> bool {
        self.path
            .iter()
            .zip(read)
            .all(|(moved, read)| **moved == *read)
    }
}