1mod breakpoint;
2mod state;
3
4use std::io;
5use std::ops::{Deref, DerefMut};
6
7use crate::{CircuitDescription, Config, Constraint, Preamble, Witness};
8
9use breakpoint::Breakpoints;
10
11pub use breakpoint::Breakpoint;
12pub use state::State;
13
14#[derive(Debug, Clone)]
16pub struct ZkDebugger<S> {
17 breakpoints: Breakpoints,
18 cdf: CircuitDescription<S>,
19 constraint: usize,
20}
21
22impl<S> Deref for ZkDebugger<S> {
23 type Target = CircuitDescription<S>;
24
25 fn deref(&self) -> &Self::Target {
26 &self.cdf
27 }
28}
29
30impl<S> DerefMut for ZkDebugger<S> {
31 fn deref_mut(&mut self) -> &mut Self::Target {
32 &mut self.cdf
33 }
34}
35
36impl<S> From<CircuitDescription<S>> for ZkDebugger<S> {
37 fn from(cdf: CircuitDescription<S>) -> Self {
38 Self {
39 breakpoints: Breakpoints::default(),
40 cdf,
41 constraint: 0,
42 }
43 }
44}
45
46impl<S> ZkDebugger<S> {
47 pub const fn config(&self) -> &Config {
49 &self.cdf.preamble().config
50 }
51
52 pub const fn preamble(&self) -> &Preamble {
54 self.cdf.preamble()
55 }
56
57 pub fn add_breakpoint(&mut self, source: String, line: Option<u64>) -> usize {
61 self.breakpoints.add(source, line)
62 }
63
64 pub fn remove_breakpoint(&mut self, id: usize) -> Option<Breakpoint> {
68 self.breakpoints.remove(id)
69 }
70
71 pub fn fetch_breakpoint(&mut self, id: usize) -> Option<&Breakpoint> {
73 self.breakpoints.find_breakpoint_from_id(id)
74 }
75}
76
77impl<S> ZkDebugger<S>
78where
79 S: io::Read + io::Seek,
80{
81 pub fn from_reader(source: S) -> io::Result<Self> {
83 CircuitDescription::from_reader(source).map(Self::from)
84 }
85
86 pub fn fetch_current_constraint(&mut self) -> io::Result<Constraint> {
88 self.cdf.fetch_constraint(self.constraint)
89 }
90
91 pub fn fetch_constraint(&mut self, idx: usize) -> io::Result<Constraint> {
93 self.cdf.fetch_constraint(idx)
94 }
95
96 pub fn fetch_witness(&mut self, idx: usize) -> io::Result<Witness> {
98 self.cdf.fetch_witness(idx)
99 }
100
101 pub fn afore(&mut self) -> io::Result<State> {
106 let Self {
107 breakpoints,
108 cdf,
109 constraint,
110 } = self;
111
112 let mut idx = *constraint;
113 if idx == 0 {
114 return Ok(State::Beginning);
115 }
116
117 let current = cdf.fetch_constraint(idx)?;
118 let source = current.name().to_string();
119 let line = current.line();
120
121 loop {
122 idx -= 1;
123
124 if idx == 0 {
125 *constraint = 0;
126 return Ok(State::Beginning);
127 }
128
129 let current = cdf.fetch_constraint(idx)?;
130 let is_invalid = !current.polynomial().evaluation;
131 let different_line = source != current.name() || line != current.line();
132
133 if different_line && is_invalid {
134 *constraint = idx;
135 return Ok(State::InvalidConstraint { id: idx });
136 }
137
138 if different_line {
139 if let Some(id) = breakpoints.find_breakpoint(¤t) {
140 *constraint = idx;
141 return Ok(State::Breakpoint { id });
142 }
143 }
144
145 if different_line {
146 break;
147 }
148 }
149
150 *constraint = idx;
151 Ok(State::Constraint { id: idx })
152 }
153
154 pub fn cont(&mut self) -> io::Result<State> {
156 let Self {
157 breakpoints,
158 cdf,
159 constraint,
160 } = self;
161
162 let mut idx = *constraint;
163 let eof = cdf.preamble().constraints.saturating_sub(1);
164
165 if idx == eof {
166 return Ok(State::End { id: idx });
167 }
168
169 let current = cdf.fetch_constraint(idx)?;
170 let source = current.name().to_string();
171 let line = current.line();
172
173 loop {
174 idx += 1;
175
176 let current = cdf.fetch_constraint(idx)?;
177 let is_invalid = !current.polynomial().evaluation;
178 let different_line = source != current.name() || line != current.line();
179
180 if different_line && is_invalid {
181 *constraint = idx;
182 return Ok(State::InvalidConstraint { id: idx });
183 }
184
185 if idx == eof {
186 *constraint = idx;
187 return Ok(State::End { id: idx });
188 }
189
190 if different_line {
191 if let Some(id) = breakpoints.find_breakpoint(¤t) {
192 *constraint = idx;
193 return Ok(State::Breakpoint { id });
194 }
195 }
196 }
197 }
198
199 pub fn goto(&mut self, idx: usize) -> io::Result<State> {
201 let Self {
202 cdf, constraint, ..
203 } = self;
204
205 if idx == 0 {
206 *constraint = 0;
207 return Ok(State::Beginning);
208 }
209
210 let current = cdf.fetch_constraint(idx)?;
211 let is_invalid = !current.polynomial().evaluation;
212
213 *constraint = idx;
214
215 if is_invalid {
216 return Ok(State::InvalidConstraint { id: idx });
217 }
218
219 if idx == cdf.preamble().constraints.saturating_sub(1) {
220 return Ok(State::End { id: idx });
221 }
222
223 Ok(State::Constraint { id: idx })
224 }
225
226 pub fn step(&mut self) -> io::Result<State> {
231 let Self {
232 breakpoints,
233 cdf,
234 constraint,
235 } = self;
236
237 let mut idx = *constraint;
238 let eof = cdf.preamble().constraints.saturating_sub(1);
239
240 if idx == eof {
241 return Ok(State::End { id: idx });
242 }
243
244 let current = cdf.fetch_constraint(idx)?;
245 let source = current.name().to_string();
246 let line = current.line();
247
248 loop {
249 idx += 1;
250
251 let current = cdf.fetch_constraint(idx)?;
252 let is_invalid = !current.polynomial().evaluation;
253 let different_line = source != current.name() || line != current.line();
254
255 if different_line && is_invalid {
256 *constraint = idx;
257 return Ok(State::InvalidConstraint { id: idx });
258 }
259
260 if idx == eof {
261 *constraint = idx;
262 return Ok(State::End { id: idx });
263 }
264
265 if different_line {
266 if let Some(id) = breakpoints.find_breakpoint(¤t) {
267 *constraint = idx;
268 return Ok(State::Breakpoint { id });
269 }
270 }
271
272 if different_line {
273 break;
274 }
275 }
276
277 *constraint = idx;
278 Ok(State::Constraint { id: idx })
279 }
280
281 pub fn turn(&mut self) -> io::Result<State> {
283 let Self {
284 breakpoints,
285 cdf,
286 constraint,
287 } = self;
288
289 let mut idx = *constraint;
290 if idx == 0 {
291 return Ok(State::Beginning);
292 }
293
294 let current = cdf.fetch_constraint(idx)?;
295 let source = current.name().to_string();
296 let line = current.line();
297
298 loop {
299 idx -= 1;
300
301 if idx == 0 {
302 *constraint = 0;
303 return Ok(State::Beginning);
304 }
305
306 let current = cdf.fetch_constraint(idx)?;
307 let is_invalid = !current.polynomial().evaluation;
308 let different_line = source != current.name() || line != current.line();
309
310 if different_line && is_invalid {
311 *constraint = idx;
312 return Ok(State::InvalidConstraint { id: idx });
313 }
314
315 if different_line {
316 if let Some(id) = breakpoints.find_breakpoint(¤t) {
317 *constraint = idx;
318 return Ok(State::Breakpoint { id });
319 }
320 }
321 }
322 }
323}