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