1mod build;
4mod resolved;
5
6use std::collections::{BTreeMap, HashMap};
7
8use sva_ast::{Arg, BinOp, ByteSpan, Expr, Literal};
9use sva_formula::filter::Shape;
10use sva_formula::note;
11
12use crate::error::EngineError;
13use crate::vocabulary::is_builtin;
14
15pub use build::{from_roots, instantiate};
16
17pub(crate) type ScopeId = u32;
18
19pub(crate) const NO_PARAMS: ScopeId = 0;
21
22#[derive(Clone, Copy, Debug)]
23pub(crate) struct Thunk<'g> {
24 pub(crate) expr: &'g Expr,
25 pub(crate) scope: ScopeId,
26}
27
28#[derive(Debug)]
30pub(crate) struct Scope<'g> {
31 pub(crate) vars: Vec<(String, Thunk<'g>)>,
32 keys: Vec<u64>,
33 seen: u64,
34}
35
36#[inline]
38pub(crate) fn packed(name: &str) -> u64 {
39 let bytes = name.as_bytes();
40 let mut key = (bytes.len() as u64) << 56;
41 for (at, byte) in bytes.iter().take(7).enumerate() {
42 key |= u64::from(*byte) << (at * 8);
43 }
44 key
45}
46
47#[inline]
48fn bit(key: u64) -> u64 {
49 1u64 << ((key ^ (key >> 32)) & 63)
50}
51
52impl<'g> Scope<'g> {
53 pub(crate) fn new(vars: Vec<(String, Thunk<'g>)>) -> Scope<'g> {
54 let keys: Vec<u64> = vars.iter().map(|(k, _)| packed(k)).collect();
55 let seen = keys.iter().fold(0, |acc, k| acc | bit(*k));
56 Scope { vars, keys, seen }
57 }
58
59 #[inline]
62 pub(crate) fn get(&self, name: &str, key: u64) -> Option<Thunk<'g>> {
63 if self.seen & bit(key) == 0 {
64 return None;
65 }
66 self.keys
67 .iter()
68 .enumerate()
69 .find(|(at, k)| **k == key && (name.len() <= 7 || self.vars[*at].0 == name))
70 .map(|(at, _)| self.vars[at].1)
71 }
72}
73
74#[derive(Clone, Copy)]
76pub struct Cx<'a> {
77 pub(crate) scope: ScopeId,
78 pub(crate) time: Option<&'a Time<'a>>,
79}
80
81pub(crate) struct Time<'a> {
82 pub(crate) expr: &'a Expr,
83 pub(crate) cx: Cx<'a>,
84}
85
86enum Move<'a> {
87 Here(&'a Expr, Cx<'a>),
88 Shifted(&'a Expr, ScopeId, &'a Expr),
89}
90
91impl<'a> Cx<'a> {
92 pub(crate) fn root(scope: ScopeId) -> Cx<'a> {
93 Cx { scope, time: None }
94 }
95
96 pub(crate) fn under(self, scope: ScopeId) -> Cx<'a> {
97 Cx {
98 scope,
99 time: self.time,
100 }
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 span: ByteSpan,
132 },
133 Own {
134 arg: &'a Expr,
135 span: ByteSpan,
136 },
137}
138
139#[derive(Debug)]
141pub struct Instances<'g> {
142 pub(crate) scopes: Vec<Scope<'g>>,
143 pub(crate) sites: HashMap<(usize, ScopeId), String>,
144 pub(crate) nodes: BTreeMap<String, Thunk<'g>>,
145 pub(crate) origin: BTreeMap<String, String>,
146 pub(crate) own_terms: BTreeMap<String, String>,
147 pub(crate) root: String,
148 pub(crate) time: Expr,
149}
150
151impl<'g> Instances<'g> {
152 pub fn origin(&self, instance: &str) -> Option<&str> {
153 self.origin.get(instance).map(String::as_str)
154 }
155
156 pub fn paths(&self) -> impl Iterator<Item = &str> {
157 self.nodes.keys().map(String::as_str)
158 }
159
160 pub fn holds(&self, path: &str) -> bool {
161 self.nodes.contains_key(path)
162 }
163
164 pub fn instance_of(&self, target: &str) -> Result<String, EngineError> {
167 if self.holds(target) {
168 return Ok(target.to_string());
169 }
170 if let Some(own) = self.own_terms.get(target) {
171 return Ok(own.clone());
172 }
173 sole(
174 target,
175 self.instances_of(target).map(|p| (p.clone(), p)).collect(),
176 )
177 }
178
179 pub fn instances_of<'a>(&'a self, file: &'a str) -> impl Iterator<Item = String> + 'a {
180 self.paths()
181 .filter(move |p| self.origin(p) == Some(file))
182 .map(str::to_string)
183 }
184
185 pub fn at<'a>(&'a self, path: &str) -> Option<(&'a Expr, Cx<'a>)> {
186 let thunk = *self.nodes.get(path)?;
187 Some((thunk.expr, Cx::root(thunk.scope)))
188 }
189
190 pub fn bindings<'a>(&'a self, path: &str) -> Option<Vec<(&'a str, &'a Expr, Cx<'a>)>> {
191 let thunk = *self.nodes.get(path)?;
192 Some(
193 self.scopes[thunk.scope as usize]
194 .vars
195 .iter()
196 .map(|(name, value)| (name.as_str(), value.expr, Cx::root(value.scope)))
197 .collect(),
198 )
199 }
200}
201
202pub(crate) fn sole<T>(target: &str, mut held: Vec<(String, T)>) -> Result<T, EngineError> {
205 match held.len() {
206 0 => Err(EngineError::UnknownNode(target.to_string())),
207 1 => Ok(held.remove(0).1),
208 _ => Err(EngineError::AmbiguousNode(
209 target.to_string(),
210 held.into_iter().map(|(name, _)| name).collect(),
211 )),
212 }
213}
214
215pub const RELEASE: &str = "release";
216
217pub(crate) fn is_free_name(name: &str) -> bool {
219 matches!(name, "t" | "f" | "i" | "pi" | "inf") || note::frequency(name).is_some()
220}
221
222pub fn is_reserved(name: &str) -> bool {
223 is_free_name(name) || name == "self" || is_builtin(name)
224}
225
226impl<'g> Instances<'g> {
227 #[inline]
228 pub(crate) fn binds(&self, scope: ScopeId, name: &str) -> Option<Thunk<'g>> {
229 self.scopes[scope as usize].get(name, packed(name))
230 }
231
232 #[inline]
234 pub fn follow<'a, R>(
235 &'a self,
236 e: &'a Expr,
237 cx: Cx<'a>,
238 go: impl FnOnce(&'a Expr, Cx<'_>) -> R,
239 ) -> Option<R> {
240 match self.step(e, cx)? {
241 Move::Here(e2, cx2) => Some(go(e2, cx2)),
242 Move::Shifted(e2, scope, when) => {
243 let moved = Time { expr: when, cx };
244 Some(go(
245 e2,
246 Cx {
247 scope,
248 time: Some(&moved),
249 },
250 ))
251 }
252 }
253 }
254
255 #[inline(always)]
257 fn step<'a>(&'a self, e: &'a Expr, cx: Cx<'a>) -> Option<Move<'a>> {
258 match e {
259 Expr::Var(name) if name == "t" => cx.time.map(|t| Move::Here(t.expr, t.cx)),
260 Expr::Var(name) => self
261 .binds(cx.scope, name)
262 .map(|b| Move::Here(b.expr, cx.under(b.scope))),
263 Expr::Call { name, args, .. } => {
264 let bound = self.binds(cx.scope, name)?;
265 let [Arg::Pos(when)] = args.as_slice() else {
266 return None;
267 };
268 Some(Move::Shifted(bound.expr, bound.scope, when))
269 }
270 _ => None,
271 }
272 }
273
274 #[inline]
275 pub fn node<'a>(&'a self, e: &'a Expr, cx: Cx<'_>) -> Node<'a> {
276 match e {
277 Expr::Lit(l) => Node::Lit(l),
278 Expr::Var(name) => Node::Name(name),
279 Expr::Bin(op, l, r) => Node::Bin(*op, l, r),
280 Expr::SelfRef { arg, span } => Node::Own { arg, span: *span },
281 Expr::Ref {
282 path, arg, span, ..
283 } => Node::Read {
284 path: self.site(e, cx.scope, path),
285 arg,
286 span: *span,
287 },
288 Expr::Call { name, args, span } if is_builtin(name) => Node::Call {
289 name,
290 args,
291 span: *span,
292 },
293 Expr::Call { name, span, .. } => Node::Read {
294 path: self.site(e, cx.scope, name),
295 arg: &self.time,
296 span: *span,
297 },
298 }
299 }
300
301 fn site<'a>(&'a self, e: &Expr, scope: ScopeId, written: &'a str) -> &'a str {
302 match self.sites.get(&(std::ptr::from_ref(e) as usize, scope)) {
303 Some(child) => child,
304 None => written,
305 }
306 }
307
308 pub(crate) fn is_now(&self, e: &Expr, cx: Cx) -> bool {
309 if let Some(r) = self.follow(e, cx, |e2, cx2| self.is_now(e2, cx2)) {
310 return r;
311 }
312 matches!(self.node(e, cx), Node::Name(n) if n == "t")
313 }
314
315 pub fn reads_self(&self, path: &str) -> bool {
316 self.at(path).is_some_and(|(e, cx)| self.holds_self(e, cx))
317 }
318
319 pub(crate) fn holds_self(&self, e: &Expr, cx: Cx) -> bool {
320 if let Some(r) = self.follow(e, cx, |e2, cx2| self.holds_self(e2, cx2)) {
321 return r;
322 }
323 match self.node(e, cx) {
324 Node::Lit(_) | Node::Name(_) => false,
325 Node::Own { .. } => true,
326 Node::Bin(_, l, r) => self.holds_self(l, cx) || self.holds_self(r, cx),
327 Node::Read { arg, .. } => self.holds_self(arg, cx),
328 Node::Call { args, .. } => args.iter().any(|a| {
329 let (Arg::Pos(x) | Arg::Named(_, x)) = a;
330 self.holds_self(x, cx)
331 }),
332 }
333 }
334
335 pub(crate) fn position_dependent(&self, e: &Expr, cx: Cx) -> Option<String> {
337 if let Some(r) = self.follow(e, cx, |e2, cx2| self.position_dependent(e2, cx2)) {
338 return r;
339 }
340 match self.node(e, cx) {
341 Node::Lit(_) | Node::Name(_) => None,
342 Node::Own { .. } => Some("self".to_string()),
343 Node::Bin(_, l, r) => self
344 .position_dependent(l, cx)
345 .or_else(|| self.position_dependent(r, cx)),
346 Node::Read { arg, .. } => self.position_dependent(arg, cx),
347 Node::Call { name, args, .. } => {
348 if Shape::from_name(name).is_some() || name == "crop" {
349 return Some(name.to_string());
350 }
351 args.iter().find_map(|a| {
352 let (Arg::Pos(x) | Arg::Named(_, x)) = a;
353 self.position_dependent(x, cx)
354 })
355 }
356 }
357 }
358
359 pub(crate) fn same<'x, 'y>(&self, a: &Expr, ax: Cx<'x>, b: &Expr, by: Cx<'y>) -> bool {
361 if let Some(moved) = self.step(a, ax) {
362 return match moved {
363 Move::Here(a2, ax2) => self.same(a2, ax2, b, by),
364 Move::Shifted(a2, scope, when) => {
365 let moved = Time { expr: when, cx: ax };
366 self.same(
367 a2,
368 Cx {
369 scope,
370 time: Some(&moved),
371 },
372 b,
373 by,
374 )
375 }
376 };
377 }
378 if let Some(moved) = self.step(b, by) {
379 return match moved {
380 Move::Here(b2, by2) => self.same(a, ax, b2, by2),
381 Move::Shifted(b2, scope, when) => {
382 let moved = Time { expr: when, cx: by };
383 self.same(
384 a,
385 ax,
386 b2,
387 Cx {
388 scope,
389 time: Some(&moved),
390 },
391 )
392 }
393 };
394 }
395 match (self.node(a, ax), self.node(b, by)) {
396 (Node::Lit(x), Node::Lit(y)) => x == y,
397 (Node::Name(x), Node::Name(y)) => x == y,
398 (Node::Bin(o1, l1, r1), Node::Bin(o2, l2, r2)) => {
399 o1 == o2 && self.same(l1, ax, l2, by) && self.same(r1, ax, r2, by)
400 }
401 (Node::Own { arg: x, .. }, Node::Own { arg: y, .. }) => self.same(x, ax, y, by),
402 (
403 Node::Read {
404 path: p, arg: x, ..
405 },
406 Node::Read {
407 path: q, arg: y, ..
408 },
409 ) => p == q && self.same(x, ax, y, by),
410 (
411 Node::Call {
412 name: n1, args: a1, ..
413 },
414 Node::Call {
415 name: n2, args: a2, ..
416 },
417 ) => {
418 n1 == n2
419 && a1.len() == a2.len()
420 && a1.iter().zip(a2).all(|(x, y)| match (x, y) {
421 (Arg::Pos(x), Arg::Pos(y)) => self.same(x, ax, y, by),
422 (Arg::Named(k1, x), Arg::Named(k2, y)) => {
423 k1 == k2 && self.same(x, ax, y, by)
424 }
425 _ => false,
426 })
427 }
428 _ => false,
429 }
430 }
431
432 pub(crate) fn same_binds(&self, scope: ScopeId, binds: &[(String, Thunk<'g>)]) -> bool {
433 let held = &self.scopes[scope as usize].vars;
434 held.len() == binds.len()
435 && held.iter().zip(binds).all(|((k1, v1), (k2, v2))| {
436 k1 == k2 && self.same(v1.expr, Cx::root(v1.scope), v2.expr, Cx::root(v2.scope))
437 })
438 }
439}