Skip to main content

panproto_expr/
env.rs

1//! Evaluation environment (variable bindings).
2
3use std::collections::BTreeMap;
4use std::sync::Arc;
5
6use crate::Literal;
7
8/// One binding, together with the scope it was added to.
9#[derive(Debug)]
10struct Binding {
11    name: Arc<str>,
12    value: Literal,
13    outer: Option<Arc<Self>>,
14}
15
16/// An evaluation environment mapping variable names to values.
17///
18/// Environments are immutable, and extending one shares it rather than copying
19/// it: the extension holds a reference to the scope it extends, so binding a
20/// name costs the same whatever else is in scope. That matters because the
21/// evaluator extends the environment on every `let`, every lambda, and every
22/// closure application.
23///
24/// A name bound twice is shadowed by the inner binding, and only the inner one
25/// is observable: `get` returns it, [`iter`](Self::iter) yields it, and
26/// [`len`](Self::len) counts the name once.
27#[derive(Clone, Default)]
28pub struct Env {
29    innermost: Option<Arc<Binding>>,
30}
31
32impl Env {
33    /// Create an empty environment.
34    #[must_use]
35    pub const fn new() -> Self {
36        Self { innermost: None }
37    }
38
39    /// Look up a variable in the environment.
40    #[must_use]
41    pub fn get(&self, name: &str) -> Option<&Literal> {
42        let mut cursor = self.innermost.as_deref();
43        while let Some(binding) = cursor {
44            if &*binding.name == name {
45                return Some(&binding.value);
46            }
47            cursor = binding.outer.as_deref();
48        }
49        None
50    }
51
52    /// Extend the environment with a new binding, returning a new environment.
53    ///
54    /// The environment extended is left as it was, and is shared rather than
55    /// copied, so this costs the same at any width.
56    #[must_use]
57    pub fn extend(&self, name: Arc<str>, value: Literal) -> Self {
58        Self {
59            innermost: Some(Arc::new(Binding {
60                name,
61                value,
62                outer: self.innermost.clone(),
63            })),
64        }
65    }
66
67    /// Returns the number of distinct names in scope.
68    #[must_use]
69    pub fn len(&self) -> usize {
70        self.iter().count()
71    }
72
73    /// Returns `true` if the environment has no bindings.
74    #[must_use]
75    pub const fn is_empty(&self) -> bool {
76        self.innermost.is_none()
77    }
78
79    /// Iterate over the bindings in scope, in name order, each name once.
80    ///
81    /// Name order rather than the order the bindings arrived in, so that two
82    /// environments holding the same bindings read the same however they were
83    /// assembled — which is what lets a hash or an encoding taken over an
84    /// environment be a function of what it binds.
85    pub fn iter(&self) -> impl Iterator<Item = (&Arc<str>, &Literal)> {
86        self.canonical().into_values()
87    }
88
89    /// The bindings in scope, keyed by name: the environment's canonical form.
90    fn canonical(&self) -> BTreeMap<&str, (&Arc<str>, &Literal)> {
91        let mut visible: BTreeMap<&str, (&Arc<str>, &Literal)> = BTreeMap::new();
92        let mut cursor = self.innermost.as_deref();
93        while let Some(binding) = cursor {
94            // Innermost first, so an outer binding of the same name never
95            // displaces the one that shadows it.
96            visible
97                .entry(&binding.name)
98                .or_insert((&binding.name, &binding.value));
99            cursor = binding.outer.as_deref();
100        }
101        visible
102    }
103}
104
105impl std::fmt::Debug for Env {
106    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        f.debug_map().entries(self.iter()).finish()
108    }
109}
110
111impl PartialEq for Env {
112    fn eq(&self, other: &Self) -> bool {
113        self.iter().eq(other.iter())
114    }
115}
116
117impl Eq for Env {}
118
119impl std::hash::Hash for Env {
120    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
121        for (name, value) in self.iter() {
122            name.hash(state);
123            value.hash(state);
124        }
125    }
126}
127
128impl serde::Serialize for Env {
129    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
130        use serde::ser::SerializeSeq as _;
131        let mut seq = serializer.serialize_seq(Some(self.len()))?;
132        for (name, value) in self.iter() {
133            seq.serialize_element(&(name, value))?;
134        }
135        seq.end()
136    }
137}
138
139impl<'de> serde::Deserialize<'de> for Env {
140    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
141        let pairs: Vec<(Arc<str>, Literal)> = serde::Deserialize::deserialize(deserializer)?;
142        Ok(pairs.into_iter().collect())
143    }
144}
145
146impl FromIterator<(Arc<str>, Literal)> for Env {
147    /// Build an environment from bindings, later ones shadowing earlier ones.
148    fn from_iter<T: IntoIterator<Item = (Arc<str>, Literal)>>(iter: T) -> Self {
149        iter.into_iter()
150            .fold(Self::new(), |env, (name, value)| env.extend(name, value))
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157
158    #[test]
159    fn extend_shadows() {
160        let env = Env::new().extend(Arc::from("x"), Literal::Int(1));
161        let env2 = env.extend(Arc::from("x"), Literal::Int(2));
162        assert_eq!(env.get("x"), Some(&Literal::Int(1)));
163        assert_eq!(env2.get("x"), Some(&Literal::Int(2)));
164    }
165
166    #[test]
167    fn missing_variable() {
168        let env = Env::new();
169        assert_eq!(env.get("x"), None);
170    }
171
172    #[test]
173    fn a_shadowed_name_is_counted_and_yielded_once() {
174        let env = Env::new()
175            .extend(Arc::from("x"), Literal::Int(1))
176            .extend(Arc::from("y"), Literal::Int(2))
177            .extend(Arc::from("x"), Literal::Int(3));
178        assert_eq!(env.len(), 2);
179        let mut seen: Vec<(&str, &Literal)> =
180            env.iter().map(|(name, value)| (&**name, value)).collect();
181        seen.sort_by_key(|(name, _)| *name);
182        assert_eq!(seen, vec![("x", &Literal::Int(3)), ("y", &Literal::Int(2))]);
183    }
184
185    #[test]
186    fn the_order_bindings_arrived_in_does_not_change_the_environment() {
187        let forward: Env = [
188            (Arc::from("a"), Literal::Int(1)),
189            (Arc::from("b"), Literal::Int(2)),
190        ]
191        .into_iter()
192        .collect();
193        let backward: Env = [
194            (Arc::from("b"), Literal::Int(2)),
195            (Arc::from("a"), Literal::Int(1)),
196        ]
197        .into_iter()
198        .collect();
199        assert_eq!(forward, backward);
200    }
201
202    #[test]
203    fn collecting_lets_the_last_binding_win() {
204        let env: Env = [
205            (Arc::from("x"), Literal::Int(1)),
206            (Arc::from("x"), Literal::Int(2)),
207        ]
208        .into_iter()
209        .collect();
210        assert_eq!(env.get("x"), Some(&Literal::Int(2)));
211        assert_eq!(env.len(), 1);
212    }
213
214    #[test]
215    fn an_environment_round_trips_through_serde() {
216        let env: Env = [
217            (Arc::from("a"), Literal::Int(1)),
218            (Arc::from("b"), Literal::Str("two".into())),
219        ]
220        .into_iter()
221        .collect();
222        let json = serde_json::to_string(&env).unwrap_or_else(|e| panic!("serialize: {e}"));
223        let back: Env = serde_json::from_str(&json).unwrap_or_else(|e| panic!("deserialize: {e}"));
224        assert_eq!(env, back);
225    }
226}