1use sva_ast::{Arg, BinOp, ByteSpan, Expr, Literal};
4use sva_formula::{Hash, NodeId};
5
6use crate::instantiate::Instances;
7use crate::typing::{SumSlot, Typing, Value};
8
9pub const NOTES: &str = "notes";
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
13pub struct Handle(pub u32);
14
15#[derive(Clone)]
16struct Term {
17 handle: Handle,
18 expr: Expr,
19 addressed: bool,
21 leaf: Option<NodeId>,
22 ends: Option<NodeId>,
23}
24
25#[derive(Clone)]
26enum Slot {
27 Live(Term),
28 Retired(Hash),
29}
30
31#[derive(Clone, Default)]
32pub(super) struct Terms {
33 next: u32,
34 slots: Vec<Slot>,
35}
36
37impl Terms {
38 fn live(&self) -> impl Iterator<Item = &Term> {
39 self.slots.iter().filter_map(|s| match s {
40 Slot::Live(t) => Some(t),
41 Slot::Retired(_) => None,
42 })
43 }
44
45 fn live_mut(&mut self) -> impl Iterator<Item = &mut Term> {
46 self.slots.iter_mut().filter_map(|s| match s {
47 Slot::Live(t) => Some(t),
48 Slot::Retired(_) => None,
49 })
50 }
51
52 pub(super) fn is_empty(&self) -> bool {
53 self.live().next().is_none()
54 }
55
56 pub(super) fn exprs(&self) -> impl Iterator<Item = &Expr> {
57 self.live().map(|t| &t.expr)
58 }
59
60 pub(super) fn added(&self, expr: Expr) -> (Terms, Handle) {
61 let mut next = self.clone();
62 let handle = Handle(next.next);
63 next.next += 1;
64 next.slots.push(Slot::Live(Term {
65 handle,
66 expr,
67 addressed: true,
68 leaf: None,
69 ends: None,
70 }));
71 (next, handle)
72 }
73
74 pub(super) fn replaced(&self, handle: Handle, expr: Expr) -> Option<Terms> {
76 let mut next = self.clone();
77 let term = next
78 .live_mut()
79 .find(|t| t.addressed && t.handle == handle)?;
80 (term.expr, term.leaf, term.ends) = (expr, None, None);
81 Some(next)
82 }
83
84 pub(super) fn removed(&self, handle: Handle, at: f64) -> Option<Terms> {
86 let mut next = self.clone();
87 let term = next
88 .live_mut()
89 .find(|t| t.addressed && t.handle == handle)?;
90 let never = Expr::Bin(
91 BinOp::Sub,
92 Box::new(Expr::Lit(Literal::Num(0.0))),
93 Box::new(Expr::Var("inf".to_string())),
94 );
95 term.expr = Expr::Call {
96 name: "crop".to_string(),
97 args: vec![
98 Arg::Pos(term.expr.clone()),
99 Arg::Pos(never),
100 Arg::Pos(Expr::Lit(Literal::Num(at))),
101 ],
102 span: ByteSpan { start: 0, end: 0 },
103 };
104 (term.addressed, term.leaf, term.ends) = (false, None, None);
105 Some(next)
106 }
107
108 pub(super) fn sum(&self) -> Expr {
109 self.exprs()
110 .cloned()
111 .reduce(|sum, t| Expr::Bin(BinOp::Add, Box::new(sum), Box::new(t)))
112 .unwrap_or(Expr::Lit(Literal::Num(0.0)))
113 }
114
115 pub(super) fn typed(&mut self, inst: &Instances, tys: &mut Typing) {
118 let Some(notes) = inst.instance_of(NOTES).ok().and_then(|path| tys.id(&path)) else {
119 return;
120 };
121 let mut at = match tys.value(notes) {
122 Value::Read {
123 source,
124 at: crate::typing::When::At(time),
125 ..
126 } if *time == crate::time::Affine::NOW => Some(*source),
127 _ => Some(notes),
128 };
129 let count = self.live().count();
130 let mut leaves = vec![None; count];
131 for k in (0..count).rev() {
132 let Some(id) = at else {
133 break;
134 };
135 match (k, tys.value(id)) {
136 (0, _) => (leaves[0], at) = (Some(id), None),
137 (_, Value::Op { name, args }) if name == "+" && args.len() == 2 => {
138 (leaves[k], at) = (Some(args[1]), Some(args[0]));
139 }
140 _ => at = None,
141 }
142 }
143 for (term, leaf) in self.live_mut().zip(leaves) {
144 term.leaf = leaf;
145 term.ends = leaf.map(|id| match tys.value(id) {
146 Value::Read { source, .. } => *source,
147 _ => id,
148 });
149 }
150 let slots = self
151 .slots
152 .iter()
153 .map(|s| match s {
154 Slot::Live(t) => t.leaf.map(SumSlot::Node),
155 Slot::Retired(h) => Some(SumSlot::Retired(*h)),
156 })
157 .collect::<Option<Vec<_>>>();
158 if let Some(slots) = slots {
159 tys.name_sum(notes, slots);
160 }
161 }
162
163 pub(super) fn prune(
167 &mut self,
168 ended: &dyn Fn(NodeId) -> bool,
169 named: &dyn Fn(NodeId) -> Option<Hash>,
170 ) -> bool {
171 let live = self.live().count();
172 let gone = |t: &Term| t.ends.is_some_and(ended);
173 let keep = match self.live().all(gone) {
174 true => self.live().last().map(|t| t.handle),
175 false => None,
176 };
177 self.slots = std::mem::take(&mut self.slots)
178 .into_iter()
179 .filter_map(|slot| match slot {
180 Slot::Live(t) if gone(&t) && Some(t.handle) != keep => {
181 t.leaf.and_then(named).map(Slot::Retired)
182 }
183 other => Some(other),
184 })
185 .collect();
186 self.live().count() != live
187 }
188}