lambda_ref_cat/env.rs
1//! Persistent lexical environments.
2//!
3//! Identical in shape to the spike-1 env: a cons-list of `(name, value)`
4//! bindings with shadowing and persistent extension.
5
6use crate::syntax::VarName;
7use crate::value::Value;
8
9/// A persistent lexical environment.
10#[derive(Debug, Clone, Default, PartialEq, Eq)]
11pub enum Env {
12 /// The empty environment.
13 #[default]
14 Empty,
15 /// A binding of `name` to `value`, atop the rest of the environment.
16 Cons {
17 /// The bound name.
18 name: VarName,
19 /// The bound value.
20 value: Box<Value>,
21 /// The environment without this binding.
22 rest: Box<Env>,
23 },
24}
25
26impl Env {
27 /// The environment with no bindings.
28 #[must_use]
29 pub fn empty() -> Self {
30 Self::Empty
31 }
32
33 /// Return a new environment with `name` bound to `value` shadowing any
34 /// prior binding of `name`.
35 #[must_use]
36 pub fn extend(&self, name: VarName, value: Value) -> Self {
37 Self::Cons {
38 name,
39 value: Box::new(value),
40 rest: Box::new(self.clone()),
41 }
42 }
43
44 /// Look up the most recent binding of `name`, or `None` if unbound.
45 #[must_use]
46 pub fn lookup(&self, name: &VarName) -> Option<&Value> {
47 match self {
48 Self::Empty => None,
49 Self::Cons {
50 name: bound,
51 value,
52 rest,
53 } => {
54 if bound == name {
55 Some(value)
56 } else {
57 rest.lookup(name)
58 }
59 }
60 }
61 }
62}
63
64impl std::fmt::Display for Env {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 match self {
67 Self::Empty => f.write_str("{}"),
68 Self::Cons { name, value, rest } => {
69 write!(f, "{{{name} = {value}}} :: {rest}")
70 }
71 }
72 }
73}