1use std::{collections::HashMap, sync::Arc};
5
6use reifydb_core::{
7 internal,
8 value::column::{ColumnWithName, columns::Columns},
9};
10use reifydb_rql::instruction::{CompiledClosure, CompiledFunction, ScopeType};
11use reifydb_type::{error, fragment::Fragment, value::Value};
12
13use crate::{Result, error::EngineError};
14
15#[derive(Debug, Clone)]
16pub struct Stack {
17 variables: Vec<Variable>,
18}
19
20impl Stack {
21 pub fn new() -> Self {
22 Self {
23 variables: Vec::new(),
24 }
25 }
26
27 pub fn push(&mut self, value: Variable) {
28 self.variables.push(value);
29 }
30
31 pub fn pop(&mut self) -> Result<Variable> {
32 self.variables.pop().ok_or_else(|| error!(internal!("VM data stack underflow")))
33 }
34
35 pub fn peek(&self) -> Option<&Variable> {
36 self.variables.last()
37 }
38
39 pub fn is_empty(&self) -> bool {
40 self.variables.is_empty()
41 }
42
43 pub fn len(&self) -> usize {
44 self.variables.len()
45 }
46}
47
48impl Default for Stack {
49 fn default() -> Self {
50 Self::new()
51 }
52}
53
54#[derive(Debug, Clone)]
55pub struct ClosureValue {
56 pub def: CompiledClosure,
57 pub captured: HashMap<String, Variable>,
58}
59
60#[derive(Debug, Clone)]
61pub enum Variable {
62 Columns {
63 columns: Columns,
64 },
65
66 ForIterator {
67 columns: Columns,
68 index: usize,
69 },
70
71 Closure(ClosureValue),
72}
73
74impl Variable {
75 pub fn scalar(value: Value) -> Self {
76 Variable::Columns {
77 columns: Columns::single_row([("value", value)]),
78 }
79 }
80
81 pub fn scalar_named(name: &str, value: Value) -> Self {
82 let mut columns = Columns::single_row([("value", value)]);
83 columns.names.make_mut()[0] = Fragment::internal(name);
84 Variable::Columns {
85 columns,
86 }
87 }
88
89 pub fn columns(columns: Columns) -> Self {
90 Variable::Columns {
91 columns,
92 }
93 }
94
95 pub fn is_scalar(&self) -> bool {
96 matches!(
97 self,
98 Variable::Columns { columns } if columns.is_scalar()
99 )
100 }
101
102 pub fn as_columns(&self) -> Option<&Columns> {
103 match self {
104 Variable::Columns {
105 columns,
106 ..
107 }
108 | Variable::ForIterator {
109 columns,
110 ..
111 } => Some(columns),
112 Variable::Closure(_) => None,
113 }
114 }
115
116 pub fn into_column(self) -> Result<ColumnWithName> {
117 let cols = match self {
118 Variable::Columns {
119 columns: c,
120 ..
121 }
122 | Variable::ForIterator {
123 columns: c,
124 ..
125 } => c,
126 Variable::Closure(_) => Columns::single_row([("value", Value::none())]),
127 };
128 let actual = cols.len();
129 if actual == 1 {
130 let name = cols.names.into_inner().into_iter().next().unwrap();
131 let data = cols.columns.into_inner().into_iter().next().unwrap();
132 Ok(ColumnWithName::new(name, data))
133 } else {
134 Err(error::TypeError::Runtime {
135 kind: error::RuntimeErrorKind::ExpectedSingleColumn {
136 actual,
137 },
138 message: format!("Expected a single column but got {}", actual),
139 }
140 .into())
141 }
142 }
143}
144
145#[derive(Debug, Clone)]
146pub struct SymbolTable {
147 inner: Arc<SymbolTableInner>,
148}
149
150#[derive(Debug, Clone)]
151struct SymbolTableInner {
152 scopes: Vec<Scope>,
153
154 functions: HashMap<String, CompiledFunction>,
155}
156
157#[derive(Debug, Clone)]
158struct Scope {
159 variables: HashMap<String, VariableBinding>,
160 scope_type: ScopeType,
161}
162
163#[derive(Debug, Clone)]
164pub enum ControlFlow {
165 Normal,
166 Break,
167 Continue,
168 Return(Option<Columns>),
169}
170
171impl ControlFlow {
172 pub fn is_normal(&self) -> bool {
173 matches!(self, ControlFlow::Normal)
174 }
175}
176
177#[derive(Debug, Clone)]
178struct VariableBinding {
179 variable: Variable,
180 mutable: bool,
181}
182
183impl SymbolTable {
184 pub fn new() -> Self {
185 let global_scope = Scope {
186 variables: HashMap::new(),
187 scope_type: ScopeType::Global,
188 };
189
190 Self {
191 inner: Arc::new(SymbolTableInner {
192 scopes: vec![global_scope],
193 functions: HashMap::new(),
194 }),
195 }
196 }
197
198 pub fn enter_scope(&mut self, scope_type: ScopeType) {
199 let new_scope = Scope {
200 variables: HashMap::new(),
201 scope_type,
202 };
203 Arc::make_mut(&mut self.inner).scopes.push(new_scope);
204 }
205
206 pub fn exit_scope(&mut self) -> Result<()> {
207 if self.inner.scopes.len() <= 1 {
208 return Err(error!(internal!("Cannot exit global scope")));
209 }
210 Arc::make_mut(&mut self.inner).scopes.pop();
211 Ok(())
212 }
213
214 pub fn scope_depth(&self) -> usize {
215 self.inner.scopes.len() - 1
216 }
217
218 pub fn current_scope_type(&self) -> &ScopeType {
219 &self.inner.scopes.last().unwrap().scope_type
220 }
221
222 pub fn set(&mut self, name: String, variable: Variable, mutable: bool) -> Result<()> {
223 self.set_in_current_scope(name, variable, mutable)
224 }
225
226 pub fn reassign(&mut self, name: String, variable: Variable) -> Result<()> {
227 let inner = Arc::make_mut(&mut self.inner);
228
229 for scope in inner.scopes.iter_mut().rev() {
230 if let Some(existing) = scope.variables.get(&name) {
231 if !existing.mutable {
232 return Err(EngineError::VariableIsImmutable {
233 name: name.clone(),
234 }
235 .into());
236 }
237 let mutable = existing.mutable;
238 scope.variables.insert(
239 name,
240 VariableBinding {
241 variable,
242 mutable,
243 },
244 );
245 return Ok(());
246 }
247 }
248
249 Err(EngineError::VariableNotFound {
250 name: name.clone(),
251 }
252 .into())
253 }
254
255 pub fn set_in_current_scope(&mut self, name: String, variable: Variable, mutable: bool) -> Result<()> {
256 let inner = Arc::make_mut(&mut self.inner);
257 let current_scope = inner.scopes.last_mut().unwrap();
258
259 current_scope.variables.insert(
260 name,
261 VariableBinding {
262 variable,
263 mutable,
264 },
265 );
266 Ok(())
267 }
268
269 pub fn get(&self, name: &str) -> Option<&Variable> {
270 for scope in self.inner.scopes.iter().rev() {
271 if let Some(binding) = scope.variables.get(name) {
272 return Some(&binding.variable);
273 }
274 }
275 None
276 }
277
278 pub fn get_with_scope(&self, name: &str) -> Option<(&Variable, usize)> {
279 for (depth_from_end, scope) in self.inner.scopes.iter().rev().enumerate() {
280 if let Some(binding) = scope.variables.get(name) {
281 let scope_depth = self.inner.scopes.len() - 1 - depth_from_end;
282 return Some((&binding.variable, scope_depth));
283 }
284 }
285 None
286 }
287
288 pub fn exists_in_current_scope(&self, name: &str) -> bool {
289 self.inner.scopes.last().unwrap().variables.contains_key(name)
290 }
291
292 pub fn exists_in_any_scope(&self, name: &str) -> bool {
293 self.get(name).is_some()
294 }
295
296 pub fn is_mutable(&self, name: &str) -> bool {
297 for scope in self.inner.scopes.iter().rev() {
298 if let Some(binding) = scope.variables.get(name) {
299 return binding.mutable;
300 }
301 }
302 false
303 }
304
305 pub fn all_variable_names(&self) -> Vec<String> {
306 let mut names = Vec::new();
307 for (scope_idx, scope) in self.inner.scopes.iter().enumerate() {
308 for name in scope.variables.keys() {
309 names.push(format!("{}@scope{}", name, scope_idx));
310 }
311 }
312 names
313 }
314
315 pub fn visible_variable_names(&self) -> Vec<String> {
316 let mut visible = HashMap::new();
317
318 for scope in &self.inner.scopes {
319 for name in scope.variables.keys() {
320 visible.insert(name.clone(), ());
321 }
322 }
323
324 visible.keys().cloned().collect()
325 }
326
327 pub fn clear(&mut self) {
328 let inner = Arc::make_mut(&mut self.inner);
329 inner.scopes.clear();
330 inner.scopes.push(Scope {
331 variables: HashMap::new(),
332 scope_type: ScopeType::Global,
333 });
334 inner.functions.clear();
335 }
336
337 pub fn define_function(&mut self, name: String, func: CompiledFunction) {
338 Arc::make_mut(&mut self.inner).functions.insert(name, func);
339 }
340
341 pub fn get_function(&self, name: &str) -> Option<&CompiledFunction> {
342 self.inner.functions.get(name)
343 }
344
345 pub fn function_exists(&self, name: &str) -> bool {
346 self.inner.functions.contains_key(name)
347 }
348}
349
350impl Default for SymbolTable {
351 fn default() -> Self {
352 Self::new()
353 }
354}
355
356#[cfg(test)]
357pub mod tests {
358 use reifydb_core::value::column::{ColumnWithName, buffer::ColumnBuffer};
359 use reifydb_type::value::{Value, r#type::Type};
360
361 use super::*;
362
363 fn create_test_columns(values: Vec<Value>) -> Columns {
365 if values.is_empty() {
366 let column_data = ColumnBuffer::none_typed(Type::Boolean, 0);
367 let column = ColumnWithName::new("test_col", column_data);
368 return Columns::new(vec![column]);
369 }
370
371 let mut column_data = ColumnBuffer::none_typed(Type::Boolean, 0);
372 for value in values {
373 column_data.push_value(value);
374 }
375
376 let column = ColumnWithName::new("test_col", column_data);
377 Columns::new(vec![column])
378 }
379
380 #[test]
381 fn test_basic_variable_operations() {
382 let mut ctx = SymbolTable::new();
383 let cols = create_test_columns(vec![Value::utf8("Alice".to_string())]);
384
385 ctx.set("name".to_string(), Variable::columns(cols.clone()), false).unwrap();
387
388 assert!(ctx.get("name").is_some());
390 assert!(!ctx.is_mutable("name"));
391 assert!(ctx.exists_in_any_scope("name"));
392 assert!(ctx.exists_in_current_scope("name"));
393 }
394
395 #[test]
396 fn test_mutable_variable() {
397 let mut ctx = SymbolTable::new();
398 let cols1 = create_test_columns(vec![Value::Int4(42)]);
399 let cols2 = create_test_columns(vec![Value::Int4(84)]);
400
401 ctx.set("counter".to_string(), Variable::columns(cols1.clone()), true).unwrap();
403 assert!(ctx.is_mutable("counter"));
404 assert!(ctx.get("counter").is_some());
405
406 ctx.set("counter".to_string(), Variable::columns(cols2.clone()), true).unwrap();
408 assert!(ctx.get("counter").is_some());
409 }
410
411 #[test]
412 #[ignore]
413 fn test_immutable_variable_reassignment_fails() {
414 let mut ctx = SymbolTable::new();
415 let cols1 = create_test_columns(vec![Value::utf8("Alice".to_string())]);
416 let cols2 = create_test_columns(vec![Value::utf8("Bob".to_string())]);
417
418 ctx.set("name".to_string(), Variable::columns(cols1.clone()), false).unwrap();
420
421 let result = ctx.set("name".to_string(), Variable::columns(cols2), false);
423 assert!(result.is_err());
424
425 assert!(ctx.get("name").is_some());
427 }
428
429 #[test]
430 fn test_scope_management() {
431 let mut ctx = SymbolTable::new();
432
433 assert_eq!(ctx.scope_depth(), 0);
435 assert_eq!(ctx.current_scope_type(), &ScopeType::Global);
436
437 ctx.enter_scope(ScopeType::Function);
439 assert_eq!(ctx.scope_depth(), 1);
440 assert_eq!(ctx.current_scope_type(), &ScopeType::Function);
441
442 ctx.enter_scope(ScopeType::Block);
444 assert_eq!(ctx.scope_depth(), 2);
445 assert_eq!(ctx.current_scope_type(), &ScopeType::Block);
446
447 ctx.exit_scope().unwrap();
449 assert_eq!(ctx.scope_depth(), 1);
450 assert_eq!(ctx.current_scope_type(), &ScopeType::Function);
451
452 ctx.exit_scope().unwrap();
454 assert_eq!(ctx.scope_depth(), 0);
455 assert_eq!(ctx.current_scope_type(), &ScopeType::Global);
456
457 assert!(ctx.exit_scope().is_err());
459 }
460
461 #[test]
462 fn test_variable_shadowing() {
463 let mut ctx = SymbolTable::new();
464 let outer_cols = create_test_columns(vec![Value::utf8("outer".to_string())]);
465 let inner_cols = create_test_columns(vec![Value::utf8("inner".to_string())]);
466
467 ctx.set("var".to_string(), Variable::columns(outer_cols.clone()), false).unwrap();
469 assert!(ctx.get("var").is_some());
470
471 ctx.enter_scope(ScopeType::Block);
473 ctx.set("var".to_string(), Variable::columns(inner_cols.clone()), false).unwrap();
474
475 assert!(ctx.get("var").is_some());
477 assert!(ctx.exists_in_current_scope("var"));
478
479 ctx.exit_scope().unwrap();
481 assert!(ctx.get("var").is_some());
482 }
483
484 #[test]
485 fn test_parent_scope_access() {
486 let mut ctx = SymbolTable::new();
487 let outer_cols = create_test_columns(vec![Value::utf8("outer".to_string())]);
488
489 ctx.set("global_var".to_string(), Variable::columns(outer_cols.clone()), false).unwrap();
491
492 ctx.enter_scope(ScopeType::Function);
494
495 assert!(ctx.get("global_var").is_some());
497 assert!(!ctx.exists_in_current_scope("global_var"));
498 assert!(ctx.exists_in_any_scope("global_var"));
499
500 let (_, scope_depth) = ctx.get_with_scope("global_var").unwrap();
502 assert_eq!(scope_depth, 0); }
504
505 #[test]
506 fn test_scope_specific_mutability() {
507 let mut ctx = SymbolTable::new();
508 let cols1 = create_test_columns(vec![Value::utf8("value1".to_string())]);
509 let cols2 = create_test_columns(vec![Value::utf8("value2".to_string())]);
510
511 ctx.set("var".to_string(), Variable::columns(cols1.clone()), false).unwrap();
513
514 ctx.enter_scope(ScopeType::Block);
516 ctx.set("var".to_string(), Variable::columns(cols2.clone()), true).unwrap(); assert!(ctx.is_mutable("var"));
520
521 ctx.exit_scope().unwrap();
523 assert!(!ctx.is_mutable("var"));
524 }
525
526 #[test]
527 fn test_visible_variable_names() {
528 let mut ctx = SymbolTable::new();
529 let cols = create_test_columns(vec![Value::utf8("test".to_string())]);
530
531 ctx.set("global1".to_string(), Variable::columns(cols.clone()), false).unwrap();
533 ctx.set("global2".to_string(), Variable::columns(cols.clone()), false).unwrap();
534
535 let global_visible = ctx.visible_variable_names();
536 assert_eq!(global_visible.len(), 2);
537 assert!(global_visible.contains(&"global1".to_string()));
538 assert!(global_visible.contains(&"global2".to_string()));
539
540 ctx.enter_scope(ScopeType::Function);
542 ctx.set("local1".to_string(), Variable::columns(cols.clone()), false).unwrap();
543 ctx.set("global1".to_string(), Variable::columns(cols.clone()), false).unwrap(); let function_visible = ctx.visible_variable_names();
546 assert_eq!(function_visible.len(), 3); assert!(function_visible.contains(&"global1".to_string()));
548 assert!(function_visible.contains(&"global2".to_string()));
549 assert!(function_visible.contains(&"local1".to_string()));
550 }
551
552 #[test]
553 fn test_clear_resets_to_global() {
554 let mut ctx = SymbolTable::new();
555 let cols = create_test_columns(vec![Value::utf8("test".to_string())]);
556
557 ctx.set("var1".to_string(), Variable::columns(cols.clone()), false).unwrap();
559 ctx.enter_scope(ScopeType::Function);
560 ctx.set("var2".to_string(), Variable::columns(cols.clone()), false).unwrap();
561 ctx.enter_scope(ScopeType::Block);
562 ctx.set("var3".to_string(), Variable::columns(cols.clone()), false).unwrap();
563
564 assert_eq!(ctx.scope_depth(), 2);
565 assert_eq!(ctx.visible_variable_names().len(), 3);
566
567 ctx.clear();
569 assert_eq!(ctx.scope_depth(), 0);
570 assert_eq!(ctx.current_scope_type(), &ScopeType::Global);
571 assert_eq!(ctx.visible_variable_names().len(), 0);
572 }
573
574 #[test]
575 fn test_nonexistent_variable() {
576 let ctx = SymbolTable::new();
577
578 assert!(ctx.get("nonexistent").is_none());
579 assert!(!ctx.exists_in_any_scope("nonexistent"));
580 assert!(!ctx.exists_in_current_scope("nonexistent"));
581 assert!(!ctx.is_mutable("nonexistent"));
582 assert!(ctx.get_with_scope("nonexistent").is_none());
583 }
584}