knave-expand 0.14.21

The reading, scoping and expansion stages of knave
Documentation
use super::list_item::ListItem;

pub struct ListSize;

impl ListSize {
    pub const EMPTY: usize = 1;
    pub const ATOM: usize = 1;

    pub fn of_items<I: ListItem>(items: &[I]) -> usize {
        Self::of_counts(items.iter().map(ListItem::list_size))
    }

    pub fn of_counts(counts: impl Iterator<Item = usize>) -> usize {
        counts.fold(Self::EMPTY, |size, count| size + count)
    }

    pub fn of_texts(count: usize) -> usize {
        Self::EMPTY + count * Self::ATOM
    }

    pub fn appended<I: ListItem>(size: usize, extra: &[I]) -> usize {
        size.saturating_add(Self::of_items(extra) - Self::EMPTY)
    }

    pub fn stored(size: usize) -> u32 {
        u32::try_from(size).unwrap_or(u32::MAX)
    }
}

#[cfg(test)]
mod tests {
    use super::ListSize;
    use crate::interpret::Value;
    use crate::reader::{Form, FormKind, Symbol};
    use crate::source::{SourceId, Span};

    struct SizedValues;

    impl SizedValues {
        fn span() -> Span {
            Span::new(SourceId::new(0), 0..1)
        }

        fn leaf() -> Form {
            Form::new(FormKind::Symbol(Symbol::new("leaf")), Self::span())
        }

        fn pair() -> Form {
            Form::new(
                FormKind::List(vec![Self::leaf(), Self::leaf()]),
                Self::span(),
            )
        }
    }

    #[test]
    fn a_size_past_what_a_list_stores_is_kept_at_the_largest() {
        assert_eq!(ListSize::stored((1 << 32) + 1), u32::MAX);
    }

    #[test]
    fn a_list_of_forms_counts_itself_and_every_form_of_each_tree() {
        let forms = [SizedValues::pair(), SizedValues::leaf()];
        assert_eq!(ListSize::of_items(&forms), 1 + 3 + 1);
    }

    #[test]
    fn a_list_of_texts_counts_itself_and_one_value_for_each_text() {
        assert_eq!(ListSize::of_texts(4), 1 + 4);
    }

    #[test]
    fn appending_adds_the_size_of_each_new_item() {
        let extra = [SizedValues::pair()];
        assert_eq!(ListSize::appended(5, &extra), 5 + 3);
    }

    #[test]
    fn a_value_that_is_not_code_counts_one() {
        assert_eq!(ListSize::of_items(&[Value::Unit, Value::Bool(true)]), 1 + 2);
    }
}