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}