Skip to main content

atman_runtime/
env.rs

1use std::sync::Arc;
2
3use crate::value::Value;
4
5#[derive(Debug, Clone, Default)]
6pub struct Env {
7    inner: Arc<EnvInner>,
8}
9
10#[derive(Debug, Clone, Default)]
11struct EnvInner {
12    bindings: Vec<(String, Value)>,
13    parent: Option<Arc<EnvInner>>,
14}
15
16impl Env {
17    pub fn new() -> Self {
18        Self::default()
19    }
20
21    pub fn bind(&mut self, name: impl Into<String>, value: Value) {
22        let inner = Arc::make_mut(&mut self.inner);
23        inner.bindings.push((name.into(), value));
24    }
25
26    /// Create a child environment that inherits from this one.
27    /// The child starts with no own bindings; lookups fall through to parent.
28    pub fn child(&self) -> Self {
29        Env {
30            inner: Arc::new(EnvInner {
31                bindings: Vec::new(),
32                parent: Some(Arc::clone(&self.inner)),
33            }),
34        }
35    }
36
37    pub fn lookup(&self, name: &str) -> Option<&Value> {
38        let mut inner = &self.inner;
39        loop {
40            if let Some(v) = inner
41                .bindings
42                .iter()
43                .rev()
44                .find(|(k, _)| k == name)
45                .map(|(_, v)| v)
46            {
47                return Some(v);
48            }
49            match &inner.parent {
50                Some(p) => inner = p,
51                None => return None,
52            }
53        }
54    }
55
56    pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
57        let mut chain: Vec<&EnvInner> = Vec::new();
58        let mut inner = &*self.inner;
59        loop {
60            chain.push(inner);
61            match &inner.parent {
62                Some(p) => inner = p,
63                None => break,
64            }
65        }
66        chain
67            .into_iter()
68            .flat_map(|inner| inner.bindings.iter().map(|(k, v)| (k.as_str(), v)))
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn empty_env_lookup_returns_none() {
78        assert!(Env::new().lookup("x").is_none());
79    }
80
81    #[test]
82    fn bind_then_lookup_returns_value() {
83        let mut env = Env::new();
84        env.bind("x", Value::Int(1));
85        assert!(matches!(env.lookup("x"), Some(Value::Int(1))));
86    }
87
88    #[test]
89    fn later_binding_shadows_earlier() {
90        let mut env = Env::new();
91        env.bind("x", Value::Int(1));
92        env.bind("x", Value::Int(2));
93        assert!(matches!(env.lookup("x"), Some(Value::Int(2))));
94    }
95
96    #[test]
97    fn unrelated_lookup_after_shadow_still_works() {
98        let mut env = Env::new();
99        env.bind("x", Value::Int(1));
100        env.bind("y", Value::Str("hi".into()));
101        env.bind("x", Value::Int(2));
102        assert!(matches!(env.lookup("y"), Some(Value::Str(s)) if s == "hi"));
103    }
104
105    #[test]
106    fn iter_yields_declaration_order() {
107        let mut env = Env::new();
108        env.bind("a", Value::Int(1));
109        env.bind("b", Value::Int(2));
110        let names: Vec<_> = env.iter().map(|(k, _)| k).collect();
111        assert_eq!(names, vec!["a", "b"]);
112    }
113
114    #[test]
115    fn child_env_inherits_parent() {
116        let mut parent = Env::new();
117        parent.bind("x", Value::Int(42));
118        let child = parent.child();
119        assert!(matches!(child.lookup("x"), Some(Value::Int(42))));
120    }
121
122    #[test]
123    fn child_env_bind_does_not_affect_parent() {
124        let mut parent = Env::new();
125        parent.bind("x", Value::Int(1));
126        let mut child = parent.child();
127        child.bind("x", Value::Int(99));
128        assert!(matches!(parent.lookup("x"), Some(Value::Int(1))));
129        assert!(matches!(child.lookup("x"), Some(Value::Int(99))));
130    }
131
132    #[test]
133    fn clone_is_cheap() {
134        let mut env = Env::new();
135        env.bind("x", Value::Int(1));
136        let cloned = env.clone();
137        // Both should see the same binding
138        assert!(matches!(cloned.lookup("x"), Some(Value::Int(1))));
139        // Mutating original after clone should not affect clone (COW)
140        env.bind("y", Value::Int(2));
141        assert!(cloned.lookup("y").is_none());
142    }
143}