spydecy_debugger/
stepper.rs1use 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
15pub struct Stepper {
17 state: TranspilationState,
18 breakpoints: Vec<Breakpoint>,
19}
20
21impl Stepper {
22 #[must_use]
24 pub fn new(state: TranspilationState) -> Self {
25 Self {
26 state,
27 breakpoints: Vec::new(),
28 }
29 }
30
31 #[must_use]
33 pub const fn state(&self) -> &TranspilationState {
34 &self.state
35 }
36
37 pub fn add_breakpoint(&mut self, bp: Breakpoint) {
39 self.breakpoints.push(bp);
40 }
41
42 #[must_use]
44 pub fn breakpoints(&self) -> &[Breakpoint] {
45 &self.breakpoints
46 }
47
48 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 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 }
75 }
76 }
77 false
78 }
79
80 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 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 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 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}