1use std::cell::RefCell;
4use std::collections::{BTreeMap, BTreeSet, HashMap};
5use std::sync::Arc;
6
7use sva_ast::{Expr, Graph, Held};
8use sva_formula::{Hash, NodeId};
9
10use sva_samples::{Extent, Profile};
11
12use super::end::End;
13use super::terms::{Handle, NOTES, Terms, is_term};
14use super::value_graph::support::Memo;
15use super::{Decided, RenderConfig, default_end, start_over};
16use crate::cache::{Known as Answer, Stored};
17use crate::error::{Diagnostic, EngineError, Located};
18use crate::instantiate::Instances;
19use crate::query::Representation;
20use crate::schedule;
21use crate::typing::Typing;
22
23pub const STREAMED: &str = "streamed";
24
25pub(super) type Found<'f> = &'f mut dyn FnMut(&str, Hash) -> Answer;
26
27pub(super) struct World {
28 pub(super) graph: Graph,
29 pub(super) instances: Instances,
30 pub(super) typing: Typing,
31 known: BTreeMap<String, Known>,
32 edits: Vec<(String, Option<Held>)>,
33 pub(super) notes: bool,
36 replaced: Option<Instances>,
37 bounds: RefCell<Bounds>,
38}
39
40#[derive(Default)]
43struct Bounds {
44 supports: Memo,
45 ends: HashMap<NodeId, (Profile, End)>,
46}
47
48#[derive(Clone)]
49pub(super) struct Known {
50 identity: Option<Hash>,
52 group: Arc<[String]>,
53 pinned: bool,
55 reads_notes: bool,
56 looked: bool,
57}
58
59pub(super) enum Root<'a> {
61 Streamed(&'a Expr),
62 Node(&'a str),
63}
64
65pub(super) struct Wanted<'a> {
66 pub(super) root: Root<'a>,
67 pub(super) terms: &'a Terms,
68 pub(super) term: Option<(Handle, &'a Expr)>,
70 pub(super) from: Option<(&'a Graph, Vec<String>)>,
72 pub(super) whole: bool,
74}
75
76pub(super) struct Advance {
77 adopted: usize,
78 named: Vec<String>,
79 found: BTreeMap<String, Known>,
80 changed: BTreeSet<String>,
81 fresh: BTreeMap<String, BTreeSet<String>>,
82}
83
84pub(super) struct Plan {
85 pub(super) adopted: usize,
86 pub(super) named: usize,
87 pub(super) visited: usize,
88 pub(super) stored: BTreeMap<String, Arc<Stored>>,
89 pub(super) found: BTreeMap<String, Known>,
90}
91
92pub(super) enum Walked {
93 Asks(Vec<Hash>),
94 Planned(Plan),
95}
96
97impl World {
98 pub(super) fn over(graph: &Graph, rate: u32, notes: bool) -> World {
100 World {
101 graph: graph.clone(),
102 instances: Instances::new(rate),
103 typing: Typing::default(),
104 known: BTreeMap::new(),
105 edits: Vec::new(),
106 notes,
107 replaced: None,
108 bounds: RefCell::default(),
109 }
110 }
111
112 pub(super) fn bounds(&self, root: NodeId, profile: &Profile) -> Decided {
114 let bounds = self.bounds.borrow();
115 let end = bounds.ends.get(&root).filter(|(under, _)| under == profile);
116 (bounds.supports.clone(), end.map(|(_, end)| *end))
117 }
118
119 pub(super) fn bound(&self, root: NodeId, profile: &Profile, (found, end): &Decided) {
121 let mut bounds = self.bounds.borrow_mut();
122 bounds.supports.extend(found.clone());
123 if let Some(end) = end {
124 bounds.ends.insert(root, (*profile, *end));
125 }
126 }
127
128 fn unbound(&mut self, freed: &[NodeId]) {
129 let bounds = self.bounds.get_mut();
130 bounds.supports.forget(freed);
131 for id in freed {
132 bounds.ends.remove(id);
133 }
134 }
135
136 pub(super) fn rendered<'w>(
140 world: &'w mut Option<World>,
141 graph: &Graph,
142 target: &str,
143 rate: u32,
144 ) -> Result<(&'w World, String), EngineError> {
145 let world = match world.take() {
146 Some(held) if held.instances.rate() == rate => world.insert(held),
147 _ => world.insert(World::over(graph, rate, false)),
148 };
149 let wanted = Wanted {
150 root: Root::Node(target),
151 terms: &Terms::default(),
152 term: None,
153 from: Some((graph, vec![target.to_string()])),
154 whole: true,
155 };
156 match world.advanced(&wanted) {
157 Ok(advance) => {
158 world.commit(advance.found);
159 let root = world.instances.instance_of(target)?;
160 Ok((world, root))
161 }
162 Err(refused) => {
163 world.abort();
164 Err(refused)
165 }
166 }
167 }
168
169 pub(super) fn plan(
173 &mut self,
174 wanted: &Wanted,
175 config: &RenderConfig,
176 found: Found,
177 ) -> Result<Walked, EngineError> {
178 let planned = self
179 .advanced(wanted)
180 .map(|advance| self.walked(advance, config, found));
181 match &planned {
182 Ok(Walked::Planned(_)) => {}
183 _ => {
184 self.abort();
185 }
186 }
187 planned
188 }
189
190 fn advanced(&mut self, wanted: &Wanted) -> Result<Advance, EngineError> {
191 let root = match wanted.root {
192 Root::Streamed(_) => STREAMED,
193 Root::Node(path) => path,
194 };
195 let held: Vec<&String> = self.instances.own_terms.keys().collect();
196 let moved = !held.is_empty() && held != [root];
197 let adopted = match &wanted.from {
198 Some((graph, roots)) => {
199 self.renew(graph, roots, (moved, wanted.whole));
200 self.adopt(graph, roots)
201 }
202 None => 0,
203 };
204 let mut rewritten = Vec::new();
205 if let Root::Streamed(target) = wanted.root {
206 self.set(STREAMED, target, &mut rewritten);
207 }
208 if !wanted.terms.is_empty() && !self.notes {
209 return Err(EngineError::refused(Diagnostic {
210 code: "engine.no_stream".to_string(),
211 message: format!(
212 "a term is added to `@{NOTES}`, and this composition defines its own `{NOTES}`"
213 ),
214 location: Located::at(NOTES, None),
215 help: format!(
216 "rename the composition's `{NOTES}`, or play it without adding terms"
217 ),
218 }));
219 }
220 let first = !self.instances.own_terms.contains_key(root);
221 if let Some((handle, term)) = wanted.term {
222 self.set(&handle.node(), term, &mut rewritten);
223 }
224 if self.notes {
225 self.set(NOTES, &wanted.terms.sum(), &mut rewritten);
226 }
227 match first {
228 true => self
229 .instances
230 .hold(&self.graph, &[root.to_string()])
231 .map(|_| ())?,
232 false => self.instances.rewrite(&self.graph, &rewritten)?,
233 }
234 let (named, rescanned) = self.instances.changing();
235 let rescanned = rescanned.map(|(n, before)| (n.to_string(), before.to_vec()));
236 let (mut named, rescanned): (Vec<String>, Vec<(String, Vec<String>)>) =
237 (named.to_vec(), rescanned.collect());
238 if let Some(old) = &self.replaced {
239 let held = |path: &String| old.resolution(path) == self.instances.resolution(path);
240 named.retain(|path| !held(path));
241 }
242 let region = self.region(&named, &rescanned);
243 self.typing.lower(&self.instances, ®ion)?;
244 if self.notes {
245 wanted.terms.name(&mut self.typing);
246 }
247 let (found, changed) = self.named(®ion);
248 let mut fresh: BTreeMap<String, BTreeSet<String>> = BTreeMap::new();
249 for name in &named {
250 let deps = self.instances.deps(name).iter().cloned();
251 fresh.insert(name.clone(), deps.collect());
252 }
253 for (name, before) in &rescanned {
254 let deps = self.instances.deps(name).iter();
255 let new = deps.filter(|d| !before.contains(d)).cloned();
256 fresh.insert(name.clone(), new.collect());
257 }
258 Ok(Advance {
259 adopted,
260 named,
261 found,
262 changed,
263 fresh,
264 })
265 }
266
267 fn walked(&self, advance: Advance, config: &RenderConfig, found: Found) -> Walked {
268 let walking = Walking {
269 root: STREAMED,
270 config,
271 whole: false,
272 under: false,
273 };
274 let reached = match self.reach(&walking, Some(&advance), found) {
275 Reach::Asks(keys) => return Walked::Asks(keys),
276 Reach::Reached(reached) => reached,
277 };
278 Walked::Planned(Plan {
279 adopted: advance.adopted,
280 named: advance.named.len(),
281 visited: reached.visited.len(),
282 stored: reached.held,
283 found: reached.known,
284 })
285 }
286
287 pub(super) fn walk(&self, walking: &Walking, found: Found) -> Reach {
290 self.reach(walking, None, found)
291 }
292
293 fn reach(&self, walking: &Walking, change: Option<&Advance>, found: Found) -> Reach {
294 let walk = Walk {
295 world: self,
296 known: change.map_or_else(BTreeMap::new, |c| c.found.clone()),
297 changed: change.map(|c| &c.changed),
298 fresh: change.map(|c| &c.fresh),
299 pins: Pins::of(&self.instances, walking.config),
300 walking,
301 found,
302 };
303 walk.walked()
304 }
305
306 fn keyed(&self, path: &str, known: &Known, config: &RenderConfig) -> Option<Hash> {
308 let id = self.typing.id(path)?;
309 let identity = known.identity?;
310 Some(super::value_graph::node_key(
311 &self.typing,
312 id,
313 identity,
314 &config.profile,
315 ))
316 }
317
318 fn set(&mut self, path: &str, expr: &Expr, rewritten: &mut Vec<String>) {
319 if self.graph.expr(path) == Some(expr) {
320 return;
321 }
322 let held = self.graph.defining(expr.clone());
323 let before = self.graph.set(path, Some(held));
324 self.edits.push((path.to_string(), before));
325 rewritten.push(path.to_string());
326 }
327
328 fn renew(&mut self, from: &Graph, roots: &[String], (moved, whole): (bool, bool)) {
332 let read = from.reaching(roots);
333 let played = |p: &str| self.instances.instances_of(p).next().is_some() || read.contains(p);
334 let gone = self.graph.paths().filter(|p| whole && !from.defines(p));
335 let paths = from.paths().filter(|p| self.graph.defines(p)).chain(gone);
336 let held = |p: &&str| self.graph.holds_as(p, from) && self.graph.read_as(p, from);
337 let paths = paths.filter(|p| played(p) && !held(p));
338 let taken: Vec<String> = paths.map(str::to_string).collect();
339 let reparsed = taken.iter().any(|p| !self.graph.holds_as(p, from));
340 for path in taken {
341 let before = self.graph.set(&path, from.held(&path));
342 self.edits.push((path, before));
343 }
344 if !reparsed && !moved {
345 return;
346 }
347 let fresh = Instances::new(self.instances.rate());
348 self.replaced = Some(std::mem::replace(&mut self.instances, fresh));
349 }
350
351 fn adopt(&mut self, from: &Graph, roots: &[String]) -> usize {
352 let taken = self.graph.adopt(from, roots);
353 let count = taken.len();
354 self.edits
355 .extend(taken.into_iter().map(|path| (path, None)));
356 count
357 }
358
359 fn region(&self, named: &[String], rescanned: &[(String, Vec<String>)]) -> Vec<Vec<String>> {
362 let mut region: BTreeSet<String> = named.iter().cloned().collect();
363 for (name, _) in rescanned {
364 region.insert(name.clone());
365 if let Some(known) = self.known.get(name) {
366 region.extend(known.group.iter().cloned());
367 }
368 }
369 let mut up: Vec<String> = region.iter().cloned().collect();
370 while let Some(at) = up.pop() {
371 for reader in self.instances.readers(&at) {
372 if region.insert(reader.to_string()) {
373 up.push(reader.to_string());
374 }
375 }
376 }
377 let starts: Vec<String> = region.iter().cloned().collect();
378 schedule::grouped(&self.instances, &starts, &|path| region.contains(path))
379 }
380
381 fn named(&self, region: &[Vec<String>]) -> (BTreeMap<String, Known>, BTreeSet<String>) {
384 let mut found: BTreeMap<String, Known> = BTreeMap::new();
385 let mut changed = BTreeSet::new();
386 for group in region {
387 let looped = schedule::is_loop(&self.instances, group);
388 let reads_notes = self.notes
389 && group.iter().any(|path| {
390 path == NOTES
391 || self.instances.deps(path).iter().any(|read| {
392 let known = found.get(read).or_else(|| self.known.get(read));
393 read == NOTES || known.is_some_and(|k| k.reads_notes)
394 })
395 });
396 let members: Arc<[String]> = group.clone().into();
397 for path in group {
398 let typed = self.typing.id(path);
399 let identity = typed.and_then(|id| crate::refs::identity(&self.typing, id).ok());
400 let old = self
401 .known
402 .get(path)
403 .filter(|old| identity.is_some() && old.identity == identity);
404 if old.is_none() {
405 changed.insert(path.clone());
406 }
407 let pinned =
408 identity.is_none() || reads_notes || looped || (self.notes && is_term(path));
409 let known = Known {
410 identity,
411 group: Arc::clone(&members),
412 pinned,
413 reads_notes,
414 looked: old.is_some_and(|old| old.looked),
415 };
416 found.insert(path.clone(), known);
417 }
418 }
419 (found, changed)
420 }
421
422 pub(super) fn commit(&mut self, found: BTreeMap<String, Known>) -> Vec<sva_formula::NodeId> {
424 let mut removed = self.instances.commit();
425 if let Some(old) = self.replaced.take() {
426 let gone = old.paths().filter(|p| !self.instances.holds(p));
427 removed.extend(gone.map(str::to_string));
428 }
429 self.typing.hide(removed.iter().cloned());
430 let freed = self.typing.commit(&self.instances);
431 self.unbound(&freed);
432 self.known.extend(found);
433 for path in &removed {
434 self.known.remove(path);
435 if is_term(path) && self.notes {
436 self.graph.set(path, None);
437 }
438 }
439 self.edits.clear();
440 freed
441 }
442
443 pub(super) fn abort(&mut self) -> Vec<sva_formula::NodeId> {
445 let freed = self.typing.abort();
446 self.unbound(&freed);
447 self.instances.abort();
448 if let Some(old) = self.replaced.take() {
449 self.instances = old;
450 }
451 for (path, before) in std::mem::take(&mut self.edits).into_iter().rev() {
452 self.graph.set(&path, before);
453 }
454 freed
455 }
456}
457
458pub(super) struct Walking<'w> {
460 pub(super) root: &'w str,
461 pub(super) config: &'w RenderConfig,
462 pub(super) whole: bool,
464 pub(super) under: bool,
467}
468
469pub(super) struct Reached {
470 pub(super) visited: BTreeSet<String>,
471 pub(super) held: BTreeMap<String, Arc<Stored>>,
472 known: BTreeMap<String, Known>,
473}
474
475pub(super) enum Reach {
476 Asks(Vec<Hash>),
477 Reached(Reached),
478}
479
480struct Pins {
483 all: bool,
484 pinned: BTreeSet<String>,
485 asked: BTreeMap<String, Vec<Representation>>,
486}
487
488impl Pins {
489 fn of(inst: &Instances, config: &RenderConfig) -> Pins {
490 let ledger =
491 |ask: &&crate::query::Ask| matches!(ask.representation, Representation::Ledger { .. });
492 let mut pins = Pins {
493 all: config.asks.iter().any(|ask| ledger(&ask)),
494 pinned: BTreeSet::new(),
495 asked: BTreeMap::new(),
496 };
497 for ask in &config.asks {
498 let Ok(path) = inst.instance_of(&ask.node) else {
499 continue;
500 };
501 if !ask.representation.off_samples(true) {
502 pins.pinned.insert(path.clone());
503 }
504 pins.asked.entry(path).or_default().push(ask.representation);
505 }
506 let mut up: Vec<&str> = pins.asked.keys().map(String::as_str).collect();
507 while let Some(at) = up.pop() {
508 for reader in inst.readers(at) {
509 if pins.pinned.insert(reader.to_string()) {
510 up.push(reader);
511 }
512 }
513 }
514 pins
515 }
516
517 fn holds(&self, path: &str) -> bool {
518 self.all || self.pinned.contains(path)
519 }
520}
521
522struct Walk<'w> {
523 world: &'w World,
524 known: BTreeMap<String, Known>,
525 changed: Option<&'w BTreeSet<String>>,
526 fresh: Option<&'w BTreeMap<String, BTreeSet<String>>>,
527 pins: Pins,
528 walking: &'w Walking<'w>,
529 found: Found<'w>,
530}
531
532struct Visit(String, bool);
533
534impl Walk<'_> {
535 fn known(&self, path: &str) -> Known {
536 let known = self.known.get(path).or_else(|| self.world.known.get(path));
537 known.cloned().expect("an instance named")
538 }
539
540 fn walked(mut self) -> Reach {
541 let (mut visited, mut asks) = (BTreeSet::new(), Vec::new());
542 let mut held: BTreeMap<String, Arc<Stored>> = BTreeMap::new();
543 let (config, whole) = (self.walking.config, self.walking.whole);
544 let mut stack = match self.walking.under {
545 false => vec![Visit(self.walking.root.to_string(), false)],
546 true => {
547 visited.insert(self.walking.root.to_string());
548 let reads = self.world.instances.deps(self.walking.root).iter().rev();
549 reads.map(|read| Visit(read.clone(), false)).collect()
550 }
551 };
552 while let Some(Visit(path, anew)) = stack.pop() {
553 let mut known = self.known(&path);
554 let pinned = known.pinned || self.pins.holds(&path);
555 let key = self.world.keyed(&path, &known, config).filter(|_| !pinned);
556 if visited.contains(&path) {
557 let asked = anew && !held.contains_key(&path);
558 if let Some(key) = key.filter(|_| asked)
559 && matches!((self.found)(&path, key), Answer::Unknown)
560 {
561 asks.push(key);
562 }
563 continue;
564 }
565 let changed = self.changed.is_some_and(|c| c.contains(&path));
566 if !(whole || anew || !known.looked || changed) {
567 continue;
568 }
569 visited.insert(path.clone());
570 let stored = match key {
571 None => None,
572 Some(key) => match (self.found)(&path, key) {
573 Answer::Hit(hit) => Some((key, hit)),
574 Answer::Miss => None,
575 Answer::Unknown => {
576 asks.push(key);
577 continue;
578 }
579 },
580 };
581 let root = whole && path == self.walking.root;
582 let read = self.pins.asked.get(&path).map_or(&[][..], Vec::as_slice);
583 let answering = |hit: &Arc<Stored>| {
584 answers(hit, root, config) && read.iter().all(|r| r.off_samples(hit.sampled))
585 };
586 known.looked = true;
587 match stored.filter(|(_, hit)| answering(hit)) {
588 Some((_, stored)) => {
589 held.insert(path.clone(), stored);
590 }
591 None => {
592 let fresh = self.fresh.and_then(|f| f.get(&path));
593 for read in self.world.instances.deps(&path).iter().rev() {
594 let anew = fresh.is_some_and(|f| f.contains(read));
595 stack.push(Visit(read.clone(), anew));
596 }
597 }
598 }
599 self.known.insert(path, known);
600 }
601 if !asks.is_empty() {
602 asks.sort();
603 asks.dedup();
604 return Reach::Asks(asks);
605 }
606 Reach::Reached(Reached {
607 visited,
608 held,
609 known: self.known,
610 })
611 }
612}
613
614fn answers(hit: &Stored, root: bool, config: &RenderConfig) -> bool {
617 let support = hit.support;
618 if !root {
619 return hit.readable;
620 }
621 let start = start_over(config, support);
622 let Some(end) = config.range.end.or(default_end(support)) else {
623 return false;
624 };
625 hit.holds(Extent::new(start, end.max(start)).intersect(support))
626}