Skip to main content

reifydb_engine/vm/
stack.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use arrow_array::RecordBatch;
5use reifydb_core::internal;
6use reifydb_evaluate::stack::Variable;
7use reifydb_value::{error, reifydb_assertions};
8
9use crate::Result;
10
11#[derive(Debug, Clone)]
12pub struct Stack {
13	variables: Vec<Variable>,
14}
15
16impl Stack {
17	pub fn new() -> Self {
18		Self {
19			variables: Vec::new(),
20		}
21	}
22
23	pub fn push(&mut self, value: Variable) {
24		reifydb_assertions! {
25			match &value {
26				Variable::Columns {
27					batch,
28				}
29				| Variable::ForIterator {
30					batch,
31					..
32				} => reifydb_value::value::canonical::assert_canonical_floats(batch, "stack push"),
33				Variable::Closure(_) => {}
34			}
35		}
36		self.variables.push(value);
37	}
38
39	pub fn pop(&mut self) -> Result<Variable> {
40		self.variables.pop().ok_or_else(|| error!(internal!("VM data stack underflow")))
41	}
42
43	pub fn peek(&self) -> Option<&Variable> {
44		self.variables.last()
45	}
46
47	pub fn is_empty(&self) -> bool {
48		self.variables.is_empty()
49	}
50
51	pub fn len(&self) -> usize {
52		self.variables.len()
53	}
54}
55
56impl Default for Stack {
57	fn default() -> Self {
58		Self::new()
59	}
60}
61
62#[derive(Debug, Clone)]
63pub enum ControlFlow {
64	Normal,
65	Break,
66	Continue,
67	Return(Option<RecordBatch>),
68}
69
70impl ControlFlow {
71	pub fn is_normal(&self) -> bool {
72		matches!(self, ControlFlow::Normal)
73	}
74}
75
76#[cfg(all(test, reifydb_assertions))]
77mod tests {
78	use std::sync::Arc;
79
80	use arrow_array::{ArrayRef, Float64Array, RecordBatch};
81	use reifydb_evaluate::stack::Variable;
82
83	use super::Stack;
84
85	fn negative_zero_batch() -> RecordBatch {
86		let column: ArrayRef = Arc::new(Float64Array::from(vec![-0.0f64]));
87		RecordBatch::try_from_iter([("c", column)]).unwrap()
88	}
89
90	#[test]
91	#[cfg(reifydb_assertions)]
92	#[should_panic(expected = "is not canonical")]
93	fn test_push_columns_with_negative_zero_panics() {
94		Stack::new().push(Variable::Columns {
95			batch: negative_zero_batch(),
96		});
97	}
98
99	#[test]
100	#[cfg(reifydb_assertions)]
101	#[should_panic(expected = "is not canonical")]
102	fn test_push_for_iterator_with_negative_zero_panics() {
103		Stack::new().push(Variable::ForIterator {
104			batch: negative_zero_batch(),
105			index: 0,
106		});
107	}
108}