use std::collections::HashMap;
use crate::{
ctx::{ctx_lookup, CtxEntry},
env::Env,
reduce::{instantiate, whnf},
subst::{shift, subst},
term::Term,
};
use super::{
ast::{Arm, Binder, Expr, Pattern},
meta::{ElabError, MetaCtx},
};
#[derive(Debug)]
pub struct ElabState {
pub mctx: MetaCtx,
pub globals: HashMap<String, (Term, Term)>,
pub locals: Vec<(String, Term)>,
}
impl ElabState {
pub fn new() -> Self {
ElabState { mctx: MetaCtx::new(), globals: HashMap::new(), locals: Vec::new() }
}
pub fn add_stdlib(&mut self) {
use crate::stdlib::*;
let nat_arr = Term::Pi(Box::new(nat()), Box::new(nat()));
let type1 = Term::Sort(1);
let list_fn = Term::Lam(Box::new(type1.clone()),
Box::new(Term::Ind(LIST_ID, vec![Term::Var(0)])));
let list_ty = Term::Pi(Box::new(type1.clone()), Box::new(type1.clone()));
let nil_fn = Term::Lam(Box::new(type1.clone()),
Box::new(Term::Ctor(LIST_ID, LIST_NIL_IDX, vec![Term::Var(0)])));
let nil_ty = Term::Pi(Box::new(type1.clone()),
Box::new(Term::Ind(LIST_ID, vec![Term::Var(0)])));
let next_fn = Term::Lam(Box::new(nat()),
Box::new(Term::Ctor(NAT_ID, NAT_NEXT, vec![Term::Var(0)])));
let eq_fn = Term::Lam(Box::new(type1.clone()), Box::new(
Term::Lam(Box::new(Term::Var(0)), Box::new(
Term::Lam(Box::new(Term::Var(1)), Box::new(
Term::Ind(EQ_ID, vec![Term::Var(2), Term::Var(1), Term::Var(0)])))))));
let eq_ty = Term::Pi(Box::new(type1.clone()), Box::new(
Term::Pi(Box::new(Term::Var(0)), Box::new(
Term::Pi(Box::new(Term::Var(1)), Box::new(Term::Sort(0)))))));
self.globals.insert("Nat".into(), (nat(), type1.clone()));
self.globals.insert("Nat.zero".into(), (nat_zero(), nat()));
self.globals.insert("Nat.next".into(), (next_fn.clone(), nat_arr.clone()));
self.globals.insert("Bool".into(), (bool_ty(), type1.clone()));
self.globals.insert("Bool.false".into(),(bool_false(), bool_ty()));
self.globals.insert("Bool.true".into(), (bool_true(), bool_ty()));
self.globals.insert("True".into(), (Term::Ind(TRUE_ID, vec![]), Term::Sort(0)));
self.globals.insert("False".into(), (Term::Ind(FALSE_ID, vec![]), Term::Sort(0)));
self.globals.insert("List".into(), (list_fn, list_ty));
self.globals.insert("List.nil".into(), (nil_fn, nil_ty));
self.globals.insert("Eq".into(), (eq_fn, eq_ty));
let nat_nat_nat = Term::Pi(Box::new(nat()), Box::new(Term::Pi(Box::new(nat()), Box::new(nat()))));
let add_fn = Term::Lam(Box::new(nat()), Box::new(Term::Lam(
Box::new(nat()),
Box::new(nat_add(Term::Var(1), Term::Var(0))),
)));
let mul_fn = Term::Lam(Box::new(nat()), Box::new(Term::Lam(
Box::new(nat()),
Box::new(nat_mul(Term::Var(1), Term::Var(0))),
)));
self.globals.insert("Nat.add".into(), (add_fn, nat_nat_nat.clone()));
self.globals.insert("Nat.mul".into(), (mul_fn, nat_nat_nat.clone()));
self.globals.insert("Nat.succ".into(), (next_fn.clone(), nat_arr.clone()));
let eq_refl_fn = Term::Lam(Box::new(type1.clone()), Box::new(Term::Lam(
Box::new(Term::Var(0)),
Box::new(eq_refl(Term::Var(1), Term::Var(0))),
)));
let eq_refl_ty = Term::Pi(Box::new(type1.clone()), Box::new(
Term::Pi(Box::new(Term::Var(0)),
Box::new(Term::Ind(EQ_ID, vec![Term::Var(1), Term::Var(0), Term::Var(0)])))));
self.globals.insert("Eq.refl".into(), (eq_refl_fn, eq_refl_ty));
let and_fn = Term::Lam(Box::new(Term::Sort(0)), Box::new(Term::Lam(
Box::new(Term::Sort(0)),
Box::new(Term::Ind(AND_ID, vec![Term::Var(1), Term::Var(0)])),
)));
let and_ty = Term::Pi(Box::new(Term::Sort(0)), Box::new(
Term::Pi(Box::new(Term::Sort(0)), Box::new(Term::Sort(0)))));
self.globals.insert("And".into(), (and_fn, and_ty));
let and_intro_fn = Term::Lam(Box::new(Term::Sort(0)), Box::new( Term::Lam(Box::new(Term::Sort(0)), Box::new( Term::Lam(Box::new(Term::Var(1)), Box::new( Term::Lam(Box::new(Term::Var(1)), Box::new( Term::Ctor(AND_ID, 0, vec![
Term::Var(3), Term::Var(2), Term::Var(1), Term::Var(0)
])
))
))
))
));
let and_intro_ty = Term::Pi(Box::new(Term::Sort(0)), Box::new( Term::Pi(Box::new(Term::Sort(0)), Box::new( Term::Pi(Box::new(Term::Var(1)), Box::new( Term::Pi(Box::new(Term::Var(1)), Box::new( Term::Ind(AND_ID, vec![Term::Var(3), Term::Var(2)])
))
))
))
));
self.globals.insert("And.intro".into(), (and_intro_fn, and_intro_ty));
self.globals.insert("Le".into(), (le_type_lam(), le_type_pi()));
self.globals.insert("Nat.Le".into(), (le_type_lam(), le_type_pi()));
let le_refl_ty = Term::Pi(
Box::new(nat()),
Box::new(Term::Ind(LE_ID, vec![Term::Var(0), Term::Var(0)])),
);
let le_refl_term = Term::Lam(
Box::new(nat()),
Box::new(Term::Ctor(LE_ID, LE_REFL_IDX, vec![Term::Var(0), Term::Var(0)])),
);
self.globals.insert("Le.refl".into(), (le_refl_term.clone(), le_refl_ty.clone()));
self.globals.insert("Nat.Le.refl".into(), (le_refl_term, le_refl_ty));
let le_step_ty = Term::Pi(
Box::new(nat()),
Box::new(Term::Pi(
Box::new(nat()),
Box::new(Term::Pi(
Box::new(Term::Ind(LE_ID, vec![Term::Var(1), Term::Var(0)])),
Box::new(Term::Ind(LE_ID, vec![Term::Var(2), nat_next(Term::Var(1))])),
)),
)),
);
let le_step_fn = Term::Lam(
Box::new(nat()),
Box::new(Term::Lam(
Box::new(nat()),
Box::new(Term::Lam(
Box::new(Term::Ind(LE_ID, vec![Term::Var(1), Term::Var(0)])),
Box::new(Term::Ctor(LE_ID, LE_STEP_IDX, vec![Term::Var(2), Term::Var(1), Term::Var(0)])),
)),
)),
);
self.globals.insert("Le.step".into(), (le_step_fn.clone(), le_step_ty.clone()));
self.globals.insert("Nat.Le.step".into(), (le_step_fn, le_step_ty));
self.globals.insert("Nat.min".into(), (nat_min_term(), nat_nat_nat.clone()));
self.globals.insert("Nat.max".into(), (nat_max_term(), nat_nat_nat.clone()));
let sub_fn = Term::Lam(Box::new(nat()), Box::new(Term::Lam(
Box::new(nat()),
Box::new(crate::stdlib::nat_sub(Term::Var(1), Term::Var(0))),
)));
self.globals.insert("Nat.sub".into(), (sub_fn, nat_nat_nat.clone()));
self.globals.insert("Nat.div".into(), (nat_nat_nat.clone(), nat_nat_nat.clone()));
let opaque = Term::Sort(0);
self.globals.insert("Nat.min_le_right".into(), (opaque.clone(), opaque.clone()));
self.globals.insert("Nat.div_le_self".into(), (opaque.clone(), opaque.clone()));
self.globals.insert("Nat.div_le_div_right".into(), (opaque.clone(), opaque.clone()));
self.globals.insert("Nat.mul_le_mul_right".into(), (opaque.clone(), opaque.clone()));
self.globals.insert("Nat.mul_div_cancel_left".into(), (opaque.clone(), opaque));
}
pub fn depth(&self) -> usize { self.locals.len() }
pub fn ctx(&self) -> crate::ctx::Ctx {
self.locals.iter().map(|(_, t)| CtxEntry::Var(t.clone())).collect()
}
pub fn push(&mut self, name: String, ty: Term) {
self.locals.insert(0, (name, ty));
}
pub fn pop(&mut self) {
if !self.locals.is_empty() { self.locals.remove(0); }
}
pub fn lookup_local(&self, name: &str) -> Option<(u64, Term)> {
let ctx = self.ctx();
for (i, (n, _)) in self.locals.iter().enumerate() {
if n == name {
let ty = match ctx_lookup(&ctx, i)? {
CtxEntry::Var(t) | CtxEntry::Let(t, _) => t,
};
return Some((i as u64, ty));
}
}
None
}
}
pub fn elab_expr(st: &mut ElabState, env: &Env, expr: &Expr) -> Result<(Term, Term), ElabError> {
match expr {
Expr::Name(n) => {
if let Some((idx, ty)) = st.lookup_local(n) {
return Ok((Term::Var(idx), ty));
}
if let Some((t, ty)) = st.globals.get(n).cloned() {
return Ok((t, ty));
}
Err(ElabError::UnknownName(n.clone()))
}
Expr::Sort(level) => {
let u: u64 = match level {
None => 0, Some(0) => 1, Some(n) => 1 + n, };
Ok((Term::Sort(u), Term::Sort(u + 1)))
}
Expr::Arrow(a, b) => {
let (a_term, a_ty) = elab_expr(st, env, a)?;
let sort_a = require_sort(a_ty)?;
let (b_term, b_ty) = elab_expr(st, env, b)?;
let sort_b = require_sort(b_ty)?;
Ok((
Term::Pi(Box::new(a_term), Box::new(shift(&b_term, 1))),
Term::Sort(Term::prop_max(sort_a, sort_b)),
))
}
Expr::Pi(binder, body) => {
let (a_term, a_ty) = elab_expr(st, env, binder.ty())?;
let sort_a = require_sort(a_ty)?;
st.push(binder.name().to_string(), a_term.clone());
let res = elab_expr(st, env, body);
st.pop();
let (b_term, b_ty) = res?;
let sort_b = require_sort(b_ty)?;
Ok((
Term::Pi(Box::new(a_term), Box::new(b_term)),
Term::Sort(Term::prop_max(sort_a, sort_b)),
))
}
Expr::Fun(binder, body) => {
let (a_term, _) = elab_expr(st, env, binder.ty())?;
st.push(binder.name().to_string(), a_term.clone());
let res = elab_expr(st, env, body);
st.pop();
let (body_term, body_ty) = res?;
Ok((
Term::Lam(Box::new(a_term.clone()), Box::new(body_term)),
Term::Pi(Box::new(a_term), Box::new(body_ty)),
))
}
Expr::App(f_expr, a_expr) => {
let (f_term, f_ty) = elab_expr(st, env, f_expr)?;
let ctx = st.ctx();
let f_ty_w = whnf(env, &ctx, st.mctx.zonk(&f_ty));
match f_ty_w {
Term::Pi(a_ty, b_ty) => {
let (a_term, a_actual) = elab_expr(st, env, a_expr)?;
let ctx2 = st.ctx();
st.mctx.unify(env, &ctx2, &a_ty, &a_actual)?;
let res_ty = subst(&b_ty, &a_term);
Ok((Term::App(Box::new(f_term), Box::new(a_term)), res_ty))
}
other => Err(ElabError::ExpectedPi(other)),
}
}
Expr::Let(x, ty_expr, val_expr, body_expr) => {
let (ty_term, _) = elab_expr(st, env, ty_expr)?;
let (val_term, val_ty) = elab_expr(st, env, val_expr)?;
let ctx = st.ctx();
st.mctx.unify(env, &ctx, &ty_term, &val_ty)?;
st.push(x.clone(), ty_term.clone());
let res = elab_expr(st, env, body_expr);
st.pop();
let (body_term, body_ty) = res?;
let res_ty = subst(&body_ty, &val_term);
Ok((
Term::Let(Box::new(ty_term), Box::new(val_term), Box::new(body_term)),
res_ty,
))
}
Expr::Hole => {
let ty = st.mctx.fresh();
let tm = st.mctx.fresh();
Ok((tm, ty))
}
Expr::NatLit(n) => Ok((crate::stdlib::nat_lit(*n), crate::stdlib::nat())),
Expr::Match(target, motive, arms) =>
elab_match(st, env, target, motive.as_deref(), arms),
Expr::Explicit(name, args) => {
let (mut t, mut ty) = elab_expr(st, env, &Expr::Name(name.clone()))?;
for arg in args {
let (a, a_ty) = elab_expr(st, env, arg)?;
let ctx = st.ctx();
match whnf(env, &ctx, st.mctx.zonk(&ty)) {
Term::Pi(dom, cod) => {
st.mctx.unify(env, &ctx, &dom, &a_ty)?;
ty = subst(&cod, &a);
t = Term::App(Box::new(t), Box::new(a));
}
other => return Err(ElabError::ExpectedPi(other)),
}
}
Ok((t, ty))
}
}
}
fn elab_match(
st: &mut ElabState,
env: &Env,
target: &Expr,
motive: Option<&Expr>,
arms: &[Arm],
) -> Result<(Term, Term), ElabError> {
let (tg_term, tg_ty) = elab_expr(st, env, target)?;
let ctx = st.ctx();
let tg_ty_w = whnf(env, &ctx, st.mctx.zonk(&tg_ty));
let (ind_id, params) = match tg_ty_w {
Term::Ind(id, ps) => (id, ps),
other => return Err(ElabError::ExpectedInd(other)),
};
let desc = env.lookup(ind_id)
.ok_or_else(|| ElabError::UnknownName(format!("{ind_id:#x}")))?;
if arms.len() != desc.constructors.len() {
return Err(ElabError::CaseMismatch {
expected: desc.constructors.len(),
got: arms.len(),
});
}
let motive_term = match motive {
Some(m) => elab_expr(st, env, m)?.0,
None => st.mctx.fresh(),
};
let ctors = desc.constructors.clone();
let mut cases = Vec::with_capacity(arms.len());
for (arm, ctor_tel) in arms.iter().zip(ctors.iter()) {
cases.push(elab_arm(st, env, arm, ctor_tel, ¶ms, ind_id)?);
}
let res_ty = Term::App(Box::new(motive_term.clone()), Box::new(tg_term.clone()));
Ok((Term::Elim(ind_id, Box::new(motive_term), cases, Box::new(tg_term)), res_ty))
}
fn elab_arm(
st: &mut ElabState,
env: &Env,
arm: &Arm,
ctor_tel: &Term,
params: &[Term],
ind_id: u64,
) -> Result<Term, ElabError> {
let inst_tel = instantiate(ctor_tel, params);
let field_names: Vec<&str> = match &arm.pattern {
Pattern::Wildcard | Pattern::Name(_) => vec![],
Pattern::App(_, vars) => vars.iter().map(|s| s.as_str()).collect(),
};
let (pushed, lam_types) = push_arm_locals(st, env, inst_tel, &field_names, ind_id)?;
let res = elab_expr(st, env, &arm.body);
for _ in 0..pushed { st.pop(); }
let (body_term, _) = res?;
let mut result = body_term;
for ty in lam_types.into_iter().rev() {
result = Term::Lam(Box::new(ty), Box::new(result));
}
Ok(result)
}
fn push_arm_locals(
st: &mut ElabState,
env: &Env,
mut tel: Term,
names: &[&str],
ind_id: u64,
) -> Result<(usize, Vec<Term>), ElabError> {
let mut pushed = 0;
let mut lam_types: Vec<Term> = Vec::new();
let mut name_idx = 0;
loop {
let ctx = st.ctx();
match whnf(env, &ctx, tel) {
Term::Pi(a, rest) => {
let fname = if name_idx < names.len() {
names[name_idx].to_string()
} else {
format!("_f{name_idx}")
};
name_idx += 1;
let a_w = whnf(env, &ctx, *a.clone());
let is_rec = matches!(&a_w, Term::Ind(fid, _) if *fid == ind_id);
st.push(fname, *a.clone());
pushed += 1;
lam_types.push(*a.clone());
if is_rec {
let ih_ty = st.mctx.fresh();
st.push("_ih".to_string(), ih_ty.clone());
pushed += 1;
lam_types.push(ih_ty);
}
tel = subst(&rest, &Term::Var(0));
}
_ => break,
}
}
Ok((pushed, lam_types))
}
fn require_sort(ty: Term) -> Result<u64, ElabError> {
match ty {
Term::Sort(u) => Ok(u),
other => Err(ElabError::ExpectedSort(other)),
}
}
pub fn push_params(
st: &mut ElabState,
env: &Env,
params: &[Binder],
) -> Result<Vec<Term>, ElabError> {
let mut types = Vec::with_capacity(params.len());
for b in params {
let (ty_term, _) = elab_expr(st, env, b.ty())?;
st.push(b.name().to_string(), ty_term.clone());
types.push(ty_term);
}
Ok(types)
}
pub fn fold_pi(param_types: &[Term], body: Term) -> Term {
let mut result = body;
for ty in param_types.iter().rev() {
result = Term::Pi(Box::new(ty.clone()), Box::new(result));
}
result
}
pub fn fold_lam(param_types: &[Term], body: Term) -> Term {
let mut result = body;
for ty in param_types.iter().rev() {
result = Term::Lam(Box::new(ty.clone()), Box::new(result));
}
result
}