Skip to main content

swan_core/value/
scope.rs

1use super::*;
2
3
4// ----------------------------------------------------------------------------
5//  Scope  (evaluation environment — NOT a first-class Swan value)
6//
7//  A `Scope` is the lexical environment used during evaluation. It forms a
8//  parent-chained singly-linked list of binding tables, one per lexical block.
9//  It is reference-counted so that closures can cheaply capture a snapshot of
10//  the current environment.
11//
12//  This type is intentionally separate from `Value::Namespace`. A Scope is an
13//  internal evaluator concept; a Namespace is a first-class Swan value.
14// ----------------------------------------------------------------------------
15
16/// A lexical environment: a table of bindings with an optional parent.
17///
18/// Name lookup ([`Scope::get`]) searches the scope itself and then its
19/// ancestors. New bindings ([`Scope::set`]) always go into the scope itself.
20/// A `Scope` is also the storage of a [`Value::Namespace`].
21///
22/// `Scope` is a cheap handle: clones share the same bindings.
23///
24/// # Examples
25///
26/// ```
27/// use swan_core::value::{Scope, Value};
28///
29/// let parent = Scope::new();
30/// parent.set("x", Value::Numb(1.0));
31///
32/// let child = parent.new_child();
33/// child.set("y", Value::Numb(2.0));
34///
35/// assert_eq!(child.get("x"), Value::Numb(1.0));   // inherited
36/// assert_eq!(parent.get("y"), Value::Nothing);    // not visible upwards
37/// ```
38#[derive(Clone, Debug)]
39pub struct Scope(pub Rc<RefCell<ScopeData>>);
40
41/// The shared state behind a [`Scope`].
42#[derive(Clone)]
43pub struct ScopeData {
44    /// The enclosing scope, searched when a name is not bound here.
45    pub parent: Option<Scope>,
46    /// The bindings defined in this scope (not in its ancestors).
47    pub bindings: HashMap<String, Value>,
48}
49
50impl Scope {
51    /// Create a top-level scope with no parent.
52    pub fn new() -> Self {
53        Self(Rc::new(RefCell::new(ScopeData {
54            parent: None,
55            bindings: HashMap::new(),
56        })))
57    }
58
59    /// Create a child scope that inherits from a parent.
60    pub fn with_parent(parent: Scope) -> Self {
61        Self(Rc::new(RefCell::new(ScopeData {
62            parent: Some(parent),
63            bindings: HashMap::new(),
64        })))
65    }
66
67    /// Creates a new child scope using `self` as the parent environment.
68    pub fn new_child(&self) -> Self {
69        Self::with_parent(self.clone())
70    }
71
72    /// Returns the value bound to `key` in this scope or its nearest ancestor
73    /// that binds it, or [`Value::Nothing`] if no scope binds it.
74    ///
75    /// The borrow on `self` is released *before* recursing into the parent so
76    /// that there is no live `RefCell` borrow held across the recursive call.
77    /// This prevents a runtime panic in the (unlikely but possible) event that
78    /// the same `Scope` node appears more than once in the chain.
79    pub fn get(&self, key: &str) -> Value {
80        // Release the borrow before any recursive call.
81        let (found, parent) = {
82            let inner = self.0.borrow();
83            let found = inner.bindings.get(key).cloned();
84            let parent = inner.parent.clone();
85            (found, parent)
86        };
87
88        found.unwrap_or_else(|| {
89            parent
90                .map(|p| p.get(key))
91                .unwrap_or(Value::Nothing)
92        })
93    }
94
95    /// Returns the value bound to `key` in the *outermost* scope of the chain
96    /// that binds it, or [`Value::Nothing`] if no scope binds it.
97    ///
98    /// This is the opposite search order of [`Scope::get`]. It implements the
99    /// Swan lifting operator `$name`.
100    pub fn lift(&self, key: &str) -> Value {
101        // Build a vector of scopes starting from the root down to `self`
102        let mut chain = Vec::new();
103        let mut current = Some(self.clone());
104
105        while let Some(scope) = current {
106            chain.push(scope.clone());
107            let inner = scope.0.borrow();
108            current = inner.parent.clone();
109        }
110
111        // Search top-down (root is at the back of `chain`)
112        for scope in chain.iter().rev() {
113            let inner = scope.0.borrow();
114            if let Some(val) = inner.bindings.get(key) {
115                return val.clone();
116            }
117        }
118
119        Value::Nothing
120    }
121
122    /// Inserts or updates a binding in the current local scope only.
123    pub fn set(&self, key: impl Into<String>, val: Value) {
124        self.0.borrow_mut().bindings.insert(key.into(), val);
125    }
126}
127
128impl PartialEq for ScopeData {
129
130    fn eq(&self, other: &Self) -> bool {
131
132        // Compare bindings by value
133        if self.bindings != other.bindings {
134            return false;
135        }
136
137        // Check if both parents exist or both are None
138        match (&self.parent, &other.parent) {
139            (None, None) => true,
140            (Some(p1), Some(p2)) => Rc::ptr_eq(&p1.0, &p2.0), // Same parent pointer instance
141            _ => false,
142        }
143    }
144}
145
146impl fmt::Debug for ScopeData {
147    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148        // Print only the binding names: values may be Funcs whose closures
149        // point back to this very scope, which would recurse forever.
150        let mut keys: Vec<&String> = self.bindings.keys().collect();
151        keys.sort();
152        f.debug_struct("ScopeData")
153            .field("bindings", &keys)
154            .field("has_parent", &self.parent.is_some())
155            .finish()
156    }
157}
158
159impl PartialEq for Scope {
160    fn eq(&self, other: &Self) -> bool {
161        // Optional performance shortcut: check if both Scopes share the exact same ScopeData instance
162        if Rc::ptr_eq(&self.0, &other.0) {
163            return true;
164        }
165
166        // Compare the inner ScopeData fields
167        self.0.borrow().eq(&other.0.borrow())
168    }
169}
170
171impl Eq for Scope {}
172
173
174
175
176
177
178
179#[cfg(test)]
180mod tests {
181    use super::*;
182
183    #[test]
184    fn test_scope_new_and_set_get() {
185        let scope = Scope::new();
186
187        // Getting an unbound key returns Nothing
188        assert_eq!(scope.get("a"), Value::Nothing);
189
190        // Setting a binding and retrieving it
191        scope.set("a", Value::Numb(42.0));
192        assert_eq!(scope.get("a"), Value::Numb(42.0));
193    }
194
195    #[test]
196    fn test_scope_with_parent() {
197        let parent = Scope::new();
198        parent.set("x", Value::Numb(10.0));
199
200        let child = Scope::with_parent(parent);
201        
202        // Child can look up variables from parent
203        assert_eq!(child.get("x"), Value::Numb(10.0));
204
205        // Unbound key in both child and parent resolves to Nothing
206        assert_eq!(child.get("y"), Value::Nothing);
207    }
208
209    #[test]
210    fn test_scope_new_child() {
211        let parent = Scope::new();
212        parent.set("greeting", Value::Text("hello".into()));
213
214        let child = parent.new_child();
215
216        // Child inherits parent binding
217        assert_eq!(child.get("greeting"), Value::Text("hello".into()));
218
219        // Local set in child does not pollute parent
220        child.set("local_var", Value::Bool(true));
221        assert_eq!(child.get("local_var"), Value::Bool(true));
222        assert_eq!(parent.get("local_var"), Value::Nothing);
223    }
224
225    #[test]
226    fn test_scope_shadowing() {
227        let parent = Scope::new();
228        parent.set("x", Value::Numb(1.0));
229
230        let child = parent.new_child();
231        // Shadow 'x' in the child scope
232        child.set("x", Value::Numb(2.0));
233
234        // Child sees shadowed value, parent retains original value
235        assert_eq!(child.get("x"), Value::Numb(2.0));
236        assert_eq!(parent.get("x"), Value::Numb(1.0));
237    }
238
239    #[test]
240    fn test_parent_mutation_visible_to_child() {
241        let scope1 = Scope::new();
242        let scope2 = scope1.new_child();
243
244        // Modify parent AFTER child creation
245        scope1.set("x", Value::Numb(10.0));
246
247        // Child reflects post-creation modifications in parent
248        assert_eq!(scope2.get("x"), Value::Numb(10.0));
249    }
250
251    #[test]
252    fn test_deep_scope_chain() {
253        let grand_parent = Scope::new();
254        grand_parent.set("a", Value::Numb(1.0));
255
256        let parent = grand_parent.new_child();
257        parent.set("b", Value::Numb(2.0));
258
259        let child = parent.new_child();
260        child.set("c", Value::Numb(3.0));
261
262        // Child can resolve up the entire chain
263        assert_eq!(child.get("a"), Value::Numb(1.0));
264        assert_eq!(child.get("b"), Value::Numb(2.0));
265        assert_eq!(child.get("c"), Value::Numb(3.0));
266
267        // Parent cannot see child bindings
268        assert_eq!(parent.get("c"), Value::Nothing);
269        assert_eq!(grand_parent.get("b"), Value::Nothing);
270    }
271
272    #[test]
273    fn test_scope_equality_same_instance() {
274        let scope = Scope::new();
275        scope.set("a", Value::Numb(1.0));
276
277        // A scope is equal to its clone (pointer identity shortcut)
278        let clone = scope.clone();
279        assert_eq!(scope, clone);
280    }
281
282    #[test]
283    fn test_scope_equality_top_level() {
284        let scope1 = Scope::new();
285        scope1.set("a", Value::Numb(1.0));
286
287        let scope2 = Scope::new();
288        scope2.set("a", Value::Numb(1.0));
289
290        // Both are top-level (parent: None) with equal bindings
291        assert_eq!(scope1, scope2);
292
293        // Modifying one breaks equality
294        scope2.set("b", Value::Numb(2.0));
295        assert_ne!(scope1, scope2);
296    }
297
298    #[test]
299    fn test_scope_equality_shared_parent_instance() {
300        let parent = Scope::new();
301        parent.set("shared", Value::Bool(true));
302
303        let child1 = parent.new_child();
304        child1.set("x", Value::Numb(10.0));
305
306        let child2 = parent.new_child();
307        child2.set("x", Value::Numb(10.0));
308
309        // Both children share the EXACT same parent reference and have equal bindings
310        assert_eq!(child1, child2);
311    }
312
313    #[test]
314    fn test_scope_equality_different_parent_instances() {
315        // Parent 1 and Parent 2 have identical bindings, but are DIFFERENT memory instances
316        let parent1 = Scope::new();
317        parent1.set("shared", Value::Bool(true));
318
319        let parent2 = Scope::new();
320        parent2.set("shared", Value::Bool(true));
321
322        let child1 = parent1.new_child();
323        child1.set("x", Value::Numb(10.0));
324
325        let child2 = parent2.new_child();
326        child2.set("x", Value::Numb(10.0));
327
328        // Must NOT be equal because parents are different instances (Rc::ptr_eq fails)
329        assert_ne!(child1, child2);
330    }
331
332    #[test]
333    fn test_scope_lift() {
334        let root = Scope::new();
335        root.set("x", Value::Numb(1.0));
336        root.set("y", Value::Text("root".into()));
337
338        let parent = root.new_child();
339        parent.set("x", Value::Numb(2.0));
340        parent.set("z", Value::Bool(true));
341
342        let child = parent.new_child();
343        child.set("x", Value::Numb(3.0));
344
345        // 1. `x` is defined at root, parent, and child. 
346        //    `lift` should return the root's value (1.0), unlike `get` which returns 3.0.
347        assert_eq!(child.lift("x"), Value::Numb(1.0));
348        assert_eq!(child.get("x"), Value::Numb(3.0));
349
350        // 2. `y` is only defined at root.
351        assert_eq!(child.lift("y"), Value::Text("root".into()));
352
353        // 3. `z` is defined in parent and child chain, but not root.
354        //    `lift` finds the top-most occurrence down the chain (the parent's value).
355        assert_eq!(child.lift("z"), Value::Bool(true));
356
357        // 4. Looking up a non-existent binding returns `Value::Nothing`.
358        assert_eq!(child.lift("unbound"), Value::Nothing);
359    }
360}