use crate::{
diagnostics::WarningType,
fun::{Book, Ctx, Name, Term},
maybe_grow,
};
use std::collections::{hash_map::Entry, HashMap};
#[derive(Clone, Copy, Debug, PartialEq)]
enum Used {
Main,
NonBuiltin,
Ctr,
}
type Definitions = HashMap<Name, Used>;
impl Ctx<'_> {
pub fn prune(&mut self, prune_all: bool) {
let mut used = Definitions::new();
if let Some(main) = &self.book.entrypoint {
let def = self.book.defs.get(main).unwrap();
used.insert(main.clone(), Used::Main);
self.book.find_used_definitions(&def.rule().body, Used::Main, &mut used);
}
for def in self.book.defs.values() {
if !def.builtin && !(used.get(&def.name) == Some(&Used::Main)) {
if self.book.ctrs.contains_key(&def.name) {
used.insert(def.name.clone(), Used::Ctr);
} else {
used.insert(def.name.clone(), Used::NonBuiltin);
}
self.book.find_used_definitions(&def.rule().body, Used::NonBuiltin, &mut used);
}
}
for def in self.book.defs.keys().cloned().collect::<Vec<_>>() {
if let Some(use_) = used.get(&def) {
match use_ {
Used::Main => {
}
Used::NonBuiltin => {
if prune_all {
self.book.defs.shift_remove(&def);
} else {
self.info.add_rule_warning("Definition is unused.", WarningType::UnusedDefinition, def);
}
}
Used::Ctr => {
if prune_all {
self.book.defs.shift_remove(&def);
} else {
}
}
}
} else {
self.book.defs.shift_remove(&def);
}
}
}
}
impl Book {
fn find_used_definitions(&self, term: &Term, used: Used, uses: &mut Definitions) {
maybe_grow(|| {
let mut to_find = vec![term];
while let Some(term) = to_find.pop() {
match term {
Term::Ref { nam: def_name } => self.insert_used(def_name, used, uses),
Term::List { .. } => {
self.insert_used(&Name::new(crate::fun::builtins::LCONS), used, uses);
self.insert_used(&Name::new(crate::fun::builtins::LNIL), used, uses);
}
Term::Str { .. } => {
self.insert_used(&Name::new(crate::fun::builtins::SCONS), used, uses);
self.insert_used(&Name::new(crate::fun::builtins::SNIL), used, uses);
}
_ => {}
}
for child in term.children() {
to_find.push(child);
}
}
})
}
fn insert_used(&self, def_name: &Name, used: Used, uses: &mut Definitions) {
if let Entry::Vacant(e) = uses.entry(def_name.clone()) {
e.insert(used);
for rule in &self.defs[def_name].rules {
self.find_used_definitions(&rule.body, used, uses);
}
}
}
}