1use crate::Result;
14use shape_ast::error::ShapeError;
15use shape_value::ValueWord;
16use std::collections::HashMap;
17
18#[derive(Debug)]
25pub struct ModuleBindingRegistry {
26 name_to_index: HashMap<String, u32>,
28
29 index_to_name: Vec<String>,
31
32 values: Vec<ValueWord>,
34
35 is_const: Vec<bool>,
37}
38
39impl Default for ModuleBindingRegistry {
40 fn default() -> Self {
41 Self::new()
42 }
43}
44
45impl ModuleBindingRegistry {
46 pub fn new() -> Self {
48 Self {
49 name_to_index: HashMap::new(),
50 index_to_name: Vec::new(),
51 values: Vec::new(),
52 is_const: Vec::new(),
53 }
54 }
55
56 pub fn with_capacity(capacity: usize) -> Self {
58 Self {
59 name_to_index: HashMap::with_capacity(capacity),
60 index_to_name: Vec::with_capacity(capacity),
61 values: Vec::with_capacity(capacity),
62 is_const: Vec::with_capacity(capacity),
63 }
64 }
65
66 pub fn register(&mut self, name: &str, value: ValueWord, is_const: bool) -> Result<u32> {
81 self.register_nb(name, value, is_const)
82 }
83
84 pub fn register_nb(&mut self, name: &str, value: ValueWord, is_const: bool) -> Result<u32> {
86 if let Some(&idx) = self.name_to_index.get(name) {
87 let idx_usize = idx as usize;
88
89 if self.is_const[idx_usize] && !is_const {
92 return Err(ShapeError::RuntimeError {
93 message: format!("Cannot redeclare const '{}' as mutable", name),
94 location: None,
95 });
96 }
97
98 self.values[idx_usize] = value;
99 self.is_const[idx_usize] = is_const;
100 Ok(idx)
101 } else {
102 let idx = self.values.len() as u32;
104 self.name_to_index.insert(name.to_string(), idx);
105 self.index_to_name.push(name.to_string());
106 self.values.push(value);
107 self.is_const.push(is_const);
108 Ok(idx)
109 }
110 }
111
112 pub fn register_const(&mut self, name: &str, value: ValueWord) -> Result<u32> {
114 self.register(name, value, true)
115 }
116
117 pub fn register_mut(&mut self, name: &str, value: ValueWord) -> Result<u32> {
119 self.register_nb(name, value, false)
120 }
121
122 pub fn contains(&self, name: &str) -> bool {
124 self.name_to_index.contains_key(name)
125 }
126
127 pub fn resolve(&self, name: &str) -> Option<u32> {
129 self.name_to_index.get(name).copied()
130 }
131
132 pub fn get_name(&self, idx: u32) -> Option<&str> {
134 self.index_to_name.get(idx as usize).map(|s| s.as_str())
135 }
136
137 pub fn get_by_name(&self, name: &str) -> Option<ValueWord> {
139 self.name_to_index
140 .get(name)
141 .map(|&idx| self.values[idx as usize].clone())
142 }
143
144 #[inline]
146 pub fn get_by_index(&self, idx: u32) -> Option<&ValueWord> {
147 self.values.get(idx as usize)
148 }
149
150 pub fn set_by_index(&mut self, idx: u32, value: ValueWord) -> Result<()> {
152 let idx_usize = idx as usize;
153 if idx_usize >= self.values.len() {
154 return Err(ShapeError::RuntimeError {
155 message: format!("module binding index {} out of bounds", idx),
156 location: None,
157 });
158 }
159 if self.is_const[idx_usize] {
160 return Err(ShapeError::RuntimeError {
161 message: format!("Cannot assign to const '{}'", self.index_to_name[idx_usize]),
162 location: None,
163 });
164 }
165 self.values[idx_usize] = value;
166 Ok(())
167 }
168
169 pub fn is_const(&self, name: &str) -> Option<bool> {
171 self.name_to_index
172 .get(name)
173 .map(|&idx| self.is_const[idx as usize])
174 }
175
176 pub fn is_const_by_index(&self, idx: u32) -> Option<bool> {
178 self.is_const.get(idx as usize).copied()
179 }
180
181 pub fn len(&self) -> usize {
183 self.values.len()
184 }
185
186 pub fn is_empty(&self) -> bool {
188 self.values.is_empty()
189 }
190
191 pub fn names(&self) -> impl Iterator<Item = &str> {
193 self.index_to_name.iter().map(|s| s.as_str())
194 }
195
196 #[inline]
202 pub fn get_ptr(&self, idx: u32) -> Option<*const ValueWord> {
203 self.values.get(idx as usize).map(|v| v as *const ValueWord)
204 }
205
206 pub fn snapshot_constants(&self) -> Vec<(u32, ValueWord)> {
208 self.values
209 .iter()
210 .enumerate()
211 .filter(|(i, _)| self.is_const[*i])
212 .map(|(i, v)| (i as u32, v.clone()))
213 .collect()
214 }
215
216 pub fn clear(&mut self) {
218 self.name_to_index.clear();
219 self.index_to_name.clear();
220 self.values.clear();
221 self.is_const.clear();
222 }
223}
224
225#[cfg(test)]
226mod tests {
227 use super::*;
228 use shape_value::heap_value::HeapValue;
229
230 #[test]
231 fn test_register_and_resolve() {
232 let mut registry = ModuleBindingRegistry::new();
233
234 let idx = registry
235 .register_const("x", ValueWord::from_f64(42.0))
236 .unwrap();
237 assert_eq!(idx, 0);
238
239 let idx2 = registry
240 .register_const("y", ValueWord::from_f64(100.0))
241 .unwrap();
242 assert_eq!(idx2, 1);
243
244 assert_eq!(registry.resolve("x"), Some(0));
245 assert_eq!(registry.resolve("y"), Some(1));
246 assert_eq!(registry.resolve("z"), None);
247 }
248
249 #[test]
250 fn test_get_by_name() {
251 let mut registry = ModuleBindingRegistry::new();
252 registry
253 .register_const("pi", ValueWord::from_f64(3.14159))
254 .unwrap();
255
256 let val = registry.get_by_name("pi");
257 assert!(val.is_some());
258 assert!((val.unwrap().as_f64().unwrap() - 3.14159).abs() < 0.0001);
259
260 assert!(registry.get_by_name("unknown").is_none());
261 }
262
263 #[test]
264 fn test_get_by_index() {
265 let mut registry = ModuleBindingRegistry::new();
266 registry
267 .register_const("a", ValueWord::from_f64(1.0))
268 .unwrap();
269 registry
270 .register_const("b", ValueWord::from_f64(2.0))
271 .unwrap();
272
273 assert_eq!(
274 registry.get_by_index(0).and_then(|nb| nb.as_f64()),
275 Some(1.0)
276 );
277 assert_eq!(
278 registry.get_by_index(1).and_then(|nb| nb.as_f64()),
279 Some(2.0)
280 );
281 assert!(registry.get_by_index(99).is_none());
282 }
283
284 #[test]
285 fn test_const_protection() {
286 let mut registry = ModuleBindingRegistry::new();
287 registry
288 .register_const("CONST_VAL", ValueWord::from_f64(42.0))
289 .unwrap();
290
291 let result = registry.set_by_index(0, ValueWord::from_f64(100.0));
293 assert!(result.is_err());
294
295 assert_eq!(
297 registry.get_by_index(0).and_then(|nb| nb.as_f64()),
298 Some(42.0)
299 );
300 }
301
302 #[test]
303 fn test_mutable_module_binding() {
304 let mut registry = ModuleBindingRegistry::new();
305 registry
306 .register_mut("counter", ValueWord::from_f64(0.0))
307 .unwrap();
308
309 registry.set_by_index(0, ValueWord::from_f64(1.0)).unwrap();
311 assert_eq!(
312 registry.get_by_index(0).and_then(|nb| nb.as_f64()),
313 Some(1.0)
314 );
315 }
316
317 #[test]
318 fn test_re_register_const() {
319 let mut registry = ModuleBindingRegistry::new();
320 registry
321 .register_const("func", ValueWord::from_f64(1.0))
322 .unwrap();
323
324 registry
326 .register_const("func", ValueWord::from_f64(2.0))
327 .unwrap();
328 assert_eq!(
329 registry.get_by_name("func").and_then(|nb| nb.as_f64()),
330 Some(2.0)
331 );
332
333 assert_eq!(registry.resolve("func"), Some(0));
335 }
336
337 #[test]
338 fn test_snapshot_constants() {
339 let mut registry = ModuleBindingRegistry::new();
340 registry
341 .register_const("a", ValueWord::from_f64(1.0))
342 .unwrap();
343 registry
344 .register_mut("b", ValueWord::from_f64(2.0))
345 .unwrap();
346 registry
347 .register_const("c", ValueWord::from_f64(3.0))
348 .unwrap();
349
350 let constants = registry.snapshot_constants();
351 assert_eq!(constants.len(), 2); let indices: Vec<u32> = constants.iter().map(|(i, _)| *i).collect();
355 assert!(indices.contains(&0)); assert!(indices.contains(&2)); }
358
359 #[test]
360 fn test_closure_registration() {
361 let mut registry = ModuleBindingRegistry::new();
362
363 let closure_val = ValueWord::from_heap_value(HeapValue::Closure {
364 function_id: 0,
365 upvalues: vec![],
366 });
367
368 let idx = registry.register_const("test_func", closure_val).unwrap();
369 assert_eq!(idx, 0);
370
371 let val = registry.get_by_name("test_func");
373 assert!(
374 matches!(val, Some(nb) if nb.as_heap_ref().is_some_and(|h| matches!(h, HeapValue::Closure { .. })))
375 );
376 }
377
378 #[test]
379 fn test_contains() {
380 let mut registry = ModuleBindingRegistry::new();
381 registry
382 .register_const("exists", ValueWord::from_f64(1.0))
383 .unwrap();
384
385 assert!(registry.contains("exists"));
386 assert!(!registry.contains("not_exists"));
387 }
388
389 #[test]
390 fn test_is_const() {
391 let mut registry = ModuleBindingRegistry::new();
392 registry
393 .register_const("constant", ValueWord::from_f64(1.0))
394 .unwrap();
395 registry
396 .register_mut("mutable", ValueWord::from_f64(2.0))
397 .unwrap();
398
399 assert_eq!(registry.is_const("constant"), Some(true));
400 assert_eq!(registry.is_const("mutable"), Some(false));
401 assert_eq!(registry.is_const("unknown"), None);
402 }
403}