Skip to main content

panproto_expr/
env.rs

1//! Evaluation environment (variable bindings).
2
3use std::sync::Arc;
4
5use rustc_hash::FxHashMap;
6
7use crate::Literal;
8
9/// An evaluation environment mapping variable names to values.
10///
11/// Environments are immutable — extending creates a new environment
12/// that shadows the parent's bindings. This is implemented via clone
13/// since environments are typically small (lambda parameters, let bindings).
14#[derive(Debug, Clone, Default)]
15pub struct Env {
16    bindings: FxHashMap<Arc<str>, Literal>,
17}
18
19impl Env {
20    /// Create an empty environment.
21    #[must_use]
22    pub fn new() -> Self {
23        Self::default()
24    }
25
26    /// Look up a variable in the environment.
27    #[must_use]
28    pub fn get(&self, name: &str) -> Option<&Literal> {
29        self.bindings.get(name)
30    }
31
32    /// Extend the environment with a new binding, returning a new environment.
33    #[must_use]
34    pub fn extend(&self, name: Arc<str>, value: Literal) -> Self {
35        let mut bindings = self.bindings.clone();
36        bindings.insert(name, value);
37        Self { bindings }
38    }
39
40    /// Returns the number of bindings.
41    #[must_use]
42    pub fn len(&self) -> usize {
43        self.bindings.len()
44    }
45
46    /// Returns `true` if the environment has no bindings.
47    #[must_use]
48    pub fn is_empty(&self) -> bool {
49        self.bindings.is_empty()
50    }
51
52    /// Iterate over all bindings in the environment.
53    pub fn iter(&self) -> impl Iterator<Item = (&Arc<str>, &Literal)> {
54        self.bindings.iter()
55    }
56}
57
58impl FromIterator<(Arc<str>, Literal)> for Env {
59    fn from_iter<T: IntoIterator<Item = (Arc<str>, Literal)>>(iter: T) -> Self {
60        Self {
61            bindings: iter.into_iter().collect(),
62        }
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn extend_shadows() {
72        let env = Env::new().extend(Arc::from("x"), Literal::Int(1));
73        let env2 = env.extend(Arc::from("x"), Literal::Int(2));
74        assert_eq!(env.get("x"), Some(&Literal::Int(1)));
75        assert_eq!(env2.get("x"), Some(&Literal::Int(2)));
76    }
77
78    #[test]
79    fn missing_variable() {
80        let env = Env::new();
81        assert_eq!(env.get("x"), None);
82    }
83}