use std::{convert::TryFrom, rc::Rc};
use voile_util::uid::{DBI, UID};
use crate::{
check::{
monad::{TCE, TCMS, TCS},
pats::CoreCopat,
rules::{
clause::{
eqs::{classify_eqs, AsBind, PatVars},
split::split_con,
state::LhsState,
},
term::is_eta_var_ref,
ERROR_MSG,
},
},
syntax::{
core::{
subst::{DeBruijn, RedEx, Subst},
Tele, TeleS, Term,
},
pat::{Copat, Pat, PatCommon},
},
};
#[derive(Clone)]
pub(super) struct Lhs {
pub(super) tele: Tele,
pub(super) has_absurd: bool,
pub(super) pats: Vec<CoreCopat>,
pub(super) ty: Term,
pub(super) pat_sub: Rc<Subst>,
pub(super) as_binds: Vec<AsBind>,
}
fn user_variable_names(tele: &TeleS, mut pat_vars: PatVars) -> (Vec<Option<UID>>, Vec<AsBind>) {
let len_rng = 0..tele.len();
let mut as_binds = Vec::with_capacity(pat_vars.len());
let mut names = Vec::with_capacity(tele.len());
for (bind, ix) in tele.iter().zip(len_rng.rev().map(DBI)) {
let ids = pat_vars.remove(&ix).unwrap_or_default();
names.push(ids.first().copied());
for uid in ids {
let ty = bind.ty.clone().reduce_dbi(Subst::raise(ix + 1));
let as_bind = AsBind::new(uid, DeBruijn::from_dbi(ix), ty);
as_binds.push(as_bind)
}
}
(names, as_binds)
}
fn final_check(tcs: TCS, mut lhs: LhsState) -> TCMS<Lhs> {
debug_assert!(lhs.problem.todo_pats.is_empty());
let len_pats = lhs.len_pats();
let pat_sub = Subst::parallel(
(lhs.pats.iter().take(len_pats).rev().cloned())
.map(Term::try_from)
.map(|t| t.expect(ERROR_MSG)),
);
let equations = lhs.problem.equations;
let (classified, tcs) = tcs.under(&mut lhs.tele, |tcs| classify_eqs(tcs, equations))?;
debug_assert!(classified.other_pats.is_empty());
let (vars, mut asb) = user_variable_names(&lhs.tele, classified.pat_vars);
let ren = Subst::parallel(
(vars.into_iter().rev())
.enumerate()
.map(|(b, _)| DeBruijn::from_dbi(DBI(b))),
);
let mut as_binds = classified.as_binds;
as_binds.append(&mut asb);
let lhs_result = Lhs {
tele: lhs.tele,
has_absurd: classified.absurd_count > 0,
pats: lhs.pats.reduce_dbi(ren),
ty: lhs.target,
pat_sub,
as_binds,
};
Ok((lhs_result, tcs))
}
pub(super) fn check_lhs(mut tcs: TCS, mut lhs: LhsState) -> TCMS<Lhs> {
let problem_equations = (lhs.problem.equations.iter())
.filter(|e| e.in_pat.is_split())
.cloned()
.collect::<Vec<_>>();
for split in problem_equations {
use Copat::{App, Proj};
use Pat::{Absurd, Forced};
if lhs.problem.is_all_solved() {
return final_check(tcs, lhs);
}
let (is_eta, tcs0) = is_eta_var_ref(tcs, &split.inst, &split.ty)?;
tcs = tcs0;
let e = || TCE::split_on_non_var(split.inst.clone(), split.ty.clone());
let ix = is_eta.ok_or_else(e)?;
match split.in_pat {
App(Pat::Refl) => unimplemented!(),
App(Pat::Cons(force, a, b)) => {
let (lhs0, tcs0) = split_con(tcs, ix, lhs, force, a, b)?;
tcs = tcs0;
lhs = lhs0;
}
App(Pat::Var(..)) | App(Absurd) | App(Forced(..)) | Proj(..) => unreachable!(),
}
}
debug_assert!(lhs.problem.is_all_solved());
final_check(tcs, lhs)
}