use crate::bignat::BigNat;
use crate::declaration::RecursorVal;
use crate::expr_util::{get_app_arg_nodes, get_app_fn, get_app_fn_args, mk_app, mk_app_nodes};
use crate::instantiate::instantiate_type_lparams;
use crate::Node;
use crate::{Environment, Expr, Level, Literal, Name, TypeChecker};
use std::rc::Rc;
pub(super) fn nat_lit_to_ctor(n: &BigNat, rec_val: &RecursorVal) -> Option<Expr> {
if rec_val.all.len() != 1 || rec_val.all[0].to_string() != "Nat" {
return None;
}
if n.is_zero() {
let zero_rule = rec_val.rules.iter().find(|r| r.nfields == 0)?;
Some(Expr::Const(zero_rule.ctor.clone(), vec![]))
} else {
let succ_rule = rec_val.rules.iter().find(|r| r.nfields == 1)?;
Some(Expr::App(
Node::new(Expr::Const(succ_rule.ctor.clone(), vec![])),
Node::new(Expr::Lit(Literal::Nat(n.pred()))),
))
}
}
fn char_list_of_str(s: &str, hierarchical: bool) -> Expr {
let name_of = |dotted: &str| {
if hierarchical {
Name::from_str(dotted)
} else {
Name::str(dotted)
}
};
let char_ty = Expr::Const(name_of("Char"), vec![]);
let mut list = mk_app(
Expr::Const(name_of("List.nil"), vec![Level::zero()]),
std::slice::from_ref(&char_ty),
);
for c in s.chars().rev() {
let code = Expr::Lit(Literal::Nat(BigNat::from(c as u32)));
let ch = Expr::App(
Node::new(Expr::Const(name_of("Char.ofNat"), vec![])),
Node::new(code),
);
list = mk_app(
Expr::Const(name_of("List.cons"), vec![Level::zero()]),
&[char_ty.clone(), ch, list],
);
}
list
}
pub(crate) fn str_lit_expansion(s: &str, env: &Environment) -> Option<Expr> {
for (of_list, hierarchical) in [
(Name::str("String").append_str("ofList"), true),
(Name::str("String.ofList"), false),
] {
if env.find(&of_list).is_some() {
return Some(Expr::App(
Node::new(Expr::Const(of_list, vec![])),
Node::new(char_list_of_str(s, hierarchical)),
));
}
}
if let Some(iv) = env.get_inductive_val(&Name::str("String")) {
let ctor_name = iv.ctors.first()?;
let cv = env.get_constructor_val(ctor_name)?;
if iv.ctors.len() == 1 && cv.num_fields == 1 && cv.num_params == 0 {
let hierarchical = ctor_name.depth() > 1;
return Some(Expr::App(
Node::new(Expr::Const(ctor_name.clone(), vec![])),
Node::new(char_list_of_str(s, hierarchical)),
));
}
}
None
}
pub(super) fn to_ctor_when_k(
rec_val: &RecursorVal,
major: &Expr,
env: &Environment,
locals: &crate::local_types::LocalTypes,
) -> Option<Expr> {
let ind_name = rec_val.all.first()?;
let mut tc = TypeChecker::with_local_types(env, locals);
let app_ty = tc.infer_type(major).ok()?;
let app_ty = tc.whnf(&app_ty);
let (head, ty_args) = get_app_fn_args(&app_ty);
let (head_name, levels) = match head {
Expr::Const(n, ls) => (n, ls),
_ => return None,
};
if head_name != ind_name {
return None;
}
let iv = env.get_inductive_val(ind_name)?;
if ty_args.len() != (iv.num_params + iv.num_indices) as usize {
return None;
}
let ctor_name = iv.ctors.first()?;
let params: Vec<Expr> = ty_args[..iv.num_params as usize]
.iter()
.map(|e| (*e).clone())
.collect();
let new_ctor = mk_app(Expr::Const(ctor_name.clone(), levels.clone()), ¶ms);
let new_ty = tc.infer_type(&new_ctor).ok()?;
if !tc.is_def_eq(&app_ty, &new_ty) {
return None;
}
Some(new_ctor)
}
pub(super) fn to_ctor_when_struct(
rec_val: &RecursorVal,
major: &Expr,
env: &Environment,
locals: &crate::local_types::LocalTypes,
) -> Option<Expr> {
if rec_val.all.len() != 1 {
return None;
}
let ind_name = rec_val.all.first()?;
if !env.is_structure_like(ind_name) {
return None;
}
if let (Expr::Const(head_name, _), _) = get_app_fn_args(major) {
if env.is_constructor(head_name) {
return None;
}
}
let mut tc = TypeChecker::with_local_types(env, locals);
let e_ty = tc.infer_type(major).ok()?;
let e_ty = tc.whnf(&e_ty);
let (ty_head, ty_args) = get_app_fn_args(&e_ty);
let (head_name, levels) = match ty_head {
Expr::Const(n, ls) => (n, ls),
_ => return None,
};
if head_name != ind_name {
return None;
}
let e_ty_sort = tc.infer_type(&e_ty).ok()?;
let e_ty_sort = tc.whnf(&e_ty_sort);
if matches!(&e_ty_sort, Expr::Sort(l) if l.is_zero()) {
return None;
}
let iv = env.get_inductive_val(ind_name)?;
if ty_args.len() != iv.num_params as usize {
return None;
}
let ctor_name = iv.ctors.first()?;
let cv = env.get_constructor_val(ctor_name)?;
let params: Vec<Expr> = ty_args.iter().map(|e| (*e).clone()).collect();
let mut result = mk_app(Expr::Const(ctor_name.clone(), levels.clone()), ¶ms);
for i in 0..cv.num_fields {
result = Expr::App(
Node::new(result),
Node::new(Expr::Proj(ind_name.clone(), i, Node::new(major.clone()))),
);
}
Some(result)
}
pub(super) fn apply_recursor_rule(
rec_val: &RecursorVal,
rec_levels: &[Level],
rec_args: &[&Node],
major_idx: usize,
major: &Expr,
env: &Environment,
) -> Option<Expr> {
let ctor_fn = get_app_fn(major);
let ctor_args = get_app_arg_nodes(major);
let ctor_name = match ctor_fn {
Expr::Const(name, _) => name,
_ => return None,
};
if !env.is_constructor(ctor_name) {
return None;
}
let rule = rec_val.get_rule(ctor_name)?;
let nfields = rule.nfields as usize;
if ctor_args.len() < nfields {
return None;
}
let rhs = if rec_val.common.level_params.is_empty() || rec_levels.is_empty() {
rule.rhs.clone()
} else {
instantiate_type_lparams(&rule.rhs, &rec_val.common.level_params, rec_levels)
};
let n_premises = (rec_val.num_params + rec_val.num_motives + rec_val.num_minors) as usize;
if rec_args.len() < n_premises {
return None;
}
let mut r = mk_app_nodes(rhs, &rec_args[..n_premises]);
r = mk_app_nodes(r, &ctor_args[ctor_args.len() - nfields..]);
r = mk_app_nodes(r, &rec_args[major_idx + 1..]);
Some(r)
}