knave 0.10.0

A small Lisp for humans and agents that infers types and ownership and compiles to readable Rust
Documentation
use crate::source::Span;

#[derive(Clone, Debug)]
pub enum Fragment {
    Text(String),
    Newline,
    Indented(Vec<Fragment>),
    Spanned(Span, Vec<Fragment>),
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Precedence {
    Atom,
    Prefix,
    Operator,
    Block,
}

#[derive(Clone, Debug)]
pub struct Code {
    pub fragments: Vec<Fragment>,
    pub precedence: Precedence,
    pub bare_struct_literal: bool,
}

impl Code {
    pub fn atom(text: impl Into<String>) -> Self {
        Self::with(text, Precedence::Atom)
    }

    pub fn with(text: impl Into<String>, precedence: Precedence) -> Self {
        Self {
            fragments: vec![Fragment::Text(text.into())],
            precedence,
            bare_struct_literal: false,
        }
    }

    pub fn joined(parts: Vec<Code>, precedence: Precedence) -> Self {
        Self {
            fragments: parts.into_iter().flat_map(|part| part.fragments).collect(),
            precedence,
            bare_struct_literal: false,
        }
    }

    pub fn joined_outside_delimiters(parts: Vec<Code>, precedence: Precedence) -> Self {
        let bare_struct_literal = parts.iter().any(|part| part.bare_struct_literal);
        Self {
            bare_struct_literal,
            ..Self::joined(parts, precedence)
        }
    }

    pub fn struct_literal(parts: Vec<Code>) -> Self {
        Self {
            bare_struct_literal: true,
            ..Self::joined(parts, Precedence::Atom)
        }
    }

    pub fn block(fragments: Vec<Fragment>) -> Self {
        Self {
            fragments,
            precedence: Precedence::Block,
            bare_struct_literal: false,
        }
    }

    pub fn spanned(self, span: Span) -> Self {
        Self {
            fragments: vec![Fragment::Spanned(span, self.fragments)],
            ..self
        }
    }

    pub fn parenthesized(self) -> Self {
        Self::joined(
            vec![Code::atom("("), self, Code::atom(")")],
            Precedence::Atom,
        )
    }

    pub fn into_head(self) -> Self {
        if self.bare_struct_literal {
            self.parenthesized()
        } else {
            self
        }
    }

    pub fn is_atomic(&self) -> bool {
        self.precedence == Precedence::Atom
    }

    pub fn is_plain_name(&self) -> bool {
        self.is_atomic()
            && self
                .text()
                .chars()
                .all(|character| character.is_alphanumeric() || "_:#".contains(character))
    }

    pub fn text(&self) -> String {
        Self::text_of(&self.fragments)
    }

    pub fn text_of(fragments: &[Fragment]) -> String {
        let mut text = String::new();
        Self::collect_text(fragments, &mut text);
        text
    }

    fn collect_text(fragments: &[Fragment], text: &mut String) {
        for fragment in fragments {
            match fragment {
                Fragment::Text(part) => text.push_str(part),
                Fragment::Newline => text.push('\n'),
                Fragment::Indented(inner) | Fragment::Spanned(_, inner) => {
                    Self::collect_text(inner, text)
                }
            }
        }
    }
}