//! Phase 136 follow-up (Step 4/7): BindingContext extraction //! //! Consolidates variable binding management: //! - binding_map: String -> BindingId mapping (parallel to variable_map) //! - BindingId allocation (via CoreContext.next_binding()) //! - Scope restoration logic (stored in ScopeContext frames) //! //! ## Relationship with other contexts: //! - **CoreContext**: Allocates BindingId via next_binding() //! - **ScopeContext**: Manages lexical scope frames with restore_binding data //! - **TypeContext**: Independent (type tracking vs binding tracking) //! //! ## Design: //! - BindingId tracks variable binding identity (survives SSA renaming) //! - Parallel to ValueId (variable_map), but for binding semantics //! - Restored on scope exit (see LexicalScopeFrame.restore_binding) //! //! Phase 74: BindingId system introduction //! Phase 136 Step 4/7: Extraction into dedicated context use crate::mir::BindingId; use std::collections::BTreeMap; /// Phase 136 Step 4/7: Binding context for variable binding management /// /// Manages the mapping from variable names to their BindingId. /// Parallel to `variable_map` (String -> ValueId), but tracks binding identity. /// /// ## Key responsibilities: /// - Maintain current binding_map (String -> BindingId) /// - Provide lookup/insertion/removal operations /// - Work with ScopeContext for scope-based restoration /// /// ## Implementation note: /// - Uses BTreeMap for deterministic iteration (Phase 25.1 consistency) /// - BindingId allocation is delegated to CoreContext.next_binding() #[derive(Debug, Clone)] pub struct BindingContext { /// Phase 74: BindingId mapping for lexical variable bindings /// Maps variable names to their current BindingId. /// Parallel to `variable_map` (String -> ValueId), but tracks binding identity. /// Restored on lexical scope exit (see ScopeContext restore_binding). pub(super) binding_map: BTreeMap, } impl Default for BindingContext { fn default() -> Self { Self::new() } } impl BindingContext { /// Create a new BindingContext pub fn new() -> Self { Self { binding_map: BTreeMap::new(), } } /// Lookup a variable's BindingId pub fn lookup(&self, name: &str) -> Option { self.binding_map.get(name).copied() } /// Insert a variable binding pub fn insert(&mut self, name: String, binding_id: BindingId) { self.binding_map.insert(name, binding_id); } /// Remove a variable binding pub fn remove(&mut self, name: &str) -> Option { self.binding_map.remove(name) } /// Get immutable reference to the binding map (for BindingMapProvider) pub fn binding_map(&self) -> &BTreeMap { &self.binding_map } /// Check if a variable has a binding pub fn contains(&self, name: &str) -> bool { self.binding_map.contains_key(name) } /// Get the number of bindings pub fn len(&self) -> usize { self.binding_map.len() } /// Check if there are no bindings pub fn is_empty(&self) -> bool { self.binding_map.is_empty() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_binding_context_basic() { let mut ctx = BindingContext::new(); assert!(ctx.is_empty()); assert_eq!(ctx.len(), 0); let bid = BindingId::new(0); ctx.insert("x".to_string(), bid); assert_eq!(ctx.lookup("x"), Some(bid)); assert_eq!(ctx.len(), 1); assert!(!ctx.is_empty()); ctx.remove("x"); assert_eq!(ctx.lookup("x"), None); assert!(ctx.is_empty()); } #[test] fn test_binding_context_contains() { let mut ctx = BindingContext::new(); assert!(!ctx.contains("x")); ctx.insert("x".to_string(), BindingId::new(0)); assert!(ctx.contains("x")); } #[test] fn test_binding_map_access() { let mut ctx = BindingContext::new(); ctx.insert("a".to_string(), BindingId::new(1)); ctx.insert("b".to_string(), BindingId::new(2)); let map = ctx.binding_map(); assert_eq!(map.len(), 2); assert_eq!(map.get("a"), Some(&BindingId::new(1))); assert_eq!(map.get("b"), Some(&BindingId::new(2))); } }