cao_lang/collections/
value_stack.rs1use crate::value::Value;
5use thiserror::Error;
6
7#[derive(Debug)]
8pub struct ValueStack {
9 count: usize,
10 data: Box<[Value]>,
11}
12
13#[derive(Debug, Error)]
14pub enum StackError {
15 #[error("Stack is full")]
16 Full,
17 #[error("Index out of bounds: capacity: {capacity} index: {index}")]
18 OutOfBounds { capacity: usize, index: usize },
19}
20impl std::fmt::Display for ValueStack {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 if self.count == 0 {
23 return write!(f, "[]");
24 }
25 writeln!(f, "[")?;
26 unsafe {
27 let data = self.data.as_ptr();
28 for i in (0..self.count).rev() {
29 writeln!(f, "\t{:?}: {:?}", data.add(i), &self.data[i])?;
30 }
31 }
32 write!(f, " ]")
33 }
34}
35
36impl ValueStack {
37 pub fn new(size: usize) -> Self {
38 assert!(size > 0);
39 Self {
40 count: 0,
41 data: vec![Value::Nil; size].into_boxed_slice(),
42 }
43 }
44
45 #[inline]
46 pub fn as_slice(&self) -> &[Value] {
47 &self.data[0..self.count]
48 }
49
50 #[inline]
51 pub fn push<T: Into<Value>>(&mut self, value: T) -> Result<(), StackError> {
52 if self.count + 1 < self.data.len() {
53 self.data[self.count] = value.into();
54 self.count += 1;
55 Ok(())
56 } else {
57 Err(StackError::Full)
58 }
59 }
60
61 #[inline]
62 pub fn clear(&mut self) {
63 self.count = 0;
64 self.data[0] = Value::Nil; }
66
67 #[inline]
68 pub fn len(&self) -> usize {
69 self.count
70 }
71
72 #[inline]
74 pub fn pop(&mut self) -> Value {
75 let count = self.count.saturating_sub(1);
76 let value = self.data[count];
77 self.count = count;
78 self.data[self.count] = Value::Nil;
79 value
80 }
81
82 #[inline]
83 pub fn pop_n<const N: usize>(&mut self) -> [Value; N] {
84 let mut result = [Value::Nil; N];
85 let n = self.count.min(N);
86 #[allow(clippy::needless_range_loop)]
87 for i in 0..n {
88 result[i] = self.data[self.count - i - 1];
89 }
90 self.count -= n;
91 result
92 }
93
94 pub fn pop_w_offset(&mut self, offset: usize) -> Value {
109 if self.count <= offset {
110 return Value::Nil;
111 }
112 self.pop()
113 }
114
115 pub fn set(&mut self, index: usize, value: Value) -> Result<Value, StackError> {
120 if index > self.count {
121 return Err(StackError::OutOfBounds {
122 capacity: self.count,
123 index,
124 });
125 }
126 if index == self.count {
127 self.push(value)?;
128 Ok(Value::Nil)
129 } else {
130 let old = std::mem::replace(&mut self.data[index], value);
131 Ok(old)
132 }
133 }
134
135 pub fn get(&mut self, index: usize) -> Value {
136 if index >= self.count {
137 return Value::Nil;
138 }
139 self.data[index]
140 }
141
142 pub fn clear_until(&mut self, index: usize) -> Value {
144 let res = self.last();
145 self.count = index;
146 res
147 }
148
149 #[inline]
150 pub fn is_empty(&self) -> bool {
151 self.count == 0
152 }
153
154 #[inline]
156 pub fn last(&self) -> Value {
157 if self.count > 0 {
158 self.data[self.count - 1]
159 } else {
160 Value::Nil
161 }
162 }
163
164 #[inline]
166 pub fn peek_last(&self, n: usize) -> Value {
167 if self.count > n {
168 self.data[self.count - n - 1]
169 } else {
170 Value::Nil
171 }
172 }
173
174 pub fn iter(&self) -> impl Iterator<Item = Value> {
175 let ptr = self.data.as_ptr();
176 (0..self.count).map(move |i| unsafe { *ptr.add(i) })
177 }
178
179 pub fn top_location(&self) -> *const Value {
180 if self.count == 0 {
181 return std::ptr::null();
182 }
183 unsafe { self.data.as_ptr().add(self.count - 1) }
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190
191 #[test]
192 fn pop_n_not_enough_in_stack_test() {
193 let mut stack = ValueStack::new(4);
194 stack.push(Value::Integer(42)).unwrap();
195
196 let result = stack.pop_n::<8>();
197
198 assert_eq!(result.len(), 8);
199 assert_eq!(result[0], Value::Integer(42));
200 for i in 1..8 {
201 assert!(result[i].is_null());
202 }
203 }
204}