use std::collections::{BTreeSet, HashMap, HashSet};
use crate::{
fun::{Book, Name, Term},
maybe_grow,
};
impl Term {
pub fn expand_generated(&mut self, book: &Book, recursive_defs: &RecursiveDefs) {
maybe_grow(|| {
if let Term::Ref { nam } = &*self {
if nam.is_generated() && !recursive_defs.contains(nam) {
*self = book.defs.get(nam).unwrap().rule().body.clone();
}
}
for child in self.children_mut() {
child.expand_generated(book, recursive_defs);
}
})
}
}
type DepGraph = HashMap<Name, HashSet<Name>>;
type Cycles = Vec<Vec<Name>>;
type RecursiveDefs = BTreeSet<Name>;
impl Book {
pub fn recursive_defs(&self) -> RecursiveDefs {
let mut cycle_map = BTreeSet::new();
let deps = book_def_deps(self);
let cycles = cycles(&deps);
for cycle in cycles {
for name in cycle {
cycle_map.insert(name);
}
}
cycle_map
}
}
fn cycles(deps: &DepGraph) -> Cycles {
let mut cycles = vec![];
let mut visited = HashSet::new();
for nam in deps.keys() {
if !visited.contains(nam) {
find_cycles(deps, nam, &mut visited, &mut cycles);
}
}
cycles
}
fn find_cycles(deps: &DepGraph, nam: &Name, visited: &mut HashSet<Name>, cycles: &mut Cycles) {
let mut stack = vec![(nam.clone(), vec![])];
while let Some((current, path)) = stack.pop() {
if visited.contains(¤t) {
if let Some(cycle_start) = path.iter().position(|n| n == ¤t) {
cycles.push(path[cycle_start..].to_vec());
}
continue;
}
visited.insert(current.clone());
let mut new_path = path.clone();
new_path.push(current.clone());
if let Some(deps) = deps.get(¤t) {
for dep in deps {
stack.push((dep.clone(), new_path.clone()));
}
}
}
}
fn book_def_deps(book: &Book) -> DepGraph {
book.defs.iter().map(|(nam, def)| (nam.clone(), def_deps(def))).collect()
}
fn def_deps(def: &crate::fun::Definition) -> HashSet<Name> {
fn collect_refs(term: &Term, set: &mut HashSet<Name>) {
if let Term::Ref { nam } = term {
set.insert(nam.clone());
}
for children in term.children() {
collect_refs(children, set);
}
}
let mut set = HashSet::new();
let term = &def.rule().body;
collect_refs(term, &mut set);
set
}