Skip to main content

atman_runtime/
env.rs

1use crate::value::Value;
2
3#[derive(Debug, Clone, Default)]
4pub struct Env {
5    bindings: Vec<(String, Value)>,
6}
7
8impl Env {
9    pub fn new() -> Self {
10        Self::default()
11    }
12
13    pub fn bind(&mut self, name: impl Into<String>, value: Value) {
14        self.bindings.push((name.into(), value));
15    }
16
17    pub fn lookup(&self, name: &str) -> Option<&Value> {
18        self.bindings
19            .iter()
20            .rev()
21            .find(|(k, _)| k == name)
22            .map(|(_, v)| v)
23    }
24
25    pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
26        self.bindings.iter().map(|(k, v)| (k.as_str(), v))
27    }
28}
29
30#[cfg(test)]
31mod tests {
32    use super::*;
33
34    #[test]
35    fn empty_env_lookup_returns_none() {
36        assert!(Env::new().lookup("x").is_none());
37    }
38
39    #[test]
40    fn bind_then_lookup_returns_value() {
41        let mut env = Env::new();
42        env.bind("x", Value::Int(1));
43        assert!(matches!(env.lookup("x"), Some(Value::Int(1))));
44    }
45
46    #[test]
47    fn later_binding_shadows_earlier() {
48        let mut env = Env::new();
49        env.bind("x", Value::Int(1));
50        env.bind("x", Value::Int(2));
51        assert!(matches!(env.lookup("x"), Some(Value::Int(2))));
52    }
53
54    #[test]
55    fn unrelated_lookup_after_shadow_still_works() {
56        let mut env = Env::new();
57        env.bind("x", Value::Int(1));
58        env.bind("y", Value::Str("hi".into()));
59        env.bind("x", Value::Int(2));
60        assert!(matches!(env.lookup("y"), Some(Value::Str(s)) if s == "hi"));
61    }
62
63    #[test]
64    fn iter_yields_declaration_order() {
65        let mut env = Env::new();
66        env.bind("a", Value::Int(1));
67        env.bind("b", Value::Int(2));
68        let names: Vec<_> = env.iter().map(|(k, _)| k).collect();
69        assert_eq!(names, vec!["a", "b"]);
70    }
71}