1mod build;
4mod edges;
5mod resolved;
6
7use std::collections::{BTreeMap, BTreeSet, HashMap};
8use std::sync::Arc;
9
10use sva_ast::{Address, Arg, BinOp, ByteSpan, Defined, Expr, Literal};
11
12use crate::error::EngineError;
13use crate::time::Grid;
14use sva_ast::is_builtin;
15
16use build::Journal;
17pub use build::{from_roots, instantiate};
18use edges::Edges;
19pub(crate) use resolved::Resolution;
20
21pub(crate) type ScopeId = u32;
22
23pub(crate) const NO_PARAMS: ScopeId = 0;
25
26#[derive(Clone, Copy, Debug)]
27pub struct Thunk<'a> {
28 pub(crate) expr: &'a Expr,
29 pub(crate) scope: ScopeId,
30}
31
32#[derive(Clone, Debug)]
34pub(crate) struct Written {
35 defined: Arc<Defined>,
36 path: Arc<[u32]>,
37}
38
39impl Written {
40 pub(crate) fn of(defined: &Arc<Defined>, line: u32) -> Written {
42 Written {
43 defined: Arc::clone(defined),
44 path: [line].into(),
45 }
46 }
47
48 pub(crate) fn expr(&self) -> &Expr {
49 let (line, path) = self.path.split_first().expect("a line");
50 let top = match *line {
51 0 => &self.defined.body,
52 k => &self.defined.defaults[k as usize - 1].1,
53 };
54 let mut at = top;
55 for k in path {
56 at = child(at, *k).expect("a path down this expression");
57 }
58 at
59 }
60}
61
62#[derive(Clone, Copy)]
64pub(crate) enum Child {
65 First,
66 Right,
67 Arg(u32),
68 Bind(u32),
69}
70
71impl Child {
72 pub(crate) fn index(self) -> u32 {
73 match self {
74 Child::First => 0,
75 Child::Right => 1,
76 Child::Arg(k) => k,
77 Child::Bind(k) => k + 1,
78 }
79 }
80}
81
82fn child(e: &Expr, k: u32) -> Option<&Expr> {
83 let k = k as usize;
84 match e {
85 Expr::Bin(_, l, r) => [l, r].get(k).map(|c| &***c),
86 Expr::Call { args, .. } => args.get(k).map(|(Arg::Pos(x) | Arg::Named(_, x))| x),
87 Expr::Ref { arg, binds, .. } => match k {
88 0 => Some(arg),
89 k => binds.get(k - 1).map(|(_, x)| x),
90 },
91 Expr::SelfRef { arg, .. } | Expr::Indexed { arg, .. } => (k == 0).then_some(&**arg),
92 Expr::Lit(_) | Expr::Var(_) => None,
93 }
94}
95
96#[derive(Clone, Debug)]
97pub(crate) struct Bound {
98 pub(crate) written: Written,
99 pub(crate) scope: ScopeId,
100}
101
102impl Bound {
103 pub(crate) fn thunk(&self) -> Thunk<'_> {
104 Thunk {
105 expr: self.written.expr(),
106 scope: self.scope,
107 }
108 }
109}
110
111#[derive(Debug)]
113pub(crate) struct Scope {
114 pub(crate) vars: Vec<(String, Bound)>,
115 keys: Vec<u64>,
116 seen: u64,
117 chained: bool,
118 held: u32,
119 sites: HashMap<usize, String>,
121}
122
123#[derive(Debug)]
125pub(crate) struct Instance {
126 pub(crate) body: Bound,
127 pub(crate) file: String,
128 given: Vec<String>,
129 site: (String, Option<ByteSpan>),
130 held: u32,
131 scanned: bool,
132}
133
134#[inline]
136pub(crate) fn packed(name: &str) -> u64 {
137 let bytes = name.as_bytes();
138 let mut key = (bytes.len() as u64) << 56;
139 for (at, byte) in bytes.iter().take(7).enumerate() {
140 key |= u64::from(*byte) << (at * 8);
141 }
142 key
143}
144
145#[inline]
146fn bit(key: u64) -> u64 {
147 1u64 << ((key ^ (key >> 32)) & 63)
148}
149
150impl Scope {
151 pub(crate) fn new(vars: Vec<(String, Bound)>, chained: bool) -> Scope {
152 let keys: Vec<u64> = vars.iter().map(|(k, _)| packed(k)).collect();
153 let seen = keys.iter().fold(0, |acc, k| acc | bit(*k));
154 Scope {
155 vars,
156 keys,
157 seen,
158 chained,
159 held: 0,
160 sites: HashMap::new(),
161 }
162 }
163
164 #[inline]
167 pub(crate) fn get(&self, name: &str, key: u64) -> Option<&Bound> {
168 if self.seen & bit(key) == 0 {
169 return None;
170 }
171 self.keys
172 .iter()
173 .enumerate()
174 .find(|(at, k)| **k == key && (name.len() <= 7 || self.vars[*at].0 == name))
175 .map(|(at, _)| &self.vars[at].1)
176 }
177}
178
179#[derive(Clone, Copy)]
182pub struct Cx<'a> {
183 pub(crate) scope: ScopeId,
184 pub(crate) time: Option<&'a Time<'a>>,
185 pub(crate) grid: Grid,
186}
187
188pub(crate) struct Time<'a> {
189 pub(crate) expr: &'a Expr,
190 pub(crate) cx: Cx<'a>,
191}
192
193enum Move<'a> {
194 Here(&'a Expr, Cx<'a>),
195 Shifted(&'a Expr, ScopeId, &'a Expr),
196}
197
198impl<'a> Cx<'a> {
199 pub(crate) fn under(self, scope: ScopeId) -> Cx<'a> {
200 Cx { scope, ..self }
201 }
202
203 pub(crate) fn on(self, grid: Grid) -> Cx<'a> {
204 Cx { grid, ..self }
205 }
206}
207
208pub fn resolve_ref_path(referencing: &str, ref_path: &str) -> Result<String, EngineError> {
210 sva_ast::resolve_ref_path(referencing, ref_path)
211 .ok_or_else(|| EngineError::RefAboveRoot(referencing.to_string(), ref_path.to_string()))
212}
213
214pub const SIGNAL_PARAM: &str = "x";
217
218pub const MAX_INSTANCES: usize = 4096;
220
221#[derive(Clone, Copy)]
223pub enum Node<'a> {
224 Lit(&'a Literal),
225 Name(&'a str),
226 Bin(BinOp, &'a Expr, &'a Expr),
227 Call {
228 name: &'a str,
229 args: &'a [Arg],
230 span: ByteSpan,
231 },
232 Read {
233 path: &'a str,
234 arg: &'a Expr,
235 address: Address,
236 span: ByteSpan,
237 },
238 Own {
239 arg: &'a Expr,
240 address: Address,
241 span: ByteSpan,
242 },
243 Signal {
245 name: &'a str,
246 of: Thunk<'a>,
247 arg: &'a Expr,
248 span: ByteSpan,
249 },
250}
251
252#[derive(Debug)]
255pub struct Instances {
256 pub(crate) scopes: Vec<Option<Scope>>,
257 free: Vec<ScopeId>,
258 nodes: BTreeMap<String, Instance>,
259 edges: Edges,
260 pub(crate) own_terms: BTreeMap<String, String>,
261 files: BTreeMap<String, BTreeSet<String>>,
262 journal: Journal,
263 pub(crate) time: Expr,
264 pub(crate) rate: u32,
266}
267
268impl Instances {
269 pub fn new(rate: u32) -> Instances {
270 Instances {
271 scopes: vec![Some(Scope::new(Vec::new(), false))],
272 free: Vec::new(),
273 nodes: BTreeMap::new(),
274 edges: Edges::default(),
275 own_terms: BTreeMap::new(),
276 files: BTreeMap::new(),
277 journal: Journal::default(),
278 time: Expr::Var("t".to_string()),
279 rate,
280 }
281 }
282
283 pub(crate) fn rate(&self) -> u32 {
284 self.rate
285 }
286
287 pub(crate) fn grid(&self) -> Grid {
288 Grid::of(self.rate)
289 }
290
291 pub(crate) fn cx(&self, scope: ScopeId) -> Cx<'_> {
292 Cx {
293 scope,
294 time: None,
295 grid: self.grid(),
296 }
297 }
298
299 pub fn origin(&self, instance: &str) -> Option<&str> {
300 self.nodes.get(instance).map(|held| held.file.as_str())
301 }
302
303 pub fn deps(&self, path: &str) -> &[String] {
304 self.edges.of(path)
305 }
306
307 pub(crate) fn readers(&self, path: &str) -> impl Iterator<Item = &str> {
308 self.edges.readers(path)
309 }
310
311 fn insert(&mut self, name: String, held: Instance) {
313 let files = self.files.entry(held.file.clone()).or_default();
314 files.insert(name.clone());
315 self.nodes.insert(name, held);
316 }
317
318 fn remove(&mut self, name: &str) -> (Instance, Vec<String>) {
320 let held = self.nodes.remove(name).expect("an instance held");
321 let files = self
322 .files
323 .get_mut(&held.file)
324 .expect("its file's instances");
325 files.remove(name);
326 if files.is_empty() {
327 self.files.remove(&held.file);
328 }
329 (held, self.edges.cleared(name))
330 }
331
332 pub(crate) fn scope(&self, id: ScopeId) -> &Scope {
333 self.scopes[id as usize]
334 .as_ref()
335 .expect("a scope something holds")
336 }
337
338 pub fn paths(&self) -> impl Iterator<Item = &str> {
339 self.nodes.keys().map(String::as_str)
340 }
341
342 pub fn holds(&self, path: &str) -> bool {
343 self.nodes.contains_key(path)
344 }
345
346 pub fn instance_of(&self, target: &str) -> Result<String, EngineError> {
349 if self.holds(target) {
350 return Ok(target.to_string());
351 }
352 if let Some(own) = self.own_terms.get(target) {
353 return Ok(own.clone());
354 }
355 sole(
356 target,
357 self.instances_of(target).map(|p| (p.clone(), p)).collect(),
358 )
359 }
360
361 pub fn instances_of<'a>(&'a self, file: &'a str) -> impl Iterator<Item = String> + 'a {
362 let held = self.files.get(file).into_iter().flatten();
363 held.map(String::clone)
364 }
365
366 pub fn at<'a>(&'a self, path: &str) -> Option<(&'a Expr, Cx<'a>)> {
367 let thunk = self.nodes.get(path)?.body.thunk();
368 Some((thunk.expr, self.cx(thunk.scope)))
369 }
370
371 pub fn bindings<'a>(&'a self, path: &str) -> Option<Vec<(&'a str, &'a Expr, Cx<'a>)>> {
372 let scope = self.nodes.get(path)?.body.scope;
373 Some(
374 self.vars(scope)
375 .map(|(name, value)| (name, value.expr, self.cx(value.scope)))
376 .collect(),
377 )
378 }
379
380 pub(crate) fn vars(&self, scope: ScopeId) -> impl Iterator<Item = (&str, Thunk<'_>)> {
382 let vars = self.scope(scope).vars.iter();
383 vars.map(|(name, bound)| (name.as_str(), bound.thunk()))
384 }
385
386 pub(crate) fn bound_names(&self) -> impl Iterator<Item = &str> {
388 let vars = self
389 .scopes
390 .iter()
391 .flatten()
392 .flat_map(|scope| scope.vars.iter());
393 vars.map(|(name, _)| name.as_str())
394 }
395}
396
397pub(crate) fn sole<T>(target: &str, mut held: Vec<(String, T)>) -> Result<T, EngineError> {
400 match held.len() {
401 0 => Err(EngineError::UnknownNode(target.to_string())),
402 1 => Ok(held.remove(0).1),
403 _ => Err(EngineError::AmbiguousNode(
404 target.to_string(),
405 held.into_iter().map(|(name, _)| name).collect(),
406 )),
407 }
408}
409
410fn implicit_time(name: &str) -> bool {
411 sva_ast::FINITE_DIFFERENCE.contains(&name)
412 || sva_ast::MODAL.contains(&name)
413 || matches!(name, "noise" | "stft" | "istft")
414}
415
416impl Instances {
417 #[inline]
418 pub(crate) fn binds(&self, scope: ScopeId, name: &str) -> Option<Thunk<'_>> {
419 let bound = self.scope(scope).get(name, packed(name))?;
420 Some(bound.thunk())
421 }
422
423 #[inline]
425 pub fn follow<'a, R>(
426 &'a self,
427 e: &'a Expr,
428 cx: Cx<'a>,
429 go: impl FnOnce(&'a Expr, Cx<'_>) -> R,
430 ) -> Option<R> {
431 match self.step(e, cx)? {
432 Move::Here(e2, cx2) => Some(go(e2, cx2)),
433 Move::Shifted(e2, scope, when) => {
434 let moved = Time { expr: when, cx };
435 Some(go(
436 e2,
437 Cx {
438 scope,
439 time: Some(&moved),
440 grid: cx.grid,
441 },
442 ))
443 }
444 }
445 }
446
447 #[inline(always)]
449 fn step<'a>(&'a self, e: &'a Expr, cx: Cx<'a>) -> Option<Move<'a>> {
450 match e {
451 Expr::Var(name) if name == "t" => cx.time.map(|t| Move::Here(t.expr, t.cx)),
452 Expr::Var(name) => self
453 .binds(cx.scope, name)
454 .map(|b| Move::Here(b.expr, cx.under(b.scope))),
455 Expr::Call { name, args, .. } => {
456 let bound = self.binds(cx.scope, name)?;
457 let [Arg::Pos(when)] = args.as_slice() else {
458 return None;
459 };
460 Some(Move::Shifted(bound.expr, bound.scope, when))
461 }
462 _ => None,
463 }
464 }
465
466 #[inline]
467 pub fn node<'a>(&'a self, e: &'a Expr, cx: Cx<'_>) -> Node<'a> {
468 match e {
469 Expr::Lit(l) => Node::Lit(l),
470 Expr::Var(name) => Node::Name(name),
471 Expr::Bin(op, l, r) => Node::Bin(*op, l, r),
472 Expr::SelfRef { arg, address, span } => Node::Own {
473 arg,
474 address: *address,
475 span: *span,
476 },
477 Expr::Ref {
478 path,
479 arg,
480 address,
481 span,
482 ..
483 } => Node::Read {
484 path: self.site(e, cx.scope, path),
485 arg,
486 address: *address,
487 span: *span,
488 },
489 Expr::Indexed { name, arg, span } => match self.binds(cx.scope, name) {
490 Some(of) => Node::Signal {
491 name,
492 of,
493 arg,
494 span: *span,
495 },
496 None => Node::Name(name),
497 },
498 Expr::Call { name, args, span } if is_builtin(name) => Node::Call {
499 name,
500 args,
501 span: *span,
502 },
503 Expr::Call { name, span, .. } => Node::Read {
504 path: self.site(e, cx.scope, name),
505 arg: &self.time,
506 address: Address::Time,
507 span: *span,
508 },
509 }
510 }
511
512 fn site<'a>(&'a self, e: &Expr, scope: ScopeId, written: &'a str) -> &'a str {
513 match self
514 .scope(scope)
515 .sites
516 .get(&(std::ptr::from_ref(e) as usize))
517 {
518 Some(child) => child,
519 None => written,
520 }
521 }
522
523 pub(crate) fn signal<'a>(&self, of: Thunk<'a>, cx: Cx<'a>) -> Cx<'a> {
525 Cx {
526 scope: of.scope,
527 time: None,
528 grid: cx.grid,
529 }
530 }
531
532 pub(crate) fn is_now(&self, e: &Expr, cx: Cx) -> bool {
533 if let Some(r) = self.follow(e, cx, |e2, cx2| self.is_now(e2, cx2)) {
534 return r;
535 }
536 matches!(self.node(e, cx), Node::Name(n) if n == "t")
537 }
538
539 fn direct_deps(&self, path: &str) -> Vec<String> {
541 let (e, cx) = self.at(path).expect("an instance");
542 let mut out = Vec::new();
543 self.direct_refs(e, cx, &mut out);
544 out.sort();
545 out.dedup();
546 out
547 }
548
549 fn direct_refs(&self, e: &Expr, cx: Cx, out: &mut Vec<String>) {
550 if self
551 .follow(e, cx, |e2, cx2| self.direct_refs(e2, cx2, out))
552 .is_some()
553 {
554 return;
555 }
556 match self.node(e, cx) {
557 Node::Lit(_) | Node::Name(_) => {}
558 Node::Bin(_, l, r) => {
559 self.direct_refs(l, cx, out);
560 self.direct_refs(r, cx, out);
561 }
562 Node::Call { args, .. } => {
563 for a in args {
564 let (Arg::Pos(x) | Arg::Named(_, x)) = a;
565 self.direct_refs(x, cx, out);
566 }
567 }
568 Node::Read { path, arg, .. } => {
569 out.push(path.to_string());
570 self.direct_refs(arg, cx, out);
571 }
572 Node::Own { arg, .. } => self.direct_refs(arg, cx, out),
573 Node::Signal { of, arg, .. } => {
574 self.direct_refs(of.expr, self.signal(of, cx), out);
575 self.direct_refs(arg, cx, out);
576 }
577 }
578 }
579
580 pub fn reads_self(&self, path: &str) -> bool {
581 self.at(path).is_some_and(|(e, cx)| self.holds_self(e, cx))
582 }
583
584 pub(crate) fn holds_self(&self, e: &Expr, cx: Cx) -> bool {
585 if let Some(r) = self.follow(e, cx, |e2, cx2| self.holds_self(e2, cx2)) {
586 return r;
587 }
588 match self.node(e, cx) {
589 Node::Lit(_) | Node::Name(_) => false,
590 Node::Own { .. } => true,
591 Node::Bin(_, l, r) => self.holds_self(l, cx) || self.holds_self(r, cx),
592 Node::Read { arg, .. } | Node::Signal { arg, .. } => self.holds_self(arg, cx),
593 Node::Call { args, .. } => args.iter().any(|a| {
594 let (Arg::Pos(x) | Arg::Named(_, x)) = a;
595 self.holds_self(x, cx)
596 }),
597 }
598 }
599
600 pub(crate) fn position_dependent(&self, e: &Expr, cx: Cx) -> Option<String> {
602 if let Some(r) = self.follow(e, cx, |e2, cx2| self.position_dependent(e2, cx2)) {
603 return r;
604 }
605 match self.node(e, cx) {
606 Node::Lit(_) | Node::Name(_) => None,
607 Node::Own { .. } => Some("self".to_string()),
608 Node::Bin(_, l, r) => self
609 .position_dependent(l, cx)
610 .or_else(|| self.position_dependent(r, cx)),
611 Node::Read { arg, .. } | Node::Signal { arg, .. } => self.position_dependent(arg, cx),
612 Node::Call { name, args, .. } => {
613 if crate::vocabulary::shape(name).is_some() || name == "crop" || implicit_time(name)
614 {
615 return Some(name.to_string());
616 }
617 args.iter().find_map(|a| {
618 let (Arg::Pos(x) | Arg::Named(_, x)) = a;
619 self.position_dependent(x, cx)
620 })
621 }
622 }
623 }
624
625 pub(crate) fn same<'x, 'y>(&self, a: &Expr, ax: Cx<'x>, b: &Expr, by: Cx<'y>) -> bool {
627 if let Some(moved) = self.step(a, ax) {
628 return match moved {
629 Move::Here(a2, ax2) => self.same(a2, ax2, b, by),
630 Move::Shifted(a2, scope, when) => {
631 let moved = Time { expr: when, cx: ax };
632 self.same(
633 a2,
634 Cx {
635 scope,
636 time: Some(&moved),
637 grid: ax.grid,
638 },
639 b,
640 by,
641 )
642 }
643 };
644 }
645 if let Some(moved) = self.step(b, by) {
646 return match moved {
647 Move::Here(b2, by2) => self.same(a, ax, b2, by2),
648 Move::Shifted(b2, scope, when) => {
649 let moved = Time { expr: when, cx: by };
650 self.same(
651 a,
652 ax,
653 b2,
654 Cx {
655 scope,
656 time: Some(&moved),
657 grid: by.grid,
658 },
659 )
660 }
661 };
662 }
663 match (self.node(a, ax), self.node(b, by)) {
664 (Node::Lit(x), Node::Lit(y)) => x == y,
665 (Node::Name(x), Node::Name(y)) => x == y,
666 (Node::Bin(o1, l1, r1), Node::Bin(o2, l2, r2)) => {
667 o1 == o2 && self.same(l1, ax, l2, by) && self.same(r1, ax, r2, by)
668 }
669 (
670 Node::Own {
671 arg: x, address: i, ..
672 },
673 Node::Own {
674 arg: y, address: j, ..
675 },
676 ) => i == j && self.same(x, ax, y, by),
677 (
678 Node::Read {
679 path: p,
680 arg: x,
681 address: i,
682 ..
683 },
684 Node::Read {
685 path: q,
686 arg: y,
687 address: j,
688 ..
689 },
690 ) => p == q && i == j && self.same(x, ax, y, by),
691 (Node::Signal { of: f, arg: x, .. }, Node::Signal { of: g, arg: y, .. }) => {
692 self.same(f.expr, self.signal(f, ax), g.expr, self.signal(g, by))
693 && self.same(x, ax, y, by)
694 }
695 (
696 Node::Call {
697 name: n1, args: a1, ..
698 },
699 Node::Call {
700 name: n2, args: a2, ..
701 },
702 ) => {
703 n1 == n2
704 && a1.len() == a2.len()
705 && a1.iter().zip(a2).all(|(x, y)| match (x, y) {
706 (Arg::Pos(x), Arg::Pos(y)) => self.same(x, ax, y, by),
707 (Arg::Named(k1, x), Arg::Named(k2, y)) => {
708 k1 == k2 && self.same(x, ax, y, by)
709 }
710 _ => false,
711 })
712 }
713 _ => false,
714 }
715 }
716
717 pub(crate) fn same_binds(&self, scope: ScopeId, binds: &[(String, Bound)]) -> bool {
718 let held = &self.scope(scope).vars;
719 held.len() == binds.len()
720 && held.iter().zip(binds).all(|((k1, v1), (k2, v2))| {
721 let (v1, v2) = (v1.thunk(), v2.thunk());
722 k1 == k2 && self.same(v1.expr, self.cx(v1.scope), v2.expr, self.cx(v2.scope))
723 })
724 }
725}