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 = 4096;
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 rate(&self) -> u32 {
298 self.rate
299 }
300
301 pub(crate) fn grid(&self) -> Grid {
302 Grid::of(self.rate)
303 }
304
305 pub(crate) fn cx(&self, scope: ScopeId) -> Cx<'_> {
306 Cx {
307 scope,
308 time: None,
309 grid: self.grid(),
310 }
311 }
312
313 pub fn origin(&self, instance: &str) -> Option<&str> {
314 self.nodes.get(instance).map(|held| held.file.as_str())
315 }
316
317 pub fn deps(&self, path: &str) -> &[String] {
318 self.edges.of(path)
319 }
320
321 pub(crate) fn readers(&self, path: &str) -> impl Iterator<Item = &str> {
322 self.edges.readers(path)
323 }
324
325 fn insert(&mut self, name: String, held: Instance) {
327 let files = self.files.entry(held.file.clone()).or_default();
328 files.insert(name.clone());
329 self.nodes.insert(name, held);
330 }
331
332 fn remove(&mut self, name: &str) -> (Instance, Vec<String>) {
334 let held = self.nodes.remove(name).expect("an instance held");
335 let files = self
336 .files
337 .get_mut(&held.file)
338 .expect("its file's instances");
339 files.remove(name);
340 if files.is_empty() {
341 self.files.remove(&held.file);
342 }
343 (held, self.edges.cleared(name))
344 }
345
346 pub(crate) fn scope(&self, id: ScopeId) -> &Scope {
347 self.scopes[id as usize]
348 .as_ref()
349 .expect("a scope something holds")
350 }
351
352 pub fn paths(&self) -> impl Iterator<Item = &str> {
353 self.nodes.keys().map(String::as_str)
354 }
355
356 pub fn holds(&self, path: &str) -> bool {
357 self.nodes.contains_key(path)
358 }
359
360 pub fn instance_of(&self, target: &str) -> Result<String, EngineError> {
363 if self.holds(target) {
364 return Ok(target.to_string());
365 }
366 if let Some(own) = self.own_terms.get(target) {
367 return Ok(own.clone());
368 }
369 sole(
370 target,
371 self.instances_of(target).map(|p| (p.clone(), p)).collect(),
372 )
373 }
374
375 pub fn instances_of<'a>(&'a self, file: &'a str) -> impl Iterator<Item = String> + 'a {
376 let held = self.files.get(file).into_iter().flatten();
377 held.map(String::clone)
378 }
379
380 pub fn at<'a>(&'a self, path: &str) -> Option<(&'a Expr, Cx<'a>)> {
381 let thunk = self.nodes.get(path)?.body.thunk();
382 Some((thunk.expr, self.cx(thunk.scope)))
383 }
384
385 pub fn bindings<'a>(&'a self, path: &str) -> Option<Vec<(&'a str, &'a Expr, Cx<'a>)>> {
386 let scope = self.nodes.get(path)?.body.scope;
387 Some(
388 self.vars(scope)
389 .map(|(name, value)| (name, value.expr, self.cx(value.scope)))
390 .collect(),
391 )
392 }
393
394 pub(crate) fn vars(&self, scope: ScopeId) -> impl Iterator<Item = (&str, Thunk<'_>)> {
396 let vars = self.scope(scope).vars.iter();
397 vars.map(|(name, bound)| (name.as_str(), bound.thunk()))
398 }
399
400 pub(crate) fn bound_names(&self) -> impl Iterator<Item = &str> {
402 let vars = self
403 .scopes
404 .iter()
405 .flatten()
406 .flat_map(|scope| scope.vars.iter());
407 vars.map(|(name, _)| name.as_str())
408 }
409}
410
411pub(crate) fn sole<T>(target: &str, mut held: Vec<(String, T)>) -> Result<T, EngineError> {
414 match held.len() {
415 0 => Err(EngineError::UnknownNode(target.to_string())),
416 1 => Ok(held.remove(0).1),
417 _ => Err(EngineError::AmbiguousNode(
418 target.to_string(),
419 held.into_iter().map(|(name, _)| name).collect(),
420 )),
421 }
422}
423
424fn implicit_time(name: &str) -> bool {
425 sva_ast::FINITE_DIFFERENCE.contains(&name)
426 || sva_ast::MODAL.contains(&name)
427 || matches!(name, "noise" | "stft" | "istft")
428}
429
430impl Instances {
431 #[inline]
432 pub(crate) fn binds(&self, scope: ScopeId, name: &str) -> Option<Thunk<'_>> {
433 let bound = self.scope(scope).get(name, packed(name))?;
434 Some(bound.thunk())
435 }
436
437 #[inline]
439 pub fn follow<'a, R>(
440 &'a self,
441 e: &'a Expr,
442 cx: Cx<'a>,
443 go: impl FnOnce(&'a Expr, Cx<'_>) -> R,
444 ) -> Option<R> {
445 match self.step(e, cx)? {
446 Move::Here(e2, cx2) => Some(go(e2, cx2)),
447 Move::Shifted(e2, scope, when) => {
448 let moved = Time { expr: when, cx };
449 Some(go(
450 e2,
451 Cx {
452 scope,
453 time: Some(&moved),
454 grid: cx.grid,
455 },
456 ))
457 }
458 }
459 }
460
461 #[inline(always)]
463 fn step<'a>(&'a self, e: &'a Expr, cx: Cx<'a>) -> Option<Move<'a>> {
464 match e {
465 Expr::Var(name) if name == "t" => cx.time.map(|t| Move::Here(t.expr, t.cx)),
466 Expr::Var(name) => self
467 .binds(cx.scope, name)
468 .map(|b| Move::Here(b.expr, cx.under(b.scope))),
469 Expr::Call { name, args, .. } => {
470 let bound = self.binds(cx.scope, name)?;
471 let [Arg::Pos(when)] = args.as_slice() else {
472 return None;
473 };
474 Some(Move::Shifted(bound.expr, bound.scope, when))
475 }
476 _ => None,
477 }
478 }
479
480 #[inline]
481 pub fn node<'a>(&'a self, e: &'a Expr, cx: Cx<'_>) -> Node<'a> {
482 match e {
483 Expr::Lit(l) => Node::Lit(l),
484 Expr::Var(name) => Node::Name(name),
485 Expr::Bin(op, l, r) => Node::Bin(*op, l, r),
486 Expr::SelfRef { arg, address, span } => Node::Own {
487 arg,
488 address: *address,
489 span: *span,
490 },
491 Expr::Ref {
492 path,
493 arg,
494 address,
495 span,
496 ..
497 } => Node::Read {
498 path: self.site(e, cx.scope, path),
499 arg,
500 address: *address,
501 span: *span,
502 },
503 Expr::Indexed { name, arg, span } => match self.binds(cx.scope, name) {
504 Some(of) => Node::Signal {
505 name,
506 of,
507 arg,
508 span: *span,
509 },
510 None => Node::Name(name),
511 },
512 Expr::Call { name, args, span } if is_builtin(name) => Node::Call {
513 name,
514 args,
515 span: *span,
516 },
517 Expr::Call { name, span, .. } => Node::Read {
518 path: self.site(e, cx.scope, name),
519 arg: &self.time,
520 address: Address::Time,
521 span: *span,
522 },
523 }
524 }
525
526 fn site<'a>(&'a self, e: &Expr, scope: ScopeId, written: &'a str) -> &'a str {
527 match self
528 .scope(scope)
529 .sites
530 .get(&(std::ptr::from_ref(e) as usize))
531 {
532 Some(child) => child,
533 None => written,
534 }
535 }
536
537 pub(crate) fn signal<'a>(&self, of: Thunk<'a>, cx: Cx<'a>) -> Cx<'a> {
539 Cx {
540 scope: of.scope,
541 time: None,
542 grid: cx.grid,
543 }
544 }
545
546 pub(crate) fn is_now(&self, e: &Expr, cx: Cx) -> bool {
547 if let Some(r) = self.follow(e, cx, |e2, cx2| self.is_now(e2, cx2)) {
548 return r;
549 }
550 matches!(self.node(e, cx), Node::Name(n) if n == "t")
551 }
552
553 fn direct_deps(&self, path: &str) -> Vec<String> {
555 let (e, cx) = self.at(path).expect("an instance");
556 let mut out = Vec::new();
557 self.direct_refs(e, cx, &mut out);
558 out.sort();
559 out.dedup();
560 out
561 }
562
563 fn direct_refs(&self, e: &Expr, cx: Cx, out: &mut Vec<String>) {
564 if self
565 .follow(e, cx, |e2, cx2| self.direct_refs(e2, cx2, out))
566 .is_some()
567 {
568 return;
569 }
570 match self.node(e, cx) {
571 Node::Lit(_) | Node::Name(_) => {}
572 Node::Bin(_, l, r) => {
573 self.direct_refs(l, cx, out);
574 self.direct_refs(r, cx, out);
575 }
576 Node::Call { args, .. } => {
577 for a in args {
578 let (Arg::Pos(x) | Arg::Named(_, x)) = a;
579 self.direct_refs(x, cx, out);
580 }
581 }
582 Node::Read { path, arg, .. } => {
583 out.push(path.to_string());
584 self.direct_refs(arg, cx, out);
585 }
586 Node::Own { arg, .. } => self.direct_refs(arg, cx, out),
587 Node::Signal { of, arg, .. } => {
588 self.direct_refs(of.expr, self.signal(of, cx), out);
589 self.direct_refs(arg, cx, out);
590 }
591 }
592 }
593
594 pub fn reads_self(&self, path: &str) -> bool {
595 self.at(path).is_some_and(|(e, cx)| self.holds_self(e, cx))
596 }
597
598 pub(crate) fn holds_self(&self, e: &Expr, cx: Cx) -> bool {
599 if let Some(r) = self.follow(e, cx, |e2, cx2| self.holds_self(e2, cx2)) {
600 return r;
601 }
602 match self.node(e, cx) {
603 Node::Lit(_) | Node::Name(_) => false,
604 Node::Own { .. } => true,
605 Node::Bin(_, l, r) => self.holds_self(l, cx) || self.holds_self(r, cx),
606 Node::Read { arg, .. } | Node::Signal { arg, .. } => self.holds_self(arg, cx),
607 Node::Call { args, .. } => args.iter().any(|a| {
608 let (Arg::Pos(x) | Arg::Named(_, x)) = a;
609 self.holds_self(x, cx)
610 }),
611 }
612 }
613
614 pub(crate) fn position_dependent(&self, e: &Expr, cx: Cx) -> Option<String> {
616 if let Some(r) = self.follow(e, cx, |e2, cx2| self.position_dependent(e2, cx2)) {
617 return r;
618 }
619 match self.node(e, cx) {
620 Node::Lit(_) | Node::Name(_) => None,
621 Node::Own { .. } => Some("self".to_string()),
622 Node::Bin(_, l, r) => self
623 .position_dependent(l, cx)
624 .or_else(|| self.position_dependent(r, cx)),
625 Node::Read { arg, .. } | Node::Signal { arg, .. } => self.position_dependent(arg, cx),
626 Node::Call { name, args, .. } => {
627 if crate::vocabulary::shape(name).is_some() || name == "crop" || implicit_time(name)
628 {
629 return Some(name.to_string());
630 }
631 args.iter().find_map(|a| {
632 let (Arg::Pos(x) | Arg::Named(_, x)) = a;
633 self.position_dependent(x, cx)
634 })
635 }
636 }
637 }
638
639 pub(crate) fn same<'x, 'y>(&self, a: &Expr, ax: Cx<'x>, b: &Expr, by: Cx<'y>) -> bool {
641 if let Some(moved) = self.step(a, ax) {
642 return match moved {
643 Move::Here(a2, ax2) => self.same(a2, ax2, b, by),
644 Move::Shifted(a2, scope, when) => {
645 let moved = Time { expr: when, cx: ax };
646 self.same(
647 a2,
648 Cx {
649 scope,
650 time: Some(&moved),
651 grid: ax.grid,
652 },
653 b,
654 by,
655 )
656 }
657 };
658 }
659 if let Some(moved) = self.step(b, by) {
660 return match moved {
661 Move::Here(b2, by2) => self.same(a, ax, b2, by2),
662 Move::Shifted(b2, scope, when) => {
663 let moved = Time { expr: when, cx: by };
664 self.same(
665 a,
666 ax,
667 b2,
668 Cx {
669 scope,
670 time: Some(&moved),
671 grid: by.grid,
672 },
673 )
674 }
675 };
676 }
677 match (self.node(a, ax), self.node(b, by)) {
678 (Node::Lit(x), Node::Lit(y)) => x == y,
679 (Node::Name(x), Node::Name(y)) => x == y,
680 (Node::Bin(o1, l1, r1), Node::Bin(o2, l2, r2)) => {
681 o1 == o2 && self.same(l1, ax, l2, by) && self.same(r1, ax, r2, by)
682 }
683 (
684 Node::Own {
685 arg: x, address: i, ..
686 },
687 Node::Own {
688 arg: y, address: j, ..
689 },
690 ) => i == j && self.same(x, ax, y, by),
691 (
692 Node::Read {
693 path: p,
694 arg: x,
695 address: i,
696 ..
697 },
698 Node::Read {
699 path: q,
700 arg: y,
701 address: j,
702 ..
703 },
704 ) => p == q && i == j && self.same(x, ax, y, by),
705 (Node::Signal { of: f, arg: x, .. }, Node::Signal { of: g, arg: y, .. }) => {
706 self.same(f.expr, self.signal(f, ax), g.expr, self.signal(g, by))
707 && self.same(x, ax, y, by)
708 }
709 (
710 Node::Call {
711 name: n1, args: a1, ..
712 },
713 Node::Call {
714 name: n2, args: a2, ..
715 },
716 ) => {
717 n1 == n2
718 && a1.len() == a2.len()
719 && a1.iter().zip(a2).all(|(x, y)| match (x, y) {
720 (Arg::Pos(x), Arg::Pos(y)) => self.same(x, ax, y, by),
721 (Arg::Named(k1, x), Arg::Named(k2, y)) => {
722 k1 == k2 && self.same(x, ax, y, by)
723 }
724 _ => false,
725 })
726 }
727 _ => false,
728 }
729 }
730
731 pub(crate) fn same_binds(&self, scope: ScopeId, binds: &[(String, Bound)]) -> bool {
732 let held = &self.scope(scope).vars;
733 held.len() == binds.len()
734 && held.iter().zip(binds).all(|((k1, v1), (k2, v2))| {
735 let (v1, v2) = (v1.thunk(), v2.thunk());
736 k1 == k2 && self.same(v1.expr, self.cx(v1.scope), v2.expr, self.cx(v2.scope))
737 })
738 }
739}