Skip to main content

shape_runtime/context/
scope.rs

1//! Scope management for ExecutionContext
2//!
3//! Handles pushing/popping scopes for function calls and creating parallel execution contexts.
4
5use std::collections::HashMap;
6use std::sync::Arc;
7
8impl super::ExecutionContext {
9    /// Push a new variable scope (for function calls)
10    pub fn push_scope(&mut self) {
11        self.variable_scopes.push(HashMap::new());
12    }
13
14    /// Pop a variable scope (after function return)
15    pub fn pop_scope(&mut self) {
16        if self.variable_scopes.len() > 1 {
17            self.variable_scopes.pop();
18        }
19    }
20
21    /// Reset for new execution (Interpreter mode)
22    ///
23    /// Clears script-level variables while preserving:
24    /// - Data cache (loaded instruments)
25    /// - Registered functions (from stdlib)
26    /// - Data providers
27    /// - Module exports
28    ///
29    /// This allows re-execution of code with the same variable names
30    /// without "Variable already declared" errors.
31    pub fn reset_for_new_execution(&mut self) {
32        // Clear the root scope but keep the structure
33        if let Some(root_scope) = self.variable_scopes.first_mut() {
34            root_scope.clear();
35        }
36        // Ensure we only have the root scope (remove any leftover nested scopes)
37        self.variable_scopes.truncate(1);
38    }
39
40    /// Create a fresh context for parallel execution that shares Arc-wrapped resources
41    /// but has independent mutable state (variables, position manager, caches)
42    pub fn fork_for_parallel(&self) -> Self {
43        Self {
44            // Shared immutable resources (Arc-wrapped)
45            data_provider: self.data_provider.clone(),
46            data_cache: None,
47            provider_registry: Arc::new(super::super::provider_registry::ProviderRegistry::new()),
48            type_mapping_registry: Arc::new(super::super::type_mapping::TypeMappingRegistry::new()),
49            type_schema_registry: self.type_schema_registry.clone(),
50            metadata_registry: self.metadata_registry.clone(),
51            data_load_mode: super::DataLoadMode::default(),
52            type_method_registry: self.type_method_registry.clone(),
53
54            // Copied configuration
55            current_id: self.current_id.clone(),
56            current_row_index: self.current_row_index,
57            variable_scopes: self.variable_scopes.clone(),
58            reference_datetime: self.reference_datetime,
59            current_timeframe: self.current_timeframe,
60            base_timeframe: self.base_timeframe,
61            lookahead_guard: self.lookahead_guard.clone(),
62            date_range: self.date_range,
63            range_start: self.range_start,
64            range_end: self.range_end,
65            range_active: self.range_active,
66            pattern_registry: self.pattern_registry.clone(),
67            annotation_context: self.annotation_context.clone(),
68            annotation_registry: self.annotation_registry.clone(),
69            event_queue: self.event_queue.clone(),
70            suspension_state: self.suspension_state.clone(),
71            alert_pipeline: self.alert_pipeline.clone(),
72            output_adapter: self.output_adapter.clone(),
73            type_alias_registry: self.type_alias_registry.clone(),
74            enum_registry: self.enum_registry.clone(),
75            progress_registry: self.progress_registry.clone(),
76            kernel_compiler: self.kernel_compiler.clone(),
77        }
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::super::ExecutionContext;
84
85    #[test]
86    fn test_execution_context_scope_push_pop() {
87        let mut ctx = ExecutionContext::new_empty();
88
89        // Push and pop scopes
90        ctx.push_scope();
91        ctx.pop_scope();
92        // Should not panic
93    }
94}