use super::sterm::{SComb, STerm};
use super::{Error, GCtx};
use alloc::vec::Vec;
use core::fmt::{self, Display};
type Result<T> = core::result::Result<T, Error>;
#[derive(Default)]
pub struct LCtx<'s, 't>(Vec<STerm<'s, 't>>);
impl<'s, 't> LCtx<'s, 't> {
fn get_type(&self, n: usize) -> Option<STerm<'s, 't>> {
Some(self.0.iter().rev().nth(n)?.clone().shift(n + 1))
}
fn bind<A, F>(&mut self, arg: STerm<'s, 't>, f: F) -> Result<A>
where
F: FnOnce(&mut LCtx<'s, 't>) -> Result<A>,
{
self.0.push(arg);
let y = f(self)?;
self.0.pop();
Ok(y)
}
fn bind_of_type<A, F>(&mut self, gc: &'t GCtx<'s>, arg: &STerm<'s, 't>, f: F) -> Result<A>
where
F: FnOnce(&mut LCtx<'s, 't>) -> Result<A>,
{
match arg.infer(gc, self)? {
STerm::Type => self.bind(arg.clone(), f),
_ => Err(Error::BindNoType),
}
}
}
impl<'s, 't> Display for LCtx<'s, 't> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[")?;
for (i, x) in self.0.iter().rev().enumerate() {
write!(f, "{} : {}, ", STerm::Var(i), x.clone().shift(i + 1))?;
}
write!(f, "]")
}
}
impl<'s, 't> FromIterator<STerm<'s, 't>> for LCtx<'s, 't> {
fn from_iter<I: IntoIterator<Item = STerm<'s, 't>>>(iter: I) -> Self {
Self(Vec::from_iter(iter))
}
}
impl<'s, 't> STerm<'s, 't> {
pub fn infer(&self, gc: &'t GCtx<'s>, lc: &mut LCtx<'s, 't>) -> Result<STerm<'s, 't>> {
debug!("infer type of {}", self);
match self {
Self::Kind => Err(Error::KindNotTypable),
Self::Type => Ok(Self::Kind),
Self::Const(c) => Ok((&**gc.get_type(c).ok_or(Error::TypeNotFound)?).into()),
Self::Var(v) => Ok(lc.get_type(*v).ok_or(Error::TypeNotFound)?),
Self::SComb(c) => c.infer(gc, lc),
Self::LComb(c) => SComb::from(*c).infer(gc, lc),
}
}
pub fn check(&self, gc: &'t GCtx<'s>, lc: &mut LCtx<'s, 't>, ty: Self) -> Result<bool> {
debug!("check {} is of type {} when {}", self, ty, lc);
if let Some((_, arg_ty, tm)) = self.get_abst() {
let (_, ty_a, ty_b) = ty.whnf(gc).get_prod().ok_or(Error::ProductExpected)?;
if let Some(arg_ty) = arg_ty {
let _ = arg_ty.infer(gc, lc)?;
if !Self::convertible(arg_ty, ty_a.clone(), gc) {
return Ok(false);
}
};
Ok(lc.bind(ty_a, |lc| tm.check(gc, lc, ty_b))?)
} else {
let ty_inf = self.infer(gc, lc)?;
debug!("checking convertibility: {} ~ {}", ty_inf, ty);
Ok(Self::convertible(ty_inf, ty, gc))
}
}
}
impl<'s, 't> SComb<'s, 't> {
fn infer(&self, gc: &'t GCtx<'s>, lc: &mut LCtx<'s, 't>) -> Result<STerm<'s, 't>> {
use STerm::*;
match self {
Self::Appl(tm, args) => {
let tm_ty = tm.infer(gc, lc)?;
args.iter().try_fold(tm_ty, |ty, arg| {
let (_, a, b) = ty.whnf(gc).get_prod().ok_or(Error::ProductExpected)?;
arg.check(gc, lc, a)?
.then(|| b.subst(arg))
.ok_or(Error::Unconvertible)
})
}
Self::Abst(id, Some(ty), tm) => {
let tm_ty = lc.bind_of_type(gc, ty, |lc| tm.infer(gc, lc))?;
if tm_ty == Kind {
Err(Error::UnexpectedKind)
} else {
Ok(SComb(Self::Prod(id, ty.clone(), tm_ty).into()))
}
}
Self::Prod(_, ty, tm) => Some(lc.bind_of_type(gc, ty, |lc| tm.infer(gc, lc))?)
.filter(|tt| matches!(tt, Kind | Type))
.ok_or(Error::SortExpected),
Self::Abst(_, None, _) => Err(Error::DomainFreeAbstraction),
}
}
}