1use crate::Result;
14use shape_ast::error::ShapeError;
15use shape_value::KindedSlot;
22use std::collections::HashMap;
23
24#[derive(Debug)]
31pub struct ModuleBindingRegistry {
32 name_to_index: HashMap<String, u32>,
34
35 index_to_name: Vec<String>,
37
38 values: Vec<KindedSlot>,
42
43 is_const: Vec<bool>,
45}
46
47impl Default for ModuleBindingRegistry {
48 fn default() -> Self {
49 Self::new()
50 }
51}
52
53impl ModuleBindingRegistry {
54 pub fn new() -> Self {
56 Self {
57 name_to_index: HashMap::new(),
58 index_to_name: Vec::new(),
59 values: Vec::new(),
60 is_const: Vec::new(),
61 }
62 }
63
64 pub fn with_capacity(capacity: usize) -> Self {
66 Self {
67 name_to_index: HashMap::with_capacity(capacity),
68 index_to_name: Vec::with_capacity(capacity),
69 values: Vec::with_capacity(capacity),
70 is_const: Vec::with_capacity(capacity),
71 }
72 }
73
74 pub fn register(&mut self, name: &str, value: KindedSlot, is_const: bool) -> Result<u32> {
89 self.register_nb(name, value, is_const)
90 }
91
92 pub fn register_nb(&mut self, name: &str, value: KindedSlot, is_const: bool) -> Result<u32> {
94 if let Some(&idx) = self.name_to_index.get(name) {
95 let idx_usize = idx as usize;
96
97 if self.is_const[idx_usize] && !is_const {
100 return Err(ShapeError::RuntimeError {
101 message: format!("Cannot redeclare const '{}' as mutable", name),
102 location: None,
103 });
104 }
105
106 self.values[idx_usize] = value;
107 self.is_const[idx_usize] = is_const;
108 Ok(idx)
109 } else {
110 let idx = self.values.len() as u32;
112 self.name_to_index.insert(name.to_string(), idx);
113 self.index_to_name.push(name.to_string());
114 self.values.push(value);
115 self.is_const.push(is_const);
116 Ok(idx)
117 }
118 }
119
120 pub fn register_const(&mut self, name: &str, value: KindedSlot) -> Result<u32> {
122 self.register(name, value, true)
123 }
124
125 pub fn register_mut(&mut self, name: &str, value: KindedSlot) -> Result<u32> {
127 self.register_nb(name, value, false)
128 }
129
130 pub fn contains(&self, name: &str) -> bool {
132 self.name_to_index.contains_key(name)
133 }
134
135 pub fn resolve(&self, name: &str) -> Option<u32> {
137 self.name_to_index.get(name).copied()
138 }
139
140 pub fn get_name(&self, idx: u32) -> Option<&str> {
142 self.index_to_name.get(idx as usize).map(|s| s.as_str())
143 }
144
145 pub fn get_by_name(&self, name: &str) -> Option<KindedSlot> {
148 self.name_to_index
149 .get(name)
150 .map(|&idx| self.values[idx as usize].clone())
151 }
152
153 #[inline]
155 pub fn get_by_index(&self, idx: u32) -> Option<&KindedSlot> {
156 self.values.get(idx as usize)
157 }
158
159 pub fn set_by_index(&mut self, idx: u32, value: KindedSlot) -> Result<()> {
163 let idx_usize = idx as usize;
164 if idx_usize >= self.values.len() {
165 return Err(ShapeError::RuntimeError {
166 message: format!("module binding index {} out of bounds", idx),
167 location: None,
168 });
169 }
170 if self.is_const[idx_usize] {
171 return Err(ShapeError::RuntimeError {
172 message: format!("Cannot assign to const '{}'", self.index_to_name[idx_usize]),
173 location: None,
174 });
175 }
176 self.values[idx_usize] = value;
177 Ok(())
178 }
179
180 pub fn is_const(&self, name: &str) -> Option<bool> {
182 self.name_to_index
183 .get(name)
184 .map(|&idx| self.is_const[idx as usize])
185 }
186
187 pub fn is_const_by_index(&self, idx: u32) -> Option<bool> {
189 self.is_const.get(idx as usize).copied()
190 }
191
192 pub fn len(&self) -> usize {
194 self.values.len()
195 }
196
197 pub fn is_empty(&self) -> bool {
199 self.values.is_empty()
200 }
201
202 pub fn names(&self) -> impl Iterator<Item = &str> {
204 self.index_to_name.iter().map(|s| s.as_str())
205 }
206
207 #[inline]
213 pub fn get_ptr(&self, idx: u32) -> Option<*const KindedSlot> {
214 self.values.get(idx as usize).map(|v| v as *const KindedSlot)
215 }
216
217 pub fn snapshot_constants(&self) -> Vec<(u32, KindedSlot)> {
221 self.values
222 .iter()
223 .enumerate()
224 .filter(|(i, _)| self.is_const[*i])
225 .map(|(i, v)| (i as u32, v.clone()))
226 .collect()
227 }
228
229 pub fn clear(&mut self) {
231 self.name_to_index.clear();
232 self.index_to_name.clear();
233 self.values.clear();
234 self.is_const.clear();
235 }
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241
242 #[test]
243 fn test_register_and_resolve() {
244 let mut registry = ModuleBindingRegistry::new();
245
246 let idx = registry
247 .register_const("x", KindedSlot::from_number(42.0))
248 .unwrap();
249 assert_eq!(idx, 0);
250
251 let idx2 = registry
252 .register_const("y", KindedSlot::from_number(100.0))
253 .unwrap();
254 assert_eq!(idx2, 1);
255
256 assert_eq!(registry.resolve("x"), Some(0));
257 assert_eq!(registry.resolve("y"), Some(1));
258 assert_eq!(registry.resolve("z"), None);
259 }
260
261 #[test]
262 fn test_get_by_name() {
263 let mut registry = ModuleBindingRegistry::new();
264 registry
265 .register_const("pi", KindedSlot::from_number(3.14159))
266 .unwrap();
267
268 let val = registry.get_by_name("pi");
269 assert!(val.is_some());
270 assert!((val.unwrap().slot().as_f64() - 3.14159).abs() < 0.0001);
271
272 assert!(registry.get_by_name("unknown").is_none());
273 }
274
275 #[test]
276 fn test_get_by_index() {
277 let mut registry = ModuleBindingRegistry::new();
278 registry
279 .register_const("a", KindedSlot::from_number(1.0))
280 .unwrap();
281 registry
282 .register_const("b", KindedSlot::from_number(2.0))
283 .unwrap();
284
285 assert_eq!(registry.get_by_index(0).map(|ks| ks.slot().as_f64()), Some(1.0));
286 assert_eq!(registry.get_by_index(1).map(|ks| ks.slot().as_f64()), Some(2.0));
287 assert!(registry.get_by_index(99).is_none());
288 }
289
290 #[test]
291 fn test_const_protection() {
292 let mut registry = ModuleBindingRegistry::new();
293 registry
294 .register_const("CONST_VAL", KindedSlot::from_number(42.0))
295 .unwrap();
296
297 let result = registry.set_by_index(0, KindedSlot::from_number(100.0));
299 assert!(result.is_err());
300
301 assert_eq!(registry.get_by_index(0).map(|ks| ks.slot().as_f64()), Some(42.0));
303 }
304
305 #[test]
306 fn test_mutable_module_binding() {
307 let mut registry = ModuleBindingRegistry::new();
308 registry
309 .register_mut("counter", KindedSlot::from_number(0.0))
310 .unwrap();
311
312 registry.set_by_index(0, KindedSlot::from_number(1.0)).unwrap();
314 assert_eq!(registry.get_by_index(0).map(|ks| ks.slot().as_f64()), Some(1.0));
315 }
316
317 #[test]
318 fn test_re_register_const() {
319 let mut registry = ModuleBindingRegistry::new();
320 registry
321 .register_const("func", KindedSlot::from_number(1.0))
322 .unwrap();
323
324 registry
326 .register_const("func", KindedSlot::from_number(2.0))
327 .unwrap();
328 assert_eq!(
329 registry.get_by_name("func").map(|ks| ks.slot().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", KindedSlot::from_number(1.0))
342 .unwrap();
343 registry
344 .register_mut("b", KindedSlot::from_number(2.0))
345 .unwrap();
346 registry
347 .register_const("c", KindedSlot::from_number(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_contains() {
361 let mut registry = ModuleBindingRegistry::new();
362 registry
363 .register_const("exists", KindedSlot::from_number(1.0))
364 .unwrap();
365
366 assert!(registry.contains("exists"));
367 assert!(!registry.contains("not_exists"));
368 }
369
370 #[test]
371 fn test_is_const() {
372 let mut registry = ModuleBindingRegistry::new();
373 registry
374 .register_const("constant", KindedSlot::from_number(1.0))
375 .unwrap();
376 registry
377 .register_mut("mutable", KindedSlot::from_number(2.0))
378 .unwrap();
379
380 assert_eq!(registry.is_const("constant"), Some(true));
381 assert_eq!(registry.is_const("mutable"), Some(false));
382 assert_eq!(registry.is_const("unknown"), None);
383 }
384}