Skip to main content

oxilean_kernel/export/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::declaration::ConstantInfo;
6use crate::{
7    BinderInfo, Declaration, Environment, Expr, FVarId, Level, LevelMVarId, Literal, Name,
8};
9use std::collections::HashMap;
10
11use super::functions::import_module;
12
13/// A pair of `StatSummary` values tracking before/after a transformation.
14#[allow(dead_code)]
15pub struct TransformStat {
16    before: StatSummary,
17    after: StatSummary,
18}
19#[allow(dead_code)]
20impl TransformStat {
21    /// Creates a new transform stat recorder.
22    pub fn new() -> Self {
23        Self {
24            before: StatSummary::new(),
25            after: StatSummary::new(),
26        }
27    }
28    /// Records a before value.
29    pub fn record_before(&mut self, v: f64) {
30        self.before.record(v);
31    }
32    /// Records an after value.
33    pub fn record_after(&mut self, v: f64) {
34        self.after.record(v);
35    }
36    /// Returns the mean reduction ratio (after/before).
37    pub fn mean_ratio(&self) -> Option<f64> {
38        let b = self.before.mean()?;
39        let a = self.after.mean()?;
40        if b.abs() < f64::EPSILON {
41            return None;
42        }
43        Some(a / b)
44    }
45}
46/// Result of a module integrity check.
47#[derive(Debug, Clone, PartialEq, Eq)]
48pub enum IntegrityCheckResult {
49    /// All checks passed.
50    Ok,
51    /// The module is empty.
52    EmptyModule,
53    /// The module has duplicate declaration names.
54    DuplicateNames(Vec<Name>),
55    /// The magic number in the binary header is wrong.
56    BadMagicNumber,
57    /// The version is unsupported.
58    UnsupportedVersion(u32),
59}
60/// A label set for a graph node.
61#[allow(dead_code)]
62pub struct LabelSet {
63    labels: Vec<String>,
64}
65#[allow(dead_code)]
66impl LabelSet {
67    /// Creates a new empty label set.
68    pub fn new() -> Self {
69        Self { labels: Vec::new() }
70    }
71    /// Adds a label (deduplicates).
72    pub fn add(&mut self, label: impl Into<String>) {
73        let s = label.into();
74        if !self.labels.contains(&s) {
75            self.labels.push(s);
76        }
77    }
78    /// Returns `true` if `label` is present.
79    pub fn has(&self, label: &str) -> bool {
80        self.labels.iter().any(|l| l == label)
81    }
82    /// Returns the count of labels.
83    pub fn count(&self) -> usize {
84        self.labels.len()
85    }
86    /// Returns all labels.
87    pub fn all(&self) -> &[String] {
88        &self.labels
89    }
90}
91/// A hierarchical configuration tree.
92#[allow(dead_code)]
93pub struct ConfigNode {
94    key: String,
95    value: Option<String>,
96    children: Vec<ConfigNode>,
97}
98#[allow(dead_code)]
99impl ConfigNode {
100    /// Creates a leaf config node with a value.
101    pub fn leaf(key: impl Into<String>, value: impl Into<String>) -> Self {
102        Self {
103            key: key.into(),
104            value: Some(value.into()),
105            children: Vec::new(),
106        }
107    }
108    /// Creates a section node with children.
109    pub fn section(key: impl Into<String>) -> Self {
110        Self {
111            key: key.into(),
112            value: None,
113            children: Vec::new(),
114        }
115    }
116    /// Adds a child node.
117    pub fn add_child(&mut self, child: ConfigNode) {
118        self.children.push(child);
119    }
120    /// Returns the key.
121    pub fn key(&self) -> &str {
122        &self.key
123    }
124    /// Returns the value, or `None` for section nodes.
125    pub fn value(&self) -> Option<&str> {
126        self.value.as_deref()
127    }
128    /// Returns the number of children.
129    pub fn num_children(&self) -> usize {
130        self.children.len()
131    }
132    /// Looks up a dot-separated path.
133    pub fn lookup(&self, path: &str) -> Option<&str> {
134        let mut parts = path.splitn(2, '.');
135        let head = parts.next()?;
136        let tail = parts.next();
137        if head != self.key {
138            return None;
139        }
140        match tail {
141            None => self.value.as_deref(),
142            Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
143        }
144    }
145    fn lookup_relative(&self, path: &str) -> Option<&str> {
146        let mut parts = path.splitn(2, '.');
147        let head = parts.next()?;
148        let tail = parts.next();
149        if head != self.key {
150            return None;
151        }
152        match tail {
153            None => self.value.as_deref(),
154            Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
155        }
156    }
157}
158/// A dependency graph between modules.
159///
160/// Allows topological ordering for deterministic import order.
161#[derive(Debug, Default)]
162pub struct ModuleDependencyGraph {
163    /// Adjacency list: module → list of modules it depends on.
164    edges: HashMap<String, Vec<String>>,
165}
166impl ModuleDependencyGraph {
167    /// Create an empty dependency graph.
168    pub fn new() -> Self {
169        Self::default()
170    }
171    /// Add a module to the graph.
172    pub fn add_module(&mut self, name: String) {
173        self.edges.entry(name).or_default();
174    }
175    /// Add a dependency edge: `from` depends on `to`.
176    pub fn add_dep(&mut self, from: String, to: String) {
177        self.edges.entry(from).or_default().push(to);
178    }
179    /// Build a dependency graph from a module cache.
180    pub fn from_cache(cache: &ModuleCache) -> Self {
181        let mut g = Self::new();
182        for name in cache.all_modules() {
183            g.add_module(name.to_string());
184            if let Some(module) = cache.get(name) {
185                for dep in &module.dependencies {
186                    g.add_dep(name.to_string(), dep.clone());
187                }
188            }
189        }
190        g
191    }
192    /// Compute a topological ordering of modules (Kahn's algorithm).
193    ///
194    /// Returns `Err` if there is a cycle.
195    pub fn topological_order(&self) -> Result<Vec<String>, String> {
196        use std::collections::VecDeque;
197        let mut in_degree: HashMap<String, usize> = HashMap::new();
198        for (node, deps) in &self.edges {
199            in_degree.entry(node.clone()).or_insert(0);
200            for dep in deps {
201                in_degree.entry(dep.clone()).or_insert(0);
202            }
203        }
204        for deps in self.edges.values() {
205            for dep in deps {
206                *in_degree.entry(dep.clone()).or_insert(0) += 0;
207            }
208        }
209        let mut in_degree: HashMap<String, usize> = HashMap::new();
210        for node in self.edges.keys() {
211            let deps = self.edges.get(node).map(|v| v.len()).unwrap_or(0);
212            in_degree.insert(node.clone(), deps);
213        }
214        let mut queue: VecDeque<String> = in_degree
215            .iter()
216            .filter(|(_, d)| **d == 0)
217            .map(|(k, _)| k.clone())
218            .collect();
219        let mut order = Vec::new();
220        while let Some(node) = queue.pop_front() {
221            order.push(node.clone());
222            for (other, deps) in &self.edges {
223                if deps.contains(&node) {
224                    let deg = in_degree.get_mut(other).ok_or_else(|| {
225                        format!("module `{other}` is missing from the in-degree map")
226                    })?;
227                    *deg = deg.saturating_sub(1);
228                    if *deg == 0 {
229                        queue.push_back(other.clone());
230                    }
231                }
232            }
233        }
234        if order.len() != self.edges.len() {
235            Err("Cycle detected in module dependency graph".to_string())
236        } else {
237            Ok(order)
238        }
239    }
240    /// Check if module `a` depends (transitively) on module `b`.
241    pub fn depends_on(&self, a: &str, b: &str) -> bool {
242        if let Some(deps) = self.edges.get(a) {
243            if deps.iter().any(|d| d == b) {
244                return true;
245            }
246            deps.iter().any(|d| self.depends_on(d, b))
247        } else {
248            false
249        }
250    }
251    /// Number of modules in the graph.
252    pub fn num_modules(&self) -> usize {
253        self.edges.len()
254    }
255}
256/// A versioned record that stores a history of values.
257#[allow(dead_code)]
258pub struct VersionedRecord<T: Clone> {
259    /// Every earlier value, oldest first: version `n` is `earlier[n]`.
260    earlier: Vec<T>,
261    /// The latest value (version `earlier.len()`), declared last so it drops last.
262    current: T,
263}
264#[allow(dead_code)]
265impl<T: Clone> VersionedRecord<T> {
266    /// Creates a new record with an initial value.
267    pub fn new(initial: T) -> Self {
268        Self {
269            earlier: Vec::new(),
270            current: initial,
271        }
272    }
273    /// Updates the record with a new version.
274    pub fn update(&mut self, val: T) {
275        let previous = std::mem::replace(&mut self.current, val);
276        self.earlier.push(previous);
277    }
278    /// Returns the current (latest) value.
279    pub fn current(&self) -> &T {
280        &self.current
281    }
282    /// Returns the value at version `n` (0-indexed), or `None`.
283    pub fn at_version(&self, n: usize) -> Option<&T> {
284        match self.earlier.get(n) {
285            Some(value) => Some(value),
286            None if n == self.earlier.len() => Some(&self.current),
287            None => None,
288        }
289    }
290    /// Returns the version number of the current value.
291    pub fn version(&self) -> usize {
292        self.earlier.len()
293    }
294    /// Returns `true` if more than one version exists.
295    pub fn has_history(&self) -> bool {
296        !self.earlier.is_empty()
297    }
298}
299/// A non-empty list (at least one element guaranteed).
300#[allow(dead_code)]
301pub struct NonEmptyVec<T> {
302    head: T,
303    tail: Vec<T>,
304}
305#[allow(dead_code)]
306impl<T> NonEmptyVec<T> {
307    /// Creates a non-empty vec with a single element.
308    pub fn singleton(val: T) -> Self {
309        Self {
310            head: val,
311            tail: Vec::new(),
312        }
313    }
314    /// Pushes an element.
315    pub fn push(&mut self, val: T) {
316        self.tail.push(val);
317    }
318    /// Returns a reference to the first element.
319    pub fn first(&self) -> &T {
320        &self.head
321    }
322    /// Returns a reference to the last element.
323    pub fn last(&self) -> &T {
324        self.tail.last().unwrap_or(&self.head)
325    }
326    /// Returns the number of elements.
327    pub fn len(&self) -> usize {
328        1 + self.tail.len()
329    }
330    /// Returns whether the collection is empty.
331    pub fn is_empty(&self) -> bool {
332        self.len() == 0
333    }
334    /// Returns all elements as a Vec.
335    pub fn to_vec(&self) -> Vec<&T> {
336        let mut v = vec![&self.head];
337        v.extend(self.tail.iter());
338        v
339    }
340}
341/// A registry that tracks which declarations come from which module.
342///
343/// Used during compilation to produce better error messages and to support
344/// incremental builds.
345#[derive(Debug, Default)]
346pub struct ModuleRegistry {
347    /// Maps declaration name to the module it was imported from.
348    decl_to_module: HashMap<Name, String>,
349    /// Maps module name to the set of declarations it provides.
350    module_to_decls: HashMap<String, Vec<Name>>,
351}
352impl ModuleRegistry {
353    /// Create a new registry.
354    pub fn new() -> Self {
355        Self::default()
356    }
357    /// Register a declaration as coming from a module.
358    pub fn register(&mut self, decl_name: Name, module_name: String) {
359        self.decl_to_module
360            .insert(decl_name.clone(), module_name.clone());
361        self.module_to_decls
362            .entry(module_name)
363            .or_default()
364            .push(decl_name);
365    }
366    /// Register all declarations from a module.
367    pub fn register_module(&mut self, module: &ExportedModule) {
368        for name in module.declaration_names() {
369            self.register(name.clone(), module.name.clone());
370        }
371    }
372    /// Look up which module a declaration came from.
373    pub fn module_for(&self, decl: &Name) -> Option<&str> {
374        self.decl_to_module.get(decl).map(|s| s.as_str())
375    }
376    /// Get all declarations provided by a module.
377    pub fn decls_for_module(&self, module: &str) -> &[Name] {
378        self.module_to_decls
379            .get(module)
380            .map(|v| v.as_slice())
381            .unwrap_or(&[])
382    }
383    /// Get all registered module names.
384    pub fn all_module_names(&self) -> Vec<&str> {
385        self.module_to_decls.keys().map(|s| s.as_str()).collect()
386    }
387    /// Check if a declaration is registered.
388    pub fn contains_decl(&self, decl: &Name) -> bool {
389        self.decl_to_module.contains_key(decl)
390    }
391    /// Number of registered declarations.
392    pub fn num_decls(&self) -> usize {
393        self.decl_to_module.len()
394    }
395    /// Number of registered modules.
396    pub fn num_modules(&self) -> usize {
397        self.module_to_decls.len()
398    }
399}
400/// A generic counter that tracks min/max/sum for statistical summaries.
401#[allow(dead_code)]
402pub struct StatSummary {
403    count: u64,
404    sum: f64,
405    min: f64,
406    max: f64,
407}
408#[allow(dead_code)]
409impl StatSummary {
410    /// Creates an empty summary.
411    pub fn new() -> Self {
412        Self {
413            count: 0,
414            sum: 0.0,
415            min: f64::INFINITY,
416            max: f64::NEG_INFINITY,
417        }
418    }
419    /// Records a sample.
420    pub fn record(&mut self, val: f64) {
421        self.count += 1;
422        self.sum += val;
423        if val < self.min {
424            self.min = val;
425        }
426        if val > self.max {
427            self.max = val;
428        }
429    }
430    /// Returns the mean, or `None` if no samples.
431    pub fn mean(&self) -> Option<f64> {
432        if self.count == 0 {
433            None
434        } else {
435            Some(self.sum / self.count as f64)
436        }
437    }
438    /// Returns the minimum, or `None` if no samples.
439    pub fn min(&self) -> Option<f64> {
440        if self.count == 0 {
441            None
442        } else {
443            Some(self.min)
444        }
445    }
446    /// Returns the maximum, or `None` if no samples.
447    pub fn max(&self) -> Option<f64> {
448        if self.count == 0 {
449            None
450        } else {
451            Some(self.max)
452        }
453    }
454    /// Returns the count of recorded samples.
455    pub fn count(&self) -> u64 {
456        self.count
457    }
458}
459/// A fixed-size sliding window that computes a running sum.
460#[allow(dead_code)]
461pub struct SlidingSum {
462    window: Vec<f64>,
463    capacity: usize,
464    pos: usize,
465    sum: f64,
466    count: usize,
467}
468#[allow(dead_code)]
469impl SlidingSum {
470    /// Creates a sliding sum with the given window size.
471    pub fn new(capacity: usize) -> Self {
472        Self {
473            window: vec![0.0; capacity],
474            capacity,
475            pos: 0,
476            sum: 0.0,
477            count: 0,
478        }
479    }
480    /// Adds a value to the window, removing the oldest if full.
481    pub fn push(&mut self, val: f64) {
482        let oldest = self.window[self.pos];
483        self.sum -= oldest;
484        self.sum += val;
485        self.window[self.pos] = val;
486        self.pos = (self.pos + 1) % self.capacity;
487        if self.count < self.capacity {
488            self.count += 1;
489        }
490    }
491    /// Returns the current window sum.
492    pub fn sum(&self) -> f64 {
493        self.sum
494    }
495    /// Returns the window mean, or `None` if empty.
496    pub fn mean(&self) -> Option<f64> {
497        if self.count == 0 {
498            None
499        } else {
500            Some(self.sum / self.count as f64)
501        }
502    }
503    /// Returns the current window size (number of valid elements).
504    pub fn count(&self) -> usize {
505        self.count
506    }
507}
508/// A window iterator that yields overlapping windows of size `n`.
509#[allow(dead_code)]
510pub struct WindowIterator<'a, T> {
511    pub(super) data: &'a [T],
512    pub(super) pos: usize,
513    pub(super) window: usize,
514}
515#[allow(dead_code)]
516impl<'a, T> WindowIterator<'a, T> {
517    /// Creates a new window iterator.
518    pub fn new(data: &'a [T], window: usize) -> Self {
519        Self {
520            data,
521            pos: 0,
522            window,
523        }
524    }
525}
526/// Exported module containing declarations.
527#[derive(Debug, Clone)]
528pub struct ExportedModule {
529    /// Module name
530    pub name: String,
531    /// Exported legacy declarations
532    pub declarations: Vec<(Name, Declaration)>,
533    /// Exported constant info entries
534    pub constants: Vec<(Name, ConstantInfo)>,
535    /// Module dependencies (other module names)
536    pub dependencies: Vec<String>,
537    /// Module version
538    pub version: String,
539    /// Metadata (arbitrary key-value pairs)
540    pub metadata: HashMap<String, String>,
541}
542impl ExportedModule {
543    /// Create a new exported module.
544    pub fn new(name: String) -> Self {
545        Self {
546            name,
547            declarations: Vec::new(),
548            constants: Vec::new(),
549            dependencies: Vec::new(),
550            version: "0.1.1".to_string(),
551            metadata: HashMap::new(),
552        }
553    }
554    /// Add a legacy declaration to the module.
555    pub fn add_declaration(&mut self, name: Name, decl: Declaration) {
556        self.declarations.push((name, decl));
557    }
558    /// Add a ConstantInfo to the module.
559    pub fn add_constant(&mut self, name: Name, ci: ConstantInfo) {
560        self.constants.push((name, ci));
561    }
562    /// Add a dependency.
563    pub fn add_dependency(&mut self, dep: String) {
564        if !self.dependencies.contains(&dep) {
565            self.dependencies.push(dep);
566        }
567    }
568    /// Add metadata.
569    pub fn set_metadata(&mut self, key: String, value: String) {
570        self.metadata.insert(key, value);
571    }
572    /// Get all declaration names (both legacy and constant info).
573    pub fn declaration_names(&self) -> Vec<&Name> {
574        let mut names: Vec<&Name> = self.declarations.iter().map(|(name, _)| name).collect();
575        names.extend(self.constants.iter().map(|(name, _)| name));
576        names
577    }
578    /// Get the total number of entries.
579    pub fn num_entries(&self) -> usize {
580        self.declarations.len() + self.constants.len()
581    }
582    /// Check if the module is empty.
583    pub fn is_empty(&self) -> bool {
584        self.declarations.is_empty() && self.constants.is_empty()
585    }
586}
587/// A reusable scratch buffer for path computations.
588#[allow(dead_code)]
589pub struct PathBuf {
590    components: Vec<String>,
591}
592#[allow(dead_code)]
593impl PathBuf {
594    /// Creates a new empty path buffer.
595    pub fn new() -> Self {
596        Self {
597            components: Vec::new(),
598        }
599    }
600    /// Pushes a component.
601    pub fn push(&mut self, comp: impl Into<String>) {
602        self.components.push(comp.into());
603    }
604    /// Pops the last component.
605    pub fn pop(&mut self) {
606        self.components.pop();
607    }
608    /// Returns the current path as a `/`-separated string.
609    pub fn as_str(&self) -> String {
610        self.components.join("/")
611    }
612    /// Returns the depth of the path.
613    pub fn depth(&self) -> usize {
614        self.components.len()
615    }
616    /// Clears the path.
617    pub fn clear(&mut self) {
618        self.components.clear();
619    }
620}
621/// A pool of reusable string buffers.
622#[allow(dead_code)]
623pub struct StringPool {
624    free: Vec<String>,
625}
626#[allow(dead_code)]
627impl StringPool {
628    /// Creates a new empty string pool.
629    pub fn new() -> Self {
630        Self { free: Vec::new() }
631    }
632    /// Takes a string from the pool (may be empty).
633    pub fn take(&mut self) -> String {
634        self.free.pop().unwrap_or_default()
635    }
636    /// Returns a string to the pool.
637    pub fn give(&mut self, mut s: String) {
638        s.clear();
639        self.free.push(s);
640    }
641    /// Returns the number of free strings in the pool.
642    pub fn free_count(&self) -> usize {
643        self.free.len()
644    }
645}
646/// A simple key-value store backed by a sorted Vec for small maps.
647#[allow(dead_code)]
648pub struct SmallMap<K: Ord + Clone, V: Clone> {
649    entries: Vec<(K, V)>,
650}
651#[allow(dead_code)]
652impl<K: Ord + Clone, V: Clone> SmallMap<K, V> {
653    /// Creates a new empty small map.
654    pub fn new() -> Self {
655        Self {
656            entries: Vec::new(),
657        }
658    }
659    /// Inserts or replaces the value for `key`.
660    pub fn insert(&mut self, key: K, val: V) {
661        match self.entries.binary_search_by_key(&&key, |(k, _)| k) {
662            Ok(i) => self.entries[i].1 = val,
663            Err(i) => self.entries.insert(i, (key, val)),
664        }
665    }
666    /// Returns the value for `key`, or `None`.
667    pub fn get(&self, key: &K) -> Option<&V> {
668        self.entries
669            .binary_search_by_key(&key, |(k, _)| k)
670            .ok()
671            .map(|i| &self.entries[i].1)
672    }
673    /// Returns the number of entries.
674    pub fn len(&self) -> usize {
675        self.entries.len()
676    }
677    /// Returns `true` if empty.
678    pub fn is_empty(&self) -> bool {
679        self.entries.is_empty()
680    }
681    /// Returns all keys.
682    pub fn keys(&self) -> Vec<&K> {
683        self.entries.iter().map(|(k, _)| k).collect()
684    }
685    /// Returns all values.
686    pub fn values(&self) -> Vec<&V> {
687        self.entries.iter().map(|(_, v)| v).collect()
688    }
689}
690/// A dependency closure builder (transitive closure via BFS).
691#[allow(dead_code)]
692pub struct TransitiveClosure {
693    adj: Vec<Vec<usize>>,
694    n: usize,
695}
696#[allow(dead_code)]
697impl TransitiveClosure {
698    /// Creates a transitive closure builder for `n` nodes.
699    pub fn new(n: usize) -> Self {
700        Self {
701            adj: vec![Vec::new(); n],
702            n,
703        }
704    }
705    /// Adds a direct edge.
706    pub fn add_edge(&mut self, from: usize, to: usize) {
707        if from < self.n {
708            self.adj[from].push(to);
709        }
710    }
711    /// Computes all nodes reachable from `start` (including `start`).
712    pub fn reachable_from(&self, start: usize) -> Vec<usize> {
713        let mut visited = vec![false; self.n];
714        let mut queue = std::collections::VecDeque::new();
715        queue.push_back(start);
716        while let Some(node) = queue.pop_front() {
717            if node >= self.n || visited[node] {
718                continue;
719            }
720            visited[node] = true;
721            for &next in &self.adj[node] {
722                queue.push_back(next);
723            }
724        }
725        (0..self.n).filter(|&i| visited[i]).collect()
726    }
727    /// Returns `true` if `from` can transitively reach `to`.
728    pub fn can_reach(&self, from: usize, to: usize) -> bool {
729        self.reachable_from(from).contains(&to)
730    }
731}
732/// The difference between two module versions.
733#[derive(Debug, Clone, Default)]
734pub struct ModuleDiff {
735    /// Names added in the new version.
736    pub added: Vec<Name>,
737    /// Names removed from the old version.
738    pub removed: Vec<Name>,
739    /// Names present in both but with changed content.
740    pub changed: Vec<Name>,
741}
742impl ModuleDiff {
743    /// Create an empty diff.
744    pub fn new() -> Self {
745        Self::default()
746    }
747    /// Check if the diff is empty (no changes).
748    pub fn is_empty(&self) -> bool {
749        self.added.is_empty() && self.removed.is_empty() && self.changed.is_empty()
750    }
751    /// Total number of changed items.
752    pub fn total_changes(&self) -> usize {
753        self.added.len() + self.removed.len() + self.changed.len()
754    }
755}
756/// A set of rewrite rules.
757#[allow(dead_code)]
758pub struct RewriteRuleSet {
759    rules: Vec<RewriteRule>,
760}
761#[allow(dead_code)]
762impl RewriteRuleSet {
763    /// Creates an empty rule set.
764    pub fn new() -> Self {
765        Self { rules: Vec::new() }
766    }
767    /// Adds a rule.
768    pub fn add(&mut self, rule: RewriteRule) {
769        self.rules.push(rule);
770    }
771    /// Returns the number of rules.
772    pub fn len(&self) -> usize {
773        self.rules.len()
774    }
775    /// Returns `true` if the set is empty.
776    pub fn is_empty(&self) -> bool {
777        self.rules.is_empty()
778    }
779    /// Returns all conditional rules.
780    pub fn conditional_rules(&self) -> Vec<&RewriteRule> {
781        self.rules.iter().filter(|r| r.conditional).collect()
782    }
783    /// Returns all unconditional rules.
784    pub fn unconditional_rules(&self) -> Vec<&RewriteRule> {
785        self.rules.iter().filter(|r| !r.conditional).collect()
786    }
787    /// Looks up a rule by name.
788    pub fn get(&self, name: &str) -> Option<&RewriteRule> {
789        self.rules.iter().find(|r| r.name == name)
790    }
791}
792/// A semantic version for a module.
793#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
794pub struct ModuleVersion {
795    /// Major version number.
796    pub major: u32,
797    /// Minor version number.
798    pub minor: u32,
799    /// Patch version number.
800    pub patch: u32,
801}
802impl ModuleVersion {
803    /// Create a new version.
804    pub fn new(major: u32, minor: u32, patch: u32) -> Self {
805        Self {
806            major,
807            minor,
808            patch,
809        }
810    }
811    /// Parse a version string of the form "M.N.P".
812    pub fn parse(s: &str) -> Option<Self> {
813        let parts: Vec<&str> = s.split('.').collect();
814        if parts.len() != 3 {
815            return None;
816        }
817        let major = parts[0].parse().ok()?;
818        let minor = parts[1].parse().ok()?;
819        let patch = parts[2].parse().ok()?;
820        Some(Self {
821            major,
822            minor,
823            patch,
824        })
825    }
826    /// Whether this version is backwards compatible with `other`.
827    ///
828    /// Compatible if same major version and this is at least as new.
829    pub fn is_compatible_with(&self, other: &Self) -> bool {
830        self.major == other.major && *self >= *other
831    }
832}
833/// A token bucket rate limiter.
834#[allow(dead_code)]
835pub struct TokenBucket {
836    capacity: u64,
837    tokens: u64,
838    refill_per_ms: u64,
839    last_refill: crate::wall_clock::Instant,
840}
841#[allow(dead_code)]
842impl TokenBucket {
843    /// Creates a new token bucket.
844    pub fn new(capacity: u64, refill_per_ms: u64) -> Self {
845        Self {
846            capacity,
847            tokens: capacity,
848            refill_per_ms,
849            last_refill: crate::wall_clock::Instant::now(),
850        }
851    }
852    /// Attempts to consume `n` tokens.  Returns `true` on success.
853    pub fn try_consume(&mut self, n: u64) -> bool {
854        self.refill();
855        if self.tokens >= n {
856            self.tokens -= n;
857            true
858        } else {
859            false
860        }
861    }
862    fn refill(&mut self) {
863        let now = crate::wall_clock::Instant::now();
864        let elapsed_ms = now.duration_since(self.last_refill).as_millis() as u64;
865        if elapsed_ms > 0 {
866            let new_tokens = elapsed_ms * self.refill_per_ms;
867            self.tokens = (self.tokens + new_tokens).min(self.capacity);
868            self.last_refill = now;
869        }
870    }
871    /// Returns the number of currently available tokens.
872    pub fn available(&self) -> u64 {
873        self.tokens
874    }
875    /// Returns the bucket capacity.
876    pub fn capacity(&self) -> u64 {
877        self.capacity
878    }
879}
880/// A type-erased function pointer with arity tracking.
881#[allow(dead_code)]
882pub struct RawFnPtr {
883    /// The raw function pointer (stored as usize for type erasure).
884    ptr: usize,
885    arity: usize,
886    name: String,
887}
888#[allow(dead_code)]
889impl RawFnPtr {
890    /// Creates a new raw function pointer descriptor.
891    pub fn new(ptr: usize, arity: usize, name: impl Into<String>) -> Self {
892        Self {
893            ptr,
894            arity,
895            name: name.into(),
896        }
897    }
898    /// Returns the arity.
899    pub fn arity(&self) -> usize {
900        self.arity
901    }
902    /// Returns the name.
903    pub fn name(&self) -> &str {
904        &self.name
905    }
906    /// Returns the raw pointer value.
907    pub fn raw(&self) -> usize {
908        self.ptr
909    }
910}
911/// Extended metadata for a module.
912#[derive(Clone, Debug, Default)]
913pub struct ModuleInfo {
914    /// The module's semantic version.
915    pub version: Option<ModuleVersion>,
916    /// Author or maintainer.
917    pub author: Option<String>,
918    /// License identifier (e.g. "MIT").
919    pub license: Option<String>,
920    /// Short description.
921    pub description: Option<String>,
922}
923impl ModuleInfo {
924    /// Create an empty info struct.
925    pub fn new() -> Self {
926        Self::default()
927    }
928    /// Attach a version.
929    pub fn with_version(mut self, v: ModuleVersion) -> Self {
930        self.version = Some(v);
931        self
932    }
933    /// Attach an author.
934    pub fn with_author(mut self, a: impl Into<String>) -> Self {
935        self.author = Some(a.into());
936        self
937    }
938    /// Attach a license.
939    pub fn with_license(mut self, l: impl Into<String>) -> Self {
940        self.license = Some(l.into());
941        self
942    }
943    /// Attach a description.
944    pub fn with_description(mut self, d: impl Into<String>) -> Self {
945        self.description = Some(d.into());
946        self
947    }
948}
949/// Module cache for managing multiple modules.
950pub struct ModuleCache {
951    /// Cached modules indexed by name
952    modules: HashMap<String, ExportedModule>,
953    /// Import order (for deterministic behavior)
954    import_order: Vec<String>,
955}
956impl ModuleCache {
957    /// Create a new module cache.
958    pub fn new() -> Self {
959        Self {
960            modules: HashMap::new(),
961            import_order: Vec::new(),
962        }
963    }
964    /// Add a module to the cache.
965    pub fn add(&mut self, module: ExportedModule) {
966        let name = module.name.clone();
967        self.modules.insert(name.clone(), module);
968        if !self.import_order.contains(&name) {
969            self.import_order.push(name);
970        }
971    }
972    /// Get a module from the cache.
973    pub fn get(&self, name: &str) -> Option<&ExportedModule> {
974        self.modules.get(name)
975    }
976    /// Check if a module is cached.
977    pub fn contains(&self, name: &str) -> bool {
978        self.modules.contains_key(name)
979    }
980    /// Get all cached module names.
981    pub fn all_modules(&self) -> Vec<&str> {
982        self.import_order.iter().map(|s| s.as_str()).collect()
983    }
984    /// Get the number of cached modules.
985    pub fn num_modules(&self) -> usize {
986        self.modules.len()
987    }
988    /// Import all cached modules into an environment (in order).
989    pub fn import_all(&self, env: &mut Environment) -> Result<(), String> {
990        for name in &self.import_order {
991            if let Some(module) = self.modules.get(name) {
992                import_module(env, module)?;
993            }
994        }
995        Ok(())
996    }
997    /// Import a specific module and its transitive dependencies.
998    pub fn import_with_deps(&self, env: &mut Environment, name: &str) -> Result<(), String> {
999        let module = self
1000            .modules
1001            .get(name)
1002            .ok_or_else(|| format!("Module '{}' not found in cache", name))?;
1003        for dep in &module.dependencies {
1004            if self.contains(dep) {
1005                self.import_with_deps(env, dep)?;
1006            }
1007        }
1008        import_module(env, module)
1009    }
1010    /// Remove a module from the cache.
1011    pub fn remove(&mut self, name: &str) -> Option<ExportedModule> {
1012        self.import_order.retain(|n| n != name);
1013        self.modules.remove(name)
1014    }
1015    /// Clear the cache.
1016    pub fn clear(&mut self) {
1017        self.modules.clear();
1018        self.import_order.clear();
1019    }
1020}
1021/// A simple directed acyclic graph.
1022#[allow(dead_code)]
1023pub struct SimpleDag {
1024    /// `edges[i]` is the list of direct successors of node `i`.
1025    edges: Vec<Vec<usize>>,
1026}
1027#[allow(dead_code)]
1028impl SimpleDag {
1029    /// Creates a DAG with `n` nodes and no edges.
1030    pub fn new(n: usize) -> Self {
1031        Self {
1032            edges: vec![Vec::new(); n],
1033        }
1034    }
1035    /// Adds an edge from `from` to `to`.
1036    pub fn add_edge(&mut self, from: usize, to: usize) {
1037        if from < self.edges.len() {
1038            self.edges[from].push(to);
1039        }
1040    }
1041    /// Returns the successors of `node`.
1042    pub fn successors(&self, node: usize) -> &[usize] {
1043        self.edges.get(node).map(|v| v.as_slice()).unwrap_or(&[])
1044    }
1045    /// Returns `true` if `from` can reach `to` via DFS.
1046    pub fn can_reach(&self, from: usize, to: usize) -> bool {
1047        let mut visited = vec![false; self.edges.len()];
1048        self.dfs(from, to, &mut visited)
1049    }
1050    fn dfs(&self, cur: usize, target: usize, visited: &mut Vec<bool>) -> bool {
1051        if cur == target {
1052            return true;
1053        }
1054        if cur >= visited.len() || visited[cur] {
1055            return false;
1056        }
1057        visited[cur] = true;
1058        for &next in self.successors(cur) {
1059            if self.dfs(next, target, visited) {
1060                return true;
1061            }
1062        }
1063        false
1064    }
1065    /// Returns the topological order of nodes, or `None` if a cycle is detected.
1066    pub fn topological_sort(&self) -> Option<Vec<usize>> {
1067        let n = self.edges.len();
1068        let mut in_degree = vec![0usize; n];
1069        for succs in &self.edges {
1070            for &s in succs {
1071                if s < n {
1072                    in_degree[s] += 1;
1073                }
1074            }
1075        }
1076        let mut queue: std::collections::VecDeque<usize> =
1077            (0..n).filter(|&i| in_degree[i] == 0).collect();
1078        let mut order = Vec::new();
1079        while let Some(node) = queue.pop_front() {
1080            order.push(node);
1081            for &s in self.successors(node) {
1082                if s < n {
1083                    in_degree[s] -= 1;
1084                    if in_degree[s] == 0 {
1085                        queue.push_back(s);
1086                    }
1087                }
1088            }
1089        }
1090        if order.len() == n {
1091            Some(order)
1092        } else {
1093            None
1094        }
1095    }
1096    /// Returns the number of nodes.
1097    pub fn num_nodes(&self) -> usize {
1098        self.edges.len()
1099    }
1100}
1101/// Represents a rewrite rule `lhs → rhs`.
1102#[allow(dead_code)]
1103#[allow(missing_docs)]
1104pub struct RewriteRule {
1105    /// The name of the rule.
1106    pub name: String,
1107    /// A string representation of the LHS pattern.
1108    pub lhs: String,
1109    /// A string representation of the RHS.
1110    pub rhs: String,
1111    /// Whether this is a conditional rule (has side conditions).
1112    pub conditional: bool,
1113}
1114#[allow(dead_code)]
1115impl RewriteRule {
1116    /// Creates an unconditional rewrite rule.
1117    pub fn unconditional(
1118        name: impl Into<String>,
1119        lhs: impl Into<String>,
1120        rhs: impl Into<String>,
1121    ) -> Self {
1122        Self {
1123            name: name.into(),
1124            lhs: lhs.into(),
1125            rhs: rhs.into(),
1126            conditional: false,
1127        }
1128    }
1129    /// Creates a conditional rewrite rule.
1130    pub fn conditional(
1131        name: impl Into<String>,
1132        lhs: impl Into<String>,
1133        rhs: impl Into<String>,
1134    ) -> Self {
1135        Self {
1136            name: name.into(),
1137            lhs: lhs.into(),
1138            rhs: rhs.into(),
1139            conditional: true,
1140        }
1141    }
1142    /// Returns a textual representation.
1143    pub fn display(&self) -> String {
1144        format!("{}: {} → {}", self.name, self.lhs, self.rhs)
1145    }
1146}
1147/// A mutable reference stack for tracking the current "focus" in a tree traversal.
1148#[allow(dead_code)]
1149pub struct FocusStack<T> {
1150    items: Vec<T>,
1151}
1152#[allow(dead_code)]
1153impl<T> FocusStack<T> {
1154    /// Creates an empty focus stack.
1155    pub fn new() -> Self {
1156        Self { items: Vec::new() }
1157    }
1158    /// Focuses on `item`.
1159    pub fn focus(&mut self, item: T) {
1160        self.items.push(item);
1161    }
1162    /// Blurs (pops) the current focus.
1163    pub fn blur(&mut self) -> Option<T> {
1164        self.items.pop()
1165    }
1166    /// Returns the current focus, or `None`.
1167    pub fn current(&self) -> Option<&T> {
1168        self.items.last()
1169    }
1170    /// Returns the focus depth.
1171    pub fn depth(&self) -> usize {
1172        self.items.len()
1173    }
1174    /// Returns `true` if there is no current focus.
1175    pub fn is_empty(&self) -> bool {
1176        self.items.is_empty()
1177    }
1178}
1179#[cfg(test)]
1180mod topo_order_tests;