1use std::collections::BTreeMap;
2use std::path::PathBuf;
3use std::sync::{Arc, Weak};
4
5use crate::chunk::CompiledFunctionRef;
6
7use super::{VmError, VmMutex, VmValue};
8
9#[derive(Debug, Clone)]
11pub struct VmClosure {
12 pub func: CompiledFunctionRef,
13 pub env: VmEnv,
14 pub source_dir: Option<PathBuf>,
18 pub module_functions: Option<WeakModuleFunctionRegistry>,
22 pub module_state: Option<WeakModuleState>,
35}
36
37pub type ModuleFunctionRegistry = Arc<VmMutex<BTreeMap<String, Arc<VmClosure>>>>;
38pub type WeakModuleFunctionRegistry = Weak<VmMutex<BTreeMap<String, Arc<VmClosure>>>>;
39pub type ModuleState = Arc<VmMutex<VmEnv>>;
40pub type WeakModuleState = Weak<VmMutex<VmEnv>>;
41
42impl VmClosure {
43 pub(crate) fn module_functions(&self) -> Option<ModuleFunctionRegistry> {
44 self.module_functions
45 .as_ref()
46 .and_then(WeakModuleFunctionRegistry::upgrade)
47 }
48
49 pub(crate) fn module_state(&self) -> Option<ModuleState> {
50 self.module_state
51 .as_ref()
52 .and_then(WeakModuleState::upgrade)
53 }
54}
55
56#[derive(Debug, Clone)]
68pub struct VmEnv {
69 pub(crate) scopes: Vec<Scope>,
70}
71
72#[derive(Debug, Clone)]
73pub(crate) struct Scope {
74 pub(crate) vars: Arc<BTreeMap<String, (VmValue, bool)>>, }
76
77impl Scope {
78 #[inline]
79 fn empty() -> Self {
80 Self {
81 vars: Arc::new(std::collections::BTreeMap::new()),
82 }
83 }
84}
85
86impl Drop for Scope {
87 fn drop(&mut self) {
88 if let Some(map) = Arc::get_mut(&mut self.vars) {
98 if map
99 .values()
100 .any(|(value, _)| super::recursion::is_recursive_container(value))
101 {
102 let bindings = std::mem::take(map);
103 super::recursion::dismantle_values(bindings.into_values().map(|(value, _)| value));
104 }
105 }
106 }
107}
108
109impl Default for VmEnv {
110 fn default() -> Self {
111 Self::new()
112 }
113}
114
115impl VmEnv {
116 pub fn new() -> Self {
117 Self {
118 scopes: vec![Scope::empty()],
119 }
120 }
121
122 pub fn push_scope(&mut self) {
123 self.scopes.push(Scope::empty());
124 }
125
126 pub fn pop_scope(&mut self) {
127 if self.scopes.len() > 1 {
128 self.scopes.pop();
129 }
130 }
131
132 pub fn scope_depth(&self) -> usize {
133 self.scopes.len()
134 }
135
136 pub fn truncate_scopes(&mut self, target_depth: usize) {
137 let min_depth = target_depth.max(1);
138 while self.scopes.len() > min_depth {
139 self.scopes.pop();
140 }
141 }
142
143 pub fn get(&self, name: &str) -> Option<VmValue> {
144 for scope in self.scopes.iter().rev() {
145 if let Some((val, _)) = scope.vars.get(name) {
146 return Some(val.clone());
147 }
148 }
149 None
150 }
151
152 pub(crate) fn contains(&self, name: &str) -> bool {
153 self.scopes
154 .iter()
155 .rev()
156 .any(|scope| scope.vars.contains_key(name))
157 }
158
159 pub fn define(&mut self, name: &str, value: VmValue, mutable: bool) -> Result<(), VmError> {
160 if let Some(scope) = self.scopes.last_mut() {
161 if let Some((_, existing_mutable)) = scope.vars.get(name) {
162 if !existing_mutable && !mutable {
163 return Err(VmError::Runtime(format!(
164 "Cannot redeclare immutable variable '{name}' in the same scope (use 'var' for mutable bindings)"
165 )));
166 }
167 }
168 if let Some((previous, _)) =
169 Arc::make_mut(&mut scope.vars).insert(name.to_string(), (value, mutable))
170 {
171 super::recursion::dismantle(previous);
172 }
173 }
174 Ok(())
175 }
176
177 pub fn all_variables(&self) -> crate::value::DictMap {
178 let mut vars = crate::value::DictMap::new();
179 for scope in &self.scopes {
180 for (name, (value, _)) in scope.vars.iter() {
181 vars.insert(name.clone(), value.clone());
182 }
183 }
184 vars
185 }
186
187 pub fn assign(&mut self, name: &str, value: VmValue) -> Result<(), VmError> {
188 for scope in self.scopes.iter_mut().rev() {
189 if let Some((_, mutable)) = scope.vars.get(name) {
190 if !mutable {
191 return Err(VmError::ImmutableAssignment(name.to_string()));
192 }
193 if let Some((previous, _)) =
194 Arc::make_mut(&mut scope.vars).insert(name.to_string(), (value, true))
195 {
196 super::recursion::dismantle(previous);
199 }
200 return Ok(());
201 }
202 }
203 Err(VmError::UndefinedVariable(name.to_string()))
204 }
205
206 pub fn assign_debug(&mut self, name: &str, value: VmValue) -> Result<(), VmError> {
214 for scope in self.scopes.iter_mut().rev() {
215 if let Some((_, mutable)) = scope.vars.get(name) {
216 let mutable = *mutable;
217 Arc::make_mut(&mut scope.vars).insert(name.to_string(), (value, mutable));
218 return Ok(());
219 }
220 }
221 Err(VmError::UndefinedVariable(name.to_string()))
222 }
223}
224
225fn levenshtein(a: &str, b: &str) -> usize {
227 let a: Vec<char> = a.chars().collect();
228 let b: Vec<char> = b.chars().collect();
229 let (m, n) = (a.len(), b.len());
230 let mut prev = (0..=n).collect::<Vec<_>>();
231 let mut curr = vec![0; n + 1];
232 for i in 1..=m {
233 curr[0] = i;
234 for j in 1..=n {
235 let cost = usize::from(a[i - 1] != b[j - 1]);
236 curr[j] = (prev[j] + 1).min(curr[j - 1] + 1).min(prev[j - 1] + cost);
237 }
238 std::mem::swap(&mut prev, &mut curr);
239 }
240 prev[n]
241}
242
243pub fn closest_match<'a>(name: &str, candidates: impl Iterator<Item = &'a str>) -> Option<String> {
246 let max_dist = match name.len() {
247 0..=2 => 1,
248 3..=5 => 2,
249 _ => 3,
250 };
251 candidates
252 .filter(|c| *c != name && !c.starts_with("__"))
253 .map(|c| (c, levenshtein(name, c)))
254 .filter(|(_, d)| *d <= max_dist)
255 .min_by(|(a, da), (b, db)| {
257 da.cmp(db)
258 .then_with(|| {
259 let a_diff = (a.len() as isize - name.len() as isize).unsigned_abs();
260 let b_diff = (b.len() as isize - name.len() as isize).unsigned_abs();
261 a_diff.cmp(&b_diff)
262 })
263 .then_with(|| a.cmp(b))
264 })
265 .map(|(c, _)| c.to_string())
266}