mod breakpoint;
mod state;
use std::io;
use std::ops::{Deref, DerefMut};
use crate::{CircuitDescription, Config, Constraint, Preamble, Witness};
use breakpoint::Breakpoints;
pub use breakpoint::Breakpoint;
pub use state::State;
#[derive(Debug, Clone)]
pub struct ZkDebugger<S> {
breakpoints: Breakpoints,
cdf: CircuitDescription<S>,
constraint: usize,
}
impl<S> Deref for ZkDebugger<S> {
type Target = CircuitDescription<S>;
fn deref(&self) -> &Self::Target {
&self.cdf
}
}
impl<S> DerefMut for ZkDebugger<S> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.cdf
}
}
impl<S> From<CircuitDescription<S>> for ZkDebugger<S> {
fn from(cdf: CircuitDescription<S>) -> Self {
Self {
breakpoints: Breakpoints::default(),
cdf,
constraint: 0,
}
}
}
impl<S> ZkDebugger<S> {
pub const fn config(&self) -> &Config {
&self.cdf.preamble().config
}
pub const fn preamble(&self) -> &Preamble {
self.cdf.preamble()
}
pub fn add_breakpoint(&mut self, source: String, line: Option<u64>) -> usize {
self.breakpoints.add(source, line)
}
pub fn remove_breakpoint(&mut self, id: usize) -> Option<Breakpoint> {
self.breakpoints.remove(id)
}
pub fn fetch_breakpoint(&mut self, id: usize) -> Option<&Breakpoint> {
self.breakpoints.find_breakpoint_from_id(id)
}
}
impl<S> ZkDebugger<S>
where
S: io::Read + io::Seek,
{
pub fn from_reader(source: S) -> io::Result<Self> {
CircuitDescription::from_reader(source).map(Self::from)
}
pub fn fetch_current_constraint(&mut self) -> io::Result<Constraint> {
self.cdf.fetch_constraint(self.constraint)
}
pub fn fetch_constraint(&mut self, idx: usize) -> io::Result<Constraint> {
self.cdf.fetch_constraint(idx)
}
pub fn fetch_witness(&mut self, idx: usize) -> io::Result<Witness> {
self.cdf.fetch_witness(idx)
}
pub fn afore(&mut self) -> io::Result<State> {
let Self {
breakpoints,
cdf,
constraint,
} = self;
let mut idx = *constraint;
if idx == 0 {
return Ok(State::Beginning);
}
let current = cdf.fetch_constraint(idx)?;
let source = current.name().to_string();
let line = current.line();
loop {
idx -= 1;
if idx == 0 {
*constraint = 0;
return Ok(State::Beginning);
}
let current = cdf.fetch_constraint(idx)?;
let is_invalid = !current.polynomial().evaluation;
let different_line = source != current.name() || line != current.line();
if different_line && is_invalid {
*constraint = idx;
return Ok(State::InvalidConstraint { id: idx });
}
if different_line {
if let Some(id) = breakpoints.find_breakpoint(¤t) {
*constraint = idx;
return Ok(State::Breakpoint { id });
}
}
if different_line {
break;
}
}
*constraint = idx;
Ok(State::Constraint { id: idx })
}
pub fn cont(&mut self) -> io::Result<State> {
let Self {
breakpoints,
cdf,
constraint,
} = self;
let mut idx = *constraint;
let eof = cdf.preamble().constraints.saturating_sub(1);
if idx == eof {
return Ok(State::End { id: idx });
}
let current = cdf.fetch_constraint(idx)?;
let source = current.name().to_string();
let line = current.line();
loop {
idx += 1;
let current = cdf.fetch_constraint(idx)?;
let is_invalid = !current.polynomial().evaluation;
let different_line = source != current.name() || line != current.line();
if different_line && is_invalid {
*constraint = idx;
return Ok(State::InvalidConstraint { id: idx });
}
if idx == eof {
*constraint = idx;
return Ok(State::End { id: idx });
}
if different_line {
if let Some(id) = breakpoints.find_breakpoint(¤t) {
*constraint = idx;
return Ok(State::Breakpoint { id });
}
}
}
}
pub fn goto(&mut self, idx: usize) -> io::Result<State> {
let Self {
cdf, constraint, ..
} = self;
if idx == 0 {
*constraint = 0;
return Ok(State::Beginning);
}
let current = cdf.fetch_constraint(idx)?;
let is_invalid = !current.polynomial().evaluation;
*constraint = idx;
if is_invalid {
return Ok(State::InvalidConstraint { id: idx });
}
if idx == cdf.preamble().constraints.saturating_sub(1) {
return Ok(State::End { id: idx });
}
Ok(State::Constraint { id: idx })
}
pub fn step(&mut self) -> io::Result<State> {
let Self {
breakpoints,
cdf,
constraint,
} = self;
let mut idx = *constraint;
let eof = cdf.preamble().constraints.saturating_sub(1);
if idx == eof {
return Ok(State::End { id: idx });
}
let current = cdf.fetch_constraint(idx)?;
let source = current.name().to_string();
let line = current.line();
loop {
idx += 1;
let current = cdf.fetch_constraint(idx)?;
let is_invalid = !current.polynomial().evaluation;
let different_line = source != current.name() || line != current.line();
if different_line && is_invalid {
*constraint = idx;
return Ok(State::InvalidConstraint { id: idx });
}
if idx == eof {
*constraint = idx;
return Ok(State::End { id: idx });
}
if different_line {
if let Some(id) = breakpoints.find_breakpoint(¤t) {
*constraint = idx;
return Ok(State::Breakpoint { id });
}
}
if different_line {
break;
}
}
*constraint = idx;
Ok(State::Constraint { id: idx })
}
pub fn turn(&mut self) -> io::Result<State> {
let Self {
breakpoints,
cdf,
constraint,
} = self;
let mut idx = *constraint;
if idx == 0 {
return Ok(State::Beginning);
}
let current = cdf.fetch_constraint(idx)?;
let source = current.name().to_string();
let line = current.line();
loop {
idx -= 1;
if idx == 0 {
*constraint = 0;
return Ok(State::Beginning);
}
let current = cdf.fetch_constraint(idx)?;
let is_invalid = !current.polynomial().evaluation;
let different_line = source != current.name() || line != current.line();
if different_line && is_invalid {
*constraint = idx;
return Ok(State::InvalidConstraint { id: idx });
}
if different_line {
if let Some(id) = breakpoints.find_breakpoint(¤t) {
*constraint = idx;
return Ok(State::Breakpoint { id });
}
}
}
}
}