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