Skip to main content

oxilean_codegen/lua_backend/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::lcnf::*;
6use std::collections::HashMap;
7
8use super::functions::LUA_KEYWORDS;
9
10use super::functions::*;
11use std::collections::{HashSet, VecDeque};
12
13/// A text buffer for building LuaExt output source code.
14#[derive(Debug, Default)]
15pub struct LuaExtSourceBuffer {
16    pub(super) buf: String,
17    pub(super) indent_level: usize,
18    pub(super) indent_str: String,
19}
20impl LuaExtSourceBuffer {
21    pub fn new() -> Self {
22        LuaExtSourceBuffer {
23            buf: String::new(),
24            indent_level: 0,
25            indent_str: "    ".to_string(),
26        }
27    }
28    pub fn with_indent(mut self, indent: impl Into<String>) -> Self {
29        self.indent_str = indent.into();
30        self
31    }
32    pub fn push_line(&mut self, line: &str) {
33        for _ in 0..self.indent_level {
34            self.buf.push_str(&self.indent_str);
35        }
36        self.buf.push_str(line);
37        self.buf.push('\n');
38    }
39    pub fn push_raw(&mut self, s: &str) {
40        self.buf.push_str(s);
41    }
42    pub fn indent(&mut self) {
43        self.indent_level += 1;
44    }
45    pub fn dedent(&mut self) {
46        self.indent_level = self.indent_level.saturating_sub(1);
47    }
48    pub fn as_str(&self) -> &str {
49        &self.buf
50    }
51    pub fn len(&self) -> usize {
52        self.buf.len()
53    }
54    pub fn is_empty(&self) -> bool {
55        self.buf.is_empty()
56    }
57    pub fn line_count(&self) -> usize {
58        self.buf.lines().count()
59    }
60    pub fn into_string(self) -> String {
61        self.buf
62    }
63    pub fn reset(&mut self) {
64        self.buf.clear();
65        self.indent_level = 0;
66    }
67}
68#[allow(dead_code)]
69#[derive(Debug, Clone)]
70pub struct LuaDominatorTree {
71    pub idom: Vec<Option<u32>>,
72    pub dom_children: Vec<Vec<u32>>,
73    pub dom_depth: Vec<u32>,
74}
75impl LuaDominatorTree {
76    #[allow(dead_code)]
77    pub fn new(size: usize) -> Self {
78        LuaDominatorTree {
79            idom: vec![None; size],
80            dom_children: vec![Vec::new(); size],
81            dom_depth: vec![0; size],
82        }
83    }
84    #[allow(dead_code)]
85    pub fn set_idom(&mut self, node: usize, idom: u32) {
86        self.idom[node] = Some(idom);
87    }
88    #[allow(dead_code)]
89    pub fn dominates(&self, a: usize, b: usize) -> bool {
90        if a == b {
91            return true;
92        }
93        let mut cur = b;
94        loop {
95            match self.idom[cur] {
96                Some(parent) if parent as usize == a => return true,
97                Some(parent) if parent as usize == cur => return false,
98                Some(parent) => cur = parent as usize,
99                None => return false,
100            }
101        }
102    }
103    #[allow(dead_code)]
104    pub fn depth(&self, node: usize) -> u32 {
105        self.dom_depth.get(node).copied().unwrap_or(0)
106    }
107}
108/// Emission statistics for LuaExt.
109#[derive(Debug, Clone, Default)]
110pub struct LuaExtEmitStats {
111    pub bytes_emitted: usize,
112    pub items_emitted: usize,
113    pub errors: usize,
114    pub warnings: usize,
115    pub elapsed_ms: u64,
116}
117impl LuaExtEmitStats {
118    pub fn new() -> Self {
119        LuaExtEmitStats::default()
120    }
121    pub fn throughput_bps(&self) -> f64 {
122        if self.elapsed_ms == 0 {
123            0.0
124        } else {
125            self.bytes_emitted as f64 / (self.elapsed_ms as f64 / 1000.0)
126        }
127    }
128    pub fn is_clean(&self) -> bool {
129        self.errors == 0
130    }
131}
132#[allow(dead_code)]
133#[derive(Debug, Clone)]
134pub struct LuaPassConfig {
135    pub phase: LuaPassPhase,
136    pub enabled: bool,
137    pub max_iterations: u32,
138    pub debug_output: bool,
139    pub pass_name: String,
140}
141impl LuaPassConfig {
142    #[allow(dead_code)]
143    pub fn new(name: impl Into<String>, phase: LuaPassPhase) -> Self {
144        LuaPassConfig {
145            phase,
146            enabled: true,
147            max_iterations: 10,
148            debug_output: false,
149            pass_name: name.into(),
150        }
151    }
152    #[allow(dead_code)]
153    pub fn disabled(mut self) -> Self {
154        self.enabled = false;
155        self
156    }
157    #[allow(dead_code)]
158    pub fn with_debug(mut self) -> Self {
159        self.debug_output = true;
160        self
161    }
162    #[allow(dead_code)]
163    pub fn max_iter(mut self, n: u32) -> Self {
164        self.max_iterations = n;
165        self
166    }
167}
168/// A version tag for LuaExt output artifacts.
169#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
170pub struct LuaExtVersion {
171    pub major: u32,
172    pub minor: u32,
173    pub patch: u32,
174    pub pre: Option<String>,
175}
176impl LuaExtVersion {
177    pub fn new(major: u32, minor: u32, patch: u32) -> Self {
178        LuaExtVersion {
179            major,
180            minor,
181            patch,
182            pre: None,
183        }
184    }
185    pub fn with_pre(mut self, pre: impl Into<String>) -> Self {
186        self.pre = Some(pre.into());
187        self
188    }
189    pub fn is_stable(&self) -> bool {
190        self.pre.is_none()
191    }
192    pub fn is_compatible_with(&self, other: &LuaExtVersion) -> bool {
193        self.major == other.major && self.minor >= other.minor
194    }
195}
196/// Lua type representation (runtime tags + structural hints).
197#[derive(Debug, Clone, PartialEq, Eq, Hash)]
198pub enum LuaType {
199    /// `nil`
200    Nil,
201    /// `boolean`
202    Boolean,
203    /// `number` — integer when `is_int` is true, float otherwise
204    Number(bool),
205    /// `string`
206    String,
207    /// `table`
208    Table,
209    /// `function`
210    Function,
211    /// `userdata`
212    Userdata,
213    /// `thread` (coroutine)
214    Thread,
215    /// Named/custom type
216    Custom(std::string::String),
217}
218/// A named Lua function definition.
219#[derive(Debug, Clone, PartialEq)]
220pub struct LuaFunction {
221    /// Function name (None for anonymous)
222    pub name: Option<std::string::String>,
223    /// Parameter names
224    pub params: Vec<std::string::String>,
225    /// Whether the function accepts varargs (`...`)
226    pub vararg: bool,
227    /// Function body statements
228    pub body: Vec<LuaStmt>,
229    /// Whether the function is `local`
230    pub is_local: bool,
231    /// Whether the function is a method (uses `:` syntax)
232    pub is_method: bool,
233}
234impl LuaFunction {
235    /// Create a new named function.
236    pub fn new(
237        name: impl Into<std::string::String>,
238        params: Vec<std::string::String>,
239        body: Vec<LuaStmt>,
240    ) -> Self {
241        LuaFunction {
242            name: Some(name.into()),
243            params,
244            vararg: false,
245            body,
246            is_local: false,
247            is_method: false,
248        }
249    }
250    /// Create a new local function.
251    pub fn new_local(
252        name: impl Into<std::string::String>,
253        params: Vec<std::string::String>,
254        body: Vec<LuaStmt>,
255    ) -> Self {
256        LuaFunction {
257            name: Some(name.into()),
258            params,
259            vararg: false,
260            body,
261            is_local: true,
262            is_method: false,
263        }
264    }
265}
266#[allow(dead_code)]
267#[derive(Debug, Clone, PartialEq)]
268pub enum LuaPassPhase {
269    Analysis,
270    Transformation,
271    Verification,
272    Cleanup,
273}
274impl LuaPassPhase {
275    #[allow(dead_code)]
276    pub fn name(&self) -> &str {
277        match self {
278            LuaPassPhase::Analysis => "analysis",
279            LuaPassPhase::Transformation => "transformation",
280            LuaPassPhase::Verification => "verification",
281            LuaPassPhase::Cleanup => "cleanup",
282        }
283    }
284    #[allow(dead_code)]
285    pub fn is_modifying(&self) -> bool {
286        matches!(self, LuaPassPhase::Transformation | LuaPassPhase::Cleanup)
287    }
288}
289/// Worklist for LuaExt.
290#[allow(dead_code)]
291#[derive(Debug, Clone)]
292pub struct LuaExtWorklist {
293    pub(super) items: std::collections::VecDeque<usize>,
294    pub(super) present: Vec<bool>,
295}
296impl LuaExtWorklist {
297    #[allow(dead_code)]
298    pub fn new(capacity: usize) -> Self {
299        Self {
300            items: std::collections::VecDeque::new(),
301            present: vec![false; capacity],
302        }
303    }
304    #[allow(dead_code)]
305    pub fn push(&mut self, id: usize) {
306        if id < self.present.len() && !self.present[id] {
307            self.present[id] = true;
308            self.items.push_back(id);
309        }
310    }
311    #[allow(dead_code)]
312    pub fn push_front(&mut self, id: usize) {
313        if id < self.present.len() && !self.present[id] {
314            self.present[id] = true;
315            self.items.push_front(id);
316        }
317    }
318    #[allow(dead_code)]
319    pub fn pop(&mut self) -> Option<usize> {
320        let id = self.items.pop_front()?;
321        if id < self.present.len() {
322            self.present[id] = false;
323        }
324        Some(id)
325    }
326    #[allow(dead_code)]
327    pub fn is_empty(&self) -> bool {
328        self.items.is_empty()
329    }
330    #[allow(dead_code)]
331    pub fn len(&self) -> usize {
332        self.items.len()
333    }
334    #[allow(dead_code)]
335    pub fn contains(&self, id: usize) -> bool {
336        id < self.present.len() && self.present[id]
337    }
338    #[allow(dead_code)]
339    pub fn drain_all(&mut self) -> Vec<usize> {
340        let v: Vec<usize> = self.items.drain(..).collect();
341        for &id in &v {
342            if id < self.present.len() {
343                self.present[id] = false;
344            }
345        }
346        v
347    }
348}
349/// Statistics for LuaExt passes.
350#[allow(dead_code)]
351#[derive(Debug, Clone, Default)]
352pub struct LuaExtPassStats {
353    pub iterations: usize,
354    pub changed: bool,
355    pub nodes_visited: usize,
356    pub nodes_modified: usize,
357    pub time_ms: u64,
358    pub memory_bytes: usize,
359    pub errors: usize,
360}
361impl LuaExtPassStats {
362    #[allow(dead_code)]
363    pub fn new() -> Self {
364        Self::default()
365    }
366    #[allow(dead_code)]
367    pub fn visit(&mut self) {
368        self.nodes_visited += 1;
369    }
370    #[allow(dead_code)]
371    pub fn modify(&mut self) {
372        self.nodes_modified += 1;
373        self.changed = true;
374    }
375    #[allow(dead_code)]
376    pub fn iterate(&mut self) {
377        self.iterations += 1;
378    }
379    #[allow(dead_code)]
380    pub fn error(&mut self) {
381        self.errors += 1;
382    }
383    #[allow(dead_code)]
384    pub fn efficiency(&self) -> f64 {
385        if self.nodes_visited == 0 {
386            0.0
387        } else {
388            self.nodes_modified as f64 / self.nodes_visited as f64
389        }
390    }
391    #[allow(dead_code)]
392    pub fn merge(&mut self, o: &LuaExtPassStats) {
393        self.iterations += o.iterations;
394        self.changed |= o.changed;
395        self.nodes_visited += o.nodes_visited;
396        self.nodes_modified += o.nodes_modified;
397        self.time_ms += o.time_ms;
398        self.memory_bytes = self.memory_bytes.max(o.memory_bytes);
399        self.errors += o.errors;
400    }
401}
402#[allow(dead_code)]
403#[derive(Debug, Clone)]
404pub struct LuaDepGraph {
405    pub(super) nodes: Vec<u32>,
406    pub(super) edges: Vec<(u32, u32)>,
407}
408impl LuaDepGraph {
409    #[allow(dead_code)]
410    pub fn new() -> Self {
411        LuaDepGraph {
412            nodes: Vec::new(),
413            edges: Vec::new(),
414        }
415    }
416    #[allow(dead_code)]
417    pub fn add_node(&mut self, id: u32) {
418        if !self.nodes.contains(&id) {
419            self.nodes.push(id);
420        }
421    }
422    #[allow(dead_code)]
423    pub fn add_dep(&mut self, dep: u32, dependent: u32) {
424        self.add_node(dep);
425        self.add_node(dependent);
426        self.edges.push((dep, dependent));
427    }
428    #[allow(dead_code)]
429    pub fn dependents_of(&self, node: u32) -> Vec<u32> {
430        self.edges
431            .iter()
432            .filter(|(d, _)| *d == node)
433            .map(|(_, dep)| *dep)
434            .collect()
435    }
436    #[allow(dead_code)]
437    pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
438        self.edges
439            .iter()
440            .filter(|(_, dep)| *dep == node)
441            .map(|(d, _)| *d)
442            .collect()
443    }
444    #[allow(dead_code)]
445    pub fn topological_sort(&self) -> Vec<u32> {
446        let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
447        for &n in &self.nodes {
448            in_degree.insert(n, 0);
449        }
450        for (_, dep) in &self.edges {
451            *in_degree.entry(*dep).or_insert(0) += 1;
452        }
453        let mut queue: std::collections::VecDeque<u32> = self
454            .nodes
455            .iter()
456            .filter(|&&n| in_degree[&n] == 0)
457            .copied()
458            .collect();
459        let mut result = Vec::new();
460        while let Some(node) = queue.pop_front() {
461            result.push(node);
462            for dep in self.dependents_of(node) {
463                let cnt = in_degree.entry(dep).or_insert(0);
464                *cnt = cnt.saturating_sub(1);
465                if *cnt == 0 {
466                    queue.push_back(dep);
467                }
468            }
469        }
470        result
471    }
472    #[allow(dead_code)]
473    pub fn has_cycle(&self) -> bool {
474        self.topological_sort().len() < self.nodes.len()
475    }
476}
477#[allow(dead_code)]
478#[derive(Debug, Clone, Default)]
479pub struct LuaPassStats {
480    pub total_runs: u32,
481    pub successful_runs: u32,
482    pub total_changes: u64,
483    pub time_ms: u64,
484    pub iterations_used: u32,
485}
486impl LuaPassStats {
487    #[allow(dead_code)]
488    pub fn new() -> Self {
489        Self::default()
490    }
491    #[allow(dead_code)]
492    pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
493        self.total_runs += 1;
494        self.successful_runs += 1;
495        self.total_changes += changes;
496        self.time_ms += time_ms;
497        self.iterations_used = iterations;
498    }
499    #[allow(dead_code)]
500    pub fn average_changes_per_run(&self) -> f64 {
501        if self.total_runs == 0 {
502            return 0.0;
503        }
504        self.total_changes as f64 / self.total_runs as f64
505    }
506    #[allow(dead_code)]
507    pub fn success_rate(&self) -> f64 {
508        if self.total_runs == 0 {
509            return 0.0;
510        }
511        self.successful_runs as f64 / self.total_runs as f64
512    }
513    #[allow(dead_code)]
514    pub fn format_summary(&self) -> String {
515        format!(
516            "Runs: {}/{}, Changes: {}, Time: {}ms",
517            self.successful_runs, self.total_runs, self.total_changes, self.time_ms
518        )
519    }
520}
521/// Lua statement AST node.
522#[derive(Debug, Clone, PartialEq)]
523pub enum LuaStmt {
524    /// Assignment: `targets = values`
525    Assign {
526        targets: Vec<LuaExpr>,
527        values: Vec<LuaExpr>,
528    },
529    /// Local variable declaration: `local names = values`
530    LocalAssign {
531        names: Vec<std::string::String>,
532        attribs: Vec<Option<std::string::String>>,
533        values: Vec<LuaExpr>,
534    },
535    /// Do block: `do ... end`
536    Do(Vec<LuaStmt>),
537    /// While loop: `while cond do ... end`
538    While { cond: LuaExpr, body: Vec<LuaStmt> },
539    /// Repeat-until loop: `repeat ... until cond`
540    Repeat { body: Vec<LuaStmt>, cond: LuaExpr },
541    /// If-elseif-else: `if cond then ... [elseif ...] [else ...] end`
542    If {
543        cond: LuaExpr,
544        then_body: Vec<LuaStmt>,
545        elseif_clauses: Vec<(LuaExpr, Vec<LuaStmt>)>,
546        else_body: Option<Vec<LuaStmt>>,
547    },
548    /// Numeric for: `for var = start, limit[, step] do ... end`
549    For {
550        var: std::string::String,
551        start: LuaExpr,
552        limit: LuaExpr,
553        step: Option<LuaExpr>,
554        body: Vec<LuaStmt>,
555    },
556    /// Generic for: `for names in exprs do ... end`
557    ForIn {
558        names: Vec<std::string::String>,
559        exprs: Vec<LuaExpr>,
560        body: Vec<LuaStmt>,
561    },
562    /// Named function definition: `function name(params) ... end`
563    Function(LuaFunction),
564    /// Local function definition: `local function name(params) ... end`
565    Local(LuaFunction),
566    /// Return statement: `return [exprs]`
567    Return(Vec<LuaExpr>),
568    /// Break statement: `break`
569    Break,
570    /// Goto statement: `goto label`
571    Goto(std::string::String),
572    /// Label statement: `::label::`
573    Label(std::string::String),
574    /// Expression statement (function call): `expr`
575    Call(LuaExpr),
576}
577/// A fixed-capacity ring buffer of strings (for recent-event logging in LuaExt).
578#[derive(Debug)]
579pub struct LuaExtEventLog {
580    pub(super) entries: std::collections::VecDeque<String>,
581    pub(super) capacity: usize,
582}
583impl LuaExtEventLog {
584    pub fn new(capacity: usize) -> Self {
585        LuaExtEventLog {
586            entries: std::collections::VecDeque::with_capacity(capacity),
587            capacity,
588        }
589    }
590    pub fn push(&mut self, event: impl Into<String>) {
591        if self.entries.len() >= self.capacity {
592            self.entries.pop_front();
593        }
594        self.entries.push_back(event.into());
595    }
596    pub fn iter(&self) -> impl Iterator<Item = &String> {
597        self.entries.iter()
598    }
599    pub fn len(&self) -> usize {
600        self.entries.len()
601    }
602    pub fn is_empty(&self) -> bool {
603        self.entries.is_empty()
604    }
605    pub fn capacity(&self) -> usize {
606        self.capacity
607    }
608    pub fn clear(&mut self) {
609        self.entries.clear();
610    }
611}
612/// Liveness analysis for LuaExt.
613#[allow(dead_code)]
614#[derive(Debug, Clone, Default)]
615pub struct LuaExtLiveness {
616    pub live_in: Vec<Vec<usize>>,
617    pub live_out: Vec<Vec<usize>>,
618    pub defs: Vec<Vec<usize>>,
619    pub uses: Vec<Vec<usize>>,
620}
621impl LuaExtLiveness {
622    #[allow(dead_code)]
623    pub fn new(n: usize) -> Self {
624        Self {
625            live_in: vec![Vec::new(); n],
626            live_out: vec![Vec::new(); n],
627            defs: vec![Vec::new(); n],
628            uses: vec![Vec::new(); n],
629        }
630    }
631    #[allow(dead_code)]
632    pub fn live_in(&self, b: usize, v: usize) -> bool {
633        self.live_in.get(b).map(|s| s.contains(&v)).unwrap_or(false)
634    }
635    #[allow(dead_code)]
636    pub fn live_out(&self, b: usize, v: usize) -> bool {
637        self.live_out
638            .get(b)
639            .map(|s| s.contains(&v))
640            .unwrap_or(false)
641    }
642    #[allow(dead_code)]
643    pub fn add_def(&mut self, b: usize, v: usize) {
644        if let Some(s) = self.defs.get_mut(b) {
645            if !s.contains(&v) {
646                s.push(v);
647            }
648        }
649    }
650    #[allow(dead_code)]
651    pub fn add_use(&mut self, b: usize, v: usize) {
652        if let Some(s) = self.uses.get_mut(b) {
653            if !s.contains(&v) {
654                s.push(v);
655            }
656        }
657    }
658    #[allow(dead_code)]
659    pub fn var_is_used_in_block(&self, b: usize, v: usize) -> bool {
660        self.uses.get(b).map(|s| s.contains(&v)).unwrap_or(false)
661    }
662    #[allow(dead_code)]
663    pub fn var_is_def_in_block(&self, b: usize, v: usize) -> bool {
664        self.defs.get(b).map(|s| s.contains(&v)).unwrap_or(false)
665    }
666}
667/// Pipeline profiler for LuaExt.
668#[derive(Debug, Default)]
669pub struct LuaExtProfiler {
670    pub(super) timings: Vec<LuaExtPassTiming>,
671}
672impl LuaExtProfiler {
673    pub fn new() -> Self {
674        LuaExtProfiler::default()
675    }
676    pub fn record(&mut self, t: LuaExtPassTiming) {
677        self.timings.push(t);
678    }
679    pub fn total_elapsed_us(&self) -> u64 {
680        self.timings.iter().map(|t| t.elapsed_us).sum()
681    }
682    pub fn slowest_pass(&self) -> Option<&LuaExtPassTiming> {
683        self.timings.iter().max_by_key(|t| t.elapsed_us)
684    }
685    pub fn num_passes(&self) -> usize {
686        self.timings.len()
687    }
688    pub fn profitable_passes(&self) -> Vec<&LuaExtPassTiming> {
689        self.timings.iter().filter(|t| t.is_profitable()).collect()
690    }
691}
692/// Collects LuaExt diagnostics.
693#[derive(Debug, Default)]
694pub struct LuaExtDiagCollector {
695    pub(super) msgs: Vec<LuaExtDiagMsg>,
696}
697impl LuaExtDiagCollector {
698    pub fn new() -> Self {
699        LuaExtDiagCollector::default()
700    }
701    pub fn emit(&mut self, d: LuaExtDiagMsg) {
702        self.msgs.push(d);
703    }
704    pub fn has_errors(&self) -> bool {
705        self.msgs
706            .iter()
707            .any(|d| d.severity == LuaExtDiagSeverity::Error)
708    }
709    pub fn errors(&self) -> Vec<&LuaExtDiagMsg> {
710        self.msgs
711            .iter()
712            .filter(|d| d.severity == LuaExtDiagSeverity::Error)
713            .collect()
714    }
715    pub fn warnings(&self) -> Vec<&LuaExtDiagMsg> {
716        self.msgs
717            .iter()
718            .filter(|d| d.severity == LuaExtDiagSeverity::Warning)
719            .collect()
720    }
721    pub fn len(&self) -> usize {
722        self.msgs.len()
723    }
724    pub fn is_empty(&self) -> bool {
725        self.msgs.is_empty()
726    }
727    pub fn clear(&mut self) {
728        self.msgs.clear();
729    }
730}
731/// Heuristic freshness key for LuaExt incremental compilation.
732#[derive(Debug, Clone, PartialEq, Eq, Hash)]
733pub struct LuaExtIncrKey {
734    pub content_hash: u64,
735    pub config_hash: u64,
736}
737impl LuaExtIncrKey {
738    pub fn new(content: u64, config: u64) -> Self {
739        LuaExtIncrKey {
740            content_hash: content,
741            config_hash: config,
742        }
743    }
744    pub fn combined_hash(&self) -> u64 {
745        self.content_hash.wrapping_mul(0x9e3779b97f4a7c15) ^ self.config_hash
746    }
747    pub fn matches(&self, other: &LuaExtIncrKey) -> bool {
748        self.content_hash == other.content_hash && self.config_hash == other.config_hash
749    }
750}
751/// Lua code generation backend.
752pub struct LuaBackend {
753    /// Counter for fresh variable names.
754    pub(super) fresh_counter: u64,
755    /// Name mangling cache.
756    pub(super) name_cache: HashMap<std::string::String, std::string::String>,
757}
758impl LuaBackend {
759    /// Create a new `LuaBackend`.
760    pub fn new() -> Self {
761        LuaBackend {
762            fresh_counter: 0,
763            name_cache: HashMap::new(),
764        }
765    }
766    /// Generate a fresh variable name.
767    pub fn fresh_var(&mut self) -> std::string::String {
768        let n = self.fresh_counter;
769        self.fresh_counter += 1;
770        format!("_t{}", n)
771    }
772    /// Mangle an OxiLean name to a valid Lua identifier.
773    pub fn mangle_name(&mut self, name: &str) -> std::string::String {
774        if let Some(cached) = self.name_cache.get(name) {
775            return cached.clone();
776        }
777        if name.is_empty() {
778            return "_anon".to_string();
779        }
780        let mangled: std::string::String = name
781            .chars()
782            .map(|c| match c {
783                '.' | ':' => '_',
784                '\'' => '_',
785                c if c.is_alphanumeric() || c == '_' => c,
786                _ => '_',
787            })
788            .collect();
789        let mangled = if LUA_KEYWORDS.contains(&mangled.as_str())
790            || mangled.starts_with(|c: char| c.is_ascii_digit())
791        {
792            format!("_{}", mangled)
793        } else {
794            mangled
795        };
796        self.name_cache.insert(name.to_string(), mangled.clone());
797        mangled
798    }
799    /// Map an LCNF type to a Lua type hint.
800    pub fn lcnf_to_lua_type(ty: &LcnfType) -> LuaType {
801        match ty {
802            LcnfType::Nat => LuaType::Number(true),
803            LcnfType::LcnfString => LuaType::String,
804            LcnfType::Unit | LcnfType::Erased | LcnfType::Irrelevant => LuaType::Nil,
805            LcnfType::Object => LuaType::Table,
806            LcnfType::Var(name) => LuaType::Custom(name.clone()),
807            LcnfType::Fun(..) => LuaType::Function,
808            LcnfType::Ctor(name, _) => LuaType::Custom(name.clone()),
809        }
810    }
811    /// Compile an LCNF literal to a Lua expression.
812    pub fn compile_lit(lit: &LcnfLit) -> LuaExpr {
813        match lit {
814            LcnfLit::Nat(n) => LuaExpr::Int(*n as i64),
815            LcnfLit::Str(s) => LuaExpr::Str(s.clone()),
816        }
817    }
818    /// Compile an LCNF literal value to a Lua expression.
819    pub fn compile_let_value(&mut self, value: &LcnfLetValue) -> LuaExpr {
820        match value {
821            LcnfLetValue::App(func, args) => {
822                let func_expr = self.compile_arg(func);
823                let lua_args: Vec<_> = args.iter().map(|a| self.compile_arg(a)).collect();
824                LuaExpr::Call {
825                    func: Box::new(func_expr),
826                    args: lua_args,
827                }
828            }
829            LcnfLetValue::Ctor(ctor_name, _tag, fields) => {
830                let mut all_fields = vec![LuaTableField::NamedField(
831                    "tag".to_string(),
832                    LuaExpr::Str(ctor_name.clone()),
833                )];
834                for f in fields {
835                    all_fields.push(LuaTableField::ArrayItem(self.compile_arg(f)));
836                }
837                LuaExpr::TableConstructor(all_fields)
838            }
839            LcnfLetValue::Proj(_name, index, var) => {
840                let val_expr = LuaExpr::Var(var.to_string());
841                LuaExpr::IndexAccess {
842                    table: Box::new(val_expr),
843                    key: Box::new(LuaExpr::Int(*index as i64 + 2)),
844                }
845            }
846            LcnfLetValue::Lit(lit) => Self::compile_lit(lit),
847            LcnfLetValue::Erased => LuaExpr::Nil,
848            LcnfLetValue::FVar(id) => LuaExpr::Var(id.to_string()),
849            LcnfLetValue::Reset(_) => LuaExpr::Nil,
850            LcnfLetValue::Reuse(_slot, ctor_name, _tag, fields) => {
851                let mut all_fields = vec![LuaTableField::NamedField(
852                    "tag".to_string(),
853                    LuaExpr::Str(ctor_name.clone()),
854                )];
855                for f in fields {
856                    all_fields.push(LuaTableField::ArrayItem(self.compile_arg(f)));
857                }
858                LuaExpr::TableConstructor(all_fields)
859            }
860        }
861    }
862    /// Compile an LCNF argument to a Lua expression.
863    pub fn compile_arg(&mut self, arg: &LcnfArg) -> LuaExpr {
864        match arg {
865            LcnfArg::Var(id) => LuaExpr::Var(id.to_string()),
866            LcnfArg::Lit(lit) => Self::compile_lit(lit),
867            LcnfArg::Erased => LuaExpr::Nil,
868            LcnfArg::Type(_) => LuaExpr::Nil,
869        }
870    }
871    /// Compile an LCNF expression into a list of Lua statements,
872    /// returning the result expression.
873    pub fn compile_expr(&mut self, expr: &LcnfExpr, stmts: &mut Vec<LuaStmt>) -> LuaExpr {
874        match expr {
875            LcnfExpr::Return(arg) => self.compile_arg(arg),
876            LcnfExpr::Let {
877                id,
878                ty: _,
879                value,
880                body,
881                ..
882            } => {
883                let val_expr = self.compile_let_value(value);
884                stmts.push(LuaStmt::LocalAssign {
885                    names: vec![id.to_string()],
886                    attribs: vec![None],
887                    values: vec![val_expr],
888                });
889                self.compile_expr(body, stmts)
890            }
891            LcnfExpr::TailCall(func, args) => {
892                let func_expr = self.compile_arg(func);
893                let lua_args: Vec<_> = args.iter().map(|a| self.compile_arg(a)).collect();
894                LuaExpr::Call {
895                    func: Box::new(func_expr),
896                    args: lua_args,
897                }
898            }
899            LcnfExpr::Case {
900                scrutinee,
901                alts,
902                default,
903                ..
904            } => {
905                let scrut_expr = LuaExpr::Var(scrutinee.to_string());
906                let result_var = self.fresh_var();
907                stmts.push(LuaStmt::LocalAssign {
908                    names: vec![result_var.clone()],
909                    attribs: vec![None],
910                    values: vec![],
911                });
912                let tag_expr = LuaExpr::FieldAccess {
913                    table: Box::new(scrut_expr.clone()),
914                    field: "tag".to_string(),
915                };
916                let mut if_cond: Option<LuaExpr> = None;
917                let mut then_stmts: Vec<LuaStmt> = Vec::new();
918                let mut elseif_clauses: Vec<(LuaExpr, Vec<LuaStmt>)> = Vec::new();
919                for (idx, alt) in alts.iter().enumerate() {
920                    let mut case_stmts: Vec<LuaStmt> = Vec::new();
921                    for (field_idx, param) in alt.params.iter().enumerate() {
922                        let field_access = LuaExpr::IndexAccess {
923                            table: Box::new(scrut_expr.clone()),
924                            key: Box::new(LuaExpr::Int(field_idx as i64 + 2)),
925                        };
926                        case_stmts.push(LuaStmt::LocalAssign {
927                            names: vec![param.id.to_string()],
928                            attribs: vec![None],
929                            values: vec![field_access],
930                        });
931                    }
932                    let case_result = self.compile_expr(&alt.body, &mut case_stmts);
933                    case_stmts.push(LuaStmt::Assign {
934                        targets: vec![LuaExpr::Var(result_var.clone())],
935                        values: vec![case_result],
936                    });
937                    let cond = LuaExpr::BinOp {
938                        op: "==".to_string(),
939                        lhs: Box::new(tag_expr.clone()),
940                        rhs: Box::new(LuaExpr::Str(alt.ctor_name.clone())),
941                    };
942                    if idx == 0 {
943                        if_cond = Some(cond);
944                        then_stmts = case_stmts;
945                    } else {
946                        elseif_clauses.push((cond, case_stmts));
947                    }
948                }
949                let else_body = if let Some(def) = default {
950                    let mut def_stmts: Vec<LuaStmt> = Vec::new();
951                    let def_result = self.compile_expr(def, &mut def_stmts);
952                    def_stmts.push(LuaStmt::Assign {
953                        targets: vec![LuaExpr::Var(result_var.clone())],
954                        values: vec![def_result],
955                    });
956                    Some(def_stmts)
957                } else {
958                    None
959                };
960                if let Some(cond) = if_cond {
961                    stmts.push(LuaStmt::If {
962                        cond,
963                        then_body: then_stmts,
964                        elseif_clauses,
965                        else_body,
966                    });
967                }
968                LuaExpr::Var(result_var)
969            }
970            LcnfExpr::Unreachable => LuaExpr::Call {
971                func: Box::new(LuaExpr::Var("error".to_string())),
972                args: vec![LuaExpr::Str("unreachable".to_string())],
973            },
974        }
975    }
976    /// Compile an LCNF function declaration to a `LuaFunction`.
977    pub fn compile_decl(&mut self, decl: &LcnfFunDecl) -> Result<LuaFunction, std::string::String> {
978        let lua_name = self.mangle_name(&decl.name);
979        let params: Vec<_> = decl.params.iter().map(|p| p.id.to_string()).collect();
980        let mut body_stmts: Vec<LuaStmt> = Vec::new();
981        let result_expr = self.compile_expr(&decl.body, &mut body_stmts);
982        body_stmts.push(LuaStmt::Return(vec![result_expr]));
983        Ok(LuaFunction {
984            name: Some(lua_name),
985            params,
986            vararg: false,
987            body: body_stmts,
988            is_local: false,
989            is_method: false,
990        })
991    }
992    /// Compile a list of declarations and emit a `LuaModule`.
993    pub fn emit_module(&mut self, decls: &[LcnfFunDecl]) -> LuaModule {
994        let mut module = LuaModule::new();
995        for decl in decls {
996            if let Ok(func) = self.compile_decl(decl) {
997                module.functions.push(func);
998            }
999        }
1000        module
1001    }
1002}
1003/// A generic key-value configuration store for LuaExt.
1004#[derive(Debug, Clone, Default)]
1005pub struct LuaExtConfig {
1006    pub(super) entries: std::collections::HashMap<String, String>,
1007}
1008impl LuaExtConfig {
1009    pub fn new() -> Self {
1010        LuaExtConfig::default()
1011    }
1012    pub fn set(&mut self, key: impl Into<String>, value: impl Into<String>) {
1013        self.entries.insert(key.into(), value.into());
1014    }
1015    pub fn get(&self, key: &str) -> Option<&str> {
1016        self.entries.get(key).map(|s| s.as_str())
1017    }
1018    pub fn get_bool(&self, key: &str) -> bool {
1019        matches!(self.get(key), Some("true") | Some("1") | Some("yes"))
1020    }
1021    pub fn get_int(&self, key: &str) -> Option<i64> {
1022        self.get(key)?.parse().ok()
1023    }
1024    pub fn len(&self) -> usize {
1025        self.entries.len()
1026    }
1027    pub fn is_empty(&self) -> bool {
1028        self.entries.is_empty()
1029    }
1030}
1031#[allow(dead_code)]
1032#[derive(Debug, Clone)]
1033pub struct LuaCacheEntry {
1034    pub key: String,
1035    pub data: Vec<u8>,
1036    pub timestamp: u64,
1037    pub valid: bool,
1038}
1039/// A complete Lua module / script.
1040#[derive(Debug, Clone, PartialEq)]
1041pub struct LuaModule {
1042    /// `require` imports: `local modname = require("modname")`
1043    pub requires: Vec<(std::string::String, std::string::String)>,
1044    /// Top-level local declarations (before functions)
1045    pub local_decls: Vec<LuaStmt>,
1046    /// Top-level function definitions
1047    pub functions: Vec<LuaFunction>,
1048    /// Main block statements (executed at top level)
1049    pub main_block: Vec<LuaStmt>,
1050}
1051impl LuaModule {
1052    /// Create an empty Lua module.
1053    pub fn new() -> Self {
1054        LuaModule {
1055            requires: Vec::new(),
1056            local_decls: Vec::new(),
1057            functions: Vec::new(),
1058            main_block: Vec::new(),
1059        }
1060    }
1061    /// Emit valid Lua 5.4 source code for this module.
1062    pub fn emit(&self) -> std::string::String {
1063        let mut out = std::string::String::new();
1064        for (alias, path) in &self.requires {
1065            out.push_str(&format!("local {} = require(\"{}\")\n", alias, path));
1066        }
1067        if !self.requires.is_empty() {
1068            out.push('\n');
1069        }
1070        for decl in &self.local_decls {
1071            out.push_str(&format!("{}\n", emit_stmt(decl, 0)));
1072        }
1073        if !self.local_decls.is_empty() {
1074            out.push('\n');
1075        }
1076        for func in &self.functions {
1077            out.push_str(&format!("{}\n\n", emit_function(func, 0, false)));
1078        }
1079        for stmt in &self.main_block {
1080            out.push_str(&format!("{}\n", emit_stmt(stmt, 0)));
1081        }
1082        out
1083    }
1084}
1085/// Tracks declared names for LuaExt scope analysis.
1086#[derive(Debug, Default)]
1087pub struct LuaExtNameScope {
1088    pub(super) declared: std::collections::HashSet<String>,
1089    pub(super) depth: usize,
1090    pub(super) parent: Option<Box<LuaExtNameScope>>,
1091}
1092impl LuaExtNameScope {
1093    pub fn new() -> Self {
1094        LuaExtNameScope::default()
1095    }
1096    pub fn declare(&mut self, name: impl Into<String>) -> bool {
1097        self.declared.insert(name.into())
1098    }
1099    pub fn is_declared(&self, name: &str) -> bool {
1100        self.declared.contains(name)
1101    }
1102    pub fn push_scope(self) -> Self {
1103        LuaExtNameScope {
1104            declared: std::collections::HashSet::new(),
1105            depth: self.depth + 1,
1106            parent: Some(Box::new(self)),
1107        }
1108    }
1109    pub fn pop_scope(self) -> Self {
1110        *self.parent.unwrap_or_default()
1111    }
1112    pub fn depth(&self) -> usize {
1113        self.depth
1114    }
1115    pub fn len(&self) -> usize {
1116        self.declared.len()
1117    }
1118}
1119/// A Lua class emulated via metatables.
1120#[derive(Debug, Clone, PartialEq)]
1121pub struct LuaClass {
1122    /// Class name
1123    pub name: std::string::String,
1124    /// Methods (not including `new`)
1125    pub methods: Vec<LuaFunction>,
1126    /// __index implementation (default: self-reference)
1127    pub index: Option<LuaExpr>,
1128    /// __newindex implementation
1129    pub newindex: Option<LuaExpr>,
1130    /// __tostring implementation body
1131    pub tostring_body: Option<Vec<LuaStmt>>,
1132}
1133impl LuaClass {
1134    /// Create a new class with the given name.
1135    pub fn new(name: impl Into<std::string::String>) -> Self {
1136        LuaClass {
1137            name: name.into(),
1138            methods: Vec::new(),
1139            index: None,
1140            newindex: None,
1141            tostring_body: None,
1142        }
1143    }
1144    /// Emit Lua source for this class definition.
1145    pub fn emit(&self) -> std::string::String {
1146        let mut out = std::string::String::new();
1147        let n = &self.name;
1148        out.push_str(&format!("{} = {{}}\n", n));
1149        let index_str = match &self.index {
1150            Some(expr) => expr.to_string(),
1151            None => n.clone(),
1152        };
1153        out.push_str(&format!("{}.__index = {}\n", n, index_str));
1154        if let Some(ts_body) = &self.tostring_body {
1155            out.push_str(&format!(
1156                "{}.__tostring = function(self)\n{}\nend\n",
1157                n,
1158                emit_stmts(ts_body, 1)
1159            ));
1160        }
1161        if let Some(ni) = &self.newindex {
1162            out.push_str(&format!("{}.__newindex = {}\n", n, ni));
1163        }
1164        out.push_str(
1165            &format!(
1166                "function {}:new(o)\n  o = o or {{}}\n  setmetatable(o, self)\n  self.__index = self\n  return o\nend\n",
1167                n
1168            ),
1169        );
1170        for method in &self.methods {
1171            let mut m = method.clone();
1172            m.is_method = true;
1173            if let Some(ref mname) = m.name.clone() {
1174                m.name = Some(format!("{}.{}", n, mname));
1175            }
1176            out.push_str(&format!("{}\n", emit_function(&m, 0, false)));
1177        }
1178        out
1179    }
1180}
1181#[allow(dead_code)]
1182#[derive(Debug, Clone)]
1183pub struct LuaWorklist {
1184    pub(super) items: std::collections::VecDeque<u32>,
1185    pub(super) in_worklist: std::collections::HashSet<u32>,
1186}
1187impl LuaWorklist {
1188    #[allow(dead_code)]
1189    pub fn new() -> Self {
1190        LuaWorklist {
1191            items: std::collections::VecDeque::new(),
1192            in_worklist: std::collections::HashSet::new(),
1193        }
1194    }
1195    #[allow(dead_code)]
1196    pub fn push(&mut self, item: u32) -> bool {
1197        if self.in_worklist.insert(item) {
1198            self.items.push_back(item);
1199            true
1200        } else {
1201            false
1202        }
1203    }
1204    #[allow(dead_code)]
1205    pub fn pop(&mut self) -> Option<u32> {
1206        let item = self.items.pop_front()?;
1207        self.in_worklist.remove(&item);
1208        Some(item)
1209    }
1210    #[allow(dead_code)]
1211    pub fn is_empty(&self) -> bool {
1212        self.items.is_empty()
1213    }
1214    #[allow(dead_code)]
1215    pub fn len(&self) -> usize {
1216        self.items.len()
1217    }
1218    #[allow(dead_code)]
1219    pub fn contains(&self, item: u32) -> bool {
1220        self.in_worklist.contains(&item)
1221    }
1222}
1223/// Pass registry for LuaExt.
1224#[allow(dead_code)]
1225#[derive(Debug, Default)]
1226pub struct LuaExtPassRegistry {
1227    pub(super) configs: Vec<LuaExtPassConfig>,
1228    pub(super) stats: Vec<LuaExtPassStats>,
1229}
1230impl LuaExtPassRegistry {
1231    #[allow(dead_code)]
1232    pub fn new() -> Self {
1233        Self::default()
1234    }
1235    #[allow(dead_code)]
1236    pub fn register(&mut self, c: LuaExtPassConfig) {
1237        self.stats.push(LuaExtPassStats::new());
1238        self.configs.push(c);
1239    }
1240    #[allow(dead_code)]
1241    pub fn len(&self) -> usize {
1242        self.configs.len()
1243    }
1244    #[allow(dead_code)]
1245    pub fn is_empty(&self) -> bool {
1246        self.configs.is_empty()
1247    }
1248    #[allow(dead_code)]
1249    pub fn get(&self, i: usize) -> Option<&LuaExtPassConfig> {
1250        self.configs.get(i)
1251    }
1252    #[allow(dead_code)]
1253    pub fn get_stats(&self, i: usize) -> Option<&LuaExtPassStats> {
1254        self.stats.get(i)
1255    }
1256    #[allow(dead_code)]
1257    pub fn enabled_passes(&self) -> Vec<&LuaExtPassConfig> {
1258        self.configs.iter().filter(|c| c.enabled).collect()
1259    }
1260    #[allow(dead_code)]
1261    pub fn passes_in_phase(&self, ph: &LuaExtPassPhase) -> Vec<&LuaExtPassConfig> {
1262        self.configs
1263            .iter()
1264            .filter(|c| c.enabled && &c.phase == ph)
1265            .collect()
1266    }
1267    #[allow(dead_code)]
1268    pub fn total_nodes_visited(&self) -> usize {
1269        self.stats.iter().map(|s| s.nodes_visited).sum()
1270    }
1271    #[allow(dead_code)]
1272    pub fn any_changed(&self) -> bool {
1273        self.stats.iter().any(|s| s.changed)
1274    }
1275}
1276/// A feature flag set for LuaExt capabilities.
1277#[derive(Debug, Clone, Default)]
1278pub struct LuaExtFeatures {
1279    pub(super) flags: std::collections::HashSet<String>,
1280}
1281impl LuaExtFeatures {
1282    pub fn new() -> Self {
1283        LuaExtFeatures::default()
1284    }
1285    pub fn enable(&mut self, flag: impl Into<String>) {
1286        self.flags.insert(flag.into());
1287    }
1288    pub fn disable(&mut self, flag: &str) {
1289        self.flags.remove(flag);
1290    }
1291    pub fn is_enabled(&self, flag: &str) -> bool {
1292        self.flags.contains(flag)
1293    }
1294    pub fn len(&self) -> usize {
1295        self.flags.len()
1296    }
1297    pub fn is_empty(&self) -> bool {
1298        self.flags.is_empty()
1299    }
1300    pub fn union(&self, other: &LuaExtFeatures) -> LuaExtFeatures {
1301        LuaExtFeatures {
1302            flags: self.flags.union(&other.flags).cloned().collect(),
1303        }
1304    }
1305    pub fn intersection(&self, other: &LuaExtFeatures) -> LuaExtFeatures {
1306        LuaExtFeatures {
1307            flags: self.flags.intersection(&other.flags).cloned().collect(),
1308        }
1309    }
1310}
1311/// Lua expression AST node.
1312#[derive(Debug, Clone, PartialEq)]
1313pub enum LuaExpr {
1314    /// `nil`
1315    Nil,
1316    /// `true`
1317    True,
1318    /// `false`
1319    False,
1320    /// Integer literal: `42`
1321    Int(i64),
1322    /// Float literal: `3.14`
1323    Float(f64),
1324    /// String literal: `"hello"`
1325    Str(std::string::String),
1326    /// Variable reference: `x`
1327    Var(std::string::String),
1328    /// Binary operation: `lhs op rhs`
1329    BinOp {
1330        op: std::string::String,
1331        lhs: Box<LuaExpr>,
1332        rhs: Box<LuaExpr>,
1333    },
1334    /// Unary operation: `op operand`
1335    UnaryOp {
1336        op: std::string::String,
1337        operand: Box<LuaExpr>,
1338    },
1339    /// Function call: `func(args)`
1340    Call {
1341        func: Box<LuaExpr>,
1342        args: Vec<LuaExpr>,
1343    },
1344    /// Method call: `obj:method(args)`
1345    MethodCall {
1346        obj: Box<LuaExpr>,
1347        method: std::string::String,
1348        args: Vec<LuaExpr>,
1349    },
1350    /// Table constructor: `{field, ...}`
1351    TableConstructor(Vec<LuaTableField>),
1352    /// Index access: `table[key]`
1353    IndexAccess {
1354        table: Box<LuaExpr>,
1355        key: Box<LuaExpr>,
1356    },
1357    /// Field access: `table.field`
1358    FieldAccess {
1359        table: Box<LuaExpr>,
1360        field: std::string::String,
1361    },
1362    /// Lambda (anonymous function): `function(params) body end`
1363    Lambda {
1364        params: Vec<std::string::String>,
1365        vararg: bool,
1366        body: Vec<LuaStmt>,
1367    },
1368    /// Vararg expression: `...`
1369    Ellipsis,
1370}
1371/// Configuration for LuaExt passes.
1372#[allow(dead_code)]
1373#[derive(Debug, Clone)]
1374pub struct LuaExtPassConfig {
1375    pub name: String,
1376    pub phase: LuaExtPassPhase,
1377    pub enabled: bool,
1378    pub max_iterations: usize,
1379    pub debug: u32,
1380    pub timeout_ms: Option<u64>,
1381}
1382impl LuaExtPassConfig {
1383    #[allow(dead_code)]
1384    pub fn new(name: impl Into<String>) -> Self {
1385        Self {
1386            name: name.into(),
1387            phase: LuaExtPassPhase::Middle,
1388            enabled: true,
1389            max_iterations: 100,
1390            debug: 0,
1391            timeout_ms: None,
1392        }
1393    }
1394    #[allow(dead_code)]
1395    pub fn with_phase(mut self, phase: LuaExtPassPhase) -> Self {
1396        self.phase = phase;
1397        self
1398    }
1399    #[allow(dead_code)]
1400    pub fn with_max_iter(mut self, n: usize) -> Self {
1401        self.max_iterations = n;
1402        self
1403    }
1404    #[allow(dead_code)]
1405    pub fn with_debug(mut self, d: u32) -> Self {
1406        self.debug = d;
1407        self
1408    }
1409    #[allow(dead_code)]
1410    pub fn disabled(mut self) -> Self {
1411        self.enabled = false;
1412        self
1413    }
1414    #[allow(dead_code)]
1415    pub fn with_timeout(mut self, ms: u64) -> Self {
1416        self.timeout_ms = Some(ms);
1417        self
1418    }
1419    #[allow(dead_code)]
1420    pub fn is_debug_enabled(&self) -> bool {
1421        self.debug > 0
1422    }
1423}
1424#[allow(dead_code)]
1425#[derive(Debug, Clone)]
1426pub struct LuaLivenessInfo {
1427    pub live_in: Vec<std::collections::HashSet<u32>>,
1428    pub live_out: Vec<std::collections::HashSet<u32>>,
1429    pub defs: Vec<std::collections::HashSet<u32>>,
1430    pub uses: Vec<std::collections::HashSet<u32>>,
1431}
1432impl LuaLivenessInfo {
1433    #[allow(dead_code)]
1434    pub fn new(block_count: usize) -> Self {
1435        LuaLivenessInfo {
1436            live_in: vec![std::collections::HashSet::new(); block_count],
1437            live_out: vec![std::collections::HashSet::new(); block_count],
1438            defs: vec![std::collections::HashSet::new(); block_count],
1439            uses: vec![std::collections::HashSet::new(); block_count],
1440        }
1441    }
1442    #[allow(dead_code)]
1443    pub fn add_def(&mut self, block: usize, var: u32) {
1444        if block < self.defs.len() {
1445            self.defs[block].insert(var);
1446        }
1447    }
1448    #[allow(dead_code)]
1449    pub fn add_use(&mut self, block: usize, var: u32) {
1450        if block < self.uses.len() {
1451            self.uses[block].insert(var);
1452        }
1453    }
1454    #[allow(dead_code)]
1455    pub fn is_live_in(&self, block: usize, var: u32) -> bool {
1456        self.live_in
1457            .get(block)
1458            .map(|s| s.contains(&var))
1459            .unwrap_or(false)
1460    }
1461    #[allow(dead_code)]
1462    pub fn is_live_out(&self, block: usize, var: u32) -> bool {
1463        self.live_out
1464            .get(block)
1465            .map(|s| s.contains(&var))
1466            .unwrap_or(false)
1467    }
1468}
1469/// A monotonically increasing ID generator for LuaExt.
1470#[derive(Debug, Default)]
1471pub struct LuaExtIdGen {
1472    pub(super) next: u32,
1473}
1474impl LuaExtIdGen {
1475    pub fn new() -> Self {
1476        LuaExtIdGen::default()
1477    }
1478    pub fn next_id(&mut self) -> u32 {
1479        let id = self.next;
1480        self.next += 1;
1481        id
1482    }
1483    pub fn peek_next(&self) -> u32 {
1484        self.next
1485    }
1486    pub fn reset(&mut self) {
1487        self.next = 0;
1488    }
1489    pub fn skip(&mut self, n: u32) {
1490        self.next += n;
1491    }
1492}
1493#[allow(dead_code)]
1494pub struct LuaPassRegistry {
1495    pub(super) configs: Vec<LuaPassConfig>,
1496    pub(super) stats: std::collections::HashMap<String, LuaPassStats>,
1497}
1498impl LuaPassRegistry {
1499    #[allow(dead_code)]
1500    pub fn new() -> Self {
1501        LuaPassRegistry {
1502            configs: Vec::new(),
1503            stats: std::collections::HashMap::new(),
1504        }
1505    }
1506    #[allow(dead_code)]
1507    pub fn register(&mut self, config: LuaPassConfig) {
1508        self.stats
1509            .insert(config.pass_name.clone(), LuaPassStats::new());
1510        self.configs.push(config);
1511    }
1512    #[allow(dead_code)]
1513    pub fn enabled_passes(&self) -> Vec<&LuaPassConfig> {
1514        self.configs.iter().filter(|c| c.enabled).collect()
1515    }
1516    #[allow(dead_code)]
1517    pub fn get_stats(&self, name: &str) -> Option<&LuaPassStats> {
1518        self.stats.get(name)
1519    }
1520    #[allow(dead_code)]
1521    pub fn total_passes(&self) -> usize {
1522        self.configs.len()
1523    }
1524    #[allow(dead_code)]
1525    pub fn enabled_count(&self) -> usize {
1526        self.enabled_passes().len()
1527    }
1528    #[allow(dead_code)]
1529    pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
1530        if let Some(stats) = self.stats.get_mut(name) {
1531            stats.record_run(changes, time_ms, iter);
1532        }
1533    }
1534}
1535/// Constant folding helper for LuaExt.
1536#[allow(dead_code)]
1537#[derive(Debug, Clone, Default)]
1538pub struct LuaExtConstFolder {
1539    pub(super) folds: usize,
1540    pub(super) failures: usize,
1541    pub(super) enabled: bool,
1542}
1543impl LuaExtConstFolder {
1544    #[allow(dead_code)]
1545    pub fn new() -> Self {
1546        Self {
1547            folds: 0,
1548            failures: 0,
1549            enabled: true,
1550        }
1551    }
1552    #[allow(dead_code)]
1553    pub fn add_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1554        self.folds += 1;
1555        a.checked_add(b)
1556    }
1557    #[allow(dead_code)]
1558    pub fn sub_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1559        self.folds += 1;
1560        a.checked_sub(b)
1561    }
1562    #[allow(dead_code)]
1563    pub fn mul_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1564        self.folds += 1;
1565        a.checked_mul(b)
1566    }
1567    #[allow(dead_code)]
1568    pub fn div_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1569        if b == 0 {
1570            self.failures += 1;
1571            None
1572        } else {
1573            self.folds += 1;
1574            a.checked_div(b)
1575        }
1576    }
1577    #[allow(dead_code)]
1578    pub fn rem_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1579        if b == 0 {
1580            self.failures += 1;
1581            None
1582        } else {
1583            self.folds += 1;
1584            a.checked_rem(b)
1585        }
1586    }
1587    #[allow(dead_code)]
1588    pub fn neg_i64(&mut self, a: i64) -> Option<i64> {
1589        self.folds += 1;
1590        a.checked_neg()
1591    }
1592    #[allow(dead_code)]
1593    pub fn shl_i64(&mut self, a: i64, s: u32) -> Option<i64> {
1594        if s >= 64 {
1595            self.failures += 1;
1596            None
1597        } else {
1598            self.folds += 1;
1599            a.checked_shl(s)
1600        }
1601    }
1602    #[allow(dead_code)]
1603    pub fn shr_i64(&mut self, a: i64, s: u32) -> Option<i64> {
1604        if s >= 64 {
1605            self.failures += 1;
1606            None
1607        } else {
1608            self.folds += 1;
1609            a.checked_shr(s)
1610        }
1611    }
1612    #[allow(dead_code)]
1613    pub fn and_i64(&mut self, a: i64, b: i64) -> i64 {
1614        self.folds += 1;
1615        a & b
1616    }
1617    #[allow(dead_code)]
1618    pub fn or_i64(&mut self, a: i64, b: i64) -> i64 {
1619        self.folds += 1;
1620        a | b
1621    }
1622    #[allow(dead_code)]
1623    pub fn xor_i64(&mut self, a: i64, b: i64) -> i64 {
1624        self.folds += 1;
1625        a ^ b
1626    }
1627    #[allow(dead_code)]
1628    pub fn not_i64(&mut self, a: i64) -> i64 {
1629        self.folds += 1;
1630        !a
1631    }
1632    #[allow(dead_code)]
1633    pub fn fold_count(&self) -> usize {
1634        self.folds
1635    }
1636    #[allow(dead_code)]
1637    pub fn failure_count(&self) -> usize {
1638        self.failures
1639    }
1640    #[allow(dead_code)]
1641    pub fn enable(&mut self) {
1642        self.enabled = true;
1643    }
1644    #[allow(dead_code)]
1645    pub fn disable(&mut self) {
1646        self.enabled = false;
1647    }
1648    #[allow(dead_code)]
1649    pub fn is_enabled(&self) -> bool {
1650        self.enabled
1651    }
1652}
1653/// Analysis cache for LuaExt.
1654#[allow(dead_code)]
1655#[derive(Debug)]
1656pub struct LuaExtCache {
1657    pub(super) entries: Vec<(u64, Vec<u8>, bool, u32)>,
1658    pub(super) cap: usize,
1659    pub(super) total_hits: u64,
1660    pub(super) total_misses: u64,
1661}
1662impl LuaExtCache {
1663    #[allow(dead_code)]
1664    pub fn new(cap: usize) -> Self {
1665        Self {
1666            entries: Vec::new(),
1667            cap,
1668            total_hits: 0,
1669            total_misses: 0,
1670        }
1671    }
1672    #[allow(dead_code)]
1673    pub fn get(&mut self, key: u64) -> Option<&[u8]> {
1674        for e in self.entries.iter_mut() {
1675            if e.0 == key && e.2 {
1676                e.3 += 1;
1677                self.total_hits += 1;
1678                return Some(&e.1);
1679            }
1680        }
1681        self.total_misses += 1;
1682        None
1683    }
1684    #[allow(dead_code)]
1685    pub fn put(&mut self, key: u64, data: Vec<u8>) {
1686        if self.entries.len() >= self.cap {
1687            self.entries.retain(|e| e.2);
1688            if self.entries.len() >= self.cap {
1689                self.entries.remove(0);
1690            }
1691        }
1692        self.entries.push((key, data, true, 0));
1693    }
1694    #[allow(dead_code)]
1695    pub fn invalidate(&mut self) {
1696        for e in self.entries.iter_mut() {
1697            e.2 = false;
1698        }
1699    }
1700    #[allow(dead_code)]
1701    pub fn hit_rate(&self) -> f64 {
1702        let t = self.total_hits + self.total_misses;
1703        if t == 0 {
1704            0.0
1705        } else {
1706            self.total_hits as f64 / t as f64
1707        }
1708    }
1709    #[allow(dead_code)]
1710    pub fn live_count(&self) -> usize {
1711        self.entries.iter().filter(|e| e.2).count()
1712    }
1713}
1714/// Pass-timing record for LuaExt profiler.
1715#[derive(Debug, Clone)]
1716pub struct LuaExtPassTiming {
1717    pub pass_name: String,
1718    pub elapsed_us: u64,
1719    pub items_processed: usize,
1720    pub bytes_before: usize,
1721    pub bytes_after: usize,
1722}
1723impl LuaExtPassTiming {
1724    pub fn new(
1725        pass_name: impl Into<String>,
1726        elapsed_us: u64,
1727        items: usize,
1728        before: usize,
1729        after: usize,
1730    ) -> Self {
1731        LuaExtPassTiming {
1732            pass_name: pass_name.into(),
1733            elapsed_us,
1734            items_processed: items,
1735            bytes_before: before,
1736            bytes_after: after,
1737        }
1738    }
1739    pub fn throughput_mps(&self) -> f64 {
1740        if self.elapsed_us == 0 {
1741            0.0
1742        } else {
1743            self.items_processed as f64 / (self.elapsed_us as f64 / 1_000_000.0)
1744        }
1745    }
1746    pub fn size_ratio(&self) -> f64 {
1747        if self.bytes_before == 0 {
1748            1.0
1749        } else {
1750            self.bytes_after as f64 / self.bytes_before as f64
1751        }
1752    }
1753    pub fn is_profitable(&self) -> bool {
1754        self.size_ratio() <= 1.05
1755    }
1756}
1757/// Pass execution phase for LuaExt.
1758#[allow(dead_code)]
1759#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1760pub enum LuaExtPassPhase {
1761    Early,
1762    Middle,
1763    Late,
1764    Finalize,
1765}
1766impl LuaExtPassPhase {
1767    #[allow(dead_code)]
1768    pub fn is_early(&self) -> bool {
1769        matches!(self, Self::Early)
1770    }
1771    #[allow(dead_code)]
1772    pub fn is_middle(&self) -> bool {
1773        matches!(self, Self::Middle)
1774    }
1775    #[allow(dead_code)]
1776    pub fn is_late(&self) -> bool {
1777        matches!(self, Self::Late)
1778    }
1779    #[allow(dead_code)]
1780    pub fn is_finalize(&self) -> bool {
1781        matches!(self, Self::Finalize)
1782    }
1783    #[allow(dead_code)]
1784    pub fn order(&self) -> u32 {
1785        match self {
1786            Self::Early => 0,
1787            Self::Middle => 1,
1788            Self::Late => 2,
1789            Self::Finalize => 3,
1790        }
1791    }
1792    #[allow(dead_code)]
1793    pub fn from_order(n: u32) -> Option<Self> {
1794        match n {
1795            0 => Some(Self::Early),
1796            1 => Some(Self::Middle),
1797            2 => Some(Self::Late),
1798            3 => Some(Self::Finalize),
1799            _ => None,
1800        }
1801    }
1802}
1803/// A single field in a Lua table constructor.
1804#[derive(Debug, Clone, PartialEq)]
1805pub enum LuaTableField {
1806    /// Positional element: `expr` (auto-indexed)
1807    ArrayItem(LuaExpr),
1808    /// Named field: `key = expr`
1809    NamedField(std::string::String, LuaExpr),
1810    /// Computed-key field: `[key_expr] = expr`
1811    IndexedField(LuaExpr, LuaExpr),
1812}
1813#[allow(dead_code)]
1814#[derive(Debug, Clone)]
1815pub struct LuaAnalysisCache {
1816    pub(super) entries: std::collections::HashMap<String, LuaCacheEntry>,
1817    pub(super) max_size: usize,
1818    pub(super) hits: u64,
1819    pub(super) misses: u64,
1820}
1821impl LuaAnalysisCache {
1822    #[allow(dead_code)]
1823    pub fn new(max_size: usize) -> Self {
1824        LuaAnalysisCache {
1825            entries: std::collections::HashMap::new(),
1826            max_size,
1827            hits: 0,
1828            misses: 0,
1829        }
1830    }
1831    #[allow(dead_code)]
1832    pub fn get(&mut self, key: &str) -> Option<&LuaCacheEntry> {
1833        if self.entries.contains_key(key) {
1834            self.hits += 1;
1835            self.entries.get(key)
1836        } else {
1837            self.misses += 1;
1838            None
1839        }
1840    }
1841    #[allow(dead_code)]
1842    pub fn insert(&mut self, key: String, data: Vec<u8>) {
1843        if self.entries.len() >= self.max_size {
1844            if let Some(oldest) = self.entries.keys().next().cloned() {
1845                self.entries.remove(&oldest);
1846            }
1847        }
1848        self.entries.insert(
1849            key.clone(),
1850            LuaCacheEntry {
1851                key,
1852                data,
1853                timestamp: 0,
1854                valid: true,
1855            },
1856        );
1857    }
1858    #[allow(dead_code)]
1859    pub fn invalidate(&mut self, key: &str) {
1860        if let Some(entry) = self.entries.get_mut(key) {
1861            entry.valid = false;
1862        }
1863    }
1864    #[allow(dead_code)]
1865    pub fn clear(&mut self) {
1866        self.entries.clear();
1867    }
1868    #[allow(dead_code)]
1869    pub fn hit_rate(&self) -> f64 {
1870        let total = self.hits + self.misses;
1871        if total == 0 {
1872            return 0.0;
1873        }
1874        self.hits as f64 / total as f64
1875    }
1876    #[allow(dead_code)]
1877    pub fn size(&self) -> usize {
1878        self.entries.len()
1879    }
1880}
1881#[allow(dead_code)]
1882pub struct LuaConstantFoldingHelper;
1883impl LuaConstantFoldingHelper {
1884    #[allow(dead_code)]
1885    pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
1886        a.checked_add(b)
1887    }
1888    #[allow(dead_code)]
1889    pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
1890        a.checked_sub(b)
1891    }
1892    #[allow(dead_code)]
1893    pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
1894        a.checked_mul(b)
1895    }
1896    #[allow(dead_code)]
1897    pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
1898        if b == 0 {
1899            None
1900        } else {
1901            a.checked_div(b)
1902        }
1903    }
1904    #[allow(dead_code)]
1905    pub fn fold_add_f64(a: f64, b: f64) -> f64 {
1906        a + b
1907    }
1908    #[allow(dead_code)]
1909    pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
1910        a * b
1911    }
1912    #[allow(dead_code)]
1913    pub fn fold_neg_i64(a: i64) -> Option<i64> {
1914        a.checked_neg()
1915    }
1916    #[allow(dead_code)]
1917    pub fn fold_not_bool(a: bool) -> bool {
1918        !a
1919    }
1920    #[allow(dead_code)]
1921    pub fn fold_and_bool(a: bool, b: bool) -> bool {
1922        a && b
1923    }
1924    #[allow(dead_code)]
1925    pub fn fold_or_bool(a: bool, b: bool) -> bool {
1926        a || b
1927    }
1928    #[allow(dead_code)]
1929    pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
1930        a.checked_shl(b)
1931    }
1932    #[allow(dead_code)]
1933    pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
1934        a.checked_shr(b)
1935    }
1936    #[allow(dead_code)]
1937    pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
1938        if b == 0 {
1939            None
1940        } else {
1941            Some(a % b)
1942        }
1943    }
1944    #[allow(dead_code)]
1945    pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
1946        a & b
1947    }
1948    #[allow(dead_code)]
1949    pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
1950        a | b
1951    }
1952    #[allow(dead_code)]
1953    pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
1954        a ^ b
1955    }
1956    #[allow(dead_code)]
1957    pub fn fold_bitnot_i64(a: i64) -> i64 {
1958        !a
1959    }
1960}
1961/// Severity of a LuaExt diagnostic.
1962#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1963pub enum LuaExtDiagSeverity {
1964    Note,
1965    Warning,
1966    Error,
1967}
1968/// A diagnostic message from a LuaExt pass.
1969#[derive(Debug, Clone)]
1970pub struct LuaExtDiagMsg {
1971    pub severity: LuaExtDiagSeverity,
1972    pub pass: String,
1973    pub message: String,
1974}
1975impl LuaExtDiagMsg {
1976    pub fn error(pass: impl Into<String>, msg: impl Into<String>) -> Self {
1977        LuaExtDiagMsg {
1978            severity: LuaExtDiagSeverity::Error,
1979            pass: pass.into(),
1980            message: msg.into(),
1981        }
1982    }
1983    pub fn warning(pass: impl Into<String>, msg: impl Into<String>) -> Self {
1984        LuaExtDiagMsg {
1985            severity: LuaExtDiagSeverity::Warning,
1986            pass: pass.into(),
1987            message: msg.into(),
1988        }
1989    }
1990    pub fn note(pass: impl Into<String>, msg: impl Into<String>) -> Self {
1991        LuaExtDiagMsg {
1992            severity: LuaExtDiagSeverity::Note,
1993            pass: pass.into(),
1994            message: msg.into(),
1995        }
1996    }
1997}
1998/// Dominator tree for LuaExt.
1999#[allow(dead_code)]
2000#[derive(Debug, Clone)]
2001pub struct LuaExtDomTree {
2002    pub(super) idom: Vec<Option<usize>>,
2003    pub(super) children: Vec<Vec<usize>>,
2004    pub(super) depth: Vec<usize>,
2005}
2006impl LuaExtDomTree {
2007    #[allow(dead_code)]
2008    pub fn new(n: usize) -> Self {
2009        Self {
2010            idom: vec![None; n],
2011            children: vec![Vec::new(); n],
2012            depth: vec![0; n],
2013        }
2014    }
2015    #[allow(dead_code)]
2016    pub fn set_idom(&mut self, node: usize, dom: usize) {
2017        if node < self.idom.len() {
2018            self.idom[node] = Some(dom);
2019            if dom < self.children.len() {
2020                self.children[dom].push(node);
2021            }
2022            self.depth[node] = if dom < self.depth.len() {
2023                self.depth[dom] + 1
2024            } else {
2025                1
2026            };
2027        }
2028    }
2029    #[allow(dead_code)]
2030    pub fn dominates(&self, a: usize, mut b: usize) -> bool {
2031        if a == b {
2032            return true;
2033        }
2034        let n = self.idom.len();
2035        for _ in 0..n {
2036            match self.idom.get(b).copied().flatten() {
2037                None => return false,
2038                Some(p) if p == a => return true,
2039                Some(p) if p == b => return false,
2040                Some(p) => b = p,
2041            }
2042        }
2043        false
2044    }
2045    #[allow(dead_code)]
2046    pub fn children_of(&self, n: usize) -> &[usize] {
2047        self.children.get(n).map(|v| v.as_slice()).unwrap_or(&[])
2048    }
2049    #[allow(dead_code)]
2050    pub fn depth_of(&self, n: usize) -> usize {
2051        self.depth.get(n).copied().unwrap_or(0)
2052    }
2053    #[allow(dead_code)]
2054    pub fn lca(&self, mut a: usize, mut b: usize) -> usize {
2055        let n = self.idom.len();
2056        for _ in 0..(2 * n) {
2057            if a == b {
2058                return a;
2059            }
2060            if self.depth_of(a) > self.depth_of(b) {
2061                a = self.idom.get(a).and_then(|x| *x).unwrap_or(a);
2062            } else {
2063                b = self.idom.get(b).and_then(|x| *x).unwrap_or(b);
2064            }
2065        }
2066        0
2067    }
2068}
2069/// Dependency graph for LuaExt.
2070#[allow(dead_code)]
2071#[derive(Debug, Clone)]
2072pub struct LuaExtDepGraph {
2073    pub(super) n: usize,
2074    pub(super) adj: Vec<Vec<usize>>,
2075    pub(super) rev: Vec<Vec<usize>>,
2076    pub(super) edge_count: usize,
2077}
2078impl LuaExtDepGraph {
2079    #[allow(dead_code)]
2080    pub fn new(n: usize) -> Self {
2081        Self {
2082            n,
2083            adj: vec![Vec::new(); n],
2084            rev: vec![Vec::new(); n],
2085            edge_count: 0,
2086        }
2087    }
2088    #[allow(dead_code)]
2089    pub fn add_edge(&mut self, from: usize, to: usize) {
2090        if from < self.n && to < self.n {
2091            if !self.adj[from].contains(&to) {
2092                self.adj[from].push(to);
2093                self.rev[to].push(from);
2094                self.edge_count += 1;
2095            }
2096        }
2097    }
2098    #[allow(dead_code)]
2099    pub fn succs(&self, n: usize) -> &[usize] {
2100        self.adj.get(n).map(|v| v.as_slice()).unwrap_or(&[])
2101    }
2102    #[allow(dead_code)]
2103    pub fn preds(&self, n: usize) -> &[usize] {
2104        self.rev.get(n).map(|v| v.as_slice()).unwrap_or(&[])
2105    }
2106    #[allow(dead_code)]
2107    pub fn topo_sort(&self) -> Option<Vec<usize>> {
2108        let mut deg: Vec<usize> = (0..self.n).map(|i| self.rev[i].len()).collect();
2109        let mut q: std::collections::VecDeque<usize> =
2110            (0..self.n).filter(|&i| deg[i] == 0).collect();
2111        let mut out = Vec::with_capacity(self.n);
2112        while let Some(u) = q.pop_front() {
2113            out.push(u);
2114            for &v in &self.adj[u] {
2115                deg[v] -= 1;
2116                if deg[v] == 0 {
2117                    q.push_back(v);
2118                }
2119            }
2120        }
2121        if out.len() == self.n {
2122            Some(out)
2123        } else {
2124            None
2125        }
2126    }
2127    #[allow(dead_code)]
2128    pub fn has_cycle(&self) -> bool {
2129        self.topo_sort().is_none()
2130    }
2131    #[allow(dead_code)]
2132    pub fn reachable(&self, start: usize) -> Vec<usize> {
2133        let mut vis = vec![false; self.n];
2134        let mut stk = vec![start];
2135        let mut out = Vec::new();
2136        while let Some(u) = stk.pop() {
2137            if u < self.n && !vis[u] {
2138                vis[u] = true;
2139                out.push(u);
2140                for &v in &self.adj[u] {
2141                    if !vis[v] {
2142                        stk.push(v);
2143                    }
2144                }
2145            }
2146        }
2147        out
2148    }
2149    #[allow(dead_code)]
2150    pub fn scc(&self) -> Vec<Vec<usize>> {
2151        let mut visited = vec![false; self.n];
2152        let mut order = Vec::new();
2153        for i in 0..self.n {
2154            if !visited[i] {
2155                let mut stk = vec![(i, 0usize)];
2156                while let Some((u, idx)) = stk.last_mut() {
2157                    if !visited[*u] {
2158                        visited[*u] = true;
2159                    }
2160                    if *idx < self.adj[*u].len() {
2161                        let v = self.adj[*u][*idx];
2162                        *idx += 1;
2163                        if !visited[v] {
2164                            stk.push((v, 0));
2165                        }
2166                    } else {
2167                        order.push(*u);
2168                        stk.pop();
2169                    }
2170                }
2171            }
2172        }
2173        let mut comp = vec![usize::MAX; self.n];
2174        let mut components: Vec<Vec<usize>> = Vec::new();
2175        for &start in order.iter().rev() {
2176            if comp[start] == usize::MAX {
2177                let cid = components.len();
2178                let mut component = Vec::new();
2179                let mut stk = vec![start];
2180                while let Some(u) = stk.pop() {
2181                    if comp[u] == usize::MAX {
2182                        comp[u] = cid;
2183                        component.push(u);
2184                        for &v in &self.rev[u] {
2185                            if comp[v] == usize::MAX {
2186                                stk.push(v);
2187                            }
2188                        }
2189                    }
2190                }
2191                components.push(component);
2192            }
2193        }
2194        components
2195    }
2196    #[allow(dead_code)]
2197    pub fn node_count(&self) -> usize {
2198        self.n
2199    }
2200    #[allow(dead_code)]
2201    pub fn edge_count(&self) -> usize {
2202        self.edge_count
2203    }
2204}