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}