1use std::collections::BTreeMap;
8use std::sync::Arc;
9
10use crate::value::Value;
11
12#[derive(Clone, Default)]
13pub struct Env {
14 inner: Arc<EnvFrame>,
15}
16
17struct EnvFrame {
18 bindings: BTreeMap<String, Value>,
19 parent: Option<Arc<EnvFrame>>,
20}
21
22impl Default for EnvFrame {
23 fn default() -> Self {
24 Self {
25 bindings: BTreeMap::new(),
26 parent: None,
27 }
28 }
29}
30
31impl Env {
32 pub fn new() -> Self {
33 Self::default()
34 }
35
36 pub fn lookup(&self, name: &str) -> Option<Value> {
37 let mut cursor = Some(&self.inner);
38 while let Some(frame) = cursor {
39 if let Some(v) = frame.bindings.get(name) {
40 return Some(v.clone());
41 }
42 cursor = frame.parent.as_ref();
43 }
44 None
45 }
46
47 pub fn extend(&self, name: impl Into<String>, value: Value) -> Self {
49 let mut bindings = BTreeMap::new();
50 bindings.insert(name.into(), value);
51 Self {
52 inner: Arc::new(EnvFrame {
53 bindings,
54 parent: Some(self.inner.clone()),
55 }),
56 }
57 }
58
59 pub fn extend_many<I, K, V>(&self, it: I) -> Self
61 where
62 I: IntoIterator<Item = (K, V)>,
63 K: Into<String>,
64 V: Into<Value>,
65 {
66 let mut bindings = BTreeMap::new();
67 for (k, v) in it {
68 bindings.insert(k.into(), v.into());
69 }
70 Self {
71 inner: Arc::new(EnvFrame {
72 bindings,
73 parent: Some(self.inner.clone()),
74 }),
75 }
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn lookup_walks_parent_chain() {
85 let e0 = Env::new().extend("x", Value::Int(1));
86 let e1 = e0.extend("y", Value::Int(2));
87 assert!(matches!(e1.lookup("x"), Some(Value::Int(1))));
88 assert!(matches!(e1.lookup("y"), Some(Value::Int(2))));
89 assert!(e1.lookup("z").is_none());
90 }
91
92 #[test]
93 fn extend_is_non_destructive() {
94 let e0 = Env::new().extend("x", Value::Int(1));
95 let _e1 = e0.extend("x", Value::Int(2));
96 assert!(matches!(e0.lookup("x"), Some(Value::Int(1))));
98 }
99}