knave-expand 0.13.2

The reading, scoping and expansion stages of knave
Documentation
use std::fmt;
use std::rc::Rc;

use crate::scope::{ChainPrefix, Scope, ScopeSet};
use crate::source::Span;

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct Form {
    kind: Rc<FormKind>,
    span: Span,
    nesting: u32,
}

pub struct FormNesting;

impl FormNesting {
    pub fn of(form: &Form) -> u32 {
        form.nesting
    }
}

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum FormKind {
    Symbol(Symbol),
    String(Rc<str>),
    Number(Number),
    List(Vec<Form>),
}

impl Form {
    pub const NESTING_LIMIT: usize = 1024;

    pub fn new(kind: FormKind, span: Span) -> Self {
        let nesting = match &kind {
            FormKind::List(items) => 1 + items.iter().map(|item| item.nesting).max().unwrap_or(0),
            _ => 0,
        };
        Self {
            kind: Rc::new(kind),
            span,
            nesting,
        }
    }

    pub fn kind(&self) -> &FormKind {
        &self.kind
    }

    pub fn span(&self) -> Span {
        self.span
    }

    pub fn is_same_form(&self, other: &Form) -> bool {
        Rc::ptr_eq(&self.kind, &other.kind)
    }

    pub fn identity(&self) -> *const FormKind {
        Rc::as_ptr(&self.kind)
    }

    pub fn form_count(&self) -> usize {
        let items = self.as_list().unwrap_or_default();
        1 + items.iter().map(Form::form_count).sum::<usize>()
    }

    pub fn as_symbol(&self) -> Option<&Symbol> {
        match self.kind() {
            FormKind::Symbol(symbol) => Some(symbol),
            _ => None,
        }
    }

    pub fn as_list(&self) -> Option<&[Form]> {
        match self.kind() {
            FormKind::List(items) => Some(items),
            _ => None,
        }
    }

    pub fn located_at(self, span: Span) -> Self {
        Self { span, ..self }
    }

    pub fn set_scopes(&mut self, scopes: &ScopeSet) {
        self.update_scopes(&|current| current.clone_from(scopes));
    }

    pub fn add_scope(&mut self, scope: Scope) {
        self.update_scopes(&|scopes| scopes.add(scope));
    }

    pub fn add_prefix(&mut self, prefix: &ChainPrefix) {
        self.update_scopes(&|scopes| scopes.add_prefix(prefix));
    }

    pub fn flip_scope(&mut self, scope: Scope) {
        self.update_scopes(&|scopes| scopes.flip(scope));
    }

    pub fn flip_scope_within(&mut self, scope: Scope, allowance: &mut usize) -> bool {
        self.update_scopes_within(&|scopes| scopes.flip(scope), allowance)
    }

    pub fn remove_scope(&mut self, scope: Scope) {
        self.update_scopes(&|scopes| scopes.remove(scope));
    }

    pub fn retain_scopes(&mut self, keep: &dyn Fn(Scope) -> bool) {
        self.update_scopes(&|scopes| scopes.retain(keep));
    }

    pub fn relocate_outside(&mut self, region: Span) {
        if !region.contains(self.span) && !self.span.is_pinned() {
            self.span = region;
        }
        if self.as_list().is_some()
            && let FormKind::List(items) = Rc::make_mut(&mut self.kind)
        {
            items
                .iter_mut()
                .for_each(|item| item.relocate_outside(region));
        }
    }

    fn update_scopes(&mut self, update: &dyn Fn(&mut ScopeSet)) {
        match self.scoped_kind_mut() {
            Some(FormKind::Symbol(symbol)) => update(symbol.scopes_mut()),
            Some(FormKind::List(items)) => {
                items.iter_mut().for_each(|item| item.update_scopes(update))
            }
            _ => {}
        }
    }

    fn update_scopes_within(
        &mut self,
        update: &dyn Fn(&mut ScopeSet),
        allowance: &mut usize,
    ) -> bool {
        let Some(remaining) = allowance.checked_sub(1) else {
            return false;
        };
        *allowance = remaining;
        match self.scoped_kind_mut() {
            Some(FormKind::Symbol(symbol)) => {
                update(symbol.scopes_mut());
                true
            }
            Some(FormKind::List(items)) => items
                .iter_mut()
                .all(|item| item.update_scopes_within(update, allowance)),
            _ => true,
        }
    }

    #[inline(always)]
    fn scoped_kind_mut(&mut self) -> Option<&mut FormKind> {
        match self.kind() {
            FormKind::Symbol(_) | FormKind::List(_) => Some(Rc::make_mut(&mut self.kind)),
            FormKind::String(_) | FormKind::Number(_) => None,
        }
    }
}

impl fmt::Display for Form {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.kind() {
            FormKind::Symbol(symbol) => write!(formatter, "{}", symbol.name()),
            FormKind::String(text) => write!(formatter, "{text:?}"),
            FormKind::Number(number) => write!(formatter, "{}", number.text()),
            FormKind::List(items) => Self::fmt_list(items, formatter),
        }
    }
}

impl Form {
    fn fmt_list(items: &[Form], formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        let rendered: Vec<String> = items.iter().map(Form::to_string).collect();
        write!(formatter, "({})", rendered.join(" "))
    }
}

#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct Symbol {
    name: Rc<str>,
    scopes: ScopeSet,
}

impl Symbol {
    pub fn new(name: impl Into<Rc<str>>) -> Self {
        Self::with_scopes(name, ScopeSet::default())
    }

    pub fn with_scopes(name: impl Into<Rc<str>>, scopes: ScopeSet) -> Self {
        Self {
            name: name.into(),
            scopes,
        }
    }

    pub fn name(&self) -> &str {
        &self.name
    }

    pub fn shared_name(&self) -> Rc<str> {
        Rc::clone(&self.name)
    }

    pub fn scopes(&self) -> &ScopeSet {
        &self.scopes
    }

    pub fn scopes_mut(&mut self) -> &mut ScopeSet {
        &mut self.scopes
    }
}

#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum NumberValue {
    Integer(i128),
    Float(f64),
}

#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct Number {
    text: Rc<str>,
    value: NumberValue,
}

impl Number {
    pub fn new(text: impl Into<Rc<str>>, value: NumberValue) -> Self {
        Self {
            text: text.into(),
            value,
        }
    }

    pub fn text(&self) -> &str {
        &self.text
    }

    pub fn value(&self) -> &NumberValue {
        &self.value
    }
}

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

    struct RelocationFixture {
        region: Span,
    }

    impl RelocationFixture {
        fn new() -> Self {
            Self {
                region: Span::new(SourceId::new(0), 0..4),
            }
        }

        fn relocated(&self, span: Span) -> Span {
            let mut form = Form::new(FormKind::String("text".into()), span);
            form.relocate_outside(self.region);
            form.span()
        }
    }

    #[test]
    fn a_form_from_another_source_moves_onto_the_region() {
        let fixture = RelocationFixture::new();
        let outside = Span::new(SourceId::new(1), 8..12);
        assert_eq!(fixture.relocated(outside), fixture.region);
    }

    #[test]
    fn a_pinned_form_keeps_its_location() {
        let fixture = RelocationFixture::new();
        let pinned = Span::new(SourceId::new(1).pinned(), 8..12);
        assert_eq!(fixture.relocated(pinned), pinned);
    }
}