Skip to main content

ion_core/
env.rs

1use crate::intern::{StringPool, Symbol};
2use crate::value::Value;
3use std::collections::HashMap;
4
5/// Variable binding with mutability tracking.
6#[derive(Debug, Clone)]
7struct Binding {
8    sym: Symbol,
9    value: Value,
10    mutable: bool,
11}
12
13/// Lexical scope environment using a stack of frames.
14///
15/// Each frame is a Vec of bindings, scanned linearly. For typical scope sizes
16/// (<20 variables), linear scan with Symbol (u32) comparison is faster than
17/// HashMap due to cache locality.
18#[derive(Debug, Clone)]
19pub struct Env {
20    frames: Vec<Vec<Binding>>,
21    pool: StringPool,
22}
23
24impl Default for Env {
25    fn default() -> Self {
26        Self::new()
27    }
28}
29
30impl Env {
31    pub fn new() -> Self {
32        Self {
33            frames: vec![Vec::new()],
34            pool: StringPool::new(),
35        }
36    }
37
38    /// Get a reference to the string pool.
39    pub fn pool(&self) -> &StringPool {
40        &self.pool
41    }
42
43    /// Get a mutable reference to the string pool.
44    pub fn pool_mut(&mut self) -> &mut StringPool {
45        &mut self.pool
46    }
47
48    /// Intern a string and return its symbol.
49    pub fn intern(&mut self, s: &str) -> Symbol {
50        self.pool.intern(s)
51    }
52
53    /// Resolve a symbol to its string.
54    pub fn resolve(&self, sym: Symbol) -> &str {
55        self.pool.resolve(sym)
56    }
57
58    /// Push a new scope frame.
59    pub fn push_scope(&mut self) {
60        self.frames.push(Vec::new());
61    }
62
63    /// Pop the current scope frame.
64    pub fn pop_scope(&mut self) {
65        self.frames.pop();
66    }
67
68    /// Define a new variable in the current scope.
69    pub fn define(&mut self, name: String, value: Value, mutable: bool) {
70        let sym = self.pool.intern(&name);
71        self.define_sym(sym, value, mutable);
72    }
73
74    /// Define a variable by symbol in the current scope.
75    pub fn define_sym(&mut self, sym: Symbol, value: Value, mutable: bool) {
76        let frame = self.frames.last_mut().unwrap();
77        // Check if already defined in this scope (overwrite)
78        for binding in frame.iter_mut() {
79            if binding.sym == sym {
80                binding.value = value;
81                binding.mutable = mutable;
82                return;
83            }
84        }
85        frame.push(Binding {
86            sym,
87            value,
88            mutable,
89        });
90    }
91
92    /// Get a variable's value by name, searching from innermost scope outward.
93    pub fn get(&self, name: &str) -> Option<&Value> {
94        let sym = self.pool.map_get(name)?;
95        self.get_sym(sym)
96    }
97
98    /// Get a variable's value by symbol.
99    pub fn get_sym(&self, sym: Symbol) -> Option<&Value> {
100        for frame in self.frames.iter().rev() {
101            for binding in frame.iter().rev() {
102                if binding.sym == sym {
103                    return Some(&binding.value);
104                }
105            }
106        }
107        None
108    }
109
110    /// Set an existing variable's value. Returns error if not found or not mutable.
111    pub fn set(&mut self, name: &str, value: Value) -> Result<(), String> {
112        let sym = self.pool.intern(name);
113        self.set_sym(sym, value)
114    }
115
116    /// Set a variable by symbol.
117    pub fn set_sym(&mut self, sym: Symbol, value: Value) -> Result<(), String> {
118        for frame in self.frames.iter_mut().rev() {
119            for binding in frame.iter_mut().rev() {
120                if binding.sym == sym {
121                    if !binding.mutable {
122                        return Err(format!(
123                            "{}'{}'",
124                            ion_str!("cannot assign to immutable variable "),
125                            self.pool.resolve(sym),
126                        ));
127                    }
128                    binding.value = value;
129                    return Ok(());
130                }
131            }
132        }
133        Err(format!(
134            "{}{}",
135            ion_str!("undefined variable: "),
136            self.pool.resolve(sym)
137        ))
138    }
139
140    /// Get all top-level bindings (for engine.get_all()).
141    pub fn top_level(&self) -> HashMap<String, Value> {
142        self.frames
143            .first()
144            .map(|f| {
145                f.iter()
146                    .map(|b| (self.pool.resolve(b.sym).to_string(), b.value.clone()))
147                    .collect()
148            })
149            .unwrap_or_default()
150    }
151
152    /// Snapshot current environment for closure capture.
153    pub fn capture(&self) -> HashMap<String, Value> {
154        let mut captured = HashMap::new();
155        for frame in &self.frames {
156            for b in frame {
157                captured.insert(self.pool.resolve(b.sym).to_string(), b.value.clone());
158            }
159        }
160        captured
161    }
162}