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