1use super::{net_trees_mut, tree_children, tree_children_mut};
2use crate::maybe_grow;
3use core::ops::BitOr;
4use hvm::ast::{Book, Net, Tree};
5use std::collections::{HashMap, HashSet};
6
7pub fn inline_hvm_book(book: &mut Book) -> Result<HashSet<String>, String> {
8 let mut state = InlineState::default();
9 state.populate_inlinees(book)?;
10 let mut all_changed = HashSet::new();
11 for (name, net) in &mut book.defs {
12 let mut inlined = false;
13 for tree in net_trees_mut(net) {
14 inlined |= state.inline_into(tree);
15 }
16 if inlined {
17 all_changed.insert(name.to_owned());
18 }
19 }
20 Ok(all_changed)
21}
22
23#[derive(Debug, Default)]
24struct InlineState {
25 inlinees: HashMap<String, Tree>,
26}
27
28impl InlineState {
29 fn populate_inlinees(&mut self, book: &Book) -> Result<(), String> {
30 for (name, net) in &book.defs {
31 if should_inline(net) {
32 let mut hare = &net.root;
34 let mut tortoise = &net.root;
35 let mut parity = false;
37 while let Tree::Ref { nam, .. } = hare {
38 let Some(net) = &book.defs.get(nam) else { break };
39 if should_inline(net) {
40 hare = &net.root;
41 } else {
42 break;
43 }
44 if parity {
45 let Tree::Ref { nam: tortoise_nam, .. } = tortoise else { unreachable!() };
46 if tortoise_nam == nam {
47 Err(format!("infinite reference cycle in `@{nam}`"))?;
48 }
49 tortoise = &book.defs[tortoise_nam].root;
50 }
51 parity = !parity;
52 }
53 self.inlinees.insert(name.to_owned(), hare.clone());
54 }
55 }
56 Ok(())
57 }
58 fn inline_into(&self, tree: &mut Tree) -> bool {
59 maybe_grow(|| {
60 let Tree::Ref { nam, .. } = &*tree else {
61 return tree_children_mut(tree).map(|t| self.inline_into(t)).fold(false, bool::bitor);
62 };
63 if let Some(inlined) = self.inlinees.get(nam) {
64 *tree = inlined.clone();
65 true
66 } else {
67 false
68 }
69 })
70 }
71}
72
73fn should_inline(net: &Net) -> bool {
74 net.rbag.is_empty() && tree_children(&net.root).next().is_none()
75}