138 lines
4.2 KiB
Rust
138 lines
4.2 KiB
Rust
|
|
//! 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<String, BindingId>,
|
||
|
|
}
|
||
|
|
|
||
|
|
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<BindingId> {
|
||
|
|
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<BindingId> {
|
||
|
|
self.binding_map.remove(name)
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Get immutable reference to the binding map (for BindingMapProvider)
|
||
|
|
pub fn binding_map(&self) -> &BTreeMap<String, BindingId> {
|
||
|
|
&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)));
|
||
|
|
}
|
||
|
|
}
|