use crate::{
scope::ScopePointer,
r#type::Type,
type_expr::{ScopePortal, TypeExpr},
};
use std::collections::BTreeMap;
impl<T: Type> TypeExpr<T, ScopePortal<T>> {
pub fn intersection(
a: &Self,
b: &Self,
scope_a: &ScopePointer<T>,
scope_b: &ScopePointer<T>,
) -> Option<(Self, ScopePointer<T>)> {
match (a, b) {
(Self::Any, b) => Some((b.clone(), ScopePointer::clone(scope_b))),
(a, Self::Any) => Some((a.clone(), ScopePointer::clone(scope_a))),
(Self::Never, _) => Some((Self::Never, ScopePointer::clone(scope_a))),
(_, Self::Never) => Some((Self::Never, ScopePointer::clone(scope_b))),
(a @ Self::TypeParameter(local_param_a, ..), Self::TypeParameter(local_param_b, ..)) => {
let (_var_a, param_scope_a) = scope_a.lookup(local_param_a)?;
let (_var_b, param_scope_b) = scope_b.lookup(local_param_b)?;
if local_param_a == local_param_b && param_scope_a == param_scope_b {
return Some((a.clone(), ScopePointer::clone(scope_a)));
}
let (Some((inferred_a, scope_a)), Some((inferred_b, scope_b))) =
(scope_a.lookup_inferred(local_param_a), scope_b.lookup_inferred(local_param_b))
else {
return None;
};
Self::intersection(&inferred_a, &inferred_b, &scope_a, &scope_b)
}
(Self::TypeParameter(param, _infer), b) => {
let (inferred_a, scope_a) = scope_a.lookup_inferred(param)?;
Self::intersection(&inferred_a, b, &scope_a, scope_b)
}
(a, Self::TypeParameter(param, _infer)) => {
let (inferred_b, scope_b) = scope_b.lookup_inferred(param)?;
Self::intersection(a, &inferred_b, scope_a, &scope_b)
}
(Self::ScopePortal { expr, scope }, b) => Self::intersection(expr, b, &scope.portal, scope_b),
(a, Self::ScopePortal { expr, scope }) => Self::intersection(a, expr, scope_a, &scope.portal),
(Self::Intersection(a_a, a_b), b) => {
let (intersection_a, intersection_a_scope) = Self::intersection(a_a, a_b, scope_a, scope_a)?;
Self::intersection(&intersection_a, b, &intersection_a_scope, scope_b)
}
(a, Self::Intersection(b_a, b_b)) => {
let (intersection_b, intersection_b_scope) = Self::intersection(b_a, b_b, scope_b, scope_b)?;
Self::intersection(a, &intersection_b, scope_a, &intersection_b_scope)
}
(Self::Operation { a, b, operator }, b_expr) => {
let a_normalized = a.normalize(scope_a);
let b_normalized = b.normalize(scope_a);
Self::intersection(&T::operation(&a_normalized, operator, &b_normalized), b_expr, scope_a, scope_b)
}
(a_expr, Self::Operation { a, b, operator }) => {
let a_normalized = a.normalize(scope_b);
let b_normalized = b.normalize(scope_b);
Self::intersection(a_expr, &T::operation(&a_normalized, operator, &b_normalized), scope_a, scope_b)
}
(Self::Conditional(conditional), b) => {
Self::intersection(&conditional.distribute(scope_a)?, b, scope_a, scope_b)
}
(a, Self::Conditional(conditional)) => {
Self::intersection(a, &conditional.distribute(scope_b)?, scope_a, scope_b)
}
(Self::Type(a), Self::Type(b)) if a == b => Some((Self::Type(a.clone()), ScopePointer::clone(scope_a))),
(Self::Constructor { inner, .. }, Self::Type(inst)) if inner == inst => {
Some((a.clone(), ScopePointer::clone(scope_a)))
}
(Self::Type(inst), Self::Constructor { inner, .. }) if inner == inst => {
Some((b.clone(), ScopePointer::clone(scope_b)))
}
(
Self::Constructor { inner: inner_a, parameters: parameters_a },
Self::Constructor { inner: inner_b, parameters: parameters_b },
) if inner_a == inner_b => {
let mut intersected_params = BTreeMap::new();
for ident in parameters_a.keys().chain(parameters_b.keys()) {
if intersected_params.contains_key(ident) {
continue;
}
let (intersected_param, intersected_scope) =
match (parameters_a.get(ident), parameters_b.get(ident)) {
(Some(pa), Some(pb)) => Self::intersection(pa, pb, scope_a, scope_b)?,
(Some(pa), None) => (pa.clone(), ScopePointer::clone(scope_a)),
(None, Some(pb)) => (pb.clone(), ScopePointer::clone(scope_b)),
(None, None) => unreachable!(),
};
intersected_params.insert(
ident.clone(),
TypeExpr::ScopePortal {
expr: Box::new(intersected_param),
scope: ScopePortal { portal: intersected_scope },
},
);
}
Some((
Self::Constructor { inner: inner_a.clone(), parameters: intersected_params },
ScopePointer::clone(scope_a),
))
}
(Self::Constructor { .. }, Self::Constructor { .. }) => Some((Self::Never, ScopePointer::clone(scope_a))),
(Self::Index { expr, index }, b) => {
let (index_type, index_scope) = expr.index(index, scope_a, scope_a)?;
Self::intersection(&index_type, b, &index_scope, scope_b)
}
(a, Self::Index { expr, index }) => {
let (index_type, index_scope) = expr.index(index, scope_b, scope_b)?;
Self::intersection(a, &index_type, scope_a, &index_scope)
}
(Self::KeyOf(expr), b) => {
let (key_type, key_scope) = expr.keyof(scope_a)?;
Self::intersection(&key_type, b, &key_scope, scope_b)
}
(a, Self::KeyOf(expr)) => {
let (key_type, key_scope) = expr.keyof(scope_b)?;
Self::intersection(a, &key_type, scope_a, &key_scope)
}
(Self::NodeSignature(_), _) | (_, Self::NodeSignature(_)) => Some((Self::Never, ScopePointer::new_root())),
(Self::PortTypes(_), _) | (_, Self::PortTypes(_)) => Some((Self::Never, ScopePointer::new_root())),
(Self::Union(a, b), c) => {
let (a_intersection, a_scope) = Self::intersection(a, c, scope_a, scope_b)?;
let (b_intersection, b_scope) = Self::intersection(b, c, scope_a, scope_b)?;
if a_scope == b_scope {
Some((Self::Union(Box::new(a_intersection), Box::new(b_intersection)), a_scope.clone()))
} else {
Some((
Self::Union(
Box::new(Self::ScopePortal {
expr: Box::new(a_intersection),
scope: ScopePortal { portal: a_scope },
}),
Box::new(Self::ScopePortal {
expr: Box::new(b_intersection),
scope: ScopePortal { portal: b_scope },
}),
),
ScopePointer::new_root(),
))
}
}
(a, Self::Union(b, c)) => {
let (b_intersection, b_scope) = Self::intersection(a, b, scope_a, scope_b)?;
let (c_intersection, c_scope) = Self::intersection(a, c, scope_a, scope_b)?;
if b_scope == c_scope {
Some((Self::Union(Box::new(b_intersection), Box::new(c_intersection)), b_scope.clone()))
} else {
Some((
Self::Union(
Box::new(Self::ScopePortal {
expr: Box::new(b_intersection),
scope: ScopePortal { portal: b_scope },
}),
Box::new(Self::ScopePortal {
expr: Box::new(c_intersection),
scope: ScopePortal { portal: c_scope },
}),
),
ScopePointer::new_root(),
))
}
}
(Self::Type(_), _) => Some((Self::Never, ScopePointer::clone(scope_a))),
(_, Self::Type(_)) => Some((Self::Never, ScopePointer::clone(scope_a))),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::notation::parse::expr;
#[test]
fn test_intersection() {
let scope = ScopePointer::new_root();
assert_eq!(
expr("{a: Integer, b: String}"),
TypeExpr::intersection(&expr("{a: Integer}"), &expr("{b: String}"), &scope, &scope)
.unwrap()
.0
.normalize(&scope),
);
}
}