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