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