use voile_util::level::LiftEx;
use voile_util::loc::{merge_info, Ident};
use voile_util::meta::MetaSolution;
use voile_util::uid::DBI;
use crate::check::monad::TCS;
use crate::syntax::abs::{Abs, LabAbs};
use crate::syntax::core::{CaseSplit, Closure, Neutral, TraverseNeutral, Val, ValInfo, Variants};
fn evaluate(tcs: TCS, abs: Abs) -> (ValInfo, TCS) {
use Abs::*;
match abs {
Type(info, level) => (Val::Type(level).into_info(info), tcs),
Var(ident, _, i) => {
let resolved = tcs.local_val(i).ast.clone().attach_dbi(i);
(resolved.into_info(ident.loc), tcs)
}
Rec(info, fields, ext) => {
let (variants, tcs) = evaluate_variants(tcs, fields);
let record = Val::Rec(variants);
match ext {
None => (record.into_info(info), tcs),
Some(ext) => {
let (ext, tcs) = tcs.evaluate(*ext);
(record.rec_extend(ext.ast).into_info(info), tcs)
}
}
}
Ref(ident, dbi) => (tcs.glob_val(dbi).ast.clone().into_info(ident.loc), tcs),
Cons(info) => (compile_cons(info), tcs),
App(info, f, _, a) => {
let (f, tcs) = evaluate(tcs, *f);
let (a, tcs) = evaluate(tcs, *a);
let (f, tcs) = tcs.expand_global(f.ast);
let applied = f.apply(a.ast);
(applied.into_info(info), tcs)
}
Dt(info, kind, _, param_plicit, param_ty, ret_ty) => {
let (param_ty, tcs) = evaluate(tcs, *param_ty);
let (ret_ty, tcs) = evaluate(tcs, *ret_ty);
let term = Val::closure_dependent_type(kind, param_plicit, param_ty.ast, ret_ty.ast);
(term.into_info(info), tcs)
}
Pair(info, a, b) => {
let (a, tcs) = evaluate(tcs, *a);
let (b, tcs) = evaluate(tcs, *b);
(Val::pair(a.ast, b.ast).into_info(info), tcs)
}
Fst(info, p) => {
let (p, tcs) = evaluate(tcs, *p);
let (p, tcs) = tcs.expand_global(p.ast);
(p.first().into_info(info), tcs)
}
Snd(info, p) => {
let (p, tcs) = evaluate(tcs, *p);
let (p, tcs) = tcs.expand_global(p.ast);
(p.second().into_info(info), tcs)
}
Proj(info, rec, field) => {
let (rec, tcs) = evaluate(tcs, *rec);
let (rec, tcs) = tcs.expand_global(rec.ast);
(rec.project(field.text).into_info(info), tcs)
}
Lam(info, _, _, body) => {
let (body, tcs) = evaluate(tcs, *body);
(body.ast.into_info(info), tcs)
}
Lift(info, levels, expr) => {
let (expr, tcs) = evaluate(tcs, *expr);
let (expr, tcs) = tcs.expand_global(expr.ast);
(expr.lift(levels).into_info(info), tcs)
}
Meta(ident, mi) => {
if let MetaSolution::Solved(sol) = tcs.meta_context.solution(mi) {
(sol.clone().into_info(ident.loc), tcs)
} else {
(Val::meta(mi).into_info(ident.loc), tcs)
}
}
RowPoly(info, kind, variants, ext) => {
let (variants, tcs) = evaluate_variants(tcs, variants);
let row_poly = Val::RowPoly(kind, variants);
match ext {
None => (row_poly.into_info(info), tcs),
Some(ext) => {
let (ext, tcs) = tcs.evaluate(*ext);
(row_poly.row_extend(ext.ast).into_info(info), tcs)
}
}
}
RowKind(info, kind, labels) => {
let labels = labels.into_iter().map(|l| l.text).collect();
let expr = Val::RowKind(Default::default(), kind, labels);
(expr.into_info(info), tcs)
}
CaseOr(label, _, _, body, or) => {
let (or, tcs) = tcs.evaluate(*or);
let (body, tcs) = tcs.evaluate(*body);
let info = merge_info(&label, &or);
let mut split = CaseSplit::default();
split.insert(label.text, Closure::plain(body.ast));
let lam = Val::case_tree(split);
(or.ast.split_extend(lam).into_info(info), tcs)
}
Whatever(info) => (Val::Lam(Closure::default()).into_info(info), tcs),
}
}
fn evaluate_variants(mut tcs: TCS, variants: Vec<LabAbs>) -> (Variants, TCS) {
let mut out_variants = Variants::new();
for labelled in variants.into_iter() {
let (expr, new_tcs) = tcs.evaluate(labelled.expr);
tcs = new_tcs;
out_variants.insert(labelled.label.text, expr.ast);
}
(out_variants, tcs)
}
fn expand_global(tcs: TCS, expr: Val) -> (Val, TCS) {
use Neutral::*;
fn go(tcs: &TCS, neut: Neutral) -> Val {
let java = |neut: Box<Neutral>| go(tcs, *neut);
match neut {
Ref(index) => tcs.glob_val(index).ast.clone(),
Lift(levels, o) => java(o).lift(levels),
App(o, args) => args.into_iter().fold(java(o), Val::apply),
Fst(p) => java(p).first(),
Snd(p) => java(p).second(),
Proj(r, f) => java(r).project(f),
Meta(mi) => match &tcs.meta_context.solution(mi) {
MetaSolution::Solved(val) => *val.clone(),
MetaSolution::Unsolved => panic!("Cannot eval unsolved meta: {:?}", mi),
MetaSolution::Inlined => unreachable!(),
},
SplitOn(split, obj) => Val::case_tree(split).apply(java(obj)),
OrSplit(split, or) => Val::case_tree(split).split_extend(java(or)),
Row(kind, variants, ext) => Val::RowPoly(kind, variants)
.row_extend_safe(java(ext))
.unwrap_or_else(|(a, b)| match (a, b) {
(Val::RowPoly(_, v), Val::Neut(ext)) => Val::neutral_row_type(kind, v, ext),
_ => unreachable!(),
}),
Rec(fields, ext) => {
(Val::Rec(fields).rec_extend_safe(java(ext))).unwrap_or_else(|(a, b)| {
match (a, b) {
(Val::Rec(v), Val::Neut(ext)) => Val::neutral_record(v, ext),
_ => unreachable!(),
}
})
}
neut => Val::Neut(neut),
}
}
let val = expr.map_neutral(&mut |neut| go(&tcs, neut));
(val, tcs)
}
pub fn compile_cons(info: Ident) -> ValInfo {
let mut text = info.text;
text.remove(0);
Val::closure_lam(Val::cons(text, Val::var(DBI(0)))).into_info(info.loc)
}
impl TCS {
#[inline]
pub fn evaluate(self, abs: Abs) -> (ValInfo, Self) {
evaluate(self, abs)
}
#[inline]
pub fn expand_global(self, expr: Val) -> (Val, TCS) {
expand_global(self, expr)
}
}