Skip to main content

oxilean_codegen/lua_backend/
functions.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::types::{
9    LuaAnalysisCache, LuaBackend, LuaClass, LuaConstantFoldingHelper, LuaDepGraph,
10    LuaDominatorTree, LuaExpr, LuaExtCache, LuaExtConfig, LuaExtConstFolder, LuaExtDepGraph,
11    LuaExtDiagCollector, LuaExtDiagMsg, LuaExtDomTree, LuaExtEmitStats, LuaExtEventLog,
12    LuaExtFeatures, LuaExtIdGen, LuaExtIncrKey, LuaExtLiveness, LuaExtNameScope, LuaExtPassConfig,
13    LuaExtPassPhase, LuaExtPassRegistry, LuaExtPassStats, LuaExtPassTiming, LuaExtProfiler,
14    LuaExtSourceBuffer, LuaExtVersion, LuaExtWorklist, LuaFunction, LuaLivenessInfo, LuaModule,
15    LuaPassConfig, LuaPassPhase, LuaPassRegistry, LuaPassStats, LuaStmt, LuaTableField, LuaType,
16    LuaWorklist,
17};
18
19pub(super) fn emit_stmts(stmts: &[LuaStmt], indent: usize) -> std::string::String {
20    let pad = "  ".repeat(indent);
21    stmts
22        .iter()
23        .map(|s| format!("{}{}", pad, emit_stmt(s, indent)))
24        .collect::<Vec<_>>()
25        .join("\n")
26}
27pub(super) fn emit_stmt(stmt: &LuaStmt, indent: usize) -> std::string::String {
28    match stmt {
29        LuaStmt::Assign { targets, values } => {
30            let ts: Vec<_> = targets.iter().map(|t| t.to_string()).collect();
31            let vs: Vec<_> = values.iter().map(|v| v.to_string()).collect();
32            format!("{} = {}", ts.join(", "), vs.join(", "))
33        }
34        LuaStmt::LocalAssign {
35            names,
36            attribs,
37            values,
38        } => {
39            let ns: Vec<_> = names
40                .iter()
41                .enumerate()
42                .map(|(i, n)| {
43                    if let Some(Some(attr)) = attribs.get(i) {
44                        format!("{} <{}>", n, attr)
45                    } else {
46                        n.clone()
47                    }
48                })
49                .collect();
50            if values.is_empty() {
51                format!("local {}", ns.join(", "))
52            } else {
53                let vs: Vec<_> = values.iter().map(|v| v.to_string()).collect();
54                format!("local {} = {}", ns.join(", "), vs.join(", "))
55            }
56        }
57        LuaStmt::Do(body) => {
58            format!(
59                "do\n{}\n{}end",
60                emit_stmts(body, indent + 1),
61                "  ".repeat(indent)
62            )
63        }
64        LuaStmt::While { cond, body } => {
65            format!(
66                "while {} do\n{}\n{}end",
67                cond,
68                emit_stmts(body, indent + 1),
69                "  ".repeat(indent)
70            )
71        }
72        LuaStmt::Repeat { body, cond } => {
73            format!(
74                "repeat\n{}\n{}until {}",
75                emit_stmts(body, indent + 1),
76                "  ".repeat(indent),
77                cond
78            )
79        }
80        LuaStmt::If {
81            cond,
82            then_body,
83            elseif_clauses,
84            else_body,
85        } => {
86            let mut out = format!("if {} then\n{}", cond, emit_stmts(then_body, indent + 1));
87            for (ei_cond, ei_body) in elseif_clauses {
88                out.push_str(&format!(
89                    "\n{}elseif {} then\n{}",
90                    "  ".repeat(indent),
91                    ei_cond,
92                    emit_stmts(ei_body, indent + 1)
93                ));
94            }
95            if let Some(eb) = else_body {
96                out.push_str(&format!(
97                    "\n{}else\n{}",
98                    "  ".repeat(indent),
99                    emit_stmts(eb, indent + 1)
100                ));
101            }
102            out.push_str(&format!("\n{}end", "  ".repeat(indent)));
103            out
104        }
105        LuaStmt::For {
106            var,
107            start,
108            limit,
109            step,
110            body,
111        } => {
112            let step_str = step
113                .as_ref()
114                .map(|s| format!(", {}", s))
115                .unwrap_or_default();
116            format!(
117                "for {} = {}, {}{} do\n{}\n{}end",
118                var,
119                start,
120                limit,
121                step_str,
122                emit_stmts(body, indent + 1),
123                "  ".repeat(indent)
124            )
125        }
126        LuaStmt::ForIn { names, exprs, body } => {
127            let es: Vec<_> = exprs.iter().map(|e| e.to_string()).collect();
128            format!(
129                "for {} in {} do\n{}\n{}end",
130                names.join(", "),
131                es.join(", "),
132                emit_stmts(body, indent + 1),
133                "  ".repeat(indent)
134            )
135        }
136        LuaStmt::Function(func) => emit_function(func, indent, false),
137        LuaStmt::Local(func) => emit_function(func, indent, true),
138        LuaStmt::Return(exprs) => {
139            if exprs.is_empty() {
140                "return".to_string()
141            } else {
142                let es: Vec<_> = exprs.iter().map(|e| e.to_string()).collect();
143                format!("return {}", es.join(", "))
144            }
145        }
146        LuaStmt::Break => "break".to_string(),
147        LuaStmt::Goto(label) => format!("goto {}", label),
148        LuaStmt::Label(label) => format!("::{}::", label),
149        LuaStmt::Call(expr) => expr.to_string(),
150    }
151}
152pub(super) fn emit_function(
153    func: &LuaFunction,
154    indent: usize,
155    force_local: bool,
156) -> std::string::String {
157    let local_kw = if force_local || func.is_local {
158        "local "
159    } else {
160        ""
161    };
162    let name_part = func.name.as_deref().unwrap_or("_anon");
163    let sep = if func.is_method { ":" } else { "." };
164    let func_name = if func.is_method {
165        if let Some(dot) = name_part.rfind('.') {
166            format!("{}{}{}", &name_part[..dot], sep, &name_part[dot + 1..])
167        } else {
168            name_part.to_string()
169        }
170    } else {
171        name_part.to_string()
172    };
173    let mut all_params = func.params.clone();
174    if func.vararg {
175        all_params.push("...".to_string());
176    }
177    format!(
178        "{}function {}({})\n{}\n{}end",
179        local_kw,
180        func_name,
181        all_params.join(", "),
182        emit_stmts(&func.body, indent + 1),
183        "  ".repeat(indent)
184    )
185}
186/// Lua reserved keywords.
187pub const LUA_KEYWORDS: &[&str] = &[
188    "and", "break", "do", "else", "elseif", "end", "false", "for", "function", "goto", "if", "in",
189    "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while",
190];
191#[cfg(test)]
192mod tests {
193    use super::*;
194    #[test]
195    pub(super) fn test_lua_type_display_nil() {
196        assert_eq!(LuaType::Nil.to_string(), "nil");
197    }
198    #[test]
199    pub(super) fn test_lua_type_display_boolean() {
200        assert_eq!(LuaType::Boolean.to_string(), "boolean");
201    }
202    #[test]
203    pub(super) fn test_lua_type_display_number_int() {
204        assert_eq!(LuaType::Number(true).to_string(), "integer");
205    }
206    #[test]
207    pub(super) fn test_lua_type_display_number_float() {
208        assert_eq!(LuaType::Number(false).to_string(), "float");
209    }
210    #[test]
211    pub(super) fn test_lua_type_display_custom() {
212        assert_eq!(
213            LuaType::Custom("MyClass".to_string()).to_string(),
214            "MyClass"
215        );
216    }
217    #[test]
218    pub(super) fn test_lua_expr_nil() {
219        assert_eq!(LuaExpr::Nil.to_string(), "nil");
220    }
221    #[test]
222    pub(super) fn test_lua_expr_bool() {
223        assert_eq!(LuaExpr::True.to_string(), "true");
224        assert_eq!(LuaExpr::False.to_string(), "false");
225    }
226    #[test]
227    pub(super) fn test_lua_expr_int() {
228        assert_eq!(LuaExpr::Int(42).to_string(), "42");
229        assert_eq!(LuaExpr::Int(-7).to_string(), "-7");
230    }
231    #[test]
232    pub(super) fn test_lua_expr_float() {
233        assert_eq!(LuaExpr::Float(3.14).to_string(), "3.14");
234        assert_eq!(LuaExpr::Float(1.0).to_string(), "1.0");
235    }
236    #[test]
237    pub(super) fn test_lua_expr_str_escape() {
238        let s = LuaExpr::Str("hello\nworld\"test".to_string());
239        assert_eq!(s.to_string(), r#""hello\nworld\"test""#);
240    }
241    #[test]
242    pub(super) fn test_lua_expr_var() {
243        assert_eq!(LuaExpr::Var("x".to_string()).to_string(), "x");
244    }
245    #[test]
246    pub(super) fn test_lua_expr_binop() {
247        let e = LuaExpr::BinOp {
248            op: "+".to_string(),
249            lhs: Box::new(LuaExpr::Var("a".to_string())),
250            rhs: Box::new(LuaExpr::Int(1)),
251        };
252        assert_eq!(e.to_string(), "(a + 1)");
253    }
254    #[test]
255    pub(super) fn test_lua_expr_unary_not() {
256        let e = LuaExpr::UnaryOp {
257            op: "not".to_string(),
258            operand: Box::new(LuaExpr::True),
259        };
260        assert_eq!(e.to_string(), "(not true)");
261    }
262    #[test]
263    pub(super) fn test_lua_expr_call() {
264        let e = LuaExpr::Call {
265            func: Box::new(LuaExpr::Var("print".to_string())),
266            args: vec![LuaExpr::Str("hi".to_string())],
267        };
268        assert_eq!(e.to_string(), "print(\"hi\")");
269    }
270    #[test]
271    pub(super) fn test_lua_expr_method_call() {
272        let e = LuaExpr::MethodCall {
273            obj: Box::new(LuaExpr::Var("obj".to_string())),
274            method: "greet".to_string(),
275            args: vec![LuaExpr::Str("world".to_string())],
276        };
277        assert_eq!(e.to_string(), "obj:greet(\"world\")");
278    }
279    #[test]
280    pub(super) fn test_lua_expr_table_constructor() {
281        let e = LuaExpr::TableConstructor(vec![
282            LuaTableField::NamedField("x".to_string(), LuaExpr::Int(1)),
283            LuaTableField::ArrayItem(LuaExpr::Int(2)),
284        ]);
285        assert_eq!(e.to_string(), "{x = 1, 2}");
286    }
287    #[test]
288    pub(super) fn test_lua_expr_index_access() {
289        let e = LuaExpr::IndexAccess {
290            table: Box::new(LuaExpr::Var("t".to_string())),
291            key: Box::new(LuaExpr::Int(1)),
292        };
293        assert_eq!(e.to_string(), "t[1]");
294    }
295    #[test]
296    pub(super) fn test_lua_expr_field_access() {
297        let e = LuaExpr::FieldAccess {
298            table: Box::new(LuaExpr::Var("obj".to_string())),
299            field: "name".to_string(),
300        };
301        assert_eq!(e.to_string(), "obj.name");
302    }
303    #[test]
304    pub(super) fn test_lua_expr_ellipsis() {
305        assert_eq!(LuaExpr::Ellipsis.to_string(), "...");
306    }
307    #[test]
308    pub(super) fn test_lua_table_field_indexed() {
309        let f = LuaTableField::IndexedField(LuaExpr::Str("key".to_string()), LuaExpr::Int(99));
310        assert_eq!(f.to_string(), "[\"key\"] = 99");
311    }
312    #[test]
313    pub(super) fn test_lua_stmt_local_assign() {
314        let s = LuaStmt::LocalAssign {
315            names: vec!["x".to_string()],
316            attribs: vec![None],
317            values: vec![LuaExpr::Int(5)],
318        };
319        assert_eq!(s.to_string(), "local x = 5");
320    }
321    #[test]
322    pub(super) fn test_lua_stmt_local_attrib() {
323        let s = LuaStmt::LocalAssign {
324            names: vec!["x".to_string()],
325            attribs: vec![Some("const".to_string())],
326            values: vec![LuaExpr::Int(5)],
327        };
328        assert_eq!(s.to_string(), "local x <const> = 5");
329    }
330    #[test]
331    pub(super) fn test_lua_stmt_assign() {
332        let s = LuaStmt::Assign {
333            targets: vec![LuaExpr::Var("x".to_string())],
334            values: vec![LuaExpr::Int(42)],
335        };
336        assert_eq!(s.to_string(), "x = 42");
337    }
338    #[test]
339    pub(super) fn test_lua_stmt_return_empty() {
340        assert_eq!(LuaStmt::Return(vec![]).to_string(), "return");
341    }
342    #[test]
343    pub(super) fn test_lua_stmt_return_multi() {
344        let s = LuaStmt::Return(vec![LuaExpr::Int(1), LuaExpr::Int(2)]);
345        assert_eq!(s.to_string(), "return 1, 2");
346    }
347    #[test]
348    pub(super) fn test_lua_stmt_break() {
349        assert_eq!(LuaStmt::Break.to_string(), "break");
350    }
351    #[test]
352    pub(super) fn test_lua_stmt_goto_label() {
353        assert_eq!(
354            LuaStmt::Goto("continue".to_string()).to_string(),
355            "goto continue"
356        );
357        assert_eq!(
358            LuaStmt::Label("continue".to_string()).to_string(),
359            "::continue::"
360        );
361    }
362    #[test]
363    pub(super) fn test_lua_function_basic() {
364        let func = LuaFunction::new(
365            "add",
366            vec!["a".to_string(), "b".to_string()],
367            vec![LuaStmt::Return(vec![LuaExpr::BinOp {
368                op: "+".to_string(),
369                lhs: Box::new(LuaExpr::Var("a".to_string())),
370                rhs: Box::new(LuaExpr::Var("b".to_string())),
371            }])],
372        );
373        let s = func.to_string();
374        assert!(s.contains("function add(a, b)"));
375        assert!(s.contains("return (a + b)"));
376        assert!(s.contains("end"));
377    }
378    #[test]
379    pub(super) fn test_lua_function_local() {
380        let func = LuaFunction::new_local(
381            "helper",
382            vec!["x".to_string()],
383            vec![LuaStmt::Return(vec![LuaExpr::Var("x".to_string())])],
384        );
385        let s = format!("{}", LuaStmt::Local(func));
386        assert!(s.starts_with("local function helper(x)"));
387    }
388    #[test]
389    pub(super) fn test_lua_function_vararg() {
390        let func = LuaFunction {
391            name: Some("sum".to_string()),
392            params: vec![],
393            vararg: true,
394            body: vec![LuaStmt::Return(vec![LuaExpr::Ellipsis])],
395            is_local: false,
396            is_method: false,
397        };
398        let s = func.to_string();
399        assert!(s.contains("function sum(...)"));
400    }
401    #[test]
402    pub(super) fn test_lua_class_emit() {
403        let cls = LuaClass::new("Animal");
404        let s = cls.emit();
405        assert!(s.contains("Animal = {}"));
406        assert!(s.contains("Animal.__index = Animal"));
407        assert!(s.contains("function Animal:new(o)"));
408    }
409    #[test]
410    pub(super) fn test_lua_class_with_tostring() {
411        let mut cls = LuaClass::new("Dog");
412        cls.tostring_body = Some(vec![LuaStmt::Return(vec![LuaExpr::Str("Dog".to_string())])]);
413        let s = cls.emit();
414        assert!(s.contains("Dog.__tostring"));
415        assert!(s.contains("return \"Dog\""));
416    }
417    #[test]
418    pub(super) fn test_lua_module_emit_empty() {
419        let m = LuaModule::new();
420        assert_eq!(m.emit(), "");
421    }
422    #[test]
423    pub(super) fn test_lua_module_emit_with_require() {
424        let mut m = LuaModule::new();
425        m.requires.push(("json".to_string(), "dkjson".to_string()));
426        let s = m.emit();
427        assert!(s.contains("local json = require(\"dkjson\")"));
428    }
429    #[test]
430    pub(super) fn test_lua_module_emit_main_block() {
431        let mut m = LuaModule::new();
432        m.main_block.push(LuaStmt::Call(LuaExpr::Call {
433            func: Box::new(LuaExpr::Var("print".to_string())),
434            args: vec![LuaExpr::Str("hello".to_string())],
435        }));
436        let s = m.emit();
437        assert!(s.contains("print(\"hello\")"));
438    }
439    #[test]
440    pub(super) fn test_mangle_name_dot() {
441        let mut b = LuaBackend::new();
442        assert_eq!(b.mangle_name("Nat.add"), "Nat_add");
443    }
444    #[test]
445    pub(super) fn test_mangle_name_keyword() {
446        let mut b = LuaBackend::new();
447        assert_eq!(b.mangle_name("and"), "_and");
448        assert_eq!(b.mangle_name("end"), "_end");
449    }
450    #[test]
451    pub(super) fn test_mangle_name_empty() {
452        let mut b = LuaBackend::new();
453        assert_eq!(b.mangle_name(""), "_anon");
454    }
455    #[test]
456    pub(super) fn test_fresh_var() {
457        let mut b = LuaBackend::new();
458        assert_eq!(b.fresh_var(), "_t0");
459        assert_eq!(b.fresh_var(), "_t1");
460    }
461    #[test]
462    pub(super) fn test_compile_decl_simple() {
463        let decl = LcnfFunDecl {
464            name: "answer".to_string(),
465            original_name: None,
466            params: vec![],
467            ret_type: LcnfType::Nat,
468            body: LcnfExpr::Return(LcnfArg::Lit(LcnfLit::Nat(42))),
469            is_recursive: false,
470            is_lifted: false,
471            inline_cost: 0,
472        };
473        let mut b = LuaBackend::new();
474        let func = b
475            .compile_decl(&decl)
476            .expect("func compilation should succeed");
477        assert_eq!(func.name, Some("answer".to_string()));
478        let s = func.to_string();
479        assert!(s.contains("return 42"), "Expected return 42, got: {}", s);
480    }
481    #[test]
482    pub(super) fn test_compile_decl_with_param() {
483        let x_id = LcnfVarId(0);
484        let decl = LcnfFunDecl {
485            name: "identity".to_string(),
486            original_name: None,
487            params: vec![LcnfParam {
488                id: x_id,
489                name: "x".to_string(),
490                ty: LcnfType::Nat,
491                erased: false,
492                borrowed: false,
493            }],
494            ret_type: LcnfType::Nat,
495            body: LcnfExpr::Return(LcnfArg::Var(x_id)),
496            is_recursive: false,
497            is_lifted: false,
498            inline_cost: 0,
499        };
500        let mut b = LuaBackend::new();
501        let func = b
502            .compile_decl(&decl)
503            .expect("func compilation should succeed");
504        let s = func.to_string();
505        assert!(s.contains("function identity("), "Got: {}", s);
506        assert!(s.contains("return"), "Got: {}", s);
507    }
508    #[test]
509    pub(super) fn test_emit_module_multiple_decls() {
510        let decl1 = LcnfFunDecl {
511            name: "one".to_string(),
512            original_name: None,
513            params: vec![],
514            ret_type: LcnfType::Nat,
515            body: LcnfExpr::Return(LcnfArg::Lit(LcnfLit::Nat(1))),
516            is_recursive: false,
517            is_lifted: false,
518            inline_cost: 0,
519        };
520        let decl2 = LcnfFunDecl {
521            name: "two".to_string(),
522            original_name: None,
523            params: vec![],
524            ret_type: LcnfType::Nat,
525            body: LcnfExpr::Return(LcnfArg::Lit(LcnfLit::Nat(2))),
526            is_recursive: false,
527            is_lifted: false,
528            inline_cost: 0,
529        };
530        let mut b = LuaBackend::new();
531        let module = b.emit_module(&[decl1, decl2]);
532        assert_eq!(module.functions.len(), 2);
533        let s = module.emit();
534        assert!(s.contains("function one()"));
535        assert!(s.contains("function two()"));
536    }
537    #[test]
538    pub(super) fn test_for_loop_emit() {
539        let s = LuaStmt::For {
540            var: "i".to_string(),
541            start: LuaExpr::Int(1),
542            limit: LuaExpr::Int(10),
543            step: None,
544            body: vec![LuaStmt::Break],
545        };
546        let out = s.to_string();
547        assert!(out.contains("for i = 1, 10 do"));
548        assert!(out.contains("break"));
549        assert!(out.contains("end"));
550    }
551    #[test]
552    pub(super) fn test_for_loop_with_step() {
553        let s = LuaStmt::For {
554            var: "i".to_string(),
555            start: LuaExpr::Int(10),
556            limit: LuaExpr::Int(1),
557            step: Some(LuaExpr::Int(-1)),
558            body: vec![],
559        };
560        let out = s.to_string();
561        assert!(out.contains("for i = 10, 1, -1 do"));
562    }
563    #[test]
564    pub(super) fn test_for_in_emit() {
565        let s = LuaStmt::ForIn {
566            names: vec!["k".to_string(), "v".to_string()],
567            exprs: vec![LuaExpr::Call {
568                func: Box::new(LuaExpr::Var("pairs".to_string())),
569                args: vec![LuaExpr::Var("t".to_string())],
570            }],
571            body: vec![],
572        };
573        let out = s.to_string();
574        assert!(out.contains("for k, v in pairs(t) do"));
575    }
576    #[test]
577    pub(super) fn test_repeat_until_emit() {
578        let s = LuaStmt::Repeat {
579            body: vec![LuaStmt::Call(LuaExpr::Call {
580                func: Box::new(LuaExpr::Var("tick".to_string())),
581                args: vec![],
582            })],
583            cond: LuaExpr::Var("done".to_string()),
584        };
585        let out = s.to_string();
586        assert!(out.contains("repeat"));
587        assert!(out.contains("until done"));
588    }
589    #[test]
590    pub(super) fn test_do_block_emit() {
591        let s = LuaStmt::Do(vec![LuaStmt::LocalAssign {
592            names: vec!["x".to_string()],
593            attribs: vec![None],
594            values: vec![LuaExpr::Int(1)],
595        }]);
596        let out = s.to_string();
597        assert!(out.contains("do"));
598        assert!(out.contains("local x = 1"));
599        assert!(out.contains("end"));
600    }
601}
602#[cfg(test)]
603mod tests_lua_ext_extra {
604    use super::*;
605    #[test]
606    pub(super) fn test_lua_ext_config() {
607        let mut cfg = LuaExtConfig::new();
608        cfg.set("mode", "release");
609        cfg.set("verbose", "true");
610        assert_eq!(cfg.get("mode"), Some("release"));
611        assert!(cfg.get_bool("verbose"));
612        assert!(cfg.get_int("mode").is_none());
613        assert_eq!(cfg.len(), 2);
614    }
615    #[test]
616    pub(super) fn test_lua_ext_source_buffer() {
617        let mut buf = LuaExtSourceBuffer::new();
618        buf.push_line("fn main() {");
619        buf.indent();
620        buf.push_line("println!(\"hello\");");
621        buf.dedent();
622        buf.push_line("}");
623        assert!(buf.as_str().contains("fn main()"));
624        assert!(buf.as_str().contains("    println!"));
625        assert_eq!(buf.line_count(), 3);
626        buf.reset();
627        assert!(buf.is_empty());
628    }
629    #[test]
630    pub(super) fn test_lua_ext_name_scope() {
631        let mut scope = LuaExtNameScope::new();
632        assert!(scope.declare("x"));
633        assert!(!scope.declare("x"));
634        assert!(scope.is_declared("x"));
635        let scope = scope.push_scope();
636        assert_eq!(scope.depth(), 1);
637        let mut scope = scope.pop_scope();
638        assert_eq!(scope.depth(), 0);
639        scope.declare("y");
640        assert_eq!(scope.len(), 2);
641    }
642    #[test]
643    pub(super) fn test_lua_ext_diag_collector() {
644        let mut col = LuaExtDiagCollector::new();
645        col.emit(LuaExtDiagMsg::warning("pass_a", "slow"));
646        col.emit(LuaExtDiagMsg::error("pass_b", "fatal"));
647        assert!(col.has_errors());
648        assert_eq!(col.errors().len(), 1);
649        assert_eq!(col.warnings().len(), 1);
650        col.clear();
651        assert!(col.is_empty());
652    }
653    #[test]
654    pub(super) fn test_lua_ext_id_gen() {
655        let mut gen = LuaExtIdGen::new();
656        assert_eq!(gen.next_id(), 0);
657        assert_eq!(gen.next_id(), 1);
658        gen.skip(10);
659        assert_eq!(gen.next_id(), 12);
660        gen.reset();
661        assert_eq!(gen.peek_next(), 0);
662    }
663    #[test]
664    pub(super) fn test_lua_ext_incr_key() {
665        let k1 = LuaExtIncrKey::new(100, 200);
666        let k2 = LuaExtIncrKey::new(100, 200);
667        let k3 = LuaExtIncrKey::new(999, 200);
668        assert!(k1.matches(&k2));
669        assert!(!k1.matches(&k3));
670    }
671    #[test]
672    pub(super) fn test_lua_ext_profiler() {
673        let mut p = LuaExtProfiler::new();
674        p.record(LuaExtPassTiming::new("pass_a", 1000, 50, 200, 100));
675        p.record(LuaExtPassTiming::new("pass_b", 500, 30, 100, 200));
676        assert_eq!(p.total_elapsed_us(), 1500);
677        assert_eq!(
678            p.slowest_pass()
679                .expect("slowest pass should exist")
680                .pass_name,
681            "pass_a"
682        );
683        assert_eq!(p.profitable_passes().len(), 1);
684    }
685    #[test]
686    pub(super) fn test_lua_ext_event_log() {
687        let mut log = LuaExtEventLog::new(3);
688        log.push("event1");
689        log.push("event2");
690        log.push("event3");
691        assert_eq!(log.len(), 3);
692        log.push("event4");
693        assert_eq!(log.len(), 3);
694        assert_eq!(
695            log.iter()
696                .next()
697                .expect("iterator should have next element"),
698            "event2"
699        );
700    }
701    #[test]
702    pub(super) fn test_lua_ext_version() {
703        let v = LuaExtVersion::new(1, 2, 3).with_pre("alpha");
704        assert!(!v.is_stable());
705        assert_eq!(format!("{}", v), "1.2.3-alpha");
706        let stable = LuaExtVersion::new(2, 0, 0);
707        assert!(stable.is_stable());
708        assert!(stable.is_compatible_with(&LuaExtVersion::new(2, 0, 0)));
709        assert!(!stable.is_compatible_with(&LuaExtVersion::new(3, 0, 0)));
710    }
711    #[test]
712    pub(super) fn test_lua_ext_features() {
713        let mut f = LuaExtFeatures::new();
714        f.enable("sse2");
715        f.enable("avx2");
716        assert!(f.is_enabled("sse2"));
717        assert!(!f.is_enabled("avx512"));
718        f.disable("avx2");
719        assert!(!f.is_enabled("avx2"));
720        let mut g = LuaExtFeatures::new();
721        g.enable("sse2");
722        g.enable("neon");
723        let union = f.union(&g);
724        assert!(union.is_enabled("sse2") && union.is_enabled("neon"));
725        let inter = f.intersection(&g);
726        assert!(inter.is_enabled("sse2"));
727    }
728    #[test]
729    pub(super) fn test_lua_ext_emit_stats() {
730        let mut s = LuaExtEmitStats::new();
731        s.bytes_emitted = 50_000;
732        s.items_emitted = 500;
733        s.elapsed_ms = 100;
734        assert!(s.is_clean());
735        assert!((s.throughput_bps() - 500_000.0).abs() < 1.0);
736        let disp = format!("{}", s);
737        assert!(disp.contains("bytes=50000"));
738    }
739}
740#[cfg(test)]
741mod Lua_infra_tests {
742    use super::*;
743    #[test]
744    pub(super) fn test_pass_config() {
745        let config = LuaPassConfig::new("test_pass", LuaPassPhase::Transformation);
746        assert!(config.enabled);
747        assert!(config.phase.is_modifying());
748        assert_eq!(config.phase.name(), "transformation");
749    }
750    #[test]
751    pub(super) fn test_pass_stats() {
752        let mut stats = LuaPassStats::new();
753        stats.record_run(10, 100, 3);
754        stats.record_run(20, 200, 5);
755        assert_eq!(stats.total_runs, 2);
756        assert!((stats.average_changes_per_run() - 15.0).abs() < 0.01);
757        assert!((stats.success_rate() - 1.0).abs() < 0.01);
758        let s = stats.format_summary();
759        assert!(s.contains("Runs: 2/2"));
760    }
761    #[test]
762    pub(super) fn test_pass_registry() {
763        let mut reg = LuaPassRegistry::new();
764        reg.register(LuaPassConfig::new("pass_a", LuaPassPhase::Analysis));
765        reg.register(LuaPassConfig::new("pass_b", LuaPassPhase::Transformation).disabled());
766        assert_eq!(reg.total_passes(), 2);
767        assert_eq!(reg.enabled_count(), 1);
768        reg.update_stats("pass_a", 5, 50, 2);
769        let stats = reg.get_stats("pass_a").expect("stats should exist");
770        assert_eq!(stats.total_changes, 5);
771    }
772    #[test]
773    pub(super) fn test_analysis_cache() {
774        let mut cache = LuaAnalysisCache::new(10);
775        cache.insert("key1".to_string(), vec![1, 2, 3]);
776        assert!(cache.get("key1").is_some());
777        assert!(cache.get("key2").is_none());
778        assert!((cache.hit_rate() - 0.5).abs() < 0.01);
779        cache.invalidate("key1");
780        assert!(!cache.entries["key1"].valid);
781        assert_eq!(cache.size(), 1);
782    }
783    #[test]
784    pub(super) fn test_worklist() {
785        let mut wl = LuaWorklist::new();
786        assert!(wl.push(1));
787        assert!(wl.push(2));
788        assert!(!wl.push(1));
789        assert_eq!(wl.len(), 2);
790        assert_eq!(wl.pop(), Some(1));
791        assert!(!wl.contains(1));
792        assert!(wl.contains(2));
793    }
794    #[test]
795    pub(super) fn test_dominator_tree() {
796        let mut dt = LuaDominatorTree::new(5);
797        dt.set_idom(1, 0);
798        dt.set_idom(2, 0);
799        dt.set_idom(3, 1);
800        assert!(dt.dominates(0, 3));
801        assert!(dt.dominates(1, 3));
802        assert!(!dt.dominates(2, 3));
803        assert!(dt.dominates(3, 3));
804    }
805    #[test]
806    pub(super) fn test_liveness() {
807        let mut liveness = LuaLivenessInfo::new(3);
808        liveness.add_def(0, 1);
809        liveness.add_use(1, 1);
810        assert!(liveness.defs[0].contains(&1));
811        assert!(liveness.uses[1].contains(&1));
812    }
813    #[test]
814    pub(super) fn test_constant_folding() {
815        assert_eq!(LuaConstantFoldingHelper::fold_add_i64(3, 4), Some(7));
816        assert_eq!(LuaConstantFoldingHelper::fold_div_i64(10, 0), None);
817        assert_eq!(LuaConstantFoldingHelper::fold_div_i64(10, 2), Some(5));
818        assert_eq!(
819            LuaConstantFoldingHelper::fold_bitand_i64(0b1100, 0b1010),
820            0b1000
821        );
822        assert_eq!(LuaConstantFoldingHelper::fold_bitnot_i64(0), -1);
823    }
824    #[test]
825    pub(super) fn test_dep_graph() {
826        let mut g = LuaDepGraph::new();
827        g.add_dep(1, 2);
828        g.add_dep(2, 3);
829        g.add_dep(1, 3);
830        assert_eq!(g.dependencies_of(2), vec![1]);
831        let topo = g.topological_sort();
832        assert_eq!(topo.len(), 3);
833        assert!(!g.has_cycle());
834        let pos: std::collections::HashMap<u32, usize> =
835            topo.iter().enumerate().map(|(i, &n)| (n, i)).collect();
836        assert!(pos[&1] < pos[&2]);
837        assert!(pos[&1] < pos[&3]);
838        assert!(pos[&2] < pos[&3]);
839    }
840}
841#[cfg(test)]
842mod luaext_pass_tests {
843    use super::*;
844    #[test]
845    pub(super) fn test_luaext_phase_order() {
846        assert_eq!(LuaExtPassPhase::Early.order(), 0);
847        assert_eq!(LuaExtPassPhase::Middle.order(), 1);
848        assert_eq!(LuaExtPassPhase::Late.order(), 2);
849        assert_eq!(LuaExtPassPhase::Finalize.order(), 3);
850        assert!(LuaExtPassPhase::Early.is_early());
851        assert!(!LuaExtPassPhase::Early.is_late());
852    }
853    #[test]
854    pub(super) fn test_luaext_config_builder() {
855        let c = LuaExtPassConfig::new("p")
856            .with_phase(LuaExtPassPhase::Late)
857            .with_max_iter(50)
858            .with_debug(1);
859        assert_eq!(c.name, "p");
860        assert_eq!(c.max_iterations, 50);
861        assert!(c.is_debug_enabled());
862        assert!(c.enabled);
863        let c2 = c.disabled();
864        assert!(!c2.enabled);
865    }
866    #[test]
867    pub(super) fn test_luaext_stats() {
868        let mut s = LuaExtPassStats::new();
869        s.visit();
870        s.visit();
871        s.modify();
872        s.iterate();
873        assert_eq!(s.nodes_visited, 2);
874        assert_eq!(s.nodes_modified, 1);
875        assert!(s.changed);
876        assert_eq!(s.iterations, 1);
877        let e = s.efficiency();
878        assert!((e - 0.5).abs() < 1e-9);
879    }
880    #[test]
881    pub(super) fn test_luaext_registry() {
882        let mut r = LuaExtPassRegistry::new();
883        r.register(LuaExtPassConfig::new("a").with_phase(LuaExtPassPhase::Early));
884        r.register(LuaExtPassConfig::new("b").disabled());
885        assert_eq!(r.len(), 2);
886        assert_eq!(r.enabled_passes().len(), 1);
887        assert_eq!(r.passes_in_phase(&LuaExtPassPhase::Early).len(), 1);
888    }
889    #[test]
890    pub(super) fn test_luaext_cache() {
891        let mut c = LuaExtCache::new(4);
892        assert!(c.get(99).is_none());
893        c.put(99, vec![1, 2, 3]);
894        let v = c.get(99).expect("v should be present in map");
895        assert_eq!(v, &[1u8, 2, 3]);
896        assert!(c.hit_rate() > 0.0);
897        assert_eq!(c.live_count(), 1);
898    }
899    #[test]
900    pub(super) fn test_luaext_worklist() {
901        let mut w = LuaExtWorklist::new(10);
902        w.push(5);
903        w.push(3);
904        w.push(5);
905        assert_eq!(w.len(), 2);
906        assert!(w.contains(5));
907        let first = w.pop().expect("first should be available to pop");
908        assert!(!w.contains(first));
909    }
910    #[test]
911    pub(super) fn test_luaext_dom_tree() {
912        let mut dt = LuaExtDomTree::new(5);
913        dt.set_idom(1, 0);
914        dt.set_idom(2, 0);
915        dt.set_idom(3, 1);
916        dt.set_idom(4, 1);
917        assert!(dt.dominates(0, 3));
918        assert!(dt.dominates(1, 4));
919        assert!(!dt.dominates(2, 3));
920        assert_eq!(dt.depth_of(3), 2);
921    }
922    #[test]
923    pub(super) fn test_luaext_liveness() {
924        let mut lv = LuaExtLiveness::new(3);
925        lv.add_def(0, 1);
926        lv.add_use(1, 1);
927        assert!(lv.var_is_def_in_block(0, 1));
928        assert!(lv.var_is_used_in_block(1, 1));
929        assert!(!lv.var_is_def_in_block(1, 1));
930    }
931    #[test]
932    pub(super) fn test_luaext_const_folder() {
933        let mut cf = LuaExtConstFolder::new();
934        assert_eq!(cf.add_i64(3, 4), Some(7));
935        assert_eq!(cf.div_i64(10, 0), None);
936        assert_eq!(cf.mul_i64(6, 7), Some(42));
937        assert_eq!(cf.and_i64(0b1100, 0b1010), 0b1000);
938        assert_eq!(cf.fold_count(), 3);
939        assert_eq!(cf.failure_count(), 1);
940    }
941    #[test]
942    pub(super) fn test_luaext_dep_graph() {
943        let mut g = LuaExtDepGraph::new(4);
944        g.add_edge(0, 1);
945        g.add_edge(1, 2);
946        g.add_edge(2, 3);
947        assert!(!g.has_cycle());
948        assert_eq!(g.topo_sort(), Some(vec![0, 1, 2, 3]));
949        assert_eq!(g.reachable(0).len(), 4);
950        let sccs = g.scc();
951        assert_eq!(sccs.len(), 4);
952    }
953}