1use std::collections::HashMap;
6use std::collections::{HashSet, VecDeque};
7use std::fmt::Write as FmtWrite;
8
9#[allow(dead_code)]
11pub struct MatlabOptimizer {
12 pub rewrites: usize,
14}
15impl MatlabOptimizer {
16 #[allow(dead_code)]
18 pub fn new() -> Self {
19 MatlabOptimizer { rewrites: 0 }
20 }
21 #[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#[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#[derive(Debug, Clone, PartialEq)]
77pub enum MatlabType {
78 Double,
80 Single,
82 Int8,
84 Int16,
86 Int32,
88 Int64,
90 Uint8,
92 Uint16,
94 Uint32,
96 Uint64,
98 Logical,
100 Char,
102 StringArray,
104 Cell,
106 StructType(String),
108 FunctionHandle,
110 Sparse,
112 Array(Box<MatlabType>, Vec<Option<usize>>),
114 Class(String),
116 Any,
118}
119#[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#[derive(Debug, Clone, PartialEq)]
216pub enum MatlabExpr {
217 Lit(MatlabLiteral),
219 Var(String),
221 MatrixLit(Vec<Vec<MatlabExpr>>),
223 CellLit(Vec<Vec<MatlabExpr>>),
225 ColonRange {
227 start: Box<MatlabExpr>,
228 step: Option<Box<MatlabExpr>>,
229 end: Box<MatlabExpr>,
230 },
231 Call(Box<MatlabExpr>, Vec<MatlabExpr>),
233 Index {
235 obj: Box<MatlabExpr>,
236 indices: Vec<MatlabExpr>,
237 cell_index: bool,
238 },
239 FieldAccess(Box<MatlabExpr>, String),
241 BinaryOp(String, Box<MatlabExpr>, Box<MatlabExpr>),
243 UnaryOp(String, Box<MatlabExpr>, bool),
245 IfExpr(Box<MatlabExpr>, Box<MatlabExpr>, Box<MatlabExpr>),
247 AnonFunc(Vec<String>, Box<MatlabExpr>),
249 End,
251 Colon,
253 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#[allow(dead_code)]
299#[derive(Debug, Clone)]
300pub struct MatlabGenConfig {
301 pub suppress_output: bool,
303 pub emit_section_markers: bool,
305 pub octave_compat: bool,
307 pub indent: String,
309 pub emit_function_end: bool,
311 pub prefer_anon_functions: bool,
313}
314impl MatlabGenConfig {
315 #[allow(dead_code)]
317 pub fn octave() -> Self {
318 MatlabGenConfig {
319 octave_compat: true,
320 ..Default::default()
321 }
322 }
323 #[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#[derive(Debug, Clone, PartialEq)]
451pub enum MatlabLiteral {
452 Double(f64),
454 Integer(i64),
456 Logical(bool),
458 Char(String),
460 Str(String),
462 Empty,
464 NaN,
466 Inf(bool),
468 Pi,
470 Eps,
472}
473#[allow(dead_code)]
475#[derive(Debug, Clone, PartialEq)]
476pub struct MatlabAnnotation {
477 pub summary: String,
479 pub description: Option<String>,
481 pub inputs: Vec<(String, String)>,
483 pub outputs: Vec<(String, String)>,
485 pub examples: Vec<String>,
487 pub see_also: Vec<String>,
489}
490impl MatlabAnnotation {
491 #[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 #[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 #[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 #[allow(dead_code)]
517 pub fn example(mut self, code: impl Into<String>) -> Self {
518 self.examples.push(code.into());
519 self
520 }
521 #[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#[allow(dead_code)]
561pub struct MatlabTypeChecker {
562 pub env: HashMap<String, MatlabType>,
564 pub errors: Vec<String>,
566}
567impl MatlabTypeChecker {
568 #[allow(dead_code)]
570 pub fn new() -> Self {
571 MatlabTypeChecker {
572 env: HashMap::new(),
573 errors: Vec::new(),
574 }
575 }
576 #[allow(dead_code)]
578 pub fn declare(&mut self, name: impl Into<String>, ty: MatlabType) {
579 self.env.insert(name.into(), ty);
580 }
581 #[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 #[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 #[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#[allow(dead_code)]
804#[derive(Debug, Clone, PartialEq)]
805pub struct MatlabStructLiteral {
806 pub fields: Vec<MatlabStructField>,
808}
809impl MatlabStructLiteral {
810 #[allow(dead_code)]
812 pub fn new() -> Self {
813 MatlabStructLiteral { fields: Vec::new() }
814 }
815 #[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 #[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#[allow(dead_code)]
881#[derive(Debug, Clone, PartialEq)]
882pub struct MatlabMatrix {
883 pub rows: Vec<Vec<MatlabExpr>>,
885}
886impl MatlabMatrix {
887 #[allow(dead_code)]
889 pub fn new() -> Self {
890 MatlabMatrix { rows: Vec::new() }
891 }
892 #[allow(dead_code)]
894 pub fn add_row(mut self, row: Vec<MatlabExpr>) -> Self {
895 self.rows.push(row);
896 self
897 }
898 #[allow(dead_code)]
900 pub fn num_rows(&self) -> usize {
901 self.rows.len()
902 }
903 #[allow(dead_code)]
905 pub fn num_cols(&self) -> usize {
906 self.rows.first().map(|r| r.len()).unwrap_or(0)
907 }
908 #[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 #[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 #[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 #[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#[allow(dead_code)]
956#[derive(Debug, Clone, Default)]
957pub struct MatlabStats {
958 pub num_functions: usize,
960 pub num_classes: usize,
962 pub total_stmts: usize,
964 pub matrix_ops: usize,
966}
967impl MatlabStats {
968 #[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 #[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#[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#[derive(Debug, Clone, PartialEq)]
1020pub struct MatlabClassdef {
1021 pub name: String,
1023 pub superclasses: Vec<String>,
1025 pub properties: Vec<MatlabProperty>,
1027 pub methods: Vec<MatlabFunction>,
1029 pub events: Vec<String>,
1031 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#[derive(Debug, Clone)]
1052pub struct MatlabFile {
1053 pub functions: Vec<MatlabFunction>,
1055 pub scripts: Vec<MatlabStmt>,
1057 pub classdef: Option<MatlabClassdef>,
1059 pub header_comment: Option<String>,
1061 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#[allow(dead_code)]
1097#[derive(Debug, Clone, PartialEq)]
1098pub struct MatlabScript {
1099 pub name: String,
1101 pub header_comments: Vec<String>,
1103 pub statements: Vec<MatlabStmt>,
1105}
1106impl MatlabScript {
1107 #[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 #[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 #[allow(dead_code)]
1124 pub fn add_stmt(mut self, stmt: MatlabStmt) -> Self {
1125 self.statements.push(stmt);
1126 self
1127 }
1128 #[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#[allow(dead_code)]
1143#[derive(Debug, Clone, PartialEq)]
1144pub struct MatlabPlot {
1145 pub title: String,
1147 pub xlabel: String,
1149 pub ylabel: String,
1151 pub series: Vec<(String, String)>,
1153 pub grid: bool,
1155 pub legend: bool,
1157 pub figure_size: Option<[f64; 2]>,
1159}
1160impl MatlabPlot {
1161 #[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 #[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 #[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 #[allow(dead_code)]
1189 pub fn with_legend(mut self) -> Self {
1190 self.legend = true;
1191 self
1192 }
1193 #[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#[allow(dead_code)]
1240#[derive(Debug, Clone, PartialEq)]
1241pub struct MatlabStructField {
1242 pub name: String,
1244 pub value: MatlabExpr,
1246}
1247impl MatlabStructField {
1248 #[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#[derive(Debug, Clone, PartialEq)]
1259pub enum MatlabStmt {
1260 Assign {
1262 lhs: Vec<String>,
1263 rhs: MatlabExpr,
1264 suppress: bool,
1265 },
1266 AssignIndex {
1268 obj: MatlabExpr,
1269 indices: Vec<MatlabExpr>,
1270 cell_index: bool,
1271 rhs: MatlabExpr,
1272 suppress: bool,
1273 },
1274 AssignField {
1276 obj: String,
1277 field: String,
1278 rhs: MatlabExpr,
1279 suppress: bool,
1280 },
1281 ForLoop {
1283 var: String,
1284 range: MatlabExpr,
1285 body: Vec<MatlabStmt>,
1286 },
1287 WhileLoop {
1289 cond: MatlabExpr,
1290 body: Vec<MatlabStmt>,
1291 },
1292 IfElseIf {
1294 cond: MatlabExpr,
1295 then_body: Vec<MatlabStmt>,
1296 elseif_branches: Vec<(MatlabExpr, Vec<MatlabStmt>)>,
1297 else_body: Option<Vec<MatlabStmt>>,
1298 },
1299 SwitchCase {
1301 expr: MatlabExpr,
1302 cases: Vec<(MatlabExpr, Vec<MatlabStmt>)>,
1303 otherwise: Option<Vec<MatlabStmt>>,
1304 },
1305 Return,
1307 Break,
1309 Continue,
1311 Error(MatlabExpr, Vec<MatlabExpr>),
1313 Warning(MatlabExpr, Vec<MatlabExpr>),
1315 Disp(MatlabExpr),
1317 FunctionDef(MatlabFunction),
1319 TryCatch {
1321 body: Vec<MatlabStmt>,
1322 catch_var: Option<String>,
1323 catch_body: Vec<MatlabStmt>,
1324 },
1325 ValidateProp(String, MatlabExpr),
1327 Expr(MatlabExpr, bool),
1329 Comment(String),
1331 Global(Vec<String>),
1333 Persistent(Vec<String>),
1335 ClassdefStmt(String),
1337}
1338#[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#[allow(dead_code)]
1349pub struct MatlabValidation;
1350impl MatlabValidation {
1351 #[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 #[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 #[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#[allow(dead_code)]
1402#[derive(Debug, Clone, PartialEq)]
1403pub struct MatlabCellArray {
1404 pub elements: Vec<MatlabExpr>,
1406}
1407impl MatlabCellArray {
1408 #[allow(dead_code)]
1410 pub fn new() -> Self {
1411 MatlabCellArray {
1412 elements: Vec::new(),
1413 }
1414 }
1415 #[allow(dead_code)]
1417 pub fn add(mut self, elem: MatlabExpr) -> Self {
1418 self.elements.push(elem);
1419 self
1420 }
1421 #[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}
1428pub struct MatlabBackend {
1430 pub(super) output: String,
1432 pub(super) indent: usize,
1434 pub(super) indent_str: String,
1436 pub(super) classes: HashMap<String, MatlabClassdef>,
1438 pub(super) octave_compat: bool,
1440}
1441impl MatlabBackend {
1442 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 pub fn octave() -> Self {
1454 MatlabBackend {
1455 octave_compat: true,
1456 ..Self::new()
1457 }
1458 }
1459 pub fn take_output(&mut self) -> String {
1461 std::mem::take(&mut self.output)
1462 }
1463 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 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 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 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 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 pub fn emit_expr(&mut self, expr: &MatlabExpr) -> String {
1896 self.emit_expr_pure(expr)
1897 }
1898 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#[allow(dead_code)]
2064pub struct MatlabNumericOps;
2065impl MatlabNumericOps {
2066 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[allow(dead_code)]
2111 pub fn abs(x: MatlabExpr) -> MatlabExpr {
2112 MatlabExpr::Call(Box::new(MatlabExpr::Var("abs".to_string())), vec![x])
2113 }
2114 #[allow(dead_code)]
2116 pub fn sum(x: MatlabExpr) -> MatlabExpr {
2117 MatlabExpr::Call(Box::new(MatlabExpr::Var("sum".to_string())), vec![x])
2118 }
2119 #[allow(dead_code)]
2121 pub fn prod(x: MatlabExpr) -> MatlabExpr {
2122 MatlabExpr::Call(Box::new(MatlabExpr::Var("prod".to_string())), vec![x])
2123 }
2124 #[allow(dead_code)]
2126 pub fn min(x: MatlabExpr) -> MatlabExpr {
2127 MatlabExpr::Call(Box::new(MatlabExpr::Var("min".to_string())), vec![x])
2128 }
2129 #[allow(dead_code)]
2131 pub fn max(x: MatlabExpr) -> MatlabExpr {
2132 MatlabExpr::Call(Box::new(MatlabExpr::Var("max".to_string())), vec![x])
2133 }
2134 #[allow(dead_code)]
2136 pub fn mean(x: MatlabExpr) -> MatlabExpr {
2137 MatlabExpr::Call(Box::new(MatlabExpr::Var("mean".to_string())), vec![x])
2138 }
2139 #[allow(dead_code)]
2141 pub fn std(x: MatlabExpr) -> MatlabExpr {
2142 MatlabExpr::Call(Box::new(MatlabExpr::Var("std".to_string())), vec![x])
2143 }
2144 #[allow(dead_code)]
2146 pub fn sqrt(x: MatlabExpr) -> MatlabExpr {
2147 MatlabExpr::Call(Box::new(MatlabExpr::Var("sqrt".to_string())), vec![x])
2148 }
2149 #[allow(dead_code)]
2151 pub fn norm(x: MatlabExpr) -> MatlabExpr {
2152 MatlabExpr::Call(Box::new(MatlabExpr::Var("norm".to_string())), vec![x])
2153 }
2154 #[allow(dead_code)]
2156 pub fn det(a: MatlabExpr) -> MatlabExpr {
2157 MatlabExpr::Call(Box::new(MatlabExpr::Var("det".to_string())), vec![a])
2158 }
2159 #[allow(dead_code)]
2161 pub fn inv(a: MatlabExpr) -> MatlabExpr {
2162 MatlabExpr::Call(Box::new(MatlabExpr::Var("inv".to_string())), vec![a])
2163 }
2164 #[allow(dead_code)]
2166 pub fn eig(a: MatlabExpr) -> MatlabExpr {
2167 MatlabExpr::Call(Box::new(MatlabExpr::Var("eig".to_string())), vec![a])
2168 }
2169 #[allow(dead_code)]
2171 pub fn svd(a: MatlabExpr) -> MatlabExpr {
2172 MatlabExpr::Call(Box::new(MatlabExpr::Var("svd".to_string())), vec![a])
2173 }
2174 #[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 #[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 #[allow(dead_code)]
2189 pub fn floor(x: MatlabExpr) -> MatlabExpr {
2190 MatlabExpr::Call(Box::new(MatlabExpr::Var("floor".to_string())), vec![x])
2191 }
2192 #[allow(dead_code)]
2194 pub fn ceil(x: MatlabExpr) -> MatlabExpr {
2195 MatlabExpr::Call(Box::new(MatlabExpr::Var("ceil".to_string())), vec![x])
2196 }
2197 #[allow(dead_code)]
2199 pub fn round(x: MatlabExpr) -> MatlabExpr {
2200 MatlabExpr::Call(Box::new(MatlabExpr::Var("round".to_string())), vec![x])
2201 }
2202 #[allow(dead_code)]
2204 pub fn fix(x: MatlabExpr) -> MatlabExpr {
2205 MatlabExpr::Call(Box::new(MatlabExpr::Var("fix".to_string())), vec![x])
2206 }
2207 #[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#[derive(Debug, Clone, PartialEq)]
2215pub struct MatlabFunction {
2216 pub name: String,
2218 pub inputs: Vec<MatlabParam>,
2220 pub outputs: Vec<String>,
2222 pub body: Vec<MatlabStmt>,
2224 pub is_nested: bool,
2226 pub is_local: bool,
2228 pub help_text: Option<String>,
2230 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#[allow(dead_code)]
2266pub struct MatlabModuleBuilder {
2267 pub name: String,
2269 pub functions: Vec<MatlabFunction>,
2271 pub classes: Vec<MatlabClassdef>,
2273 pub scripts: Vec<MatlabScript>,
2275 pub globals: Vec<String>,
2277 pub config: MatlabGenConfig,
2279}
2280impl MatlabModuleBuilder {
2281 #[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 #[allow(dead_code)]
2295 pub fn add_function(mut self, func: MatlabFunction) -> Self {
2296 self.functions.push(func);
2297 self
2298 }
2299 #[allow(dead_code)]
2301 pub fn add_class(mut self, cls: MatlabClassdef) -> Self {
2302 self.classes.push(cls);
2303 self
2304 }
2305 #[allow(dead_code)]
2307 pub fn add_script(mut self, script: MatlabScript) -> Self {
2308 self.scripts.push(script);
2309 self
2310 }
2311 #[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 #[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 #[allow(dead_code)]
2335 pub fn total_items(&self) -> usize {
2336 self.functions.len() + self.classes.len() + self.scripts.len()
2337 }
2338}