Skip to main content

lambda_cat/
env.rs

1//! Persistent lexical environments.
2//!
3//! An [`Env`] is a cons-list of name-value bindings.  Lookup walks the list
4//! head-first so an inner binding shadows an outer one with the same name.
5//! [`Env::extend`] returns a fresh `Env` rather than mutating the receiver;
6//! the underlying tail is cloned to satisfy the no-`Rc`/`Arc` constraint.
7
8use crate::syntax::VarName;
9use crate::value::Value;
10
11/// A persistent lexical environment.
12///
13/// Variants are public so tests and downstream tools can pattern match on
14/// the structure, but construction normally goes through [`Env::empty`] and
15/// [`Env::extend`] and lookup goes through [`Env::lookup`].
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub enum Env {
18    /// The empty environment.
19    Empty,
20    /// A binding of `name` to `value`, atop the rest of the environment.
21    Cons {
22        /// The bound name.
23        name: VarName,
24        /// The bound value.
25        value: Box<Value>,
26        /// The environment without this binding.
27        rest: Box<Env>,
28    },
29}
30
31impl Env {
32    /// The environment with no bindings.
33    #[must_use]
34    pub fn empty() -> Self {
35        Self::Empty
36    }
37
38    /// Return a new environment with `name` bound to `value` shadowing any
39    /// prior binding of `name`.  The receiver is unchanged.
40    #[must_use]
41    pub fn extend(&self, name: VarName, value: Value) -> Self {
42        Self::Cons {
43            name,
44            value: Box::new(value),
45            rest: Box::new(self.clone()),
46        }
47    }
48
49    /// Look up the most recent binding of `name`, or `None` if unbound.
50    #[must_use]
51    pub fn lookup(&self, name: &VarName) -> Option<&Value> {
52        match self {
53            Self::Empty => None,
54            Self::Cons {
55                name: bound,
56                value,
57                rest,
58            } => {
59                if bound == name {
60                    Some(value)
61                } else {
62                    rest.lookup(name)
63                }
64            }
65        }
66    }
67}
68
69impl std::fmt::Display for Env {
70    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71        match self {
72            Self::Empty => f.write_str("{}"),
73            Self::Cons { name, value, rest } => {
74                write!(f, "{{{name} = {value}}} :: {rest}")
75            }
76        }
77    }
78}