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