Skip to main content

oxilean_codegen/matlab_backend/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use std::collections::HashMap;
6
7use super::types::{
8    MatlabAnalysisCache, MatlabBackend, MatlabClassdef, MatlabConstantFoldingHelper,
9    MatlabDepGraph, MatlabDominatorTree, MatlabExpr, MatlabFunction, MatlabLiteral,
10    MatlabLivenessInfo, MatlabParam, MatlabPassConfig, MatlabPassPhase, MatlabPassRegistry,
11    MatlabPassStats, MatlabProperty, MatlabStmt, MatlabType, MatlabWorklist, PropAccess,
12};
13
14#[cfg(test)]
15mod tests {
16    use super::*;
17    #[test]
18    pub(super) fn test_matlab_literal_emission() {
19        let backend = MatlabBackend::new();
20        assert_eq!(backend.emit_literal(&MatlabLiteral::Double(3.0)), "3");
21        assert_eq!(backend.emit_literal(&MatlabLiteral::Double(3.14)), "3.14");
22        assert_eq!(backend.emit_literal(&MatlabLiteral::Integer(42)), "42");
23        assert_eq!(backend.emit_literal(&MatlabLiteral::Logical(true)), "true");
24        assert_eq!(
25            backend.emit_literal(&MatlabLiteral::Logical(false)),
26            "false"
27        );
28        assert_eq!(
29            backend.emit_literal(&MatlabLiteral::Char("hello".to_string())),
30            "'hello'"
31        );
32        assert_eq!(
33            backend.emit_literal(&MatlabLiteral::Str("world".to_string())),
34            "\"world\""
35        );
36        assert_eq!(backend.emit_literal(&MatlabLiteral::Empty), "[]");
37        assert_eq!(backend.emit_literal(&MatlabLiteral::NaN), "NaN");
38        assert_eq!(backend.emit_literal(&MatlabLiteral::Inf(false)), "Inf");
39        assert_eq!(backend.emit_literal(&MatlabLiteral::Inf(true)), "-Inf");
40        assert_eq!(backend.emit_literal(&MatlabLiteral::Pi), "pi");
41    }
42    #[test]
43    pub(super) fn test_matlab_matrix_literal() {
44        let backend = MatlabBackend::new();
45        let mat = MatlabExpr::MatrixLit(vec![
46            vec![
47                MatlabExpr::Lit(MatlabLiteral::Integer(1)),
48                MatlabExpr::Lit(MatlabLiteral::Integer(2)),
49            ],
50            vec![
51                MatlabExpr::Lit(MatlabLiteral::Integer(3)),
52                MatlabExpr::Lit(MatlabLiteral::Integer(4)),
53            ],
54        ]);
55        assert_eq!(backend.emit_expr_pure(&mat), "[1, 2; 3, 4]");
56        let vec_row = MatlabExpr::MatrixLit(vec![vec![
57            MatlabExpr::Lit(MatlabLiteral::Integer(1)),
58            MatlabExpr::Lit(MatlabLiteral::Integer(2)),
59            MatlabExpr::Lit(MatlabLiteral::Integer(3)),
60        ]]);
61        assert_eq!(backend.emit_expr_pure(&vec_row), "[1, 2, 3]");
62    }
63    #[test]
64    pub(super) fn test_matlab_colon_range() {
65        let backend = MatlabBackend::new();
66        let simple = MatlabExpr::ColonRange {
67            start: Box::new(MatlabExpr::Lit(MatlabLiteral::Integer(1))),
68            step: None,
69            end: Box::new(MatlabExpr::Lit(MatlabLiteral::Integer(10))),
70        };
71        assert_eq!(backend.emit_expr_pure(&simple), "1:10");
72        let stepped = MatlabExpr::ColonRange {
73            start: Box::new(MatlabExpr::Lit(MatlabLiteral::Integer(0))),
74            step: Some(Box::new(MatlabExpr::Lit(MatlabLiteral::Double(0.1)))),
75            end: Box::new(MatlabExpr::Lit(MatlabLiteral::Integer(1))),
76        };
77        assert_eq!(backend.emit_expr_pure(&stepped), "0:0.1:1");
78    }
79    #[test]
80    pub(super) fn test_matlab_binary_unary_ops() {
81        let backend = MatlabBackend::new();
82        let add = MatlabExpr::BinaryOp(
83            "+".to_string(),
84            Box::new(MatlabExpr::Var("a".to_string())),
85            Box::new(MatlabExpr::Var("b".to_string())),
86        );
87        assert_eq!(backend.emit_expr_pure(&add), "a + b");
88        let elem_mul = MatlabExpr::BinaryOp(
89            ".*".to_string(),
90            Box::new(MatlabExpr::Var("A".to_string())),
91            Box::new(MatlabExpr::Var("B".to_string())),
92        );
93        assert_eq!(backend.emit_expr_pure(&elem_mul), "A .* B");
94        let transpose = MatlabExpr::UnaryOp(
95            "'".to_string(),
96            Box::new(MatlabExpr::Var("M".to_string())),
97            true,
98        );
99        assert_eq!(backend.emit_expr_pure(&transpose), "M'");
100        let neg = MatlabExpr::UnaryOp(
101            "-".to_string(),
102            Box::new(MatlabExpr::Var("x".to_string())),
103            false,
104        );
105        assert_eq!(backend.emit_expr_pure(&neg), "-x");
106    }
107    #[test]
108    pub(super) fn test_matlab_anon_func_and_index() {
109        let backend = MatlabBackend::new();
110        let anon = MatlabExpr::AnonFunc(
111            vec!["x".to_string(), "y".to_string()],
112            Box::new(MatlabExpr::BinaryOp(
113                "+".to_string(),
114                Box::new(MatlabExpr::Var("x".to_string())),
115                Box::new(MatlabExpr::Var("y".to_string())),
116            )),
117        );
118        assert_eq!(backend.emit_expr_pure(&anon), "@(x, y) x + y");
119        let index = MatlabExpr::Index {
120            obj: Box::new(MatlabExpr::Var("A".to_string())),
121            indices: vec![
122                MatlabExpr::Lit(MatlabLiteral::Integer(2)),
123                MatlabExpr::Lit(MatlabLiteral::Integer(3)),
124            ],
125            cell_index: false,
126        };
127        assert_eq!(backend.emit_expr_pure(&index), "A(2, 3)");
128        let cell_index = MatlabExpr::Index {
129            obj: Box::new(MatlabExpr::Var("C".to_string())),
130            indices: vec![MatlabExpr::Lit(MatlabLiteral::Integer(1))],
131            cell_index: true,
132        };
133        assert_eq!(backend.emit_expr_pure(&cell_index), "C{1}");
134    }
135    #[test]
136    pub(super) fn test_matlab_for_while_loops() {
137        let mut backend = MatlabBackend::new();
138        let for_loop = MatlabStmt::ForLoop {
139            var: "i".to_string(),
140            range: MatlabExpr::ColonRange {
141                start: Box::new(MatlabExpr::Lit(MatlabLiteral::Integer(1))),
142                step: None,
143                end: Box::new(MatlabExpr::Lit(MatlabLiteral::Integer(10))),
144            },
145            body: vec![MatlabStmt::Comment("loop body".to_string())],
146        };
147        backend.emit_stmt(&for_loop);
148        let out = backend.take_output();
149        assert!(out.contains("for i = 1:10"), "missing for: {}", out);
150        assert!(out.contains("end"), "missing end: {}", out);
151        let while_loop = MatlabStmt::WhileLoop {
152            cond: MatlabExpr::Var("running".to_string()),
153            body: vec![MatlabStmt::Break],
154        };
155        backend.emit_stmt(&while_loop);
156        let out2 = backend.take_output();
157        assert!(out2.contains("while running"), "missing while: {}", out2);
158        assert!(out2.contains("break"), "missing break: {}", out2);
159    }
160    #[test]
161    pub(super) fn test_matlab_function_emit() {
162        let mut backend = MatlabBackend::new();
163        let fun = MatlabFunction::new(
164            "add_vectors",
165            vec![MatlabParam::required("a"), MatlabParam::required("b")],
166            vec!["result".to_string()],
167            vec![MatlabStmt::Assign {
168                lhs: vec!["result".to_string()],
169                rhs: MatlabExpr::BinaryOp(
170                    "+".to_string(),
171                    Box::new(MatlabExpr::Var("a".to_string())),
172                    Box::new(MatlabExpr::Var("b".to_string())),
173                ),
174                suppress: true,
175            }],
176        );
177        backend.emit_function(&fun);
178        let out = backend.take_output();
179        assert!(
180            out.contains("function result = add_vectors(a, b)"),
181            "missing header: {}",
182            out
183        );
184        assert!(out.contains("result = a + b;"), "missing body: {}", out);
185        assert!(out.contains("end"), "missing end: {}", out);
186    }
187    #[test]
188    pub(super) fn test_matlab_if_switch() {
189        let mut backend = MatlabBackend::new();
190        let if_stmt = MatlabStmt::IfElseIf {
191            cond: MatlabExpr::BinaryOp(
192                ">".to_string(),
193                Box::new(MatlabExpr::Var("x".to_string())),
194                Box::new(MatlabExpr::Lit(MatlabLiteral::Integer(0))),
195            ),
196            then_body: vec![MatlabStmt::Return],
197            elseif_branches: vec![],
198            else_body: Some(vec![MatlabStmt::Break]),
199        };
200        backend.emit_stmt(&if_stmt);
201        let out = backend.take_output();
202        assert!(out.contains("if x > 0"), "missing if: {}", out);
203        assert!(out.contains("return;"), "missing return: {}", out);
204        assert!(out.contains("else"), "missing else: {}", out);
205        assert!(out.contains("break;"), "missing break: {}", out);
206        let switch_stmt = MatlabStmt::SwitchCase {
207            expr: MatlabExpr::Var("mode".to_string()),
208            cases: vec![
209                (
210                    MatlabExpr::Lit(MatlabLiteral::Char("fast".to_string())),
211                    vec![MatlabStmt::Comment("fast path".to_string())],
212                ),
213                (
214                    MatlabExpr::Lit(MatlabLiteral::Char("slow".to_string())),
215                    vec![MatlabStmt::Comment("slow path".to_string())],
216                ),
217            ],
218            otherwise: Some(vec![MatlabStmt::Error(
219                MatlabExpr::Lit(MatlabLiteral::Char("Unknown mode".to_string())),
220                vec![],
221            )]),
222        };
223        backend.emit_stmt(&switch_stmt);
224        let out2 = backend.take_output();
225        assert!(out2.contains("switch mode"), "missing switch: {}", out2);
226        assert!(out2.contains("case 'fast'"), "missing case: {}", out2);
227        assert!(out2.contains("otherwise"), "missing otherwise: {}", out2);
228    }
229    #[test]
230    pub(super) fn test_matlab_classdef_emit() {
231        let mut backend = MatlabBackend::new();
232        let mut cls = MatlabClassdef::new("Vehicle").inherits("handle");
233        cls.properties.push(MatlabProperty {
234            name: "speed".to_string(),
235            ty: Some(MatlabType::Double),
236            default: Some(MatlabExpr::Lit(MatlabLiteral::Double(0.0))),
237            access: PropAccess::Public,
238            is_constant: false,
239            is_dependent: false,
240        });
241        cls.methods.push(MatlabFunction::new(
242            "accelerate",
243            vec![MatlabParam::required("obj"), MatlabParam::required("delta")],
244            vec![],
245            vec![MatlabStmt::AssignField {
246                obj: "obj".to_string(),
247                field: "speed".to_string(),
248                rhs: MatlabExpr::BinaryOp(
249                    "+".to_string(),
250                    Box::new(MatlabExpr::FieldAccess(
251                        Box::new(MatlabExpr::Var("obj".to_string())),
252                        "speed".to_string(),
253                    )),
254                    Box::new(MatlabExpr::Var("delta".to_string())),
255                ),
256                suppress: true,
257            }],
258        ));
259        backend.emit_classdef(&cls);
260        let out = backend.take_output();
261        assert!(
262            out.contains("classdef Vehicle < handle"),
263            "missing classdef: {}",
264            out
265        );
266        assert!(out.contains("properties"), "missing properties: {}", out);
267        assert!(out.contains("methods"), "missing methods: {}", out);
268        assert!(
269            out.contains("function accelerate(obj, delta)"),
270            "missing method: {}",
271            out
272        );
273    }
274}
275#[cfg(test)]
276mod Matlab_infra_tests {
277    use super::*;
278    #[test]
279    pub(super) fn test_pass_config() {
280        let config = MatlabPassConfig::new("test_pass", MatlabPassPhase::Transformation);
281        assert!(config.enabled);
282        assert!(config.phase.is_modifying());
283        assert_eq!(config.phase.name(), "transformation");
284    }
285    #[test]
286    pub(super) fn test_pass_stats() {
287        let mut stats = MatlabPassStats::new();
288        stats.record_run(10, 100, 3);
289        stats.record_run(20, 200, 5);
290        assert_eq!(stats.total_runs, 2);
291        assert!((stats.average_changes_per_run() - 15.0).abs() < 0.01);
292        assert!((stats.success_rate() - 1.0).abs() < 0.01);
293        let s = stats.format_summary();
294        assert!(s.contains("Runs: 2/2"));
295    }
296    #[test]
297    pub(super) fn test_pass_registry() {
298        let mut reg = MatlabPassRegistry::new();
299        reg.register(MatlabPassConfig::new("pass_a", MatlabPassPhase::Analysis));
300        reg.register(MatlabPassConfig::new("pass_b", MatlabPassPhase::Transformation).disabled());
301        assert_eq!(reg.total_passes(), 2);
302        assert_eq!(reg.enabled_count(), 1);
303        reg.update_stats("pass_a", 5, 50, 2);
304        let stats = reg.get_stats("pass_a").expect("stats should exist");
305        assert_eq!(stats.total_changes, 5);
306    }
307    #[test]
308    pub(super) fn test_analysis_cache() {
309        let mut cache = MatlabAnalysisCache::new(10);
310        cache.insert("key1".to_string(), vec![1, 2, 3]);
311        assert!(cache.get("key1").is_some());
312        assert!(cache.get("key2").is_none());
313        assert!((cache.hit_rate() - 0.5).abs() < 0.01);
314        cache.invalidate("key1");
315        assert!(!cache.entries["key1"].valid);
316        assert_eq!(cache.size(), 1);
317    }
318    #[test]
319    pub(super) fn test_worklist() {
320        let mut wl = MatlabWorklist::new();
321        assert!(wl.push(1));
322        assert!(wl.push(2));
323        assert!(!wl.push(1));
324        assert_eq!(wl.len(), 2);
325        assert_eq!(wl.pop(), Some(1));
326        assert!(!wl.contains(1));
327        assert!(wl.contains(2));
328    }
329    #[test]
330    pub(super) fn test_dominator_tree() {
331        let mut dt = MatlabDominatorTree::new(5);
332        dt.set_idom(1, 0);
333        dt.set_idom(2, 0);
334        dt.set_idom(3, 1);
335        assert!(dt.dominates(0, 3));
336        assert!(dt.dominates(1, 3));
337        assert!(!dt.dominates(2, 3));
338        assert!(dt.dominates(3, 3));
339    }
340    #[test]
341    pub(super) fn test_liveness() {
342        let mut liveness = MatlabLivenessInfo::new(3);
343        liveness.add_def(0, 1);
344        liveness.add_use(1, 1);
345        assert!(liveness.defs[0].contains(&1));
346        assert!(liveness.uses[1].contains(&1));
347    }
348    #[test]
349    pub(super) fn test_constant_folding() {
350        assert_eq!(MatlabConstantFoldingHelper::fold_add_i64(3, 4), Some(7));
351        assert_eq!(MatlabConstantFoldingHelper::fold_div_i64(10, 0), None);
352        assert_eq!(MatlabConstantFoldingHelper::fold_div_i64(10, 2), Some(5));
353        assert_eq!(
354            MatlabConstantFoldingHelper::fold_bitand_i64(0b1100, 0b1010),
355            0b1000
356        );
357        assert_eq!(MatlabConstantFoldingHelper::fold_bitnot_i64(0), -1);
358    }
359    #[test]
360    pub(super) fn test_dep_graph() {
361        let mut g = MatlabDepGraph::new();
362        g.add_dep(1, 2);
363        g.add_dep(2, 3);
364        g.add_dep(1, 3);
365        assert_eq!(g.dependencies_of(2), vec![1]);
366        let topo = g.topological_sort();
367        assert_eq!(topo.len(), 3);
368        assert!(!g.has_cycle());
369        let pos: std::collections::HashMap<u32, usize> =
370            topo.iter().enumerate().map(|(i, &n)| (n, i)).collect();
371        assert!(pos[&1] < pos[&2]);
372        assert!(pos[&1] < pos[&3]);
373        assert!(pos[&2] < pos[&3]);
374    }
375}