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