Skip to main content

sim_lib_machine/
stack.rs

1use std::marker::PhantomData;
2
3use sim_lib_control::WorkLimit;
4
5use crate::ValueWidthPolicy;
6
7/// Exact failure evidence from a unit-accounted operand stack.
8#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9pub enum StackError {
10    /// A push would exceed the configured logical depth.
11    Overflow {
12        /// Occupied depth before the push.
13        depth: usize,
14        /// Logical width required by the pushed value.
15        width: usize,
16        /// Maximum logical depth.
17        limit: usize,
18    },
19    /// A pop was requested from an empty stack.
20    Underflow {
21        /// Occupied depth at the failed operation.
22        depth: usize,
23    },
24    /// A width policy violated its contract by returning zero.
25    ZeroWidth {
26        /// Occupied depth at which the invalid value was presented.
27        depth: usize,
28    },
29}
30
31/// A bounded LIFO stack measured in policy-defined logical units.
32pub struct UnitStack<P: ValueWidthPolicy> {
33    pub(crate) values: Vec<P::Value>,
34    pub(crate) depth: usize,
35    pub(crate) limit: WorkLimit,
36    _policy: PhantomData<P>,
37}
38
39impl<P: ValueWidthPolicy> UnitStack<P> {
40    /// Creates an empty stack using the control organ's work-limit vocabulary.
41    pub fn new(limit: WorkLimit) -> Self {
42        Self {
43            values: Vec::new(),
44            depth: 0,
45            limit,
46            _policy: PhantomData,
47        }
48    }
49
50    /// Returns the occupied logical depth.
51    pub fn depth(&self) -> usize {
52        self.depth
53    }
54
55    /// Returns whether the stack contains no values.
56    pub fn is_empty(&self) -> bool {
57        self.values.is_empty()
58    }
59
60    /// Returns the top value without removing it.
61    pub fn top(&self) -> Result<&P::Value, StackError> {
62        self.values
63            .last()
64            .ok_or(StackError::Underflow { depth: self.depth })
65    }
66
67    /// Visits operand values in deterministic bottom-to-top order.
68    pub fn visit_values(&self, mut visit: impl FnMut(&P::Value)) {
69        for value in &self.values {
70            visit(value);
71        }
72    }
73
74    /// Pushes a value if its complete logical width fits.
75    pub fn push(&mut self, value: P::Value) -> Result<(), StackError> {
76        let width = P::width(&value);
77        if width == 0 {
78            return Err(StackError::ZeroWidth { depth: self.depth });
79        }
80        let next = self
81            .depth
82            .checked_add(width)
83            .filter(|next| *next <= self.limit.0)
84            .ok_or(StackError::Overflow {
85                depth: self.depth,
86                width,
87                limit: self.limit.0,
88            })?;
89        self.values.push(value);
90        self.depth = next;
91        Ok(())
92    }
93
94    /// Pops the top value and releases all logical units it occupied.
95    pub fn pop(&mut self) -> Result<P::Value, StackError> {
96        let value = self
97            .values
98            .pop()
99            .ok_or(StackError::Underflow { depth: self.depth })?;
100        self.depth -= P::width(&value);
101        Ok(value)
102    }
103
104    /// Releases every value in deterministic LIFO order.
105    pub fn clear(&mut self) {
106        while self.pop().is_ok() {}
107    }
108}