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)]
69pub struct VmEnv {
70 pub(crate) scopes: Vec<Scope>,
71}
72
73#[derive(Debug, Clone)]
74pub(crate) struct Scope {
75 pub(crate) vars: Arc<BTreeMap<String, (VmValue, bool)>>, }
77
78impl Scope {
79 #[inline]
80 fn empty() -> Self {
81 Self {
82 vars: Arc::new(BTreeMap::new()),
83 }
84 }
85}
86
87impl Default for VmEnv {
88 fn default() -> Self {
89 Self::new()
90 }
91}
92
93impl VmEnv {
94 pub fn new() -> Self {
95 Self {
96 scopes: vec![Scope::empty()],
97 }
98 }
99
100 pub fn push_scope(&mut self) {
101 self.scopes.push(Scope::empty());
102 }
103
104 pub fn pop_scope(&mut self) {
105 if self.scopes.len() > 1 {
106 self.scopes.pop();
107 }
108 }
109
110 pub fn scope_depth(&self) -> usize {
111 self.scopes.len()
112 }
113
114 pub fn truncate_scopes(&mut self, target_depth: usize) {
115 let min_depth = target_depth.max(1);
116 while self.scopes.len() > min_depth {
117 self.scopes.pop();
118 }
119 }
120
121 pub fn get(&self, name: &str) -> Option<VmValue> {
122 for scope in self.scopes.iter().rev() {
123 if let Some((val, _)) = scope.vars.get(name) {
124 return Some(val.clone());
125 }
126 }
127 None
128 }
129
130 pub(crate) fn contains(&self, name: &str) -> bool {
131 self.scopes
132 .iter()
133 .rev()
134 .any(|scope| scope.vars.contains_key(name))
135 }
136
137 pub fn define(&mut self, name: &str, value: VmValue, mutable: bool) -> Result<(), VmError> {
138 if let Some(scope) = self.scopes.last_mut() {
139 if let Some((_, existing_mutable)) = scope.vars.get(name) {
140 if !existing_mutable && !mutable {
141 return Err(VmError::Runtime(format!(
142 "Cannot redeclare immutable variable '{name}' in the same scope (use 'var' for mutable bindings)"
143 )));
144 }
145 }
146 Arc::make_mut(&mut scope.vars).insert(name.to_string(), (value, mutable));
147 }
148 Ok(())
149 }
150
151 pub fn all_variables(&self) -> BTreeMap<String, VmValue> {
152 let mut vars = BTreeMap::new();
153 for scope in &self.scopes {
154 for (name, (value, _)) in scope.vars.iter() {
155 vars.insert(name.clone(), value.clone());
156 }
157 }
158 vars
159 }
160
161 pub fn assign(&mut self, name: &str, value: VmValue) -> Result<(), VmError> {
162 for scope in self.scopes.iter_mut().rev() {
163 if let Some((_, mutable)) = scope.vars.get(name) {
164 if !mutable {
165 return Err(VmError::ImmutableAssignment(name.to_string()));
166 }
167 Arc::make_mut(&mut scope.vars).insert(name.to_string(), (value, true));
168 return Ok(());
169 }
170 }
171 Err(VmError::UndefinedVariable(name.to_string()))
172 }
173
174 pub fn assign_debug(&mut self, name: &str, value: VmValue) -> Result<(), VmError> {
182 for scope in self.scopes.iter_mut().rev() {
183 if let Some((_, mutable)) = scope.vars.get(name) {
184 let mutable = *mutable;
185 Arc::make_mut(&mut scope.vars).insert(name.to_string(), (value, mutable));
186 return Ok(());
187 }
188 }
189 Err(VmError::UndefinedVariable(name.to_string()))
190 }
191}
192
193fn levenshtein(a: &str, b: &str) -> usize {
195 let a: Vec<char> = a.chars().collect();
196 let b: Vec<char> = b.chars().collect();
197 let (m, n) = (a.len(), b.len());
198 let mut prev = (0..=n).collect::<Vec<_>>();
199 let mut curr = vec![0; n + 1];
200 for i in 1..=m {
201 curr[0] = i;
202 for j in 1..=n {
203 let cost = usize::from(a[i - 1] != b[j - 1]);
204 curr[j] = (prev[j] + 1).min(curr[j - 1] + 1).min(prev[j - 1] + cost);
205 }
206 std::mem::swap(&mut prev, &mut curr);
207 }
208 prev[n]
209}
210
211pub fn closest_match<'a>(name: &str, candidates: impl Iterator<Item = &'a str>) -> Option<String> {
214 let max_dist = match name.len() {
215 0..=2 => 1,
216 3..=5 => 2,
217 _ => 3,
218 };
219 candidates
220 .filter(|c| *c != name && !c.starts_with("__"))
221 .map(|c| (c, levenshtein(name, c)))
222 .filter(|(_, d)| *d <= max_dist)
223 .min_by(|(a, da), (b, db)| {
225 da.cmp(db)
226 .then_with(|| {
227 let a_diff = (a.len() as isize - name.len() as isize).unsigned_abs();
228 let b_diff = (b.len() as isize - name.len() as isize).unsigned_abs();
229 a_diff.cmp(&b_diff)
230 })
231 .then_with(|| a.cmp(b))
232 })
233 .map(|(c, _)| c.to_string())
234}