1use petr_core::SelectorWord;
2
3use crate::{DispatchError, DispatchResult, OpArg, OpFrame, OperationTable};
4
5#[derive(Debug, Clone)]
7pub struct ChainStep {
8 pub selector: SelectorWord,
10 pub arg: OpArg,
12 pub next: Option<usize>,
14}
15
16#[derive(Debug, Clone, Default)]
18pub struct ChainProgram {
19 steps: Vec<ChainStep>,
20}
21
22impl ChainProgram {
23 pub fn new() -> Self { Self { steps: Vec::new() } }
25
26 pub fn push(&mut self, step: ChainStep) -> usize {
28 let idx = self.steps.len();
29 self.steps.push(step);
30 idx
31 }
32
33 pub fn run(&self, table: &OperationTable, start: usize, mut frame: OpFrame) -> DispatchResult<OpFrame> {
35 let mut pc = Some(start);
36 let mut guard = 0usize;
37 while let Some(i) = pc {
38 let step = self.steps.get(i).ok_or(DispatchError::InvalidContinuation(i))?;
39 frame = table.execute_word(step.selector, frame)?;
40 pc = step.next;
41 guard += 1;
42 if guard > self.steps.len().saturating_mul(4).max(64) {
43 return Err(DispatchError::InvalidInput("chain guard tripped; possible cycle".into()));
44 }
45 }
46 Ok(frame)
47 }
48}