Skip to main content

aura_lang/eval/
env.rs

1//! Lexical scope (SPEC §4.2, D9).
2//!
3//! D9 implementation: instead of a full chain of frozen prefixes, a frame uses
4//! internal mutability (RefCell) while a block is being built. Values remain
5//! immutable, redefinition within one frame is forbidden (E0301 in the interpreter),
6//! and data references only point upward. A function's closure would close a
7//! cycle back onto its defining env, so closures hold a `WeakEnv`; the
8//! interpreter's env arena keeps every env alive for the eval instead. The observable
9//! difference from strict freezing: a closure sees bindings from its own block
10//! declared AFTER it, but it cannot be called before they are declared in the deterministic top-down execution order.
11
12use indexmap::IndexMap;
13use std::cell::RefCell;
14use std::fmt;
15use std::sync::{Arc, Weak};
16
17use super::value::Value;
18
19pub type Env<'a> = Arc<Environment<'a>>;
20
21/// A non-owning reference to an environment. Used by function closures so that
22/// the env->function->closure->env cycle does not leak: the interpreter's env
23/// arena keeps every environment alive for the eval's duration instead (D9).
24pub type WeakEnv<'a> = Weak<Environment<'a>>;
25
26pub struct Environment<'a> {
27    vars: RefCell<IndexMap<String, Value<'a>>>,
28    parent: Option<Env<'a>>,
29}
30
31impl<'a> Environment<'a> {
32    pub fn root() -> Env<'a> {
33        Arc::new(Environment {
34            vars: RefCell::new(IndexMap::new()),
35            parent: None,
36        })
37    }
38
39    pub fn child(parent: &Env<'a>) -> Env<'a> {
40        Arc::new(Environment {
41            vars: RefCell::new(IndexMap::new()),
42            parent: Some(parent.clone()),
43        })
44    }
45
46    /// Walks up the frame chain; cloning a value is O(1) (Arc).
47    pub fn get(&self, name: &str) -> Option<Value<'a>> {
48        if let Some(v) = self.vars.borrow().get(name) {
49            return Some(v.clone());
50        }
51        self.parent.as_ref().and_then(|p| p.get(name))
52    }
53
54    pub fn defined_here(&self, name: &str) -> bool {
55        self.vars.borrow().contains_key(name)
56    }
57
58    pub fn defined_in_ancestors(&self, name: &str) -> bool {
59        match &self.parent {
60            Some(p) => p.defined_here(name) || p.defined_in_ancestors(name),
61            None => false,
62        }
63    }
64
65    /// Low-level insertion; the E0301/E0302 rules are enforced by the interpreter.
66    pub fn insert(&self, name: &str, v: Value<'a>) {
67        self.vars.borrow_mut().insert(name.to_string(), v);
68    }
69}
70
71impl fmt::Debug for Environment<'_> {
72    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73        write!(f, "<env: {} vars>", self.vars.borrow().len())
74    }
75}