knave-expand 0.14.22

The reading, scoping and expansion stages of knave
Documentation
use super::Expander;
use super::problems::ExpandProblems;
use crate::core::{
    AssociatedValue, BindingId, BindingKind, ClosureKind, KernelType, Namespace, ReferenceKind,
    RustTraitImplementor, TypeBinder, TypeBound, TypeExpr, TypeExprKind,
};
use crate::diagnostic::Diagnostic;
use crate::reader::{Form, FormKind, Symbol};
use crate::source::Span;

impl Expander<'_> {
    const FUNCTION_TYPE_PARTS: usize = 3;
    const LIFETIME_PREFIX: char = '\'';
    const WHERE_PARTS: usize = 3;
    const WITH_PARTS: usize = 3;

    pub(super) fn lower_type(&mut self, form: &Form) -> Result<TypeExpr, Diagnostic> {
        let span = form.span();
        let kind = match form.kind() {
            FormKind::Symbol(symbol) => self.lower_type_name(symbol, span)?,
            FormKind::List(items) => self.lower_type_application(form, items)?,
            FormKind::String(_) | FormKind::Number(_) => {
                return Err(ExpandProblems::not_a_type(span));
            }
        };
        Ok(TypeExpr::new(kind, span))
    }

    fn lower_type_name(&self, symbol: &Symbol, span: Span) -> Result<TypeExprKind, Diagnostic> {
        let binding = self.resolve(symbol, Namespace::Type, span)?;
        self.lower_type_binding(binding, symbol.name(), span)
    }

    fn lower_type_binding(
        &self,
        binding: BindingId,
        name: &str,
        span: Span,
    ) -> Result<TypeExprKind, Diagnostic> {
        match self.kind(binding) {
            BindingKind::KernelType(KernelType::Primitive(primitive)) => {
                Ok(TypeExprKind::Primitive(primitive))
            }
            BindingKind::KernelType(KernelType::Hole) => Ok(TypeExprKind::Hole),
            BindingKind::KernelType(constructor) => {
                Err(ExpandProblems::malformed_type(constructor, span))
            }
            _ => self.named_type_binding(binding, name, span),
        }
    }

    fn named_type_binding(
        &self,
        binding: BindingId,
        name: &str,
        span: Span,
    ) -> Result<TypeExprKind, Diagnostic> {
        match self.kind(binding) {
            BindingKind::Struct(_)
            | BindingKind::Enum(_)
            | BindingKind::RustType(_)
            | BindingKind::TypeVariable
            | BindingKind::AssociatedType => Ok(TypeExprKind::Named(binding, Vec::new())),
            BindingKind::Trait(_) => Ok(TypeExprKind::Trait(binding)),
            _ => Err(ExpandProblems::wrong_namespace(name, "a type", span)),
        }
    }

    fn lower_type_application(
        &mut self,
        form: &Form,
        items: &[Form],
    ) -> Result<TypeExprKind, Diagnostic> {
        let (head, binding) = self.type_constructor(form, items)?;
        match self.kind(binding) {
            BindingKind::KernelType(KernelType::Function(kind)) => {
                self.lower_function_type(kind, form, items)
            }
            BindingKind::KernelType(KernelType::Reference(kind)) => {
                self.lower_reference_type(kind, form, items)
            }
            BindingKind::KernelType(KernelType::Where) => self.lower_where(form, items),
            BindingKind::KernelType(KernelType::With) => self.lower_with_type(form, items),
            BindingKind::Struct(_) | BindingKind::Enum(_) | BindingKind::RustType(_) => {
                self.lower_named_application(binding, items)
            }
            _ => Err(ExpandProblems::wrong_namespace(
                &head.to_string(),
                "a type constructor",
                head.span(),
            )),
        }
    }

    fn type_constructor<'f>(
        &self,
        form: &Form,
        items: &'f [Form],
    ) -> Result<(&'f Form, BindingId), Diagnostic> {
        let head = items
            .first()
            .ok_or_else(|| ExpandProblems::not_a_type(form.span()))?;
        let symbol = head
            .as_symbol()
            .ok_or_else(|| ExpandProblems::not_a_type(head.span()))?;
        let binding = self.resolve(symbol, Namespace::Type, head.span())?;
        Ok((head, binding))
    }

    fn lower_named_application(
        &mut self,
        binding: BindingId,
        items: &[Form],
    ) -> Result<TypeExprKind, Diagnostic> {
        let arguments = items[1..]
            .iter()
            .map(|argument| self.lower_type(argument))
            .collect::<Result<Vec<TypeExpr>, Diagnostic>>()?;
        Ok(TypeExprKind::Named(binding, arguments))
    }

    fn lower_function_type(
        &mut self,
        kind: ClosureKind,
        form: &Form,
        items: &[Form],
    ) -> Result<TypeExprKind, Diagnostic> {
        let malformed = || ExpandProblems::malformed_type(KernelType::Function(kind), form.span());
        if items.len() != Self::FUNCTION_TYPE_PARTS {
            return Err(malformed());
        }
        let parameters = items[1].as_list().ok_or_else(malformed)?;
        let parameters = parameters
            .iter()
            .map(|parameter| self.lower_type(parameter))
            .collect::<Result<Vec<TypeExpr>, Diagnostic>>()?;
        let result = self.lower_type(&items[2])?;
        Ok(TypeExprKind::Function(kind, parameters, Box::new(result)))
    }

    fn lower_reference_type(
        &mut self,
        kind: ReferenceKind,
        form: &Form,
        items: &[Form],
    ) -> Result<TypeExprKind, Diagnostic> {
        let malformed = || ExpandProblems::malformed_type(KernelType::Reference(kind), form.span());
        let (lifetime, inner) = match items {
            [_, inner] => (None, inner),
            [_, lifetime, inner] => {
                let symbol = lifetime.as_symbol().ok_or_else(malformed)?;
                if !symbol.name().starts_with(Self::LIFETIME_PREFIX) {
                    return Err(malformed());
                }
                (
                    Some(self.resolve(symbol, Namespace::Type, lifetime.span())?),
                    inner,
                )
            }
            _ => return Err(malformed()),
        };
        let inner = self.lower_type(inner)?;
        Ok(TypeExprKind::Reference(kind, lifetime, Box::new(inner)))
    }

    fn lower_where(&mut self, form: &Form, items: &[Form]) -> Result<TypeExprKind, Diagnostic> {
        let malformed = || ExpandProblems::malformed_type(KernelType::Where, form.span());
        if items.len() != Self::WHERE_PARTS {
            return Err(malformed());
        }
        let parameterized = self.lower_parameters(items, malformed)?;
        Ok(TypeExprKind::Where(
            parameterized.binders,
            Box::new(self.lower_type(&parameterized.body[0])?),
        ))
    }

    pub(super) fn lower_rust_trait_implementor(
        &mut self,
        entry: &Form,
    ) -> Result<RustTraitImplementor, Diagnostic> {
        match entry.as_list() {
            Some(items) if items.len() > Self::WHERE_PARTS && self.is_where(&items[0]) => {
                self.lower_valued_implementor(entry, items)
            }
            _ => Ok(RustTraitImplementor {
                implementor: self.lower_type(entry)?,
                associated: Vec::new(),
            }),
        }
    }

    fn lower_valued_implementor(
        &mut self,
        entry: &Form,
        items: &[Form],
    ) -> Result<RustTraitImplementor, Diagnostic> {
        let malformed = || ExpandProblems::malformed_type(KernelType::Where, entry.span());
        let parameterized = self.lower_parameters(items, malformed)?;
        let (implemented, values) = parameterized
            .body
            .split_first()
            .expect("a valued implementor has a body");
        let implemented = self.lower_type(implemented)?;
        let associated = values
            .iter()
            .map(|value| self.lower_associated_value(value))
            .collect::<Result<Vec<AssociatedValue>, Diagnostic>>()?;
        Ok(RustTraitImplementor {
            implementor: TypeExpr::new(
                TypeExprKind::Where(parameterized.binders, Box::new(implemented)),
                entry.span(),
            ),
            associated,
        })
    }

    fn lower_associated_value(&mut self, clause: &Form) -> Result<AssociatedValue, Diagnostic> {
        let Some([name, value]) = clause.as_list() else {
            return Err(Self::malformed_associated_value(clause));
        };
        let symbol = name
            .as_symbol()
            .ok_or_else(|| Self::malformed_associated_value(clause))?;
        Ok(AssociatedValue {
            binding: self.resolve(symbol, Namespace::Type, name.span())?,
            value: self.lower_type(value)?,
            span: clause.span(),
        })
    }

    fn malformed_associated_value(clause: &Form) -> Diagnostic {
        let name = clause
            .as_list()
            .and_then(|items| items.first())
            .and_then(Form::as_symbol)
            .map(Symbol::name);
        ExpandProblems::malformed_associated_value(name, clause.span())
    }

    fn lower_with_type(&mut self, form: &Form, items: &[Form]) -> Result<TypeExprKind, Diagnostic> {
        if items.len() != Self::WITH_PARTS {
            return Err(ExpandProblems::malformed_type(
                KernelType::With,
                form.span(),
            ));
        }
        let implementor = self.lower_type(&items[1])?;
        let argument = self.lower_type(&items[2])?;
        Ok(TypeExprKind::With(
            Box::new(implementor),
            Box::new(argument),
        ))
    }

    pub(super) fn define_binder_variable(
        &mut self,
        binder: &Form,
    ) -> Result<BindingId, Diagnostic> {
        let name = match binder.as_list() {
            Some([name, ..]) => name,
            _ => binder,
        };
        let kind = match name.as_symbol() {
            Some(symbol) if symbol.name().starts_with(Self::LIFETIME_PREFIX) => {
                BindingKind::Lifetime
            }
            _ => BindingKind::TypeVariable,
        };
        self.define_local(name, Namespace::Type, kind, None)
    }

    pub(super) fn lower_type_binder(
        &mut self,
        binder: &Form,
        variable: BindingId,
    ) -> Result<TypeBinder, Diagnostic> {
        let bounds = match binder.as_list() {
            Some([_, bounds @ ..]) => bounds,
            _ => &[][..],
        };
        let bounds = bounds
            .iter()
            .map(|bound| self.lower_bound(bound))
            .collect::<Result<Vec<TypeBound>, Diagnostic>>()?;
        Ok(TypeBinder {
            variable,
            bounds,
            span: binder.span(),
            lifetime: matches!(self.kind(variable), BindingKind::Lifetime),
        })
    }

    pub(super) fn lower_bound(&mut self, bound: &Form) -> Result<TypeBound, Diagnostic> {
        let (name, argument) = match bound.as_list() {
            Some([name, argument]) => (name, Some(argument)),
            _ => (bound, None),
        };
        let symbol = name.as_symbol().ok_or_else(|| {
            ExpandProblems::wrong_namespace(&bound.to_string(), "a trait", bound.span())
        })?;
        let binding = self.resolve(symbol, Namespace::Type, bound.span())?;
        if !matches!(self.kind(binding), BindingKind::Trait(_)) {
            return Err(ExpandProblems::wrong_namespace(
                symbol.name(),
                "a trait",
                bound.span(),
            ));
        }
        let argument = argument
            .map(|argument| self.lower_type(argument))
            .transpose()?;
        Ok(TypeBound { binding, argument })
    }
}