use compact_str::CompactString;
use crate::Type;
use crate::ast::{TypeAnnotation, TypeParameterBound};
use crate::dimension::DimensionRegistry;
use crate::pretty_print::PrettyPrint;
use crate::span::Span;
use crate::type_variable::TypeVariable;
use crate::typed_ast::pretty_print_function_signature;
use super::map_stack::MapStack;
use super::substitutions::{ApplySubstitution, Substitution, SubstitutionError};
use super::type_scheme::TypeScheme;
type Identifier = CompactString;
#[derive(Clone, Debug)]
pub struct FunctionSignature {
pub name: CompactString,
pub definition_span: Span,
#[allow(dead_code)]
pub type_parameters: Vec<(Span, CompactString, Option<TypeParameterBound>)>,
pub parameters: Vec<(Span, CompactString, Option<TypeAnnotation>)>,
pub return_type_annotation: Option<TypeAnnotation>,
pub fn_type: TypeScheme,
}
impl FunctionSignature {
pub fn pretty_print(&self, registry: &DimensionRegistry) -> crate::markup::Markup {
let (fn_type, type_parameters) = self.fn_type.instantiate_for_printing(Some(
self.type_parameters
.iter()
.map(|(_, name, _)| name.as_str()),
));
let Type::Fn(ref parameter_types, ref return_type) = fn_type.inner else {
unreachable!()
};
let parameters =
self.parameters
.iter()
.zip(parameter_types)
.map(|((_, name, annotation), type_)| {
let readable_type = match annotation {
Some(annotation) => annotation.pretty_print(),
None => type_.to_readable_type(registry),
};
(name.as_str(), readable_type)
});
let readable_return_type = match &self.return_type_annotation {
Some(annotation) => annotation.pretty_print(),
None => return_type.to_readable_type(registry),
};
pretty_print_function_signature(
&self.name,
&fn_type,
&type_parameters,
parameters,
&readable_return_type,
)
}
}
#[derive(Clone, Debug)]
pub struct FunctionMetadata {
pub name: Option<CompactString>,
pub url: Option<CompactString>,
pub description: Option<CompactString>,
pub examples: Vec<(CompactString, Option<CompactString>)>,
}
#[derive(Clone, Debug)]
pub enum IdentifierKind {
Normal(TypeScheme, #[allow(dead_code)] Span, bool),
Function(Box<FunctionSignature>, FunctionMetadata),
Predefined(TypeScheme),
}
impl IdentifierKind {
fn get_type(&self) -> TypeScheme {
match self {
IdentifierKind::Predefined(t) => t.clone(),
IdentifierKind::Normal(t, _, _) => t.clone(),
IdentifierKind::Function(s, _) => s.fn_type.clone(),
}
}
}
#[derive(Clone, Debug, Default)]
pub struct Environment {
identifiers: MapStack<Identifier, IdentifierKind>,
}
impl Environment {
pub fn add(&mut self, i: Identifier, type_: Type, span: Span, is_unit: bool) {
self.identifiers.insert(
i,
IdentifierKind::Normal(TypeScheme::Concrete(type_), span, is_unit),
);
}
pub fn add_scheme(&mut self, i: Identifier, scheme: TypeScheme, span: Span, is_unit: bool) {
self.identifiers
.insert(i, IdentifierKind::Normal(scheme, span, is_unit));
}
pub(crate) fn save(&mut self) {
self.identifiers.save();
}
pub(crate) fn restore(&mut self) {
self.identifiers.restore();
}
pub(crate) fn add_function(
&mut self,
v: CompactString,
signature: FunctionSignature,
metadata: FunctionMetadata,
) {
self.identifiers
.insert(v, IdentifierKind::Function(Box::new(signature), metadata));
}
pub fn add_predefined(&mut self, v: Identifier, type_: TypeScheme) {
self.identifiers
.insert(v, IdentifierKind::Predefined(type_));
}
pub(crate) fn get_identifier_type(&self, v: &str) -> Option<TypeScheme> {
self.find(v).map(|k| k.get_type())
}
pub(crate) fn iter_identifiers(&self) -> impl Iterator<Item = &Identifier> {
self.identifiers.keys()
}
pub fn iter_relevant_matches(&self) -> impl Iterator<Item = (&Identifier, TypeScheme)> {
self.identifiers
.iter()
.filter(|(_, kind)| {
!matches!(
kind,
IdentifierKind::Normal(_, _, true) | IdentifierKind::Predefined(..)
)
})
.map(|(id, kind)| (id, kind.get_type()))
}
fn find(&self, name: &str) -> Option<&IdentifierKind> {
self.identifiers.get(name)
}
pub(crate) fn get_function_info(
&self,
name: &str,
) -> Option<(&FunctionSignature, &FunctionMetadata)> {
match self.find(name) {
Some(IdentifierKind::Function(signature, metadata)) => Some((signature, metadata)),
_ => None,
}
}
pub(crate) fn generalize_types(&mut self, dtype_variables: &[TypeVariable]) {
for (_, kind) in self.identifiers.iter_mut() {
match kind {
IdentifierKind::Normal(t, _, _) => {
t.generalize(dtype_variables);
}
IdentifierKind::Function(signature, _) => {
signature.fn_type.generalize(dtype_variables);
}
IdentifierKind::Predefined(t) => {
t.generalize(dtype_variables);
}
}
}
}
pub(crate) fn get_proper_function_reference<'a>(
&self,
expr: &crate::ast::Expression<'a>,
) -> Option<(&'a str, &FunctionSignature)> {
match expr {
crate::ast::Expression::Identifier(_, name) => self
.get_function_info(name)
.map(|(signature, _)| (*name, signature)),
_ => None,
}
}
}
impl ApplySubstitution for Environment {
fn apply(&mut self, substitution: &Substitution) -> Result<(), SubstitutionError> {
for (_, kind) in self.identifiers.iter_mut() {
match kind {
IdentifierKind::Normal(t, _, _) => {
t.apply(substitution)?;
}
IdentifierKind::Function(signature, _) => {
signature.fn_type.apply(substitution)?;
}
IdentifierKind::Predefined(t) => {
t.apply(substitution)?;
}
}
}
Ok(())
}
}