spydecy_debugger/
stepper.rs

1//! Transpilation Stepper
2//!
3//! Core logic for stepping through transpilation phases.
4
5use crate::commands::Breakpoint;
6use crate::state::{TranspilationPhase, TranspilationState};
7use anyhow::{Context, Result};
8use spydecy_c::parse_c;
9use spydecy_codegen::generate_rust;
10use spydecy_hir::unified::Unifier;
11use spydecy_optimizer::OptimizationPipeline;
12use spydecy_python::parse_python;
13use std::fs;
14
15/// Transpilation stepper - manages stepping through phases
16pub struct Stepper {
17    state: TranspilationState,
18    breakpoints: Vec<Breakpoint>,
19}
20
21impl Stepper {
22    /// Create new stepper
23    #[must_use]
24    pub fn new(state: TranspilationState) -> Self {
25        Self {
26            state,
27            breakpoints: Vec::new(),
28        }
29    }
30
31    /// Get current state
32    #[must_use]
33    pub const fn state(&self) -> &TranspilationState {
34        &self.state
35    }
36
37    /// Add breakpoint
38    pub fn add_breakpoint(&mut self, bp: Breakpoint) {
39        self.breakpoints.push(bp);
40    }
41
42    /// List breakpoints
43    #[must_use]
44    pub fn breakpoints(&self) -> &[Breakpoint] {
45        &self.breakpoints
46    }
47
48    /// Clear breakpoint by index
49    pub fn clear_breakpoint(&mut self, index: usize) -> bool {
50        if index < self.breakpoints.len() {
51            self.breakpoints.remove(index);
52            true
53        } else {
54            false
55        }
56    }
57
58    /// Check if breakpoint should trigger
59    fn check_breakpoint(&self) -> bool {
60        for bp in &self.breakpoints {
61            match bp {
62                Breakpoint::BoundaryElimination => {
63                    if matches!(self.state.phase, TranspilationPhase::Optimized) {
64                        return true;
65                    }
66                }
67                Breakpoint::Phase(phase_name) => {
68                    if self.state.phase.name().eq_ignore_ascii_case(phase_name) {
69                        return true;
70                    }
71                }
72                Breakpoint::Function(_) => {
73                    // Function breakpoints NYI
74                }
75            }
76        }
77        false
78    }
79
80    /// Step to next phase
81    ///
82    /// # Errors
83    ///
84    /// Returns error if phase transition fails
85    pub fn step(&mut self) -> Result<TranspilationPhase> {
86        let next_phase = self.state.advance()?;
87
88        match next_phase {
89            TranspilationPhase::PythonParsed => self.parse_python()?,
90            TranspilationPhase::CParsed => self.parse_c()?,
91            TranspilationPhase::UnifiedHIR => self.unify()?,
92            TranspilationPhase::Optimized => self.optimize()?,
93            TranspilationPhase::RustGenerated => self.generate_rust()?,
94            // These phases don't require additional processing
95            TranspilationPhase::PythonHIR
96            | TranspilationPhase::CHIR
97            | TranspilationPhase::Complete
98            | TranspilationPhase::Start => {}
99        }
100
101        Ok(next_phase)
102    }
103
104    fn parse_python(&mut self) -> Result<()> {
105        let python_file = self
106            .state
107            .python_file
108            .as_ref()
109            .context("No Python file set")?;
110
111        let source = fs::read_to_string(python_file)?;
112        self.state.python_source = Some(source.clone());
113
114        let hir = parse_python(&source, python_file.to_str().unwrap_or("input.py"))?;
115        self.state.python_hir = Some(hir);
116
117        Ok(())
118    }
119
120    fn parse_c(&mut self) -> Result<()> {
121        let c_file = self.state.c_file.as_ref().context("No C file set")?;
122
123        let source = fs::read_to_string(c_file)?;
124        self.state.c_source = Some(source.clone());
125
126        let hir = parse_c(&source, c_file.to_str().unwrap_or("input.c"))?;
127        self.state.c_hir = Some(hir);
128
129        Ok(())
130    }
131
132    fn unify(&mut self) -> Result<()> {
133        let python_hir = self
134            .state
135            .python_hir
136            .as_ref()
137            .context("No Python HIR")?
138            .clone();
139        let c_hir = self.state.c_hir.as_ref().context("No C HIR")?.clone();
140
141        // Extract callable from Python
142        let python_call = extract_python_call(python_hir)?;
143        let c_function = extract_c_function(c_hir)?;
144
145        let mut unifier = Unifier::new();
146        let unified_hir = unifier.unify(&python_call, &c_function)?;
147
148        self.state.unified_hir = Some(unified_hir);
149        Ok(())
150    }
151
152    fn optimize(&mut self) -> Result<()> {
153        let unified = self
154            .state
155            .unified_hir
156            .as_ref()
157            .context("No Unified HIR")?
158            .clone();
159
160        let pipeline = OptimizationPipeline::standard();
161        let optimized = pipeline.run(unified)?;
162
163        self.state.optimized_hir = Some(optimized);
164        Ok(())
165    }
166
167    fn generate_rust(&mut self) -> Result<()> {
168        let optimized = self
169            .state
170            .optimized_hir
171            .as_ref()
172            .context("No optimized HIR")?;
173
174        let rust_code = generate_rust(optimized)?;
175        self.state.rust_code = Some(rust_code);
176
177        Ok(())
178    }
179
180    /// Continue until breakpoint or completion
181    ///
182    /// # Errors
183    ///
184    /// Returns error if any phase fails
185    pub fn continue_execution(&mut self) -> Result<()> {
186        while !self.state.is_complete() {
187            self.step()?;
188
189            if self.check_breakpoint() {
190                break;
191            }
192        }
193        Ok(())
194    }
195}
196
197fn extract_python_call(
198    python_hir: spydecy_hir::python::PythonHIR,
199) -> Result<spydecy_hir::python::PythonHIR> {
200    use spydecy_hir::python::PythonHIR;
201
202    if let PythonHIR::Module { body, .. } = python_hir {
203        if let Some(PythonHIR::Function {
204            body: func_body, ..
205        }) = body.first()
206        {
207            if let Some(PythonHIR::Return {
208                value: Some(call), ..
209            }) = func_body.first()
210            {
211                return Ok(call.as_ref().clone());
212            }
213        }
214    }
215    anyhow::bail!("Could not extract Python call");
216}
217
218fn extract_c_function(c_hir: spydecy_hir::c::CHIR) -> Result<spydecy_hir::c::CHIR> {
219    use spydecy_hir::c::CHIR;
220
221    if let CHIR::TranslationUnit { declarations, .. } = c_hir {
222        declarations.first().cloned().context("No C declarations")
223    } else {
224        anyhow::bail!("Expected C TranslationUnit")
225    }
226}