1mod build;
4mod resolved;
5
6use std::collections::{BTreeMap, HashMap};
7
8use sva_ast::{Address, Arg, BinOp, ByteSpan, Expr, Literal};
9use sva_formula::filter::Shape;
10use sva_formula::note;
11
12use crate::error::EngineError;
13use crate::time::Grid;
14use crate::vocabulary::is_builtin;
15
16pub use build::{from_roots, has_free_parameter, instantiate};
17
18pub(crate) type ScopeId = u32;
19
20pub(crate) const NO_PARAMS: ScopeId = 0;
22
23#[derive(Clone, Copy, Debug)]
24pub struct Thunk<'g> {
25 pub(crate) expr: &'g Expr,
26 pub(crate) scope: ScopeId,
27}
28
29#[derive(Debug)]
31pub(crate) struct Scope<'g> {
32 pub(crate) vars: Vec<(String, Thunk<'g>)>,
33 keys: Vec<u64>,
34 seen: u64,
35}
36
37#[inline]
39pub(crate) fn packed(name: &str) -> u64 {
40 let bytes = name.as_bytes();
41 let mut key = (bytes.len() as u64) << 56;
42 for (at, byte) in bytes.iter().take(7).enumerate() {
43 key |= u64::from(*byte) << (at * 8);
44 }
45 key
46}
47
48#[inline]
49fn bit(key: u64) -> u64 {
50 1u64 << ((key ^ (key >> 32)) & 63)
51}
52
53impl<'g> Scope<'g> {
54 pub(crate) fn new(vars: Vec<(String, Thunk<'g>)>) -> Scope<'g> {
55 let keys: Vec<u64> = vars.iter().map(|(k, _)| packed(k)).collect();
56 let seen = keys.iter().fold(0, |acc, k| acc | bit(*k));
57 Scope { vars, keys, seen }
58 }
59
60 #[inline]
63 pub(crate) fn get(&self, name: &str, key: u64) -> Option<Thunk<'g>> {
64 if self.seen & bit(key) == 0 {
65 return None;
66 }
67 self.keys
68 .iter()
69 .enumerate()
70 .find(|(at, k)| **k == key && (name.len() <= 7 || self.vars[*at].0 == name))
71 .map(|(at, _)| self.vars[at].1)
72 }
73}
74
75#[derive(Clone, Copy)]
78pub struct Cx<'a> {
79 pub(crate) scope: ScopeId,
80 pub(crate) time: Option<&'a Time<'a>>,
81 pub(crate) grid: Grid,
82}
83
84pub(crate) struct Time<'a> {
85 pub(crate) expr: &'a Expr,
86 pub(crate) cx: Cx<'a>,
87}
88
89enum Move<'a> {
90 Here(&'a Expr, Cx<'a>),
91 Shifted(&'a Expr, ScopeId, &'a Expr),
92}
93
94impl<'a> Cx<'a> {
95 pub(crate) fn under(self, scope: ScopeId) -> Cx<'a> {
96 Cx { scope, ..self }
97 }
98
99 pub(crate) fn on(self, grid: Grid) -> Cx<'a> {
100 Cx { grid, ..self }
101 }
102}
103
104pub fn resolve_ref_path(referencing: &str, ref_path: &str) -> Result<String, EngineError> {
106 sva_ast::resolve_ref_path(referencing, ref_path)
107 .ok_or_else(|| EngineError::RefAboveRoot(referencing.to_string(), ref_path.to_string()))
108}
109
110pub const SIGNAL_PARAM: &str = "x";
113
114pub const MAX_INSTANCES: usize = 4096;
116
117#[derive(Clone, Copy)]
119pub enum Node<'a> {
120 Lit(&'a Literal),
121 Name(&'a str),
122 Bin(BinOp, &'a Expr, &'a Expr),
123 Call {
124 name: &'a str,
125 args: &'a [Arg],
126 span: ByteSpan,
127 },
128 Read {
129 path: &'a str,
130 arg: &'a Expr,
131 address: Address,
132 span: ByteSpan,
133 },
134 Own {
135 arg: &'a Expr,
136 address: Address,
137 span: ByteSpan,
138 },
139 Signal {
141 name: &'a str,
142 of: Thunk<'a>,
143 arg: &'a Expr,
144 span: ByteSpan,
145 },
146}
147
148#[derive(Debug)]
150pub struct Instances<'g> {
151 pub(crate) scopes: Vec<Scope<'g>>,
152 pub(crate) sites: HashMap<(usize, ScopeId), String>,
153 pub(crate) nodes: BTreeMap<String, Thunk<'g>>,
154 pub(crate) origin: BTreeMap<String, String>,
155 pub(crate) own_terms: BTreeMap<String, String>,
156 pub(crate) root: String,
157 pub(crate) time: Expr,
158 pub(crate) rate: u32,
160}
161
162impl<'g> Instances<'g> {
163 pub(crate) fn rate(&self) -> u32 {
164 self.rate
165 }
166
167 pub(crate) fn grid(&self) -> Grid {
168 Grid::of(self.rate)
169 }
170
171 pub(crate) fn cx(&self, scope: ScopeId) -> Cx<'_> {
172 Cx {
173 scope,
174 time: None,
175 grid: self.grid(),
176 }
177 }
178
179 pub fn origin(&self, instance: &str) -> Option<&str> {
180 self.origin.get(instance).map(String::as_str)
181 }
182
183 pub fn paths(&self) -> impl Iterator<Item = &str> {
184 self.nodes.keys().map(String::as_str)
185 }
186
187 pub fn holds(&self, path: &str) -> bool {
188 self.nodes.contains_key(path)
189 }
190
191 pub fn instance_of(&self, target: &str) -> Result<String, EngineError> {
194 if self.holds(target) {
195 return Ok(target.to_string());
196 }
197 if let Some(own) = self.own_terms.get(target) {
198 return Ok(own.clone());
199 }
200 sole(
201 target,
202 self.instances_of(target).map(|p| (p.clone(), p)).collect(),
203 )
204 }
205
206 pub fn instances_of<'a>(&'a self, file: &'a str) -> impl Iterator<Item = String> + 'a {
207 self.paths()
208 .filter(move |p| self.origin(p) == Some(file))
209 .map(str::to_string)
210 }
211
212 pub fn at<'a>(&'a self, path: &str) -> Option<(&'a Expr, Cx<'a>)> {
213 let thunk = *self.nodes.get(path)?;
214 Some((thunk.expr, self.cx(thunk.scope)))
215 }
216
217 pub fn bindings<'a>(&'a self, path: &str) -> Option<Vec<(&'a str, &'a Expr, Cx<'a>)>> {
218 let thunk = *self.nodes.get(path)?;
219 Some(
220 self.scopes[thunk.scope as usize]
221 .vars
222 .iter()
223 .map(|(name, value)| (name.as_str(), value.expr, self.cx(value.scope)))
224 .collect(),
225 )
226 }
227}
228
229pub(crate) fn sole<T>(target: &str, mut held: Vec<(String, T)>) -> Result<T, EngineError> {
232 match held.len() {
233 0 => Err(EngineError::UnknownNode(target.to_string())),
234 1 => Ok(held.remove(0).1),
235 _ => Err(EngineError::AmbiguousNode(
236 target.to_string(),
237 held.into_iter().map(|(name, _)| name).collect(),
238 )),
239 }
240}
241
242pub(crate) fn is_free_name(name: &str) -> bool {
244 let reserved = crate::vocabulary::RESERVED
245 .iter()
246 .any(|(held, _)| *held == name);
247 (reserved && name != crate::vocabulary::SELF) || note::frequency(name).is_some()
248}
249
250fn implicit_time(name: &str) -> bool {
251 crate::overload::FINITE_DIFFERENCE.contains(&name)
252 || crate::lower::physics::MODAL.contains(&name)
253 || matches!(name, "noise" | "stft" | "istft")
254}
255
256pub fn is_reserved(name: &str) -> bool {
257 is_free_name(name) || name == crate::vocabulary::SELF || is_builtin(name)
258}
259
260impl<'g> Instances<'g> {
261 #[inline]
262 pub(crate) fn binds(&self, scope: ScopeId, name: &str) -> Option<Thunk<'g>> {
263 self.scopes[scope as usize].get(name, packed(name))
264 }
265
266 #[inline]
268 pub fn follow<'a, R>(
269 &'a self,
270 e: &'a Expr,
271 cx: Cx<'a>,
272 go: impl FnOnce(&'a Expr, Cx<'_>) -> R,
273 ) -> Option<R> {
274 match self.step(e, cx)? {
275 Move::Here(e2, cx2) => Some(go(e2, cx2)),
276 Move::Shifted(e2, scope, when) => {
277 let moved = Time { expr: when, cx };
278 Some(go(
279 e2,
280 Cx {
281 scope,
282 time: Some(&moved),
283 grid: cx.grid,
284 },
285 ))
286 }
287 }
288 }
289
290 #[inline(always)]
292 fn step<'a>(&'a self, e: &'a Expr, cx: Cx<'a>) -> Option<Move<'a>> {
293 match e {
294 Expr::Var(name) if name == "t" => cx.time.map(|t| Move::Here(t.expr, t.cx)),
295 Expr::Var(name) => self
296 .binds(cx.scope, name)
297 .map(|b| Move::Here(b.expr, cx.under(b.scope))),
298 Expr::Call { name, args, .. } => {
299 let bound = self.binds(cx.scope, name)?;
300 let [Arg::Pos(when)] = args.as_slice() else {
301 return None;
302 };
303 Some(Move::Shifted(bound.expr, bound.scope, when))
304 }
305 _ => None,
306 }
307 }
308
309 #[inline]
310 pub fn node<'a>(&'a self, e: &'a Expr, cx: Cx<'_>) -> Node<'a> {
311 match e {
312 Expr::Lit(l) => Node::Lit(l),
313 Expr::Var(name) => Node::Name(name),
314 Expr::Bin(op, l, r) => Node::Bin(*op, l, r),
315 Expr::SelfRef { arg, address, span } => Node::Own {
316 arg,
317 address: *address,
318 span: *span,
319 },
320 Expr::Ref {
321 path,
322 arg,
323 address,
324 span,
325 ..
326 } => Node::Read {
327 path: self.site(e, cx.scope, path),
328 arg,
329 address: *address,
330 span: *span,
331 },
332 Expr::Indexed { name, arg, span } => match self.binds(cx.scope, name) {
333 Some(of) => Node::Signal {
334 name,
335 of,
336 arg,
337 span: *span,
338 },
339 None => Node::Name(name),
340 },
341 Expr::Call { name, args, span } if is_builtin(name) => Node::Call {
342 name,
343 args,
344 span: *span,
345 },
346 Expr::Call { name, span, .. } => Node::Read {
347 path: self.site(e, cx.scope, name),
348 arg: &self.time,
349 address: Address::Time,
350 span: *span,
351 },
352 }
353 }
354
355 fn site<'a>(&'a self, e: &Expr, scope: ScopeId, written: &'a str) -> &'a str {
356 match self.sites.get(&(std::ptr::from_ref(e) as usize, scope)) {
357 Some(child) => child,
358 None => written,
359 }
360 }
361
362 pub(crate) fn signal<'a>(&self, of: Thunk<'a>, cx: Cx<'a>) -> Cx<'a> {
364 Cx {
365 scope: of.scope,
366 time: None,
367 grid: cx.grid,
368 }
369 }
370
371 pub(crate) fn is_now(&self, e: &Expr, cx: Cx) -> bool {
372 if let Some(r) = self.follow(e, cx, |e2, cx2| self.is_now(e2, cx2)) {
373 return r;
374 }
375 matches!(self.node(e, cx), Node::Name(n) if n == "t")
376 }
377
378 pub fn reads_self(&self, path: &str) -> bool {
379 self.at(path).is_some_and(|(e, cx)| self.holds_self(e, cx))
380 }
381
382 pub(crate) fn holds_self(&self, e: &Expr, cx: Cx) -> bool {
383 if let Some(r) = self.follow(e, cx, |e2, cx2| self.holds_self(e2, cx2)) {
384 return r;
385 }
386 match self.node(e, cx) {
387 Node::Lit(_) | Node::Name(_) => false,
388 Node::Own { .. } => true,
389 Node::Bin(_, l, r) => self.holds_self(l, cx) || self.holds_self(r, cx),
390 Node::Read { arg, .. } | Node::Signal { arg, .. } => self.holds_self(arg, cx),
391 Node::Call { args, .. } => args.iter().any(|a| {
392 let (Arg::Pos(x) | Arg::Named(_, x)) = a;
393 self.holds_self(x, cx)
394 }),
395 }
396 }
397
398 pub(crate) fn position_dependent(&self, e: &Expr, cx: Cx) -> Option<String> {
400 if let Some(r) = self.follow(e, cx, |e2, cx2| self.position_dependent(e2, cx2)) {
401 return r;
402 }
403 match self.node(e, cx) {
404 Node::Lit(_) | Node::Name(_) => None,
405 Node::Own { .. } => Some("self".to_string()),
406 Node::Bin(_, l, r) => self
407 .position_dependent(l, cx)
408 .or_else(|| self.position_dependent(r, cx)),
409 Node::Read { arg, .. } | Node::Signal { arg, .. } => self.position_dependent(arg, cx),
410 Node::Call { name, args, .. } => {
411 if Shape::from_name(name).is_some() || name == "crop" || implicit_time(name) {
412 return Some(name.to_string());
413 }
414 args.iter().find_map(|a| {
415 let (Arg::Pos(x) | Arg::Named(_, x)) = a;
416 self.position_dependent(x, cx)
417 })
418 }
419 }
420 }
421
422 pub(crate) fn same<'x, 'y>(&self, a: &Expr, ax: Cx<'x>, b: &Expr, by: Cx<'y>) -> bool {
424 if let Some(moved) = self.step(a, ax) {
425 return match moved {
426 Move::Here(a2, ax2) => self.same(a2, ax2, b, by),
427 Move::Shifted(a2, scope, when) => {
428 let moved = Time { expr: when, cx: ax };
429 self.same(
430 a2,
431 Cx {
432 scope,
433 time: Some(&moved),
434 grid: ax.grid,
435 },
436 b,
437 by,
438 )
439 }
440 };
441 }
442 if let Some(moved) = self.step(b, by) {
443 return match moved {
444 Move::Here(b2, by2) => self.same(a, ax, b2, by2),
445 Move::Shifted(b2, scope, when) => {
446 let moved = Time { expr: when, cx: by };
447 self.same(
448 a,
449 ax,
450 b2,
451 Cx {
452 scope,
453 time: Some(&moved),
454 grid: by.grid,
455 },
456 )
457 }
458 };
459 }
460 match (self.node(a, ax), self.node(b, by)) {
461 (Node::Lit(x), Node::Lit(y)) => x == y,
462 (Node::Name(x), Node::Name(y)) => x == y,
463 (Node::Bin(o1, l1, r1), Node::Bin(o2, l2, r2)) => {
464 o1 == o2 && self.same(l1, ax, l2, by) && self.same(r1, ax, r2, by)
465 }
466 (
467 Node::Own {
468 arg: x, address: i, ..
469 },
470 Node::Own {
471 arg: y, address: j, ..
472 },
473 ) => i == j && self.same(x, ax, y, by),
474 (
475 Node::Read {
476 path: p,
477 arg: x,
478 address: i,
479 ..
480 },
481 Node::Read {
482 path: q,
483 arg: y,
484 address: j,
485 ..
486 },
487 ) => p == q && i == j && self.same(x, ax, y, by),
488 (Node::Signal { of: f, arg: x, .. }, Node::Signal { of: g, arg: y, .. }) => {
489 self.same(f.expr, self.signal(f, ax), g.expr, self.signal(g, by))
490 && self.same(x, ax, y, by)
491 }
492 (
493 Node::Call {
494 name: n1, args: a1, ..
495 },
496 Node::Call {
497 name: n2, args: a2, ..
498 },
499 ) => {
500 n1 == n2
501 && a1.len() == a2.len()
502 && a1.iter().zip(a2).all(|(x, y)| match (x, y) {
503 (Arg::Pos(x), Arg::Pos(y)) => self.same(x, ax, y, by),
504 (Arg::Named(k1, x), Arg::Named(k2, y)) => {
505 k1 == k2 && self.same(x, ax, y, by)
506 }
507 _ => false,
508 })
509 }
510 _ => false,
511 }
512 }
513
514 pub(crate) fn same_binds(&self, scope: ScopeId, binds: &[(String, Thunk<'g>)]) -> bool {
515 let held = &self.scopes[scope as usize].vars;
516 held.len() == binds.len()
517 && held.iter().zip(binds).all(|((k1, v1), (k2, v2))| {
518 k1 == k2 && self.same(v1.expr, self.cx(v1.scope), v2.expr, self.cx(v2.scope))
519 })
520 }
521}