diff --git a/Cargo.lock b/Cargo.lock index d2c4ce84..7714af54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,7 +62,7 @@ dependencies = [ [[package]] name = "bend-lang" -version = "0.2.29" +version = "0.2.30" dependencies = [ "TSPL", "clap", diff --git a/Cargo.toml b/Cargo.toml index 15258ce8..afb8ce93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "bend-lang" description = "A high-level, massively parallel programming language" license = "Apache-2.0" -version = "0.2.29" +version = "0.2.30" edition = "2021" rust-version = "1.74" exclude = ["tests/"] diff --git a/src/fun/transform/expand_generated.rs b/src/fun/transform/expand_generated.rs index e3d84f2f..c6574561 100644 --- a/src/fun/transform/expand_generated.rs +++ b/src/fun/transform/expand_generated.rs @@ -1,24 +1,106 @@ +use std::collections::{BTreeSet, HashMap, HashSet}; + use crate::{ - fun::{Book, Term}, + fun::{Book, Name, Term}, maybe_grow, }; -/// Dereferences any generated definitions in the term. +/// Dereferences any non recursive generated definitions in the term. /// Used after readback. impl Term { - pub fn expand_generated(&mut self, book: &Book) { + pub fn expand_generated(&mut self, book: &Book, recursive_defs: &RecursiveDefs) { maybe_grow(|| { if let Term::Ref { nam } = &*self { - if nam.is_generated() { + if nam.is_generated() && !recursive_defs.contains(nam) { *self = book.defs.get(nam).unwrap().rule().body.clone(); } } - // Note: this assumes that there will never be a loop of generated functions. - // This is true right now, but not necessarily in the future. for child in self.children_mut() { - child.expand_generated(book); + child.expand_generated(book, recursive_defs); } }) } } + +type DepGraph = HashMap>; +type Cycles = Vec>; +type RecursiveDefs = BTreeSet; + +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 + } +} + +/// Find all cycles in the dependency graph. +fn cycles(deps: &DepGraph) -> Cycles { + let mut cycles = vec![]; + let mut visited = HashSet::new(); + // let mut stack = vec![]; + 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, cycles: &mut Cycles) { + let mut stack = vec![(nam.clone(), vec![])]; + while let Some((current, path)) = stack.pop() { + if visited.contains(¤t) { + // Check if the current ref is already in the stack, which indicates a cycle. + if let Some(cycle_start) = path.iter().position(|n| n == ¤t) { + // If found, add the cycle to the cycles vector. + cycles.push(path[cycle_start..].to_vec()); + } + continue; + } + + // If the ref has not been visited yet, mark it as visited. + visited.insert(current.clone()); + // Add the current ref to the stack to keep track of the path. + let mut new_path = path.clone(); + new_path.push(current.clone()); + + // Search for cycles from each dependency. + 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 { + fn collect_refs(term: &Term, set: &mut HashSet) { + 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 +} diff --git a/src/lib.rs b/src/lib.rs index 87294ccc..fc58055b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -191,7 +191,8 @@ pub fn readback_hvm_net( let mut diags = Diagnostics::default(); let net = hvm_to_net(net); let mut term = net_to_term(&net, book, labels, linear, &mut diags); - term.expand_generated(book); + let recursive_cycles = book.recursive_defs(); + term.expand_generated(book, &recursive_cycles); term.resugar_strings(adt_encoding); term.resugar_lists(adt_encoding); (term, diags)