use petr_core::SelectorWord;
use crate::{DispatchError, DispatchResult, OpArg, OpFrame, OperationTable};
#[derive(Debug, Clone)]
pub struct ChainStep {
pub selector: SelectorWord,
pub arg: OpArg,
pub next: Option<usize>,
}
#[derive(Debug, Clone, Default)]
pub struct ChainProgram {
steps: Vec<ChainStep>,
}
impl ChainProgram {
pub fn new() -> Self { Self { steps: Vec::new() } }
pub fn push(&mut self, step: ChainStep) -> usize {
let idx = self.steps.len();
self.steps.push(step);
idx
}
pub fn run(&self, table: &OperationTable, start: usize, mut frame: OpFrame) -> DispatchResult<OpFrame> {
let mut pc = Some(start);
let mut guard = 0usize;
while let Some(i) = pc {
let step = self.steps.get(i).ok_or(DispatchError::InvalidContinuation(i))?;
frame = table.execute_word(step.selector, frame)?;
pc = step.next;
guard += 1;
if guard > self.steps.len().saturating_mul(4).max(64) {
return Err(DispatchError::InvalidInput("chain guard tripped; possible cycle".into()));
}
}
Ok(frame)
}
}