1use std::collections::HashMap;
5
6use reifydb_core::{internal, value::column::columns::Columns};
7use reifydb_rql::instruction::{CompiledClosureDef, CompiledFunctionDef, ScopeType};
8use reifydb_type::{error, value::Value};
9
10use crate::{Result, error::EngineError};
11
12#[derive(Debug, Clone)]
14pub struct Stack {
15 variables: Vec<Variable>,
16}
17
18impl Stack {
19 pub fn new() -> Self {
20 Self {
21 variables: Vec::new(),
22 }
23 }
24
25 pub fn push(&mut self, value: Variable) {
26 self.variables.push(value);
27 }
28
29 pub fn pop(&mut self) -> Result<Variable> {
30 self.variables.pop().ok_or_else(|| error!(internal!("VM data stack underflow")))
31 }
32
33 pub fn peek(&self) -> Option<&Variable> {
34 self.variables.last()
35 }
36
37 pub fn is_empty(&self) -> bool {
38 self.variables.is_empty()
39 }
40
41 pub fn len(&self) -> usize {
42 self.variables.len()
43 }
44}
45
46impl Default for Stack {
47 fn default() -> Self {
48 Self::new()
49 }
50}
51
52#[derive(Debug, Clone)]
54pub struct ClosureValue {
55 pub def: CompiledClosureDef,
56 pub captured: HashMap<String, Variable>,
57}
58
59#[derive(Debug, Clone)]
61pub enum Variable {
62 Scalar(Columns),
64 Columns(Columns),
66 ForIterator {
68 columns: Columns,
69 index: usize,
70 },
71 Closure(ClosureValue),
73}
74
75impl Variable {
76 pub fn scalar(value: Value) -> Self {
78 Variable::Scalar(Columns::scalar(value))
79 }
80
81 pub fn columns(columns: Columns) -> Self {
83 Variable::Columns(columns)
84 }
85}
86
87#[derive(Debug, Clone)]
89pub struct SymbolTable {
90 scopes: Vec<Scope>,
91 functions: HashMap<String, CompiledFunctionDef>,
93}
94
95#[derive(Debug, Clone)]
97struct Scope {
98 variables: HashMap<String, VariableBinding>,
99 scope_type: ScopeType,
100}
101
102#[derive(Debug, Clone)]
104pub enum ControlFlow {
105 Normal,
106 Break,
107 Continue,
108 Return(Option<Columns>),
109}
110
111impl ControlFlow {
112 pub fn is_normal(&self) -> bool {
113 matches!(self, ControlFlow::Normal)
114 }
115}
116
117#[derive(Debug, Clone)]
119struct VariableBinding {
120 variable: Variable,
121 mutable: bool,
122}
123
124impl SymbolTable {
125 pub fn new() -> Self {
127 let global_scope = Scope {
128 variables: HashMap::new(),
129 scope_type: ScopeType::Global,
130 };
131
132 Self {
133 scopes: vec![global_scope],
134 functions: HashMap::new(),
135 }
136 }
137
138 pub fn enter_scope(&mut self, scope_type: ScopeType) {
140 let new_scope = Scope {
141 variables: HashMap::new(),
142 scope_type,
143 };
144 self.scopes.push(new_scope);
145 }
146
147 pub fn exit_scope(&mut self) -> Result<()> {
150 if self.scopes.len() <= 1 {
151 return Err(error!(internal!("Cannot exit global scope")));
152 }
153 self.scopes.pop();
154 Ok(())
155 }
156
157 pub fn scope_depth(&self) -> usize {
159 self.scopes.len() - 1
160 }
161
162 pub fn current_scope_type(&self) -> &ScopeType {
164 &self.scopes.last().unwrap().scope_type
165 }
166
167 pub fn set(&mut self, name: String, variable: Variable, mutable: bool) -> Result<()> {
169 self.set_in_current_scope(name, variable, mutable)
170 }
171
172 pub fn reassign(&mut self, name: String, variable: Variable) -> Result<()> {
175 for scope in self.scopes.iter_mut().rev() {
177 if let Some(existing) = scope.variables.get(&name) {
178 if !existing.mutable {
179 return Err(EngineError::VariableIsImmutable {
180 name: name.clone(),
181 }
182 .into());
183 }
184 let mutable = existing.mutable;
185 scope.variables.insert(
186 name,
187 VariableBinding {
188 variable,
189 mutable,
190 },
191 );
192 return Ok(());
193 }
194 }
195
196 Err(EngineError::VariableNotFound {
197 name: name.clone(),
198 }
199 .into())
200 }
201
202 pub fn set_in_current_scope(&mut self, name: String, variable: Variable, mutable: bool) -> Result<()> {
205 let current_scope = self.scopes.last_mut().unwrap();
206
207 current_scope.variables.insert(
209 name,
210 VariableBinding {
211 variable,
212 mutable,
213 },
214 );
215 Ok(())
216 }
217
218 pub fn get(&self, name: &str) -> Option<&Variable> {
220 for scope in self.scopes.iter().rev() {
222 if let Some(binding) = scope.variables.get(name) {
223 return Some(&binding.variable);
224 }
225 }
226 None
227 }
228
229 pub fn get_with_scope(&self, name: &str) -> Option<(&Variable, usize)> {
231 for (depth_from_end, scope) in self.scopes.iter().rev().enumerate() {
233 if let Some(binding) = scope.variables.get(name) {
234 let scope_depth = self.scopes.len() - 1 - depth_from_end;
235 return Some((&binding.variable, scope_depth));
236 }
237 }
238 None
239 }
240
241 pub fn exists_in_current_scope(&self, name: &str) -> bool {
243 self.scopes.last().unwrap().variables.contains_key(name)
244 }
245
246 pub fn exists_in_any_scope(&self, name: &str) -> bool {
248 self.get(name).is_some()
249 }
250
251 pub fn is_mutable(&self, name: &str) -> bool {
253 for scope in self.scopes.iter().rev() {
254 if let Some(binding) = scope.variables.get(name) {
255 return binding.mutable;
256 }
257 }
258 false
259 }
260
261 pub fn all_variable_names(&self) -> Vec<String> {
263 let mut names = Vec::new();
264 for (scope_idx, scope) in self.scopes.iter().enumerate() {
265 for name in scope.variables.keys() {
266 names.push(format!("{}@scope{}", name, scope_idx));
267 }
268 }
269 names
270 }
271
272 pub fn visible_variable_names(&self) -> Vec<String> {
274 let mut visible = HashMap::new();
275
276 for scope in &self.scopes {
278 for name in scope.variables.keys() {
279 visible.insert(name.clone(), ());
280 }
281 }
282
283 visible.keys().cloned().collect()
284 }
285
286 pub fn clear(&mut self) {
288 self.scopes.clear();
289 self.scopes.push(Scope {
290 variables: HashMap::new(),
291 scope_type: ScopeType::Global,
292 });
293 self.functions.clear();
294 }
295
296 pub fn define_function(&mut self, name: String, func: CompiledFunctionDef) {
298 self.functions.insert(name, func);
299 }
300
301 pub fn get_function(&self, name: &str) -> Option<&CompiledFunctionDef> {
303 self.functions.get(name)
304 }
305
306 pub fn function_exists(&self, name: &str) -> bool {
308 self.functions.contains_key(name)
309 }
310}
311
312impl Default for SymbolTable {
313 fn default() -> Self {
314 Self::new()
315 }
316}
317
318#[cfg(test)]
319pub mod tests {
320 use reifydb_core::value::column::{Column, data::ColumnData};
321 use reifydb_type::value::{Value, r#type::Type};
322
323 use super::*;
324
325 fn create_test_columns(values: Vec<Value>) -> Columns {
327 if values.is_empty() {
328 let column_data = ColumnData::none_typed(Type::Boolean, 0);
329 let column = Column::new("test_col", column_data);
330 return Columns::new(vec![column]);
331 }
332
333 let mut column_data = ColumnData::none_typed(Type::Boolean, 0);
334 for value in values {
335 column_data.push_value(value);
336 }
337
338 let column = Column::new("test_col", column_data);
339 Columns::new(vec![column])
340 }
341
342 #[test]
343 fn test_basic_variable_operations() {
344 let mut ctx = SymbolTable::new();
345 let cols = create_test_columns(vec![Value::utf8("Alice".to_string())]);
346
347 ctx.set("name".to_string(), Variable::columns(cols.clone()), false).unwrap();
349
350 assert!(ctx.get("name").is_some());
352 assert!(!ctx.is_mutable("name"));
353 assert!(ctx.exists_in_any_scope("name"));
354 assert!(ctx.exists_in_current_scope("name"));
355 }
356
357 #[test]
358 fn test_mutable_variable() {
359 let mut ctx = SymbolTable::new();
360 let cols1 = create_test_columns(vec![Value::Int4(42)]);
361 let cols2 = create_test_columns(vec![Value::Int4(84)]);
362
363 ctx.set("counter".to_string(), Variable::columns(cols1.clone()), true).unwrap();
365 assert!(ctx.is_mutable("counter"));
366 assert!(ctx.get("counter").is_some());
367
368 ctx.set("counter".to_string(), Variable::columns(cols2.clone()), true).unwrap();
370 assert!(ctx.get("counter").is_some());
371 }
372
373 #[test]
374 #[ignore]
375 fn test_immutable_variable_reassignment_fails() {
376 let mut ctx = SymbolTable::new();
377 let cols1 = create_test_columns(vec![Value::utf8("Alice".to_string())]);
378 let cols2 = create_test_columns(vec![Value::utf8("Bob".to_string())]);
379
380 ctx.set("name".to_string(), Variable::columns(cols1.clone()), false).unwrap();
382
383 let result = ctx.set("name".to_string(), Variable::columns(cols2), false);
385 assert!(result.is_err());
386
387 assert!(ctx.get("name").is_some());
389 }
390
391 #[test]
392 fn test_scope_management() {
393 let mut ctx = SymbolTable::new();
394
395 assert_eq!(ctx.scope_depth(), 0);
397 assert_eq!(ctx.current_scope_type(), &ScopeType::Global);
398
399 ctx.enter_scope(ScopeType::Function);
401 assert_eq!(ctx.scope_depth(), 1);
402 assert_eq!(ctx.current_scope_type(), &ScopeType::Function);
403
404 ctx.enter_scope(ScopeType::Block);
406 assert_eq!(ctx.scope_depth(), 2);
407 assert_eq!(ctx.current_scope_type(), &ScopeType::Block);
408
409 ctx.exit_scope().unwrap();
411 assert_eq!(ctx.scope_depth(), 1);
412 assert_eq!(ctx.current_scope_type(), &ScopeType::Function);
413
414 ctx.exit_scope().unwrap();
416 assert_eq!(ctx.scope_depth(), 0);
417 assert_eq!(ctx.current_scope_type(), &ScopeType::Global);
418
419 assert!(ctx.exit_scope().is_err());
421 }
422
423 #[test]
424 fn test_variable_shadowing() {
425 let mut ctx = SymbolTable::new();
426 let outer_cols = create_test_columns(vec![Value::utf8("outer".to_string())]);
427 let inner_cols = create_test_columns(vec![Value::utf8("inner".to_string())]);
428
429 ctx.set("var".to_string(), Variable::columns(outer_cols.clone()), false).unwrap();
431 assert!(ctx.get("var").is_some());
432
433 ctx.enter_scope(ScopeType::Block);
435 ctx.set("var".to_string(), Variable::columns(inner_cols.clone()), false).unwrap();
436
437 assert!(ctx.get("var").is_some());
439 assert!(ctx.exists_in_current_scope("var"));
440
441 ctx.exit_scope().unwrap();
443 assert!(ctx.get("var").is_some());
444 }
445
446 #[test]
447 fn test_parent_scope_access() {
448 let mut ctx = SymbolTable::new();
449 let outer_cols = create_test_columns(vec![Value::utf8("outer".to_string())]);
450
451 ctx.set("global_var".to_string(), Variable::columns(outer_cols.clone()), false).unwrap();
453
454 ctx.enter_scope(ScopeType::Function);
456
457 assert!(ctx.get("global_var").is_some());
459 assert!(!ctx.exists_in_current_scope("global_var"));
460 assert!(ctx.exists_in_any_scope("global_var"));
461
462 let (_, scope_depth) = ctx.get_with_scope("global_var").unwrap();
464 assert_eq!(scope_depth, 0); }
466
467 #[test]
468 fn test_scope_specific_mutability() {
469 let mut ctx = SymbolTable::new();
470 let cols1 = create_test_columns(vec![Value::utf8("value1".to_string())]);
471 let cols2 = create_test_columns(vec![Value::utf8("value2".to_string())]);
472
473 ctx.set("var".to_string(), Variable::columns(cols1.clone()), false).unwrap();
475
476 ctx.enter_scope(ScopeType::Block);
478 ctx.set("var".to_string(), Variable::columns(cols2.clone()), true).unwrap(); assert!(ctx.is_mutable("var"));
482
483 ctx.exit_scope().unwrap();
485 assert!(!ctx.is_mutable("var"));
486 }
487
488 #[test]
489 fn test_visible_variable_names() {
490 let mut ctx = SymbolTable::new();
491 let cols = create_test_columns(vec![Value::utf8("test".to_string())]);
492
493 ctx.set("global1".to_string(), Variable::columns(cols.clone()), false).unwrap();
495 ctx.set("global2".to_string(), Variable::columns(cols.clone()), false).unwrap();
496
497 let global_visible = ctx.visible_variable_names();
498 assert_eq!(global_visible.len(), 2);
499 assert!(global_visible.contains(&"global1".to_string()));
500 assert!(global_visible.contains(&"global2".to_string()));
501
502 ctx.enter_scope(ScopeType::Function);
504 ctx.set("local1".to_string(), Variable::columns(cols.clone()), false).unwrap();
505 ctx.set("global1".to_string(), Variable::columns(cols.clone()), false).unwrap(); let function_visible = ctx.visible_variable_names();
508 assert_eq!(function_visible.len(), 3); assert!(function_visible.contains(&"global1".to_string()));
510 assert!(function_visible.contains(&"global2".to_string()));
511 assert!(function_visible.contains(&"local1".to_string()));
512 }
513
514 #[test]
515 fn test_clear_resets_to_global() {
516 let mut ctx = SymbolTable::new();
517 let cols = create_test_columns(vec![Value::utf8("test".to_string())]);
518
519 ctx.set("var1".to_string(), Variable::columns(cols.clone()), false).unwrap();
521 ctx.enter_scope(ScopeType::Function);
522 ctx.set("var2".to_string(), Variable::columns(cols.clone()), false).unwrap();
523 ctx.enter_scope(ScopeType::Block);
524 ctx.set("var3".to_string(), Variable::columns(cols.clone()), false).unwrap();
525
526 assert_eq!(ctx.scope_depth(), 2);
527 assert_eq!(ctx.visible_variable_names().len(), 3);
528
529 ctx.clear();
531 assert_eq!(ctx.scope_depth(), 0);
532 assert_eq!(ctx.current_scope_type(), &ScopeType::Global);
533 assert_eq!(ctx.visible_variable_names().len(), 0);
534 }
535
536 #[test]
537 fn test_nonexistent_variable() {
538 let ctx = SymbolTable::new();
539
540 assert!(ctx.get("nonexistent").is_none());
541 assert!(!ctx.exists_in_any_scope("nonexistent"));
542 assert!(!ctx.exists_in_current_scope("nonexistent"));
543 assert!(!ctx.is_mutable("nonexistent"));
544 assert!(ctx.get_with_scope("nonexistent").is_none());
545 }
546}