Skip to main content

oxilean_codegen/matlab_backend/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use std::collections::HashMap;
6use std::collections::{HashSet, VecDeque};
7use std::fmt::Write as FmtWrite;
8
9/// A simple constant-folding optimizer for MATLAB expressions.
10#[allow(dead_code)]
11pub struct MatlabOptimizer {
12    /// Number of rewrites performed.
13    pub rewrites: usize,
14}
15impl MatlabOptimizer {
16    /// Create a new optimizer.
17    #[allow(dead_code)]
18    pub fn new() -> Self {
19        MatlabOptimizer { rewrites: 0 }
20    }
21    /// Simplify a MATLAB expression.
22    #[allow(dead_code)]
23    pub fn simplify(&mut self, expr: MatlabExpr) -> MatlabExpr {
24        match expr {
25            MatlabExpr::BinaryOp(op, lhs, rhs) => {
26                let lhs = self.simplify(*lhs);
27                let rhs = self.simplify(*rhs);
28                if let (
29                    MatlabExpr::Lit(MatlabLiteral::Integer(a)),
30                    MatlabExpr::Lit(MatlabLiteral::Integer(b)),
31                ) = (&lhs, &rhs)
32                {
33                    match op.as_str() {
34                        "+" => {
35                            self.rewrites += 1;
36                            return MatlabExpr::Lit(MatlabLiteral::Integer(a + b));
37                        }
38                        "-" => {
39                            self.rewrites += 1;
40                            return MatlabExpr::Lit(MatlabLiteral::Integer(a - b));
41                        }
42                        "*" => {
43                            self.rewrites += 1;
44                            return MatlabExpr::Lit(MatlabLiteral::Integer(a * b));
45                        }
46                        _ => {}
47                    }
48                }
49                MatlabExpr::BinaryOp(op, Box::new(lhs), Box::new(rhs))
50            }
51            MatlabExpr::UnaryOp(op, operand, postfix) => {
52                let operand = self.simplify(*operand);
53                if op == "-" && !postfix {
54                    if let MatlabExpr::Lit(MatlabLiteral::Integer(n)) = &operand {
55                        self.rewrites += 1;
56                        return MatlabExpr::Lit(MatlabLiteral::Integer(-n));
57                    }
58                }
59                MatlabExpr::UnaryOp(op, Box::new(operand), postfix)
60            }
61            other => other,
62        }
63    }
64}
65/// MATLAB classdef property.
66#[derive(Debug, Clone, PartialEq)]
67pub struct MatlabProperty {
68    pub name: String,
69    pub ty: Option<MatlabType>,
70    pub default: Option<MatlabExpr>,
71    pub access: PropAccess,
72    pub is_constant: bool,
73    pub is_dependent: bool,
74}
75/// MATLAB type representation.
76#[derive(Debug, Clone, PartialEq)]
77pub enum MatlabType {
78    /// `double` — 64-bit float (default numeric type)
79    Double,
80    /// `single` — 32-bit float
81    Single,
82    /// `int8`
83    Int8,
84    /// `int16`
85    Int16,
86    /// `int32`
87    Int32,
88    /// `int64`
89    Int64,
90    /// `uint8`
91    Uint8,
92    /// `uint16`
93    Uint16,
94    /// `uint32`
95    Uint32,
96    /// `uint64`
97    Uint64,
98    /// `logical`
99    Logical,
100    /// `char` — character array / string (pre-R2016b)
101    Char,
102    /// `string` — string array (R2016b+)
103    StringArray,
104    /// `cell` — cell array
105    Cell,
106    /// Named struct type
107    StructType(String),
108    /// `function_handle` — `@func`
109    FunctionHandle,
110    /// `sparse` — sparse matrix
111    Sparse,
112    /// N-D array of a base type
113    Array(Box<MatlabType>, Vec<Option<usize>>),
114    /// Class instance
115    Class(String),
116    /// Any / unspecified
117    Any,
118}
119/// Property access level.
120#[derive(Debug, Clone, PartialEq, Eq)]
121pub enum PropAccess {
122    Public,
123    Protected,
124    Private,
125}
126#[allow(dead_code)]
127#[derive(Debug, Clone)]
128pub struct MatlabCacheEntry {
129    pub key: String,
130    pub data: Vec<u8>,
131    pub timestamp: u64,
132    pub valid: bool,
133}
134#[allow(dead_code)]
135pub struct MatlabConstantFoldingHelper;
136impl MatlabConstantFoldingHelper {
137    #[allow(dead_code)]
138    pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
139        a.checked_add(b)
140    }
141    #[allow(dead_code)]
142    pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
143        a.checked_sub(b)
144    }
145    #[allow(dead_code)]
146    pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
147        a.checked_mul(b)
148    }
149    #[allow(dead_code)]
150    pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
151        if b == 0 {
152            None
153        } else {
154            a.checked_div(b)
155        }
156    }
157    #[allow(dead_code)]
158    pub fn fold_add_f64(a: f64, b: f64) -> f64 {
159        a + b
160    }
161    #[allow(dead_code)]
162    pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
163        a * b
164    }
165    #[allow(dead_code)]
166    pub fn fold_neg_i64(a: i64) -> Option<i64> {
167        a.checked_neg()
168    }
169    #[allow(dead_code)]
170    pub fn fold_not_bool(a: bool) -> bool {
171        !a
172    }
173    #[allow(dead_code)]
174    pub fn fold_and_bool(a: bool, b: bool) -> bool {
175        a && b
176    }
177    #[allow(dead_code)]
178    pub fn fold_or_bool(a: bool, b: bool) -> bool {
179        a || b
180    }
181    #[allow(dead_code)]
182    pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
183        a.checked_shl(b)
184    }
185    #[allow(dead_code)]
186    pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
187        a.checked_shr(b)
188    }
189    #[allow(dead_code)]
190    pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
191        if b == 0 {
192            None
193        } else {
194            Some(a % b)
195        }
196    }
197    #[allow(dead_code)]
198    pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
199        a & b
200    }
201    #[allow(dead_code)]
202    pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
203        a | b
204    }
205    #[allow(dead_code)]
206    pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
207        a ^ b
208    }
209    #[allow(dead_code)]
210    pub fn fold_bitnot_i64(a: i64) -> i64 {
211        !a
212    }
213}
214/// MATLAB expression.
215#[derive(Debug, Clone, PartialEq)]
216pub enum MatlabExpr {
217    /// Literal value
218    Lit(MatlabLiteral),
219    /// Variable reference: `x`
220    Var(String),
221    /// Matrix literal: `[1 2; 3 4]`
222    MatrixLit(Vec<Vec<MatlabExpr>>),
223    /// Cell array literal: `{1, 'a', true}`
224    CellLit(Vec<Vec<MatlabExpr>>),
225    /// Colon range: `start:step:end` or `start:end`
226    ColonRange {
227        start: Box<MatlabExpr>,
228        step: Option<Box<MatlabExpr>>,
229        end: Box<MatlabExpr>,
230    },
231    /// Function call: `f(a, b)`
232    Call(Box<MatlabExpr>, Vec<MatlabExpr>),
233    /// Indexing: `A(i, j)` or `A{i}` (cell)
234    Index {
235        obj: Box<MatlabExpr>,
236        indices: Vec<MatlabExpr>,
237        cell_index: bool,
238    },
239    /// Struct field access: `s.field`
240    FieldAccess(Box<MatlabExpr>, String),
241    /// Binary operator: `a + b`, `a .* b`, `a & b`
242    BinaryOp(String, Box<MatlabExpr>, Box<MatlabExpr>),
243    /// Unary operator: `-x`, `~x`, `x'`, `x.'`
244    UnaryOp(String, Box<MatlabExpr>, bool),
245    /// Ternary-style if expression (MATLAB doesn't have this — emitted as inline)
246    IfExpr(Box<MatlabExpr>, Box<MatlabExpr>, Box<MatlabExpr>),
247    /// Anonymous function: `@(x, y) x + y`
248    AnonFunc(Vec<String>, Box<MatlabExpr>),
249    /// End keyword (for indexing)
250    End,
251    /// Colon alone (`:`) for all-elements indexing
252    Colon,
253    /// Nargin / nargout
254    Nargin,
255    Nargout,
256}
257#[allow(dead_code)]
258#[derive(Debug, Clone)]
259pub struct MatlabDominatorTree {
260    pub idom: Vec<Option<u32>>,
261    pub dom_children: Vec<Vec<u32>>,
262    pub dom_depth: Vec<u32>,
263}
264impl MatlabDominatorTree {
265    #[allow(dead_code)]
266    pub fn new(size: usize) -> Self {
267        MatlabDominatorTree {
268            idom: vec![None; size],
269            dom_children: vec![Vec::new(); size],
270            dom_depth: vec![0; size],
271        }
272    }
273    #[allow(dead_code)]
274    pub fn set_idom(&mut self, node: usize, idom: u32) {
275        self.idom[node] = Some(idom);
276    }
277    #[allow(dead_code)]
278    pub fn dominates(&self, a: usize, b: usize) -> bool {
279        if a == b {
280            return true;
281        }
282        let mut cur = b;
283        loop {
284            match self.idom[cur] {
285                Some(parent) if parent as usize == a => return true,
286                Some(parent) if parent as usize == cur => return false,
287                Some(parent) => cur = parent as usize,
288                None => return false,
289            }
290        }
291    }
292    #[allow(dead_code)]
293    pub fn depth(&self, node: usize) -> u32 {
294        self.dom_depth.get(node).copied().unwrap_or(0)
295    }
296}
297/// Configuration for the MATLAB code generator.
298#[allow(dead_code)]
299#[derive(Debug, Clone)]
300pub struct MatlabGenConfig {
301    /// Emit statement suppression (`;`) by default.
302    pub suppress_output: bool,
303    /// Emit `%% section` markers.
304    pub emit_section_markers: bool,
305    /// Target Octave compatibility (avoid newer MATLAB features).
306    pub octave_compat: bool,
307    /// Indent string.
308    pub indent: String,
309    /// Whether to emit function-end `end` keywords (MATLAB 2016b+).
310    pub emit_function_end: bool,
311    /// Whether to use `@(x) ...` anonymous function syntax.
312    pub prefer_anon_functions: bool,
313}
314impl MatlabGenConfig {
315    /// Create a config for Octave-compatible output.
316    #[allow(dead_code)]
317    pub fn octave() -> Self {
318        MatlabGenConfig {
319            octave_compat: true,
320            ..Default::default()
321        }
322    }
323    /// Create a config for MATLAB R2022a and newer.
324    #[allow(dead_code)]
325    pub fn matlab_r2022a() -> Self {
326        MatlabGenConfig {
327            emit_section_markers: true,
328            ..Default::default()
329        }
330    }
331}
332#[allow(dead_code)]
333#[derive(Debug, Clone)]
334pub struct MatlabDepGraph {
335    pub(super) nodes: Vec<u32>,
336    pub(super) edges: Vec<(u32, u32)>,
337}
338impl MatlabDepGraph {
339    #[allow(dead_code)]
340    pub fn new() -> Self {
341        MatlabDepGraph {
342            nodes: Vec::new(),
343            edges: Vec::new(),
344        }
345    }
346    #[allow(dead_code)]
347    pub fn add_node(&mut self, id: u32) {
348        if !self.nodes.contains(&id) {
349            self.nodes.push(id);
350        }
351    }
352    #[allow(dead_code)]
353    pub fn add_dep(&mut self, dep: u32, dependent: u32) {
354        self.add_node(dep);
355        self.add_node(dependent);
356        self.edges.push((dep, dependent));
357    }
358    #[allow(dead_code)]
359    pub fn dependents_of(&self, node: u32) -> Vec<u32> {
360        self.edges
361            .iter()
362            .filter(|(d, _)| *d == node)
363            .map(|(_, dep)| *dep)
364            .collect()
365    }
366    #[allow(dead_code)]
367    pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
368        self.edges
369            .iter()
370            .filter(|(_, dep)| *dep == node)
371            .map(|(d, _)| *d)
372            .collect()
373    }
374    #[allow(dead_code)]
375    pub fn topological_sort(&self) -> Vec<u32> {
376        let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
377        for &n in &self.nodes {
378            in_degree.insert(n, 0);
379        }
380        for (_, dep) in &self.edges {
381            *in_degree.entry(*dep).or_insert(0) += 1;
382        }
383        let mut queue: std::collections::VecDeque<u32> = self
384            .nodes
385            .iter()
386            .filter(|&&n| in_degree[&n] == 0)
387            .copied()
388            .collect();
389        let mut result = Vec::new();
390        while let Some(node) = queue.pop_front() {
391            result.push(node);
392            for dep in self.dependents_of(node) {
393                let cnt = in_degree.entry(dep).or_insert(0);
394                *cnt = cnt.saturating_sub(1);
395                if *cnt == 0 {
396                    queue.push_back(dep);
397                }
398            }
399        }
400        result
401    }
402    #[allow(dead_code)]
403    pub fn has_cycle(&self) -> bool {
404        self.topological_sort().len() < self.nodes.len()
405    }
406}
407#[allow(dead_code)]
408#[derive(Debug, Clone)]
409pub struct MatlabWorklist {
410    pub(super) items: std::collections::VecDeque<u32>,
411    pub(super) in_worklist: std::collections::HashSet<u32>,
412}
413impl MatlabWorklist {
414    #[allow(dead_code)]
415    pub fn new() -> Self {
416        MatlabWorklist {
417            items: std::collections::VecDeque::new(),
418            in_worklist: std::collections::HashSet::new(),
419        }
420    }
421    #[allow(dead_code)]
422    pub fn push(&mut self, item: u32) -> bool {
423        if self.in_worklist.insert(item) {
424            self.items.push_back(item);
425            true
426        } else {
427            false
428        }
429    }
430    #[allow(dead_code)]
431    pub fn pop(&mut self) -> Option<u32> {
432        let item = self.items.pop_front()?;
433        self.in_worklist.remove(&item);
434        Some(item)
435    }
436    #[allow(dead_code)]
437    pub fn is_empty(&self) -> bool {
438        self.items.is_empty()
439    }
440    #[allow(dead_code)]
441    pub fn len(&self) -> usize {
442        self.items.len()
443    }
444    #[allow(dead_code)]
445    pub fn contains(&self, item: u32) -> bool {
446        self.in_worklist.contains(&item)
447    }
448}
449/// Literal values in MATLAB.
450#[derive(Debug, Clone, PartialEq)]
451pub enum MatlabLiteral {
452    /// `42` or `42.0`
453    Double(f64),
454    /// `42` integer literal (will cast)
455    Integer(i64),
456    /// `true` / `false`
457    Logical(bool),
458    /// `'hello'` char array
459    Char(String),
460    /// `"hello"` string (R2016b+)
461    Str(String),
462    /// `[]` empty array / matrix
463    Empty,
464    /// `NaN`
465    NaN,
466    /// `Inf` / `-Inf`
467    Inf(bool),
468    /// `pi`
469    Pi,
470    /// `eps`
471    Eps,
472}
473/// A MATLAB function documentation annotation.
474#[allow(dead_code)]
475#[derive(Debug, Clone, PartialEq)]
476pub struct MatlabAnnotation {
477    /// Short summary line.
478    pub summary: String,
479    /// Long description.
480    pub description: Option<String>,
481    /// Input parameter descriptions `(name, description)`.
482    pub inputs: Vec<(String, String)>,
483    /// Output descriptions `(name, description)`.
484    pub outputs: Vec<(String, String)>,
485    /// Example lines.
486    pub examples: Vec<String>,
487    /// See-also references.
488    pub see_also: Vec<String>,
489}
490impl MatlabAnnotation {
491    /// Create a new annotation with just a summary.
492    #[allow(dead_code)]
493    pub fn new(summary: impl Into<String>) -> Self {
494        MatlabAnnotation {
495            summary: summary.into(),
496            description: None,
497            inputs: Vec::new(),
498            outputs: Vec::new(),
499            examples: Vec::new(),
500            see_also: Vec::new(),
501        }
502    }
503    /// Add an input description.
504    #[allow(dead_code)]
505    pub fn input(mut self, name: impl Into<String>, desc: impl Into<String>) -> Self {
506        self.inputs.push((name.into(), desc.into()));
507        self
508    }
509    /// Add an output description.
510    #[allow(dead_code)]
511    pub fn output(mut self, name: impl Into<String>, desc: impl Into<String>) -> Self {
512        self.outputs.push((name.into(), desc.into()));
513        self
514    }
515    /// Add an example.
516    #[allow(dead_code)]
517    pub fn example(mut self, code: impl Into<String>) -> Self {
518        self.examples.push(code.into());
519        self
520    }
521    /// Emit as MATLAB `%` comment block.
522    #[allow(dead_code)]
523    pub fn emit(&self) -> String {
524        let mut lines = vec![format!("%{}", self.summary)];
525        if let Some(desc) = &self.description {
526            lines.push("%".to_string());
527            for line in desc.lines() {
528                lines.push(format!("%  {}", line));
529            }
530        }
531        if !self.inputs.is_empty() {
532            lines.push("%".to_string());
533            lines.push("% Inputs:".to_string());
534            for (name, desc) in &self.inputs {
535                lines.push(format!("%   {} - {}", name, desc));
536            }
537        }
538        if !self.outputs.is_empty() {
539            lines.push("%".to_string());
540            lines.push("% Outputs:".to_string());
541            for (name, desc) in &self.outputs {
542                lines.push(format!("%   {} - {}", name, desc));
543            }
544        }
545        if !self.examples.is_empty() {
546            lines.push("%".to_string());
547            lines.push("% Examples:".to_string());
548            for ex in &self.examples {
549                lines.push(format!("%   {}", ex));
550            }
551        }
552        if !self.see_also.is_empty() {
553            lines.push("%".to_string());
554            lines.push(format!("% See also: {}", self.see_also.join(", ")));
555        }
556        lines.join("\n")
557    }
558}
559/// A basic type-consistency checker for MATLAB expressions.
560#[allow(dead_code)]
561pub struct MatlabTypeChecker {
562    /// Variable type environment.
563    pub env: HashMap<String, MatlabType>,
564    /// Type errors collected.
565    pub errors: Vec<String>,
566}
567impl MatlabTypeChecker {
568    /// Create a new checker.
569    #[allow(dead_code)]
570    pub fn new() -> Self {
571        MatlabTypeChecker {
572            env: HashMap::new(),
573            errors: Vec::new(),
574        }
575    }
576    /// Declare a variable with a type.
577    #[allow(dead_code)]
578    pub fn declare(&mut self, name: impl Into<String>, ty: MatlabType) {
579        self.env.insert(name.into(), ty);
580    }
581    /// Infer the type of a MATLAB expression.
582    #[allow(dead_code)]
583    pub fn infer(&self, expr: &MatlabExpr) -> MatlabType {
584        match expr {
585            MatlabExpr::Lit(MatlabLiteral::Integer(_)) => MatlabType::Int64,
586            MatlabExpr::Lit(MatlabLiteral::Double(_)) => MatlabType::Double,
587            MatlabExpr::Lit(MatlabLiteral::Logical(_)) => MatlabType::Logical,
588            MatlabExpr::Lit(MatlabLiteral::Char(_)) => MatlabType::Char,
589            MatlabExpr::Var(name) => self.env.get(name).cloned().unwrap_or(MatlabType::Any),
590            MatlabExpr::BinaryOp(_, lhs, rhs) => {
591                let lt = self.infer(lhs);
592                let rt = self.infer(rhs);
593                self.numeric_promote(lt, rt)
594            }
595            MatlabExpr::UnaryOp(op, inner, postfix) if (op == "'" || op == ".'") && *postfix => {
596                self.infer(inner)
597            }
598            _ => MatlabType::Any,
599        }
600    }
601    pub(super) fn numeric_promote(&self, a: MatlabType, b: MatlabType) -> MatlabType {
602        match (&a, &b) {
603            (MatlabType::Double, _) | (_, MatlabType::Double) => MatlabType::Double,
604            (MatlabType::Single, _) | (_, MatlabType::Single) => MatlabType::Single,
605            (MatlabType::Int64, _) | (_, MatlabType::Int64) => MatlabType::Int64,
606            _ => MatlabType::Any,
607        }
608    }
609    /// Check a statement for type consistency.
610    #[allow(dead_code)]
611    pub fn check_stmt(&mut self, stmt: &MatlabStmt) {
612        match stmt {
613            MatlabStmt::Assign { lhs, rhs, .. } => {
614                let _rhs_ty = self.infer(rhs);
615                for name in lhs {
616                    if !self.env.contains_key(name) {
617                        self.env.insert(name.clone(), MatlabType::Any);
618                    }
619                }
620            }
621            _ => {}
622        }
623    }
624    /// Whether any errors were found.
625    #[allow(dead_code)]
626    pub fn has_errors(&self) -> bool {
627        !self.errors.is_empty()
628    }
629}
630#[allow(dead_code)]
631pub struct MatlabPassRegistry {
632    pub(super) configs: Vec<MatlabPassConfig>,
633    pub(super) stats: std::collections::HashMap<String, MatlabPassStats>,
634}
635impl MatlabPassRegistry {
636    #[allow(dead_code)]
637    pub fn new() -> Self {
638        MatlabPassRegistry {
639            configs: Vec::new(),
640            stats: std::collections::HashMap::new(),
641        }
642    }
643    #[allow(dead_code)]
644    pub fn register(&mut self, config: MatlabPassConfig) {
645        self.stats
646            .insert(config.pass_name.clone(), MatlabPassStats::new());
647        self.configs.push(config);
648    }
649    #[allow(dead_code)]
650    pub fn enabled_passes(&self) -> Vec<&MatlabPassConfig> {
651        self.configs.iter().filter(|c| c.enabled).collect()
652    }
653    #[allow(dead_code)]
654    pub fn get_stats(&self, name: &str) -> Option<&MatlabPassStats> {
655        self.stats.get(name)
656    }
657    #[allow(dead_code)]
658    pub fn total_passes(&self) -> usize {
659        self.configs.len()
660    }
661    #[allow(dead_code)]
662    pub fn enabled_count(&self) -> usize {
663        self.enabled_passes().len()
664    }
665    #[allow(dead_code)]
666    pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
667        if let Some(stats) = self.stats.get_mut(name) {
668            stats.record_run(changes, time_ms, iter);
669        }
670    }
671}
672#[allow(dead_code)]
673#[derive(Debug, Clone)]
674pub struct MatlabAnalysisCache {
675    pub(super) entries: std::collections::HashMap<String, MatlabCacheEntry>,
676    pub(super) max_size: usize,
677    pub(super) hits: u64,
678    pub(super) misses: u64,
679}
680impl MatlabAnalysisCache {
681    #[allow(dead_code)]
682    pub fn new(max_size: usize) -> Self {
683        MatlabAnalysisCache {
684            entries: std::collections::HashMap::new(),
685            max_size,
686            hits: 0,
687            misses: 0,
688        }
689    }
690    #[allow(dead_code)]
691    pub fn get(&mut self, key: &str) -> Option<&MatlabCacheEntry> {
692        if self.entries.contains_key(key) {
693            self.hits += 1;
694            self.entries.get(key)
695        } else {
696            self.misses += 1;
697            None
698        }
699    }
700    #[allow(dead_code)]
701    pub fn insert(&mut self, key: String, data: Vec<u8>) {
702        if self.entries.len() >= self.max_size {
703            if let Some(oldest) = self.entries.keys().next().cloned() {
704                self.entries.remove(&oldest);
705            }
706        }
707        self.entries.insert(
708            key.clone(),
709            MatlabCacheEntry {
710                key,
711                data,
712                timestamp: 0,
713                valid: true,
714            },
715        );
716    }
717    #[allow(dead_code)]
718    pub fn invalidate(&mut self, key: &str) {
719        if let Some(entry) = self.entries.get_mut(key) {
720            entry.valid = false;
721        }
722    }
723    #[allow(dead_code)]
724    pub fn clear(&mut self) {
725        self.entries.clear();
726    }
727    #[allow(dead_code)]
728    pub fn hit_rate(&self) -> f64 {
729        let total = self.hits + self.misses;
730        if total == 0 {
731            return 0.0;
732        }
733        self.hits as f64 / total as f64
734    }
735    #[allow(dead_code)]
736    pub fn size(&self) -> usize {
737        self.entries.len()
738    }
739}
740#[allow(dead_code)]
741#[derive(Debug, Clone)]
742pub struct MatlabPassConfig {
743    pub phase: MatlabPassPhase,
744    pub enabled: bool,
745    pub max_iterations: u32,
746    pub debug_output: bool,
747    pub pass_name: String,
748}
749impl MatlabPassConfig {
750    #[allow(dead_code)]
751    pub fn new(name: impl Into<String>, phase: MatlabPassPhase) -> Self {
752        MatlabPassConfig {
753            phase,
754            enabled: true,
755            max_iterations: 10,
756            debug_output: false,
757            pass_name: name.into(),
758        }
759    }
760    #[allow(dead_code)]
761    pub fn disabled(mut self) -> Self {
762        self.enabled = false;
763        self
764    }
765    #[allow(dead_code)]
766    pub fn with_debug(mut self) -> Self {
767        self.debug_output = true;
768        self
769    }
770    #[allow(dead_code)]
771    pub fn max_iter(mut self, n: u32) -> Self {
772        self.max_iterations = n;
773        self
774    }
775}
776#[allow(dead_code)]
777#[derive(Debug, Clone, PartialEq)]
778pub enum MatlabPassPhase {
779    Analysis,
780    Transformation,
781    Verification,
782    Cleanup,
783}
784impl MatlabPassPhase {
785    #[allow(dead_code)]
786    pub fn name(&self) -> &str {
787        match self {
788            MatlabPassPhase::Analysis => "analysis",
789            MatlabPassPhase::Transformation => "transformation",
790            MatlabPassPhase::Verification => "verification",
791            MatlabPassPhase::Cleanup => "cleanup",
792        }
793    }
794    #[allow(dead_code)]
795    pub fn is_modifying(&self) -> bool {
796        matches!(
797            self,
798            MatlabPassPhase::Transformation | MatlabPassPhase::Cleanup
799        )
800    }
801}
802/// A MATLAB struct literal.
803#[allow(dead_code)]
804#[derive(Debug, Clone, PartialEq)]
805pub struct MatlabStructLiteral {
806    /// Fields.
807    pub fields: Vec<MatlabStructField>,
808}
809impl MatlabStructLiteral {
810    /// Create a new empty struct literal.
811    #[allow(dead_code)]
812    pub fn new() -> Self {
813        MatlabStructLiteral { fields: Vec::new() }
814    }
815    /// Add a field.
816    #[allow(dead_code)]
817    pub fn field(mut self, name: impl Into<String>, value: MatlabExpr) -> Self {
818        self.fields.push(MatlabStructField::new(name, value));
819        self
820    }
821    /// Emit as a MATLAB `struct(...)` call.
822    #[allow(dead_code)]
823    pub fn emit(&self) -> String {
824        if self.fields.is_empty() {
825            return "struct()".to_string();
826        }
827        let args: Vec<_> = self
828            .fields
829            .iter()
830            .flat_map(|f| vec![format!("'{}'", f.name), format!("{{{}}}", f.value)])
831            .collect();
832        format!("struct({})", args.join(", "))
833    }
834}
835#[allow(dead_code)]
836#[derive(Debug, Clone, Default)]
837pub struct MatlabPassStats {
838    pub total_runs: u32,
839    pub successful_runs: u32,
840    pub total_changes: u64,
841    pub time_ms: u64,
842    pub iterations_used: u32,
843}
844impl MatlabPassStats {
845    #[allow(dead_code)]
846    pub fn new() -> Self {
847        Self::default()
848    }
849    #[allow(dead_code)]
850    pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
851        self.total_runs += 1;
852        self.successful_runs += 1;
853        self.total_changes += changes;
854        self.time_ms += time_ms;
855        self.iterations_used = iterations;
856    }
857    #[allow(dead_code)]
858    pub fn average_changes_per_run(&self) -> f64 {
859        if self.total_runs == 0 {
860            return 0.0;
861        }
862        self.total_changes as f64 / self.total_runs as f64
863    }
864    #[allow(dead_code)]
865    pub fn success_rate(&self) -> f64 {
866        if self.total_runs == 0 {
867            return 0.0;
868        }
869        self.successful_runs as f64 / self.total_runs as f64
870    }
871    #[allow(dead_code)]
872    pub fn format_summary(&self) -> String {
873        format!(
874            "Runs: {}/{}, Changes: {}, Time: {}ms",
875            self.successful_runs, self.total_runs, self.total_changes, self.time_ms
876        )
877    }
878}
879/// A MATLAB matrix literal.
880#[allow(dead_code)]
881#[derive(Debug, Clone, PartialEq)]
882pub struct MatlabMatrix {
883    /// Rows of the matrix (each row is a list of expressions).
884    pub rows: Vec<Vec<MatlabExpr>>,
885}
886impl MatlabMatrix {
887    /// Create a new matrix.
888    #[allow(dead_code)]
889    pub fn new() -> Self {
890        MatlabMatrix { rows: Vec::new() }
891    }
892    /// Add a row to the matrix.
893    #[allow(dead_code)]
894    pub fn add_row(mut self, row: Vec<MatlabExpr>) -> Self {
895        self.rows.push(row);
896        self
897    }
898    /// Number of rows.
899    #[allow(dead_code)]
900    pub fn num_rows(&self) -> usize {
901        self.rows.len()
902    }
903    /// Number of columns (from the first row).
904    #[allow(dead_code)]
905    pub fn num_cols(&self) -> usize {
906        self.rows.first().map(|r| r.len()).unwrap_or(0)
907    }
908    /// Emit as a MATLAB matrix literal.
909    #[allow(dead_code)]
910    pub fn emit(&self) -> String {
911        let rows: Vec<String> = self
912            .rows
913            .iter()
914            .map(|r| {
915                r.iter()
916                    .map(|e| e.to_string())
917                    .collect::<Vec<_>>()
918                    .join(", ")
919            })
920            .collect();
921        format!("[{}]", rows.join("; "))
922    }
923    /// Create an identity matrix of size `n`.
924    #[allow(dead_code)]
925    pub fn identity(n: usize) -> MatlabExpr {
926        MatlabExpr::Call(
927            Box::new(MatlabExpr::Var("eye".to_string())),
928            vec![MatlabExpr::Lit(MatlabLiteral::Integer(n as i64))],
929        )
930    }
931    /// Create a zeros matrix of shape `(m, n)`.
932    #[allow(dead_code)]
933    pub fn zeros(m: usize, n: usize) -> MatlabExpr {
934        MatlabExpr::Call(
935            Box::new(MatlabExpr::Var("zeros".to_string())),
936            vec![
937                MatlabExpr::Lit(MatlabLiteral::Integer(m as i64)),
938                MatlabExpr::Lit(MatlabLiteral::Integer(n as i64)),
939            ],
940        )
941    }
942    /// Create an ones matrix of shape `(m, n)`.
943    #[allow(dead_code)]
944    pub fn ones(m: usize, n: usize) -> MatlabExpr {
945        MatlabExpr::Call(
946            Box::new(MatlabExpr::Var("ones".to_string())),
947            vec![
948                MatlabExpr::Lit(MatlabLiteral::Integer(m as i64)),
949                MatlabExpr::Lit(MatlabLiteral::Integer(n as i64)),
950            ],
951        )
952    }
953}
954/// Statistics about a generated MATLAB module.
955#[allow(dead_code)]
956#[derive(Debug, Clone, Default)]
957pub struct MatlabStats {
958    /// Number of functions.
959    pub num_functions: usize,
960    /// Number of classes.
961    pub num_classes: usize,
962    /// Total number of statements.
963    pub total_stmts: usize,
964    /// Number of matrix operations.
965    pub matrix_ops: usize,
966}
967impl MatlabStats {
968    /// Compute stats from a module builder.
969    #[allow(dead_code)]
970    pub fn from_module(module: &MatlabModuleBuilder) -> Self {
971        let total_stmts = module.functions.iter().map(|f| f.body.len()).sum::<usize>();
972        MatlabStats {
973            num_functions: module.functions.len(),
974            num_classes: module.classes.len(),
975            total_stmts,
976            matrix_ops: 0,
977        }
978    }
979    /// Merge another stats record.
980    #[allow(dead_code)]
981    pub fn merge(&mut self, other: &MatlabStats) {
982        self.num_functions += other.num_functions;
983        self.num_classes += other.num_classes;
984        self.total_stmts += other.total_stmts;
985        self.matrix_ops += other.matrix_ops;
986    }
987}
988/// MATLAB function parameter with optional validation.
989#[derive(Debug, Clone, PartialEq)]
990pub struct MatlabParam {
991    pub name: String,
992    pub default_value: Option<MatlabExpr>,
993    pub validator: Option<MatlabType>,
994}
995impl MatlabParam {
996    pub fn required(name: &str) -> Self {
997        MatlabParam {
998            name: name.to_string(),
999            default_value: None,
1000            validator: None,
1001        }
1002    }
1003    pub fn with_default(name: &str, default: MatlabExpr) -> Self {
1004        MatlabParam {
1005            name: name.to_string(),
1006            default_value: Some(default),
1007            validator: None,
1008        }
1009    }
1010    pub fn typed(name: &str, ty: MatlabType) -> Self {
1011        MatlabParam {
1012            name: name.to_string(),
1013            default_value: None,
1014            validator: Some(ty),
1015        }
1016    }
1017}
1018/// MATLAB classdef definition.
1019#[derive(Debug, Clone, PartialEq)]
1020pub struct MatlabClassdef {
1021    /// Class name
1022    pub name: String,
1023    /// Superclass names
1024    pub superclasses: Vec<String>,
1025    /// Properties blocks (grouped by access level)
1026    pub properties: Vec<MatlabProperty>,
1027    /// Methods
1028    pub methods: Vec<MatlabFunction>,
1029    /// Events
1030    pub events: Vec<String>,
1031    /// Enumeration members (for enumeration classes)
1032    pub enumerations: Vec<(String, Vec<MatlabExpr>)>,
1033}
1034impl MatlabClassdef {
1035    pub fn new(name: &str) -> Self {
1036        MatlabClassdef {
1037            name: name.to_string(),
1038            superclasses: Vec::new(),
1039            properties: Vec::new(),
1040            methods: Vec::new(),
1041            events: Vec::new(),
1042            enumerations: Vec::new(),
1043        }
1044    }
1045    pub fn inherits(mut self, parent: &str) -> Self {
1046        self.superclasses.push(parent.to_string());
1047        self
1048    }
1049}
1050/// Top-level MATLAB file structure.
1051#[derive(Debug, Clone)]
1052pub struct MatlabFile {
1053    /// Top-level functions (first is the main function)
1054    pub functions: Vec<MatlabFunction>,
1055    /// Script statements (for script files — no functions)
1056    pub scripts: Vec<MatlabStmt>,
1057    /// Class definition (for classdef files)
1058    pub classdef: Option<MatlabClassdef>,
1059    /// File-level comment block
1060    pub header_comment: Option<String>,
1061    /// Whether this is a script file (no function wrapper)
1062    pub is_script: bool,
1063}
1064impl MatlabFile {
1065    pub fn new() -> Self {
1066        MatlabFile {
1067            functions: Vec::new(),
1068            scripts: Vec::new(),
1069            classdef: None,
1070            header_comment: None,
1071            is_script: false,
1072        }
1073    }
1074    pub fn script() -> Self {
1075        MatlabFile {
1076            is_script: true,
1077            ..Self::new()
1078        }
1079    }
1080    pub fn add_function(&mut self, fun: MatlabFunction) {
1081        self.functions.push(fun);
1082    }
1083    pub fn add_script_stmt(&mut self, stmt: MatlabStmt) {
1084        self.scripts.push(stmt);
1085    }
1086    pub fn with_classdef(mut self, cls: MatlabClassdef) -> Self {
1087        self.classdef = Some(cls);
1088        self
1089    }
1090    pub fn with_header(mut self, comment: &str) -> Self {
1091        self.header_comment = Some(comment.to_string());
1092        self
1093    }
1094}
1095/// A MATLAB script (executed top-to-bottom, no function signature).
1096#[allow(dead_code)]
1097#[derive(Debug, Clone, PartialEq)]
1098pub struct MatlabScript {
1099    /// Script file name (without `.m`).
1100    pub name: String,
1101    /// Header comment lines.
1102    pub header_comments: Vec<String>,
1103    /// Body statements.
1104    pub statements: Vec<MatlabStmt>,
1105}
1106impl MatlabScript {
1107    /// Create a new script.
1108    #[allow(dead_code)]
1109    pub fn new(name: impl Into<String>) -> Self {
1110        MatlabScript {
1111            name: name.into(),
1112            header_comments: Vec::new(),
1113            statements: Vec::new(),
1114        }
1115    }
1116    /// Add a header comment.
1117    #[allow(dead_code)]
1118    pub fn add_comment(mut self, comment: impl Into<String>) -> Self {
1119        self.header_comments.push(comment.into());
1120        self
1121    }
1122    /// Add a statement.
1123    #[allow(dead_code)]
1124    pub fn add_stmt(mut self, stmt: MatlabStmt) -> Self {
1125        self.statements.push(stmt);
1126        self
1127    }
1128    /// Emit the full script source.
1129    #[allow(dead_code)]
1130    pub fn emit(&self) -> String {
1131        let mut backend = MatlabBackend::new();
1132        for comment in &self.header_comments {
1133            backend.emit_stmt(&MatlabStmt::Comment(comment.clone()));
1134        }
1135        for stmt in &self.statements {
1136            backend.emit_stmt(stmt);
1137        }
1138        backend.take_output()
1139    }
1140}
1141/// A MATLAB plot specification.
1142#[allow(dead_code)]
1143#[derive(Debug, Clone, PartialEq)]
1144pub struct MatlabPlot {
1145    /// Figure title.
1146    pub title: String,
1147    /// X-axis label.
1148    pub xlabel: String,
1149    /// Y-axis label.
1150    pub ylabel: String,
1151    /// Data series (each is a variable name + style string).
1152    pub series: Vec<(String, String)>,
1153    /// Whether to use a grid.
1154    pub grid: bool,
1155    /// Whether to use a legend.
1156    pub legend: bool,
1157    /// Figure size `[width, height]` in points.
1158    pub figure_size: Option<[f64; 2]>,
1159}
1160impl MatlabPlot {
1161    /// Create a new plot with defaults.
1162    #[allow(dead_code)]
1163    pub fn new(title: impl Into<String>) -> Self {
1164        MatlabPlot {
1165            title: title.into(),
1166            xlabel: String::new(),
1167            ylabel: String::new(),
1168            series: Vec::new(),
1169            grid: true,
1170            legend: false,
1171            figure_size: None,
1172        }
1173    }
1174    /// Add a data series.
1175    #[allow(dead_code)]
1176    pub fn add_series(mut self, var: impl Into<String>, style: impl Into<String>) -> Self {
1177        self.series.push((var.into(), style.into()));
1178        self
1179    }
1180    /// Set axis labels.
1181    #[allow(dead_code)]
1182    pub fn labels(mut self, xlabel: impl Into<String>, ylabel: impl Into<String>) -> Self {
1183        self.xlabel = xlabel.into();
1184        self.ylabel = ylabel.into();
1185        self
1186    }
1187    /// Enable legend.
1188    #[allow(dead_code)]
1189    pub fn with_legend(mut self) -> Self {
1190        self.legend = true;
1191        self
1192    }
1193    /// Emit MATLAB plotting code.
1194    #[allow(dead_code)]
1195    pub fn emit(&self) -> String {
1196        let mut out = String::new();
1197        out.push_str("figure;\n");
1198        if let Some([w, h]) = self.figure_size {
1199            out.push_str(&format!(
1200                "set(gcf, 'Position', [100, 100, {}, {}]);\n",
1201                w, h
1202            ));
1203        }
1204        for (i, (var, style)) in self.series.iter().enumerate() {
1205            if i == 0 {
1206                out.push_str(&format!("plot({}, '{}');\n", var, style));
1207            } else {
1208                out.push_str("hold on;\n");
1209                out.push_str(&format!("plot({}, '{}');\n", var, style));
1210            }
1211        }
1212        if !self.series.is_empty() {
1213            out.push_str("hold off;\n");
1214        }
1215        if !self.title.is_empty() {
1216            out.push_str(&format!("title('{}');\n", self.title));
1217        }
1218        if !self.xlabel.is_empty() {
1219            out.push_str(&format!("xlabel('{}');\n", self.xlabel));
1220        }
1221        if !self.ylabel.is_empty() {
1222            out.push_str(&format!("ylabel('{}');\n", self.ylabel));
1223        }
1224        if self.grid {
1225            out.push_str("grid on;\n");
1226        }
1227        if self.legend {
1228            let labels: Vec<_> = self
1229                .series
1230                .iter()
1231                .map(|(v, _)| format!("'{}'", v))
1232                .collect();
1233            out.push_str(&format!("legend({});\n", labels.join(", ")));
1234        }
1235        out
1236    }
1237}
1238/// A MATLAB struct field value.
1239#[allow(dead_code)]
1240#[derive(Debug, Clone, PartialEq)]
1241pub struct MatlabStructField {
1242    /// Field name.
1243    pub name: String,
1244    /// Field value.
1245    pub value: MatlabExpr,
1246}
1247impl MatlabStructField {
1248    /// Create a new struct field.
1249    #[allow(dead_code)]
1250    pub fn new(name: impl Into<String>, value: MatlabExpr) -> Self {
1251        MatlabStructField {
1252            name: name.into(),
1253            value,
1254        }
1255    }
1256}
1257/// MATLAB statement.
1258#[derive(Debug, Clone, PartialEq)]
1259pub enum MatlabStmt {
1260    /// Assignment: `[a, b] = f(x)` or `a = expr`
1261    Assign {
1262        lhs: Vec<String>,
1263        rhs: MatlabExpr,
1264        suppress: bool,
1265    },
1266    /// Complex left-hand side: `A(i,j) = expr`
1267    AssignIndex {
1268        obj: MatlabExpr,
1269        indices: Vec<MatlabExpr>,
1270        cell_index: bool,
1271        rhs: MatlabExpr,
1272        suppress: bool,
1273    },
1274    /// Struct field assignment: `s.field = expr`
1275    AssignField {
1276        obj: String,
1277        field: String,
1278        rhs: MatlabExpr,
1279        suppress: bool,
1280    },
1281    /// `for var = range; body; end`
1282    ForLoop {
1283        var: String,
1284        range: MatlabExpr,
1285        body: Vec<MatlabStmt>,
1286    },
1287    /// `while cond; body; end`
1288    WhileLoop {
1289        cond: MatlabExpr,
1290        body: Vec<MatlabStmt>,
1291    },
1292    /// `if cond; ... elseif ...; else ...; end`
1293    IfElseIf {
1294        cond: MatlabExpr,
1295        then_body: Vec<MatlabStmt>,
1296        elseif_branches: Vec<(MatlabExpr, Vec<MatlabStmt>)>,
1297        else_body: Option<Vec<MatlabStmt>>,
1298    },
1299    /// `switch expr; case val; ...; otherwise; ...; end`
1300    SwitchCase {
1301        expr: MatlabExpr,
1302        cases: Vec<(MatlabExpr, Vec<MatlabStmt>)>,
1303        otherwise: Option<Vec<MatlabStmt>>,
1304    },
1305    /// `return`
1306    Return,
1307    /// `break`
1308    Break,
1309    /// `continue`
1310    Continue,
1311    /// `error(msg, args...)`
1312    Error(MatlabExpr, Vec<MatlabExpr>),
1313    /// `warning(msg, args...)`
1314    Warning(MatlabExpr, Vec<MatlabExpr>),
1315    /// `disp(expr)` or `fprintf(...)`
1316    Disp(MatlabExpr),
1317    /// Function definition block
1318    FunctionDef(MatlabFunction),
1319    /// `try; ...; catch e; ...; end`
1320    TryCatch {
1321        body: Vec<MatlabStmt>,
1322        catch_var: Option<String>,
1323        catch_body: Vec<MatlabStmt>,
1324    },
1325    /// Class property validation
1326    ValidateProp(String, MatlabExpr),
1327    /// Expression statement (with or without semicolon suppression)
1328    Expr(MatlabExpr, bool),
1329    /// Comment: `% text`
1330    Comment(String),
1331    /// `global x y z`
1332    Global(Vec<String>),
1333    /// `persistent x`
1334    Persistent(Vec<String>),
1335    /// `classdef` inner block statement
1336    ClassdefStmt(String),
1337}
1338/// Argument validation block entry.
1339#[derive(Debug, Clone, PartialEq)]
1340pub struct MatlabArgValidation {
1341    pub name: String,
1342    pub size: Option<Vec<Option<usize>>>,
1343    pub class: Option<MatlabType>,
1344    pub validators: Vec<String>,
1345    pub default: Option<MatlabExpr>,
1346}
1347/// Helpers for generating MATLAB input validation statements.
1348#[allow(dead_code)]
1349pub struct MatlabValidation;
1350impl MatlabValidation {
1351    /// Emit `validateattributes(x, {'class'}, {attrs...})`.
1352    #[allow(dead_code)]
1353    pub fn validate_attributes(var: &str, class: &str, attributes: &[&str]) -> MatlabStmt {
1354        let attrs_str = attributes
1355            .iter()
1356            .map(|a| format!("'{}'", a))
1357            .collect::<Vec<_>>()
1358            .join(", ");
1359        MatlabStmt::Expr(
1360            MatlabExpr::Call(
1361                Box::new(MatlabExpr::Var("validateattributes".to_string())),
1362                vec![
1363                    MatlabExpr::Var(var.to_string()),
1364                    MatlabExpr::Lit(MatlabLiteral::Char(format!("{{{{'{}'}}}}", class))),
1365                    MatlabExpr::Lit(MatlabLiteral::Char(format!("{{{{{}}}}}", attrs_str))),
1366                ],
1367            ),
1368            true,
1369        )
1370    }
1371    /// Emit `narginchk(min, max)`.
1372    #[allow(dead_code)]
1373    pub fn narginchk(min: i64, max: i64) -> MatlabStmt {
1374        MatlabStmt::Expr(
1375            MatlabExpr::Call(
1376                Box::new(MatlabExpr::Var("narginchk".to_string())),
1377                vec![
1378                    MatlabExpr::Lit(MatlabLiteral::Integer(min)),
1379                    MatlabExpr::Lit(MatlabLiteral::Integer(max)),
1380                ],
1381            ),
1382            true,
1383        )
1384    }
1385    /// Emit `nargoutchk(min, max)`.
1386    #[allow(dead_code)]
1387    pub fn nargoutchk(min: i64, max: i64) -> MatlabStmt {
1388        MatlabStmt::Expr(
1389            MatlabExpr::Call(
1390                Box::new(MatlabExpr::Var("nargoutchk".to_string())),
1391                vec![
1392                    MatlabExpr::Lit(MatlabLiteral::Integer(min)),
1393                    MatlabExpr::Lit(MatlabLiteral::Integer(max)),
1394                ],
1395            ),
1396            true,
1397        )
1398    }
1399}
1400/// A MATLAB cell array literal.
1401#[allow(dead_code)]
1402#[derive(Debug, Clone, PartialEq)]
1403pub struct MatlabCellArray {
1404    /// Elements of the cell array.
1405    pub elements: Vec<MatlabExpr>,
1406}
1407impl MatlabCellArray {
1408    /// Create a new empty cell array.
1409    #[allow(dead_code)]
1410    pub fn new() -> Self {
1411        MatlabCellArray {
1412            elements: Vec::new(),
1413        }
1414    }
1415    /// Add an element.
1416    #[allow(dead_code)]
1417    pub fn add(mut self, elem: MatlabExpr) -> Self {
1418        self.elements.push(elem);
1419        self
1420    }
1421    /// Emit as a MATLAB cell array literal `{...}`.
1422    #[allow(dead_code)]
1423    pub fn emit(&self) -> String {
1424        let elems: Vec<_> = self.elements.iter().map(|e| e.to_string()).collect();
1425        format!("{{{}}}", elems.join(", "))
1426    }
1427}
1428/// Backend state for emitting MATLAB source code.
1429pub struct MatlabBackend {
1430    /// Accumulated output buffer
1431    pub(super) output: String,
1432    /// Current indentation level
1433    pub(super) indent: usize,
1434    /// Indentation string (default: two spaces)
1435    pub(super) indent_str: String,
1436    /// Known class definitions
1437    pub(super) classes: HashMap<String, MatlabClassdef>,
1438    /// Whether to emit Octave-compatible output (no `end` keywords)
1439    pub(super) octave_compat: bool,
1440}
1441impl MatlabBackend {
1442    /// Create a new MATLAB backend.
1443    pub fn new() -> Self {
1444        MatlabBackend {
1445            output: String::new(),
1446            indent: 0,
1447            indent_str: "  ".to_string(),
1448            classes: HashMap::new(),
1449            octave_compat: false,
1450        }
1451    }
1452    /// Create a backend configured for Octave compatibility.
1453    pub fn octave() -> Self {
1454        MatlabBackend {
1455            octave_compat: true,
1456            ..Self::new()
1457        }
1458    }
1459    /// Take the accumulated output, resetting the buffer.
1460    pub fn take_output(&mut self) -> String {
1461        std::mem::take(&mut self.output)
1462    }
1463    /// Register a known class definition.
1464    pub fn register_class(&mut self, cls: MatlabClassdef) {
1465        self.classes.insert(cls.name.clone(), cls);
1466    }
1467    pub(super) fn current_indent(&self) -> String {
1468        self.indent_str.repeat(self.indent)
1469    }
1470    pub(super) fn emit_line(&mut self, line: &str) {
1471        let indent = self.current_indent();
1472        let _ = writeln!(self.output, "{}{}", indent, line);
1473    }
1474    pub(super) fn emit_raw(&mut self, s: &str) {
1475        self.output.push_str(s);
1476    }
1477    pub(super) fn indent_up(&mut self) {
1478        self.indent += 1;
1479    }
1480    pub(super) fn indent_down(&mut self) {
1481        if self.indent > 0 {
1482            self.indent -= 1;
1483        }
1484    }
1485    /// Emit a complete MATLAB file.
1486    pub fn emit_file(&mut self, file: &MatlabFile) {
1487        if let Some(header) = &file.header_comment {
1488            for line in header.lines() {
1489                self.emit_line(&format!("% {}", line));
1490            }
1491            self.emit_line("");
1492        }
1493        if let Some(cls) = &file.classdef.clone() {
1494            self.emit_classdef(cls);
1495            return;
1496        }
1497        if file.is_script {
1498            for stmt in &file.scripts.clone() {
1499                self.emit_stmt(stmt);
1500            }
1501            return;
1502        }
1503        for (idx, fun) in file.functions.iter().enumerate() {
1504            if idx > 0 {
1505                self.emit_line("");
1506            }
1507            self.emit_function(fun);
1508        }
1509    }
1510    /// Emit a MATLAB function definition.
1511    pub fn emit_function(&mut self, fun: &MatlabFunction) {
1512        let outputs_str = match fun.outputs.len() {
1513            0 => String::new(),
1514            1 => format!("{} = ", fun.outputs[0]),
1515            _ => format!("[{}] = ", fun.outputs.join(", ")),
1516        };
1517        let inputs_str: Vec<String> = fun.inputs.iter().map(|p| p.name.clone()).collect();
1518        self.emit_line(&format!(
1519            "function {}{}({})",
1520            outputs_str,
1521            fun.name,
1522            inputs_str.join(", ")
1523        ));
1524        self.indent_up();
1525        if let Some(help) = &fun.help_text {
1526            for line in help.lines() {
1527                self.emit_line(&format!("% {}", line));
1528            }
1529        }
1530        if !fun.argument_validation.is_empty() {
1531            self.emit_line("arguments");
1532            self.indent_up();
1533            for av in &fun.argument_validation {
1534                self.emit_arg_validation(av);
1535            }
1536            self.indent_down();
1537            self.emit_line("end");
1538        }
1539        for stmt in &fun.body {
1540            self.emit_stmt(stmt);
1541        }
1542        self.indent_down();
1543        self.emit_line("end");
1544    }
1545    pub(super) fn emit_arg_validation(&mut self, av: &MatlabArgValidation) {
1546        let size_str = if let Some(sizes) = &av.size {
1547            let s: Vec<String> = sizes
1548                .iter()
1549                .map(|d| d.map(|n| n.to_string()).unwrap_or_else(|| ":".to_string()))
1550                .collect();
1551            format!("({}) ", s.join(","))
1552        } else {
1553            String::new()
1554        };
1555        let class_str = if let Some(cls) = &av.class {
1556            format!("{} ", cls)
1557        } else {
1558            String::new()
1559        };
1560        let validators_str = if !av.validators.is_empty() {
1561            format!(" {{{}}}", av.validators.join(", "))
1562        } else {
1563            String::new()
1564        };
1565        let default_str = if let Some(def) = &av.default {
1566            format!(" = {}", self.emit_expr(def))
1567        } else {
1568            String::new()
1569        };
1570        self.emit_line(&format!(
1571            "{}{}{}{}{}",
1572            av.name, size_str, class_str, validators_str, default_str
1573        ));
1574    }
1575    /// Emit a MATLAB classdef.
1576    pub fn emit_classdef(&mut self, cls: &MatlabClassdef) {
1577        let inherits_str = if cls.superclasses.is_empty() {
1578            String::new()
1579        } else {
1580            format!(" < {}", cls.superclasses.join(" & "))
1581        };
1582        self.emit_line(&format!("classdef {}{}", cls.name, inherits_str));
1583        self.indent_up();
1584        let pub_props: Vec<&MatlabProperty> = cls
1585            .properties
1586            .iter()
1587            .filter(|p| p.access == PropAccess::Public)
1588            .collect();
1589        let prot_props: Vec<&MatlabProperty> = cls
1590            .properties
1591            .iter()
1592            .filter(|p| p.access == PropAccess::Protected)
1593            .collect();
1594        let priv_props: Vec<&MatlabProperty> = cls
1595            .properties
1596            .iter()
1597            .filter(|p| p.access == PropAccess::Private)
1598            .collect();
1599        if !pub_props.is_empty() {
1600            self.emit_line("properties");
1601            self.indent_up();
1602            for prop in pub_props {
1603                self.emit_property(prop);
1604            }
1605            self.indent_down();
1606            self.emit_line("end");
1607        }
1608        if !prot_props.is_empty() {
1609            self.emit_line("properties (Access = protected)");
1610            self.indent_up();
1611            for prop in prot_props {
1612                self.emit_property(prop);
1613            }
1614            self.indent_down();
1615            self.emit_line("end");
1616        }
1617        if !priv_props.is_empty() {
1618            self.emit_line("properties (Access = private)");
1619            self.indent_up();
1620            for prop in priv_props {
1621                self.emit_property(prop);
1622            }
1623            self.indent_down();
1624            self.emit_line("end");
1625        }
1626        if !cls.events.is_empty() {
1627            self.emit_line("events");
1628            self.indent_up();
1629            for ev in &cls.events {
1630                self.emit_line(ev);
1631            }
1632            self.indent_down();
1633            self.emit_line("end");
1634        }
1635        if !cls.enumerations.is_empty() {
1636            self.emit_line("enumeration");
1637            self.indent_up();
1638            for (name, args) in &cls.enumerations {
1639                let args_str: Vec<String> = args.iter().map(|a| self.emit_expr(a)).collect();
1640                if args_str.is_empty() {
1641                    self.emit_line(name);
1642                } else {
1643                    self.emit_line(&format!("{}({})", name, args_str.join(", ")));
1644                }
1645            }
1646            self.indent_down();
1647            self.emit_line("end");
1648        }
1649        if !cls.methods.is_empty() {
1650            self.emit_line("methods");
1651            self.indent_up();
1652            for method in &cls.methods.clone() {
1653                self.emit_function(method);
1654                self.emit_line("");
1655            }
1656            self.indent_down();
1657            self.emit_line("end");
1658        }
1659        self.indent_down();
1660        self.emit_line("end");
1661    }
1662    pub(super) fn emit_property(&mut self, prop: &MatlabProperty) {
1663        let ty_str = if let Some(ty) = &prop.ty {
1664            format!(" ({})", ty)
1665        } else {
1666            String::new()
1667        };
1668        if let Some(default) = &prop.default {
1669            let default_str = self.emit_expr(default);
1670            self.emit_line(&format!("{}{} = {}", prop.name, ty_str, default_str));
1671        } else {
1672            self.emit_line(&format!("{}{}", prop.name, ty_str));
1673        }
1674    }
1675    /// Emit a single MATLAB statement.
1676    pub fn emit_stmt(&mut self, stmt: &MatlabStmt) {
1677        match stmt {
1678            MatlabStmt::Assign { lhs, rhs, suppress } => {
1679                let rhs_str = self.emit_expr(rhs);
1680                let semi = if *suppress { ";" } else { "" };
1681                match lhs.len() {
1682                    0 => self.emit_line(&format!("{}{}", rhs_str, semi)),
1683                    1 => self.emit_line(&format!("{} = {}{}", lhs[0], rhs_str, semi)),
1684                    _ => self.emit_line(&format!("[{}] = {}{}", lhs.join(", "), rhs_str, semi)),
1685                }
1686            }
1687            MatlabStmt::AssignIndex {
1688                obj,
1689                indices,
1690                cell_index,
1691                rhs,
1692                suppress,
1693            } => {
1694                let obj_str = self.emit_expr(obj);
1695                let idx_str: Vec<String> = indices.iter().map(|i| self.emit_expr(i)).collect();
1696                let rhs_str = self.emit_expr(rhs);
1697                let semi = if *suppress { ";" } else { "" };
1698                let (open, close) = if *cell_index { ("{", "}") } else { ("(", ")") };
1699                self.emit_line(&format!(
1700                    "{}{}{}{}{}){} = {}{}",
1701                    obj_str,
1702                    open,
1703                    idx_str.join(", "),
1704                    close,
1705                    "",
1706                    "",
1707                    rhs_str,
1708                    semi
1709                ));
1710                if let Some(bad) = self.output.lines().last().map(|l| l.to_string()) {
1711                    let len_to_remove = bad.len() + 1;
1712                    let new_len = self.output.len().saturating_sub(len_to_remove);
1713                    self.output.truncate(new_len);
1714                }
1715                let indent = self.current_indent();
1716                let _ = writeln!(
1717                    self.output,
1718                    "{}{}{}{}{}{}{}",
1719                    indent,
1720                    obj_str,
1721                    open,
1722                    idx_str.join(", "),
1723                    close,
1724                    format_args!(" = {}", rhs_str),
1725                    semi
1726                );
1727            }
1728            MatlabStmt::AssignField {
1729                obj,
1730                field,
1731                rhs,
1732                suppress,
1733            } => {
1734                let rhs_str = self.emit_expr(rhs);
1735                let semi = if *suppress { ";" } else { "" };
1736                self.emit_line(&format!("{}.{} = {}{}", obj, field, rhs_str, semi));
1737            }
1738            MatlabStmt::ForLoop { var, range, body } => {
1739                let range_str = self.emit_expr(range);
1740                self.emit_line(&format!("for {} = {}", var, range_str));
1741                self.indent_up();
1742                for s in body {
1743                    self.emit_stmt(s);
1744                }
1745                self.indent_down();
1746                self.emit_line("end");
1747            }
1748            MatlabStmt::WhileLoop { cond, body } => {
1749                let cond_str = self.emit_expr(cond);
1750                self.emit_line(&format!("while {}", cond_str));
1751                self.indent_up();
1752                for s in body {
1753                    self.emit_stmt(s);
1754                }
1755                self.indent_down();
1756                self.emit_line("end");
1757            }
1758            MatlabStmt::IfElseIf {
1759                cond,
1760                then_body,
1761                elseif_branches,
1762                else_body,
1763            } => {
1764                let cond_str = self.emit_expr(cond);
1765                self.emit_line(&format!("if {}", cond_str));
1766                self.indent_up();
1767                for s in then_body {
1768                    self.emit_stmt(s);
1769                }
1770                self.indent_down();
1771                for (elif_cond, elif_body) in elseif_branches {
1772                    let elif_str = self.emit_expr(elif_cond);
1773                    self.emit_line(&format!("elseif {}", elif_str));
1774                    self.indent_up();
1775                    for s in elif_body {
1776                        self.emit_stmt(s);
1777                    }
1778                    self.indent_down();
1779                }
1780                if let Some(else_stmts) = else_body {
1781                    self.emit_line("else");
1782                    self.indent_up();
1783                    for s in else_stmts {
1784                        self.emit_stmt(s);
1785                    }
1786                    self.indent_down();
1787                }
1788                self.emit_line("end");
1789            }
1790            MatlabStmt::SwitchCase {
1791                expr,
1792                cases,
1793                otherwise,
1794            } => {
1795                let expr_str = self.emit_expr(expr);
1796                self.emit_line(&format!("switch {}", expr_str));
1797                self.indent_up();
1798                for (val, body) in cases {
1799                    let val_str = self.emit_expr(val);
1800                    self.emit_line(&format!("case {}", val_str));
1801                    self.indent_up();
1802                    for s in body {
1803                        self.emit_stmt(s);
1804                    }
1805                    self.indent_down();
1806                }
1807                if let Some(other_stmts) = otherwise {
1808                    self.emit_line("otherwise");
1809                    self.indent_up();
1810                    for s in other_stmts {
1811                        self.emit_stmt(s);
1812                    }
1813                    self.indent_down();
1814                }
1815                self.indent_down();
1816                self.emit_line("end");
1817            }
1818            MatlabStmt::Return => self.emit_line("return;"),
1819            MatlabStmt::Break => self.emit_line("break;"),
1820            MatlabStmt::Continue => self.emit_line("continue;"),
1821            MatlabStmt::Error(fmt_expr, args) => {
1822                let fmt_str = self.emit_expr(fmt_expr);
1823                if args.is_empty() {
1824                    self.emit_line(&format!("error({});", fmt_str));
1825                } else {
1826                    let args_str: Vec<String> = args.iter().map(|a| self.emit_expr(a)).collect();
1827                    self.emit_line(&format!("error({}, {});", fmt_str, args_str.join(", ")));
1828                }
1829            }
1830            MatlabStmt::Warning(fmt_expr, args) => {
1831                let fmt_str = self.emit_expr(fmt_expr);
1832                if args.is_empty() {
1833                    self.emit_line(&format!("warning({});", fmt_str));
1834                } else {
1835                    let args_str: Vec<String> = args.iter().map(|a| self.emit_expr(a)).collect();
1836                    self.emit_line(&format!("warning({}, {});", fmt_str, args_str.join(", ")));
1837                }
1838            }
1839            MatlabStmt::Disp(expr) => {
1840                let e_str = self.emit_expr(expr);
1841                self.emit_line(&format!("disp({});", e_str));
1842            }
1843            MatlabStmt::FunctionDef(fun) => {
1844                self.emit_function(fun);
1845            }
1846            MatlabStmt::TryCatch {
1847                body,
1848                catch_var,
1849                catch_body,
1850            } => {
1851                self.emit_line("try");
1852                self.indent_up();
1853                for s in body {
1854                    self.emit_stmt(s);
1855                }
1856                self.indent_down();
1857                if let Some(var) = catch_var {
1858                    self.emit_line(&format!("catch {}", var));
1859                } else {
1860                    self.emit_line("catch");
1861                }
1862                self.indent_up();
1863                for s in catch_body {
1864                    self.emit_stmt(s);
1865                }
1866                self.indent_down();
1867                self.emit_line("end");
1868            }
1869            MatlabStmt::ValidateProp(name, expr) => {
1870                let e_str = self.emit_expr(expr);
1871                self.emit_line(&format!("validateattributes({}, {});", name, e_str));
1872            }
1873            MatlabStmt::Expr(expr, suppress) => {
1874                let e_str = self.emit_expr(expr);
1875                let semi = if *suppress { ";" } else { "" };
1876                self.emit_line(&format!("{}{}", e_str, semi));
1877            }
1878            MatlabStmt::Comment(text) => {
1879                for line in text.lines() {
1880                    self.emit_line(&format!("% {}", line));
1881                }
1882            }
1883            MatlabStmt::Global(names) => {
1884                self.emit_line(&format!("global {}", names.join(" ")));
1885            }
1886            MatlabStmt::Persistent(names) => {
1887                self.emit_line(&format!("persistent {}", names.join(" ")));
1888            }
1889            MatlabStmt::ClassdefStmt(s) => {
1890                self.emit_line(s);
1891            }
1892        }
1893    }
1894    /// Emit a MATLAB expression to a string.
1895    pub fn emit_expr(&mut self, expr: &MatlabExpr) -> String {
1896        self.emit_expr_pure(expr)
1897    }
1898    /// Emit a MATLAB expression to a string (pure).
1899    pub fn emit_expr_pure(&self, expr: &MatlabExpr) -> String {
1900        match expr {
1901            MatlabExpr::Lit(lit) => self.emit_literal(lit),
1902            MatlabExpr::Var(name) => name.clone(),
1903            MatlabExpr::MatrixLit(rows) => {
1904                let rows_str: Vec<String> = rows
1905                    .iter()
1906                    .map(|row| {
1907                        let elems: Vec<String> =
1908                            row.iter().map(|e| self.emit_expr_pure(e)).collect();
1909                        elems.join(", ")
1910                    })
1911                    .collect();
1912                format!("[{}]", rows_str.join("; "))
1913            }
1914            MatlabExpr::CellLit(rows) => {
1915                let rows_str: Vec<String> = rows
1916                    .iter()
1917                    .map(|row| {
1918                        let elems: Vec<String> =
1919                            row.iter().map(|e| self.emit_expr_pure(e)).collect();
1920                        elems.join(", ")
1921                    })
1922                    .collect();
1923                format!("{{{}}}", rows_str.join("; "))
1924            }
1925            MatlabExpr::ColonRange { start, step, end } => {
1926                let start_str = self.emit_expr_pure(start);
1927                let end_str = self.emit_expr_pure(end);
1928                if let Some(step_expr) = step {
1929                    let step_str = self.emit_expr_pure(step_expr);
1930                    format!("{}:{}:{}", start_str, step_str, end_str)
1931                } else {
1932                    format!("{}:{}", start_str, end_str)
1933                }
1934            }
1935            MatlabExpr::Call(func, args) => {
1936                let func_str = self.emit_expr_pure(func);
1937                let args_str: Vec<String> = args.iter().map(|a| self.emit_expr_pure(a)).collect();
1938                format!("{}({})", func_str, args_str.join(", "))
1939            }
1940            MatlabExpr::Index {
1941                obj,
1942                indices,
1943                cell_index,
1944            } => {
1945                let obj_str = self.emit_expr_pure(obj);
1946                let idx_str: Vec<String> = indices.iter().map(|i| self.emit_expr_pure(i)).collect();
1947                let (open, close) = if *cell_index { ("{", "}") } else { ("(", ")") };
1948                format!("{}{}{}{}", obj_str, open, idx_str.join(", "), close)
1949            }
1950            MatlabExpr::FieldAccess(obj, field) => {
1951                let obj_str = self.emit_expr_pure(obj);
1952                format!("{}.{}", obj_str, field)
1953            }
1954            MatlabExpr::BinaryOp(op, lhs, rhs) => {
1955                let lhs_str = self.emit_expr_pure(lhs);
1956                let rhs_str = self.emit_expr_pure(rhs);
1957                format!("{} {} {}", lhs_str, op, rhs_str)
1958            }
1959            MatlabExpr::UnaryOp(op, operand, postfix) => {
1960                let operand_str = self.emit_expr_pure(operand);
1961                if *postfix {
1962                    format!("{}{}", operand_str, op)
1963                } else {
1964                    format!("{}{}", op, operand_str)
1965                }
1966            }
1967            MatlabExpr::IfExpr(cond, then_expr, else_expr) => {
1968                let cond_str = self.emit_expr_pure(cond);
1969                let then_str = self.emit_expr_pure(then_expr);
1970                let else_str = self.emit_expr_pure(else_expr);
1971                format!("({{{}; {}}}{{{}+1}})", else_str, then_str, cond_str)
1972            }
1973            MatlabExpr::AnonFunc(params, body) => {
1974                let params_str = params.join(", ");
1975                let body_str = self.emit_expr_pure(body);
1976                format!("@({}) {}", params_str, body_str)
1977            }
1978            MatlabExpr::End => "end".to_string(),
1979            MatlabExpr::Colon => ":".to_string(),
1980            MatlabExpr::Nargin => "nargin".to_string(),
1981            MatlabExpr::Nargout => "nargout".to_string(),
1982        }
1983    }
1984    pub(super) fn emit_literal(&self, lit: &MatlabLiteral) -> String {
1985        match lit {
1986            MatlabLiteral::Double(f) => {
1987                if f.fract() == 0.0 && f.abs() < 1e15 {
1988                    format!("{}", *f as i64)
1989                } else {
1990                    format!("{}", f)
1991                }
1992            }
1993            MatlabLiteral::Integer(n) => format!("{}", n),
1994            MatlabLiteral::Logical(b) => {
1995                if *b {
1996                    "true".to_string()
1997                } else {
1998                    "false".to_string()
1999                }
2000            }
2001            MatlabLiteral::Char(s) => format!("'{}'", s.replace('\'', "''")),
2002            MatlabLiteral::Str(s) => format!("\"{}\"", s.replace('"', "\"\"")),
2003            MatlabLiteral::Empty => "[]".to_string(),
2004            MatlabLiteral::NaN => "NaN".to_string(),
2005            MatlabLiteral::Inf(neg) => {
2006                if *neg {
2007                    "-Inf".to_string()
2008                } else {
2009                    "Inf".to_string()
2010                }
2011            }
2012            MatlabLiteral::Pi => "pi".to_string(),
2013            MatlabLiteral::Eps => "eps".to_string(),
2014        }
2015    }
2016}
2017#[allow(dead_code)]
2018#[derive(Debug, Clone)]
2019pub struct MatlabLivenessInfo {
2020    pub live_in: Vec<std::collections::HashSet<u32>>,
2021    pub live_out: Vec<std::collections::HashSet<u32>>,
2022    pub defs: Vec<std::collections::HashSet<u32>>,
2023    pub uses: Vec<std::collections::HashSet<u32>>,
2024}
2025impl MatlabLivenessInfo {
2026    #[allow(dead_code)]
2027    pub fn new(block_count: usize) -> Self {
2028        MatlabLivenessInfo {
2029            live_in: vec![std::collections::HashSet::new(); block_count],
2030            live_out: vec![std::collections::HashSet::new(); block_count],
2031            defs: vec![std::collections::HashSet::new(); block_count],
2032            uses: vec![std::collections::HashSet::new(); block_count],
2033        }
2034    }
2035    #[allow(dead_code)]
2036    pub fn add_def(&mut self, block: usize, var: u32) {
2037        if block < self.defs.len() {
2038            self.defs[block].insert(var);
2039        }
2040    }
2041    #[allow(dead_code)]
2042    pub fn add_use(&mut self, block: usize, var: u32) {
2043        if block < self.uses.len() {
2044            self.uses[block].insert(var);
2045        }
2046    }
2047    #[allow(dead_code)]
2048    pub fn is_live_in(&self, block: usize, var: u32) -> bool {
2049        self.live_in
2050            .get(block)
2051            .map(|s| s.contains(&var))
2052            .unwrap_or(false)
2053    }
2054    #[allow(dead_code)]
2055    pub fn is_live_out(&self, block: usize, var: u32) -> bool {
2056        self.live_out
2057            .get(block)
2058            .map(|s| s.contains(&var))
2059            .unwrap_or(false)
2060    }
2061}
2062/// Helpers for constructing common MATLAB numeric operations.
2063#[allow(dead_code)]
2064pub struct MatlabNumericOps;
2065impl MatlabNumericOps {
2066    /// Element-wise multiply `a .* b`.
2067    #[allow(dead_code)]
2068    pub fn elem_mul(a: MatlabExpr, b: MatlabExpr) -> MatlabExpr {
2069        MatlabExpr::BinaryOp(".*".to_string(), Box::new(a), Box::new(b))
2070    }
2071    /// Element-wise divide `a ./ b`.
2072    #[allow(dead_code)]
2073    pub fn elem_div(a: MatlabExpr, b: MatlabExpr) -> MatlabExpr {
2074        MatlabExpr::BinaryOp("./".to_string(), Box::new(a), Box::new(b))
2075    }
2076    /// Matrix multiply `a * b`.
2077    #[allow(dead_code)]
2078    pub fn mat_mul(a: MatlabExpr, b: MatlabExpr) -> MatlabExpr {
2079        MatlabExpr::BinaryOp("*".to_string(), Box::new(a), Box::new(b))
2080    }
2081    /// Matrix power `a ^ n`.
2082    #[allow(dead_code)]
2083    pub fn mat_pow(a: MatlabExpr, n: MatlabExpr) -> MatlabExpr {
2084        MatlabExpr::BinaryOp("^".to_string(), Box::new(a), Box::new(n))
2085    }
2086    /// Element-wise power `a .^ n`.
2087    #[allow(dead_code)]
2088    pub fn elem_pow(a: MatlabExpr, n: MatlabExpr) -> MatlabExpr {
2089        MatlabExpr::BinaryOp(".^".to_string(), Box::new(a), Box::new(n))
2090    }
2091    /// Colon range `start:stop`.
2092    #[allow(dead_code)]
2093    pub fn range(start: MatlabExpr, stop: MatlabExpr) -> MatlabExpr {
2094        MatlabExpr::ColonRange {
2095            start: Box::new(start),
2096            step: None,
2097            end: Box::new(stop),
2098        }
2099    }
2100    /// Colon range with step `start:step:stop`.
2101    #[allow(dead_code)]
2102    pub fn range_step(start: MatlabExpr, step: MatlabExpr, stop: MatlabExpr) -> MatlabExpr {
2103        MatlabExpr::ColonRange {
2104            start: Box::new(start),
2105            step: Some(Box::new(step)),
2106            end: Box::new(stop),
2107        }
2108    }
2109    /// `abs(x)`.
2110    #[allow(dead_code)]
2111    pub fn abs(x: MatlabExpr) -> MatlabExpr {
2112        MatlabExpr::Call(Box::new(MatlabExpr::Var("abs".to_string())), vec![x])
2113    }
2114    /// `sum(x)`.
2115    #[allow(dead_code)]
2116    pub fn sum(x: MatlabExpr) -> MatlabExpr {
2117        MatlabExpr::Call(Box::new(MatlabExpr::Var("sum".to_string())), vec![x])
2118    }
2119    /// `prod(x)`.
2120    #[allow(dead_code)]
2121    pub fn prod(x: MatlabExpr) -> MatlabExpr {
2122        MatlabExpr::Call(Box::new(MatlabExpr::Var("prod".to_string())), vec![x])
2123    }
2124    /// `min(x)`.
2125    #[allow(dead_code)]
2126    pub fn min(x: MatlabExpr) -> MatlabExpr {
2127        MatlabExpr::Call(Box::new(MatlabExpr::Var("min".to_string())), vec![x])
2128    }
2129    /// `max(x)`.
2130    #[allow(dead_code)]
2131    pub fn max(x: MatlabExpr) -> MatlabExpr {
2132        MatlabExpr::Call(Box::new(MatlabExpr::Var("max".to_string())), vec![x])
2133    }
2134    /// `mean(x)`.
2135    #[allow(dead_code)]
2136    pub fn mean(x: MatlabExpr) -> MatlabExpr {
2137        MatlabExpr::Call(Box::new(MatlabExpr::Var("mean".to_string())), vec![x])
2138    }
2139    /// `std(x)`.
2140    #[allow(dead_code)]
2141    pub fn std(x: MatlabExpr) -> MatlabExpr {
2142        MatlabExpr::Call(Box::new(MatlabExpr::Var("std".to_string())), vec![x])
2143    }
2144    /// `sqrt(x)`.
2145    #[allow(dead_code)]
2146    pub fn sqrt(x: MatlabExpr) -> MatlabExpr {
2147        MatlabExpr::Call(Box::new(MatlabExpr::Var("sqrt".to_string())), vec![x])
2148    }
2149    /// `norm(x)`.
2150    #[allow(dead_code)]
2151    pub fn norm(x: MatlabExpr) -> MatlabExpr {
2152        MatlabExpr::Call(Box::new(MatlabExpr::Var("norm".to_string())), vec![x])
2153    }
2154    /// `det(A)`.
2155    #[allow(dead_code)]
2156    pub fn det(a: MatlabExpr) -> MatlabExpr {
2157        MatlabExpr::Call(Box::new(MatlabExpr::Var("det".to_string())), vec![a])
2158    }
2159    /// `inv(A)`.
2160    #[allow(dead_code)]
2161    pub fn inv(a: MatlabExpr) -> MatlabExpr {
2162        MatlabExpr::Call(Box::new(MatlabExpr::Var("inv".to_string())), vec![a])
2163    }
2164    /// `eig(A)`.
2165    #[allow(dead_code)]
2166    pub fn eig(a: MatlabExpr) -> MatlabExpr {
2167        MatlabExpr::Call(Box::new(MatlabExpr::Var("eig".to_string())), vec![a])
2168    }
2169    /// `svd(A)`.
2170    #[allow(dead_code)]
2171    pub fn svd(a: MatlabExpr) -> MatlabExpr {
2172        MatlabExpr::Call(Box::new(MatlabExpr::Var("svd".to_string())), vec![a])
2173    }
2174    /// `linspace(a, b, n)`.
2175    #[allow(dead_code)]
2176    pub fn linspace(a: MatlabExpr, b: MatlabExpr, n: MatlabExpr) -> MatlabExpr {
2177        MatlabExpr::Call(
2178            Box::new(MatlabExpr::Var("linspace".to_string())),
2179            vec![a, b, n],
2180        )
2181    }
2182    /// `mod(a, m)`.
2183    #[allow(dead_code)]
2184    pub fn matlab_mod(a: MatlabExpr, m: MatlabExpr) -> MatlabExpr {
2185        MatlabExpr::Call(Box::new(MatlabExpr::Var("mod".to_string())), vec![a, m])
2186    }
2187    /// `floor(x)`.
2188    #[allow(dead_code)]
2189    pub fn floor(x: MatlabExpr) -> MatlabExpr {
2190        MatlabExpr::Call(Box::new(MatlabExpr::Var("floor".to_string())), vec![x])
2191    }
2192    /// `ceil(x)`.
2193    #[allow(dead_code)]
2194    pub fn ceil(x: MatlabExpr) -> MatlabExpr {
2195        MatlabExpr::Call(Box::new(MatlabExpr::Var("ceil".to_string())), vec![x])
2196    }
2197    /// `round(x)`.
2198    #[allow(dead_code)]
2199    pub fn round(x: MatlabExpr) -> MatlabExpr {
2200        MatlabExpr::Call(Box::new(MatlabExpr::Var("round".to_string())), vec![x])
2201    }
2202    /// `fix(x)` — truncate toward zero.
2203    #[allow(dead_code)]
2204    pub fn fix(x: MatlabExpr) -> MatlabExpr {
2205        MatlabExpr::Call(Box::new(MatlabExpr::Var("fix".to_string())), vec![x])
2206    }
2207    /// `rem(a, m)` — remainder (sign matches dividend).
2208    #[allow(dead_code)]
2209    pub fn rem(a: MatlabExpr, m: MatlabExpr) -> MatlabExpr {
2210        MatlabExpr::Call(Box::new(MatlabExpr::Var("rem".to_string())), vec![a, m])
2211    }
2212}
2213/// A MATLAB function definition.
2214#[derive(Debug, Clone, PartialEq)]
2215pub struct MatlabFunction {
2216    /// Function name
2217    pub name: String,
2218    /// Input parameter names
2219    pub inputs: Vec<MatlabParam>,
2220    /// Output parameter names
2221    pub outputs: Vec<String>,
2222    /// Function body
2223    pub body: Vec<MatlabStmt>,
2224    /// Whether this is a nested function
2225    pub is_nested: bool,
2226    /// Whether this is a local function (appears after main function)
2227    pub is_local: bool,
2228    /// Help text (first comment block)
2229    pub help_text: Option<String>,
2230    /// Validation blocks (arguments ... end)
2231    pub argument_validation: Vec<MatlabArgValidation>,
2232}
2233impl MatlabFunction {
2234    pub fn new(
2235        name: &str,
2236        inputs: Vec<MatlabParam>,
2237        outputs: Vec<String>,
2238        body: Vec<MatlabStmt>,
2239    ) -> Self {
2240        MatlabFunction {
2241            name: name.to_string(),
2242            inputs,
2243            outputs,
2244            body,
2245            is_nested: false,
2246            is_local: false,
2247            help_text: None,
2248            argument_validation: Vec::new(),
2249        }
2250    }
2251    pub fn nested(mut self) -> Self {
2252        self.is_nested = true;
2253        self
2254    }
2255    pub fn local(mut self) -> Self {
2256        self.is_local = true;
2257        self
2258    }
2259    pub fn with_help(mut self, help: &str) -> Self {
2260        self.help_text = Some(help.to_string());
2261        self
2262    }
2263}
2264/// A high-level builder for MATLAB modules (collections of functions).
2265#[allow(dead_code)]
2266pub struct MatlabModuleBuilder {
2267    /// Module name.
2268    pub name: String,
2269    /// Functions in declaration order.
2270    pub functions: Vec<MatlabFunction>,
2271    /// Classes in declaration order.
2272    pub classes: Vec<MatlabClassdef>,
2273    /// Scripts (stand-alone statements).
2274    pub scripts: Vec<MatlabScript>,
2275    /// Global variable declarations.
2276    pub globals: Vec<String>,
2277    /// Configuration.
2278    pub config: MatlabGenConfig,
2279}
2280impl MatlabModuleBuilder {
2281    /// Create a new module builder.
2282    #[allow(dead_code)]
2283    pub fn new(name: impl Into<String>) -> Self {
2284        MatlabModuleBuilder {
2285            name: name.into(),
2286            functions: Vec::new(),
2287            classes: Vec::new(),
2288            scripts: Vec::new(),
2289            globals: Vec::new(),
2290            config: MatlabGenConfig::default(),
2291        }
2292    }
2293    /// Add a function.
2294    #[allow(dead_code)]
2295    pub fn add_function(mut self, func: MatlabFunction) -> Self {
2296        self.functions.push(func);
2297        self
2298    }
2299    /// Add a class.
2300    #[allow(dead_code)]
2301    pub fn add_class(mut self, cls: MatlabClassdef) -> Self {
2302        self.classes.push(cls);
2303        self
2304    }
2305    /// Add a script.
2306    #[allow(dead_code)]
2307    pub fn add_script(mut self, script: MatlabScript) -> Self {
2308        self.scripts.push(script);
2309        self
2310    }
2311    /// Declare a global variable.
2312    #[allow(dead_code)]
2313    pub fn declare_global(mut self, name: impl Into<String>) -> Self {
2314        self.globals.push(name.into());
2315        self
2316    }
2317    /// Emit the entire module.
2318    #[allow(dead_code)]
2319    pub fn emit(&self) -> String {
2320        let mut backend = MatlabBackend::new();
2321        if !self.globals.is_empty() {
2322            let globals = self.globals.join(" ");
2323            backend.emit_stmt(&MatlabStmt::Comment(format!("globals: {}", globals)));
2324        }
2325        for func in &self.functions {
2326            backend.emit_function(func);
2327        }
2328        for cls in &self.classes {
2329            backend.emit_classdef(cls);
2330        }
2331        backend.take_output()
2332    }
2333    /// Number of items in the module.
2334    #[allow(dead_code)]
2335    pub fn total_items(&self) -> usize {
2336        self.functions.len() + self.classes.len() + self.scripts.len()
2337    }
2338}