use crate::kernel::{Context, Term};
use std::collections::{HashMap, HashSet};
pub fn is_extractable(ctx: &Context, name: &str) -> bool {
extractable(ctx, name, &mut HashMap::new())
}
fn extractable(ctx: &Context, name: &str, memo: &mut HashMap<String, bool>) -> bool {
if name == "_" {
return true;
}
if let Some(&b) = memo.get(name) {
return b;
}
memo.insert(name.to_string(), true);
let result = if ctx.is_constructor(name) {
ctx.constructor_inductive(name)
.map(|ind| !ctx.get_constructors(ind).is_empty())
.unwrap_or(false)
} else if ctx.is_inductive(name) {
(!ctx.get_constructors(name).is_empty() && !is_logical_type(name)) || is_mapped_primitive(name)
} else if let Some(body) = ctx.get_definition_body(name) {
let mut refs = Vec::new();
collect_globals(body, &mut refs);
refs.iter().all(|g| extractable(ctx, g, memo))
&& matches_inferable(ctx, body)
&& super::codegen::arith_uses_well_formed(body)
} else {
is_mapped_primitive(name) || super::codegen::arith_operator(name).is_some()
};
memo.insert(name.to_string(), result);
result
}
fn matches_inferable(ctx: &Context, term: &Term) -> bool {
match term {
Term::Match { discriminant, motive, cases } => {
if motive_inductive(ctx, motive)
.or_else(|| disc_inductive(ctx, discriminant))
.is_none()
{
return false;
}
matches_inferable(ctx, discriminant)
&& matches_inferable(ctx, motive)
&& cases.iter().all(|c| matches_inferable(ctx, c))
}
Term::Lambda { param_type, body, .. } => {
matches_inferable(ctx, param_type) && matches_inferable(ctx, body)
}
Term::Pi { param_type, body_type, .. } => {
matches_inferable(ctx, param_type) && matches_inferable(ctx, body_type)
}
Term::App(f, a) => matches_inferable(ctx, f) && matches_inferable(ctx, a),
Term::Fix { body, .. } => matches_inferable(ctx, body),
_ => true,
}
}
fn motive_inductive(ctx: &Context, motive: &Term) -> Option<String> {
if let Term::Lambda { param_type, .. } = motive {
if let Term::Global(name) = param_type.as_ref() {
if ctx.is_inductive(name) {
return Some(name.clone());
}
}
}
None
}
fn disc_inductive(ctx: &Context, term: &Term) -> Option<String> {
match term {
Term::Global(name) => {
if ctx.is_constructor(name) {
ctx.constructor_inductive(name).map(|s| s.to_string())
} else if ctx.is_inductive(name) {
Some(name.clone())
} else {
None
}
}
Term::App(f, _) => disc_inductive(ctx, f),
_ => None,
}
}
fn is_mapped_primitive(name: &str) -> bool {
matches!(
name,
"Int" | "Float" | "Text" | "Bool" | "Duration" | "Date" | "Moment"
)
}
pub fn is_logical_type(name: &str) -> bool {
matches!(
name,
"Syntax" | "Derivation" | "Eq" | "And" | "Or" | "Iff" | "Ex" | "Not"
| "True" | "False" | "Entity" | "Univ" | "Prop"
)
}
pub fn collect_dependencies(ctx: &Context, entry: &str) -> HashSet<String> {
let mut visited = HashSet::new();
let mut to_visit = vec![entry.to_string()];
while let Some(name) = to_visit.pop() {
if visited.contains(&name) {
continue;
}
visited.insert(name.clone());
if let Some(body) = ctx.get_definition_body(&name) {
collect_globals(body, &mut to_visit);
if let Some(ty) = ctx.get_definition_type(&name) {
collect_globals(ty, &mut to_visit);
}
}
if ctx.is_inductive(&name) {
for (ctor_name, ctor_ty) in ctx.get_constructors(&name) {
collect_globals(ctor_ty, &mut to_visit);
to_visit.push(ctor_name.to_string());
}
}
if ctx.is_constructor(&name) {
if let Some(ind) = ctx.constructor_inductive(&name) {
to_visit.push(ind.to_string());
}
}
}
visited
}
pub(crate) fn collect_globals(term: &Term, deps: &mut Vec<String>) {
match term {
Term::Global(name) => deps.push(name.clone()),
Term::Const { name, .. } => deps.push(name.clone()),
Term::App(f, a) => {
collect_globals(f, deps);
collect_globals(a, deps);
}
Term::Lambda {
param_type, body, ..
} => {
collect_globals(param_type, deps);
collect_globals(body, deps);
}
Term::Pi {
param_type,
body_type,
..
} => {
collect_globals(param_type, deps);
collect_globals(body_type, deps);
}
Term::Fix { body, .. } => collect_globals(body, deps),
Term::Match {
discriminant,
motive,
cases,
} => {
collect_globals(discriminant, deps);
collect_globals(motive, deps);
for case in cases {
collect_globals(case, deps);
}
}
Term::Let {
ty, value, body, ..
} => {
collect_globals(ty, deps);
collect_globals(value, deps);
collect_globals(body, deps);
}
Term::MutualFix { defs, .. } => {
for (_, body) in defs {
collect_globals(body, deps);
}
}
Term::Sort(_) | Term::Var(_) | Term::Lit(_) | Term::Hole => {}
}
}