blots_core/
environment.rs

1use crate::values::Value;
2use std::cell::RefCell;
3use std::collections::HashMap;
4use std::rc::Rc;
5
6/// A scope chain environment for variable bindings.
7///
8/// Instead of cloning the entire HashMap on each function call,
9/// we create a linked chain of scopes. Lookups walk the chain,
10/// but creating a new scope is O(1).
11#[derive(Debug)]
12pub struct Environment {
13    /// Local bindings in this scope
14    local: RefCell<HashMap<String, Value>>,
15    /// Parent scope (if any)
16    parent: Option<Rc<Environment>>,
17}
18
19impl Environment {
20    /// Create a new root environment with no parent
21    pub fn new() -> Self {
22        Environment {
23            local: RefCell::new(HashMap::new()),
24            parent: None,
25        }
26    }
27
28    /// Create a new root environment with initial bindings
29    pub fn with_bindings(bindings: HashMap<String, Value>) -> Self {
30        Environment {
31            local: RefCell::new(bindings),
32            parent: None,
33        }
34    }
35
36    /// Create a child environment that inherits from this one
37    pub fn extend(parent: Rc<Environment>) -> Self {
38        Environment {
39            local: RefCell::new(HashMap::new()),
40            parent: Some(parent),
41        }
42    }
43
44    /// Create a child environment with initial local bindings
45    pub fn extend_with(parent: Rc<Environment>, local: HashMap<String, Value>) -> Self {
46        Environment {
47            local: RefCell::new(local),
48            parent: Some(parent),
49        }
50    }
51
52    /// Look up a variable, walking the scope chain
53    pub fn get(&self, key: &str) -> Option<Value> {
54        // Check local scope first
55        if let Some(value) = self.local.borrow().get(key) {
56            return Some(*value);
57        }
58        // Then check parent scope
59        if let Some(parent) = &self.parent {
60            return parent.get(key);
61        }
62        None
63    }
64
65    /// Insert or update a binding in the local scope
66    pub fn insert(&self, key: String, value: Value) {
67        self.local.borrow_mut().insert(key, value);
68    }
69
70    /// Check if a key exists in any scope
71    pub fn contains_key(&self, key: &str) -> bool {
72        if self.local.borrow().contains_key(key) {
73            return true;
74        }
75        if let Some(parent) = &self.parent {
76            return parent.contains_key(key);
77        }
78        false
79    }
80
81    /// Check if a key exists in the local scope only
82    pub fn contains_key_local(&self, key: &str) -> bool {
83        self.local.borrow().contains_key(key)
84    }
85}
86
87impl Default for Environment {
88    fn default() -> Self {
89        Self::new()
90    }
91}
92
93impl Clone for Environment {
94    fn clone(&self) -> Self {
95        // For closures that capture scope, we need to clone
96        // This flattens the scope chain into a single HashMap
97        Environment {
98            local: RefCell::new(self.flatten()),
99            parent: None,
100        }
101    }
102}
103
104impl Environment {
105    /// Flatten the scope chain into a single HashMap
106    /// Used when capturing variables for closures or serialization
107    pub fn flatten(&self) -> HashMap<String, Value> {
108        let mut result = HashMap::new();
109        self.flatten_into(&mut result);
110        result
111    }
112
113    fn flatten_into(&self, result: &mut HashMap<String, Value>) {
114        // First add parent bindings (so local can override)
115        if let Some(parent) = &self.parent {
116            parent.flatten_into(result);
117        }
118        // Then add local bindings
119        for (key, value) in self.local.borrow().iter() {
120            result.insert(key.clone(), *value);
121        }
122    }
123
124    /// Iterate over all bindings (flattened)
125    /// Useful for serialization
126    pub fn iter(&self) -> impl Iterator<Item = (String, Value)> {
127        self.flatten().into_iter()
128    }
129
130    /// Get all keys (flattened)
131    /// Useful for completion
132    pub fn keys(&self) -> impl Iterator<Item = String> {
133        self.flatten().into_keys()
134    }
135}