Skip to main content

petr_dispatch/
chain.rs

1use petr_core::SelectorWord;
2
3use crate::{DispatchError, DispatchResult, OpArg, OpFrame, OperationTable};
4
5/// One continuation-program step.
6#[derive(Debug, Clone)]
7pub struct ChainStep {
8    /// Selector describing trait/impl/function ids.
9    pub selector: SelectorWord,
10    /// Optional argument override for future engines. The current engine uses table-bound args.
11    pub arg: OpArg,
12    /// Next program-counter index. `None` means stop.
13    pub next: Option<usize>,
14}
15
16/// A tail-call-ish chain that repeatedly dispatches by selector.
17#[derive(Debug, Clone, Default)]
18pub struct ChainProgram {
19    steps: Vec<ChainStep>,
20}
21
22impl ChainProgram {
23    /// Create an empty chain.
24    pub fn new() -> Self { Self { steps: Vec::new() } }
25
26    /// Push a step and return its index.
27    pub fn push(&mut self, step: ChainStep) -> usize {
28        let idx = self.steps.len();
29        self.steps.push(step);
30        idx
31    }
32
33    /// Run the chain from `start`.
34    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}