1use std::{collections::HashMap, sync::Arc};
5
6use reifydb_core::{
7 internal,
8 value::column::{Column, 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::scalar(value),
84 }
85 }
86
87 pub fn scalar_named(name: &str, value: Value) -> Self {
91 let mut columns = Columns::scalar(value);
92 columns.columns.make_mut()[0].name = 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<Column> {
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::scalar(Value::none()),
140 };
141 let actual = cols.len();
142 if actual == 1 {
143 Ok(cols.columns.into_inner().into_iter().next().unwrap())
144 } else {
145 Err(error::TypeError::Runtime {
146 kind: error::RuntimeErrorKind::ExpectedSingleColumn {
147 actual,
148 },
149 message: format!("Expected a single column but got {}", actual),
150 }
151 .into())
152 }
153 }
154}
155
156#[derive(Debug, Clone)]
158pub struct SymbolTable {
159 inner: Arc<SymbolTableInner>,
160}
161
162#[derive(Debug, Clone)]
163struct SymbolTableInner {
164 scopes: Vec<Scope>,
165 functions: HashMap<String, CompiledFunction>,
167}
168
169#[derive(Debug, Clone)]
171struct Scope {
172 variables: HashMap<String, VariableBinding>,
173 scope_type: ScopeType,
174}
175
176#[derive(Debug, Clone)]
178pub enum ControlFlow {
179 Normal,
180 Break,
181 Continue,
182 Return(Option<Columns>),
183}
184
185impl ControlFlow {
186 pub fn is_normal(&self) -> bool {
187 matches!(self, ControlFlow::Normal)
188 }
189}
190
191#[derive(Debug, Clone)]
193struct VariableBinding {
194 variable: Variable,
195 mutable: bool,
196}
197
198impl SymbolTable {
199 pub fn new() -> Self {
201 let global_scope = Scope {
202 variables: HashMap::new(),
203 scope_type: ScopeType::Global,
204 };
205
206 Self {
207 inner: Arc::new(SymbolTableInner {
208 scopes: vec![global_scope],
209 functions: HashMap::new(),
210 }),
211 }
212 }
213
214 pub fn enter_scope(&mut self, scope_type: ScopeType) {
216 let new_scope = Scope {
217 variables: HashMap::new(),
218 scope_type,
219 };
220 Arc::make_mut(&mut self.inner).scopes.push(new_scope);
221 }
222
223 pub fn exit_scope(&mut self) -> Result<()> {
226 if self.inner.scopes.len() <= 1 {
227 return Err(error!(internal!("Cannot exit global scope")));
228 }
229 Arc::make_mut(&mut self.inner).scopes.pop();
230 Ok(())
231 }
232
233 pub fn scope_depth(&self) -> usize {
235 self.inner.scopes.len() - 1
236 }
237
238 pub fn current_scope_type(&self) -> &ScopeType {
240 &self.inner.scopes.last().unwrap().scope_type
241 }
242
243 pub fn set(&mut self, name: String, variable: Variable, mutable: bool) -> Result<()> {
245 self.set_in_current_scope(name, variable, mutable)
246 }
247
248 pub fn reassign(&mut self, name: String, variable: Variable) -> Result<()> {
251 let inner = Arc::make_mut(&mut self.inner);
252 for scope in inner.scopes.iter_mut().rev() {
254 if let Some(existing) = scope.variables.get(&name) {
255 if !existing.mutable {
256 return Err(EngineError::VariableIsImmutable {
257 name: name.clone(),
258 }
259 .into());
260 }
261 let mutable = existing.mutable;
262 scope.variables.insert(
263 name,
264 VariableBinding {
265 variable,
266 mutable,
267 },
268 );
269 return Ok(());
270 }
271 }
272
273 Err(EngineError::VariableNotFound {
274 name: name.clone(),
275 }
276 .into())
277 }
278
279 pub fn set_in_current_scope(&mut self, name: String, variable: Variable, mutable: bool) -> Result<()> {
282 let inner = Arc::make_mut(&mut self.inner);
283 let current_scope = inner.scopes.last_mut().unwrap();
284
285 current_scope.variables.insert(
287 name,
288 VariableBinding {
289 variable,
290 mutable,
291 },
292 );
293 Ok(())
294 }
295
296 pub fn get(&self, name: &str) -> Option<&Variable> {
298 for scope in self.inner.scopes.iter().rev() {
300 if let Some(binding) = scope.variables.get(name) {
301 return Some(&binding.variable);
302 }
303 }
304 None
305 }
306
307 pub fn get_with_scope(&self, name: &str) -> Option<(&Variable, usize)> {
309 for (depth_from_end, scope) in self.inner.scopes.iter().rev().enumerate() {
311 if let Some(binding) = scope.variables.get(name) {
312 let scope_depth = self.inner.scopes.len() - 1 - depth_from_end;
313 return Some((&binding.variable, scope_depth));
314 }
315 }
316 None
317 }
318
319 pub fn exists_in_current_scope(&self, name: &str) -> bool {
321 self.inner.scopes.last().unwrap().variables.contains_key(name)
322 }
323
324 pub fn exists_in_any_scope(&self, name: &str) -> bool {
326 self.get(name).is_some()
327 }
328
329 pub fn is_mutable(&self, name: &str) -> bool {
331 for scope in self.inner.scopes.iter().rev() {
332 if let Some(binding) = scope.variables.get(name) {
333 return binding.mutable;
334 }
335 }
336 false
337 }
338
339 pub fn all_variable_names(&self) -> Vec<String> {
341 let mut names = Vec::new();
342 for (scope_idx, scope) in self.inner.scopes.iter().enumerate() {
343 for name in scope.variables.keys() {
344 names.push(format!("{}@scope{}", name, scope_idx));
345 }
346 }
347 names
348 }
349
350 pub fn visible_variable_names(&self) -> Vec<String> {
352 let mut visible = HashMap::new();
353
354 for scope in &self.inner.scopes {
356 for name in scope.variables.keys() {
357 visible.insert(name.clone(), ());
358 }
359 }
360
361 visible.keys().cloned().collect()
362 }
363
364 pub fn clear(&mut self) {
366 let inner = Arc::make_mut(&mut self.inner);
367 inner.scopes.clear();
368 inner.scopes.push(Scope {
369 variables: HashMap::new(),
370 scope_type: ScopeType::Global,
371 });
372 inner.functions.clear();
373 }
374
375 pub fn define_function(&mut self, name: String, func: CompiledFunction) {
377 Arc::make_mut(&mut self.inner).functions.insert(name, func);
378 }
379
380 pub fn get_function(&self, name: &str) -> Option<&CompiledFunction> {
382 self.inner.functions.get(name)
383 }
384
385 pub fn function_exists(&self, name: &str) -> bool {
387 self.inner.functions.contains_key(name)
388 }
389}
390
391impl Default for SymbolTable {
392 fn default() -> Self {
393 Self::new()
394 }
395}
396
397#[cfg(test)]
398pub mod tests {
399 use reifydb_core::value::column::{Column, data::ColumnData};
400 use reifydb_type::value::{Value, r#type::Type};
401
402 use super::*;
403
404 fn create_test_columns(values: Vec<Value>) -> Columns {
406 if values.is_empty() {
407 let column_data = ColumnData::none_typed(Type::Boolean, 0);
408 let column = Column::new("test_col", column_data);
409 return Columns::new(vec![column]);
410 }
411
412 let mut column_data = ColumnData::none_typed(Type::Boolean, 0);
413 for value in values {
414 column_data.push_value(value);
415 }
416
417 let column = Column::new("test_col", column_data);
418 Columns::new(vec![column])
419 }
420
421 #[test]
422 fn test_basic_variable_operations() {
423 let mut ctx = SymbolTable::new();
424 let cols = create_test_columns(vec![Value::utf8("Alice".to_string())]);
425
426 ctx.set("name".to_string(), Variable::columns(cols.clone()), false).unwrap();
428
429 assert!(ctx.get("name").is_some());
431 assert!(!ctx.is_mutable("name"));
432 assert!(ctx.exists_in_any_scope("name"));
433 assert!(ctx.exists_in_current_scope("name"));
434 }
435
436 #[test]
437 fn test_mutable_variable() {
438 let mut ctx = SymbolTable::new();
439 let cols1 = create_test_columns(vec![Value::Int4(42)]);
440 let cols2 = create_test_columns(vec![Value::Int4(84)]);
441
442 ctx.set("counter".to_string(), Variable::columns(cols1.clone()), true).unwrap();
444 assert!(ctx.is_mutable("counter"));
445 assert!(ctx.get("counter").is_some());
446
447 ctx.set("counter".to_string(), Variable::columns(cols2.clone()), true).unwrap();
449 assert!(ctx.get("counter").is_some());
450 }
451
452 #[test]
453 #[ignore]
454 fn test_immutable_variable_reassignment_fails() {
455 let mut ctx = SymbolTable::new();
456 let cols1 = create_test_columns(vec![Value::utf8("Alice".to_string())]);
457 let cols2 = create_test_columns(vec![Value::utf8("Bob".to_string())]);
458
459 ctx.set("name".to_string(), Variable::columns(cols1.clone()), false).unwrap();
461
462 let result = ctx.set("name".to_string(), Variable::columns(cols2), false);
464 assert!(result.is_err());
465
466 assert!(ctx.get("name").is_some());
468 }
469
470 #[test]
471 fn test_scope_management() {
472 let mut ctx = SymbolTable::new();
473
474 assert_eq!(ctx.scope_depth(), 0);
476 assert_eq!(ctx.current_scope_type(), &ScopeType::Global);
477
478 ctx.enter_scope(ScopeType::Function);
480 assert_eq!(ctx.scope_depth(), 1);
481 assert_eq!(ctx.current_scope_type(), &ScopeType::Function);
482
483 ctx.enter_scope(ScopeType::Block);
485 assert_eq!(ctx.scope_depth(), 2);
486 assert_eq!(ctx.current_scope_type(), &ScopeType::Block);
487
488 ctx.exit_scope().unwrap();
490 assert_eq!(ctx.scope_depth(), 1);
491 assert_eq!(ctx.current_scope_type(), &ScopeType::Function);
492
493 ctx.exit_scope().unwrap();
495 assert_eq!(ctx.scope_depth(), 0);
496 assert_eq!(ctx.current_scope_type(), &ScopeType::Global);
497
498 assert!(ctx.exit_scope().is_err());
500 }
501
502 #[test]
503 fn test_variable_shadowing() {
504 let mut ctx = SymbolTable::new();
505 let outer_cols = create_test_columns(vec![Value::utf8("outer".to_string())]);
506 let inner_cols = create_test_columns(vec![Value::utf8("inner".to_string())]);
507
508 ctx.set("var".to_string(), Variable::columns(outer_cols.clone()), false).unwrap();
510 assert!(ctx.get("var").is_some());
511
512 ctx.enter_scope(ScopeType::Block);
514 ctx.set("var".to_string(), Variable::columns(inner_cols.clone()), false).unwrap();
515
516 assert!(ctx.get("var").is_some());
518 assert!(ctx.exists_in_current_scope("var"));
519
520 ctx.exit_scope().unwrap();
522 assert!(ctx.get("var").is_some());
523 }
524
525 #[test]
526 fn test_parent_scope_access() {
527 let mut ctx = SymbolTable::new();
528 let outer_cols = create_test_columns(vec![Value::utf8("outer".to_string())]);
529
530 ctx.set("global_var".to_string(), Variable::columns(outer_cols.clone()), false).unwrap();
532
533 ctx.enter_scope(ScopeType::Function);
535
536 assert!(ctx.get("global_var").is_some());
538 assert!(!ctx.exists_in_current_scope("global_var"));
539 assert!(ctx.exists_in_any_scope("global_var"));
540
541 let (_, scope_depth) = ctx.get_with_scope("global_var").unwrap();
543 assert_eq!(scope_depth, 0); }
545
546 #[test]
547 fn test_scope_specific_mutability() {
548 let mut ctx = SymbolTable::new();
549 let cols1 = create_test_columns(vec![Value::utf8("value1".to_string())]);
550 let cols2 = create_test_columns(vec![Value::utf8("value2".to_string())]);
551
552 ctx.set("var".to_string(), Variable::columns(cols1.clone()), false).unwrap();
554
555 ctx.enter_scope(ScopeType::Block);
557 ctx.set("var".to_string(), Variable::columns(cols2.clone()), true).unwrap(); assert!(ctx.is_mutable("var"));
561
562 ctx.exit_scope().unwrap();
564 assert!(!ctx.is_mutable("var"));
565 }
566
567 #[test]
568 fn test_visible_variable_names() {
569 let mut ctx = SymbolTable::new();
570 let cols = create_test_columns(vec![Value::utf8("test".to_string())]);
571
572 ctx.set("global1".to_string(), Variable::columns(cols.clone()), false).unwrap();
574 ctx.set("global2".to_string(), Variable::columns(cols.clone()), false).unwrap();
575
576 let global_visible = ctx.visible_variable_names();
577 assert_eq!(global_visible.len(), 2);
578 assert!(global_visible.contains(&"global1".to_string()));
579 assert!(global_visible.contains(&"global2".to_string()));
580
581 ctx.enter_scope(ScopeType::Function);
583 ctx.set("local1".to_string(), Variable::columns(cols.clone()), false).unwrap();
584 ctx.set("global1".to_string(), Variable::columns(cols.clone()), false).unwrap(); let function_visible = ctx.visible_variable_names();
587 assert_eq!(function_visible.len(), 3); assert!(function_visible.contains(&"global1".to_string()));
589 assert!(function_visible.contains(&"global2".to_string()));
590 assert!(function_visible.contains(&"local1".to_string()));
591 }
592
593 #[test]
594 fn test_clear_resets_to_global() {
595 let mut ctx = SymbolTable::new();
596 let cols = create_test_columns(vec![Value::utf8("test".to_string())]);
597
598 ctx.set("var1".to_string(), Variable::columns(cols.clone()), false).unwrap();
600 ctx.enter_scope(ScopeType::Function);
601 ctx.set("var2".to_string(), Variable::columns(cols.clone()), false).unwrap();
602 ctx.enter_scope(ScopeType::Block);
603 ctx.set("var3".to_string(), Variable::columns(cols.clone()), false).unwrap();
604
605 assert_eq!(ctx.scope_depth(), 2);
606 assert_eq!(ctx.visible_variable_names().len(), 3);
607
608 ctx.clear();
610 assert_eq!(ctx.scope_depth(), 0);
611 assert_eq!(ctx.current_scope_type(), &ScopeType::Global);
612 assert_eq!(ctx.visible_variable_names().len(), 0);
613 }
614
615 #[test]
616 fn test_nonexistent_variable() {
617 let ctx = SymbolTable::new();
618
619 assert!(ctx.get("nonexistent").is_none());
620 assert!(!ctx.exists_in_any_scope("nonexistent"));
621 assert!(!ctx.exists_in_current_scope("nonexistent"));
622 assert!(!ctx.is_mutable("nonexistent"));
623 assert!(ctx.get_with_scope("nonexistent").is_none());
624 }
625}