diff --git a/src/ast/compiler/binder.rs b/src/ast/compiler/binder.rs index 0575284..d6ef01d 100644 --- a/src/ast/compiler/binder.rs +++ b/src/ast/compiler/binder.rs @@ -37,11 +37,15 @@ struct FunctionCompiler { } impl FunctionCompiler { - fn new(identity: Identity) -> Self { + fn new(identity: Identity, initial_scopes: Vec, initial_slot_count: u32) -> Self { Self { identity, - scopes: vec![CompilerScope::new()], - slot_count: 0, + scopes: if initial_scopes.is_empty() { + vec![CompilerScope::new()] + } else { + initial_scopes + }, + slot_count: initial_slot_count, upvalues: Vec::new(), } } @@ -51,7 +55,9 @@ impl FunctionCompiler { } fn pop_scope(&mut self) { - self.scopes.pop(); + if self.scopes.len() > 1 { + self.scopes.pop(); + } } fn define_variable(&mut self, name: &Symbol, identity: Identity) -> Result { @@ -104,19 +110,18 @@ pub enum ExprContext { pub struct Binder { functions: Vec, - globals: Rc>>, capture_map: HashMap>, fixed_scope_idx: i32, } impl Binder { pub fn new( - globals: Rc>>, + initial_scopes: Vec, + initial_slot_count: u32, fixed_scope_idx: i32, ) -> Self { let mut binder = Self { functions: Vec::new(), - globals, capture_map: HashMap::new(), fixed_scope_idx, }; @@ -125,17 +130,20 @@ impl Binder { line: 0, col: 0, }), + initial_scopes, + initial_slot_count, )); binder } pub fn bind_root( - globals: Rc>>, + initial_scopes: Vec, + initial_slot_count: u32, fixed_scope_idx: i32, node: &Node, diagnostics: &mut Diagnostics, - ) -> Result<(BoundNode, HashMap>), String> { - let mut binder = Self::new(globals, fixed_scope_idx); + ) -> Result<(BoundNode, HashMap>, Vec, u32), String> { + let mut binder = Self::new(initial_scopes, initial_slot_count, fixed_scope_idx); let bound = binder.bind(node, ExprContext::Expression, diagnostics); let final_captures = binder @@ -144,7 +152,13 @@ impl Binder { .map(|(k, v)| (k, v.into_iter().collect())) .collect(); - Ok((bound, final_captures)) + let root_compiler = binder.functions.pop().unwrap(); + Ok(( + bound, + final_captures, + root_compiler.scopes, + root_compiler.slot_count, + )) } fn declare_variable( @@ -168,36 +182,6 @@ impl Binder { ); return None; } - - if current_scope_idx == 0 { - let mut globals_map = self.globals.borrow_mut(); - let addr = if let Some((idx, existing_id)) = globals_map.get(name) { - if *existing_id != identity { - diag.push_error( - format!("Variable '{}' is already defined in global scope.", name.name), - None, - ); - return None; - } - Address::Global(*idx) - } else { - let idx = GlobalIdx(globals_map.len() as u32); - globals_map.insert(name.clone(), (idx, identity.clone())); - Address::Global(idx) - }; - - let current_scope = self.functions[0].scopes.last_mut().unwrap(); - current_scope.locals.insert( - name.clone(), - LocalInfo { - addr, - identity, - _ty: StaticType::Any, - purity: Purity::Impure, - }, - ); - return Some(addr); - } } let current_fn = self.functions.last_mut().unwrap(); @@ -359,7 +343,7 @@ impl Binder { UntypedKind::Lambda { params, body } => { let identity = node.identity.clone(); self.functions - .push(FunctionCompiler::new(identity.clone())); + .push(FunctionCompiler::new(identity.clone(), vec![], 0)); let params_bound = self.bind_pattern(params, DeclarationKind::Parameter, diag); let body_bound = self.bind(body, ExprContext::Expression, diag); @@ -540,30 +524,22 @@ impl Binder { ) -> Option
{ let current_fn_idx = self.functions.len() - 1; + // 1. Try local in current function if let Some((info, _)) = self.functions[current_fn_idx].resolve_local(sym) { return Some(info.addr); } + // 2. Try enclosing scopes (capture chain) for i in (0..current_fn_idx).rev() { - if let Some((info, scope_idx)) = self.functions[i].resolve_local(sym) { - let is_frozen_root = i == 0 && (scope_idx as i32) <= self.fixed_scope_idx; - + if let Some((info, _)) = self.functions[i].resolve_local(sym) { + // If the resolved address is already Global, we don't need to capture it as an upvalue if let Address::Global(_) = info.addr { return Some(info.addr); } let mut addr = info.addr; - if is_frozen_root { - if let Address::Local(slot) = addr { - addr = Address::Global(GlobalIdx(slot.0)); - } - } - - if let Address::Global(_) = addr { - return Some(addr); - } - + // Record the capture for each lambda level in between for k in (i + 1)..=current_fn_idx { let lambda_id = self.functions[k].identity.clone(); self.capture_map @@ -576,18 +552,19 @@ impl Binder { } } - let globals = self.globals.borrow(); - if let Some((idx, _)) = globals.get(sym) { - return Some(Address::Global(*idx)); - } - + // 3. Global Fallback (search in root level with context removed) if sym.context.is_some() { let fallback_sym = Symbol { name: sym.name.clone(), context: None, }; - if let Some((idx, _)) = globals.get(&fallback_sym) { - return Some(Address::Global(*idx)); + // Search again in all functions, primarily we care about Root Scopes + for i in (0..=current_fn_idx).rev() { + if let Some((info, _)) = self.functions[i].resolve_local(&fallback_sym) { + if let Address::Global(_) = info.addr { + return Some(info.addr); + } + } } } @@ -705,9 +682,8 @@ mod tests { let mut parser = Parser::new(source); let untyped = parser.parse_expression(); - let globals = Rc::new(RefCell::new(HashMap::new())); let mut diagnostics = Diagnostics::new(); - let (bound, captures) = Binder::bind_root(globals, 0, &untyped, &mut diagnostics).unwrap(); + let (bound, captures, _scopes, _slots) = Binder::bind_root(vec![], 0, 0, &untyped, &mut diagnostics).unwrap(); if let BoundKind::Lambda { body, .. } = &bound.kind { if let BoundKind::Block { exprs } = &body.kind { @@ -735,9 +711,8 @@ mod tests { let mut parser = Parser::new(source); let untyped = parser.parse_expression(); - let globals = Rc::new(RefCell::new(HashMap::new())); let mut diagnostics = Diagnostics::new(); - let (bound, captures) = Binder::bind_root(globals, 0, &untyped, &mut diagnostics).unwrap(); + let (bound, captures, _scopes, _slots) = Binder::bind_root(vec![], 0, 0, &untyped, &mut diagnostics).unwrap(); if let BoundKind::Lambda { body, .. } = &bound.kind { if let BoundKind::Block { exprs } = &body.kind { @@ -765,9 +740,8 @@ mod tests { let mut parser = Parser::new(source); let untyped = parser.parse_expression(); - let globals = Rc::new(RefCell::new(HashMap::new())); let mut diagnostics = Diagnostics::new(); - let _ = Binder::bind_root(globals, 0, &untyped, &mut diagnostics); + let _ = Binder::bind_root(vec![], 0, 0, &untyped, &mut diagnostics); assert!(diagnostics.has_errors()); assert!(diagnostics.items.iter().any(|i| i.message.contains("already defined"))); @@ -775,17 +749,16 @@ mod tests { #[test] fn test_repro_global_redefinition() { - let globals = Rc::new(RefCell::new(HashMap::new())); - let source1 = "(def x 1) 1"; let untyped1 = Parser::new(source1).parse_expression(); let mut diagnostics = Diagnostics::new(); - assert!(Binder::bind_root(globals.clone(), 0, &untyped1, &mut diagnostics).is_ok()); + let (_, _, scopes1, slots1) = Binder::bind_root(vec![], 0, -1, &untyped1, &mut diagnostics).unwrap(); let source2 = "(def x 2) 2"; let untyped2 = Parser::new(source2).parse_expression(); let mut diagnostics2 = Diagnostics::new(); - let _ = Binder::bind_root(globals.clone(), 0, &untyped2, &mut diagnostics2); + // Here we simulate frozen scope by passing fixed_scope_idx = 0 + let _ = Binder::bind_root(scopes1, slots1, 0, &untyped2, &mut diagnostics2); assert!(diagnostics2.has_errors()); assert!(diagnostics2.items.iter().any(|i| i.message.contains("frozen/immutable"))); diff --git a/src/ast/compiler/macros.rs b/src/ast/compiler/macros.rs index 08fd059..73d93fd 100644 --- a/src/ast/compiler/macros.rs +++ b/src/ast/compiler/macros.rs @@ -623,6 +623,7 @@ impl MacroExpander { #[cfg(test)] mod tests { use super::*; + use crate::ast::compiler::bound_nodes::Address; use crate::ast::compiler::Binder; use crate::ast::parser::Parser; use crate::ast::types::{Object, Value}; @@ -764,21 +765,25 @@ mod tests { let mut expander = MacroExpander::new(MacroRegistry::new(), SimpleEvaluator); let expanded = expander.expand(untyped).unwrap(); - let mut global_names = HashMap::new(); - global_names.insert( + // Convert test globals into a CompilerScope + let mut locals = HashMap::new(); + locals.insert( Symbol::from("*"), - ( - crate::ast::compiler::bound_nodes::GlobalIdx(0), - crate::ast::types::NodeIdentity::new(crate::ast::types::SourceLocation { + crate::ast::compiler::binder::LocalInfo { + addr: Address::Local(crate::ast::compiler::bound_nodes::LocalSlot(0)), + identity: crate::ast::types::NodeIdentity::new(crate::ast::types::SourceLocation { line: 0, col: 0, }), - ), + _ty: crate::ast::types::StaticType::Any, + purity: crate::ast::types::Purity::Pure, + }, ); - let globals = Rc::new(RefCell::new(global_names)); + let initial_scopes = vec![crate::ast::compiler::binder::CompilerScope { locals }]; let mut diag = crate::ast::diagnostics::Diagnostics::new(); - let result = Binder::bind_root(globals, 0, &expanded, &mut diag); + // fixed_scope_idx = 0 means scope 0 is frozen (Global) + let result = Binder::bind_root(initial_scopes, 1, 0, &expanded, &mut diag); assert!( result.is_ok(), "Should find global '*' Error: {:?}", @@ -801,9 +806,8 @@ mod tests { let mut expander = MacroExpander::new(MacroRegistry::new(), SimpleEvaluator); let expanded = expander.expand(untyped).unwrap(); - let globals = Rc::new(RefCell::new(HashMap::new())); let mut diag = crate::ast::diagnostics::Diagnostics::new(); - let result = Binder::bind_root(globals, 0, &expanded, &mut diag); + let result = Binder::bind_root(vec![], 0, 0, &expanded, &mut diag); assert!(result.is_ok(), "Hygiene failed: {:?}", result.err()); } @@ -822,9 +826,8 @@ mod tests { let mut expander = MacroExpander::new(MacroRegistry::new(), SimpleEvaluator); let expanded = expander.expand(untyped).unwrap(); - let globals = Rc::new(RefCell::new(HashMap::new())); let mut diag = crate::ast::diagnostics::Diagnostics::new(); - let result = Binder::bind_root(globals, 0, &expanded, &mut diag); + let result = Binder::bind_root(vec![], 0, 0, &expanded, &mut diag); assert!( result.is_ok(), "Explicit Hygiene with Backticks failed: {:?}", diff --git a/src/ast/environment.rs b/src/ast/environment.rs index 5c0cf11..4524a27 100644 --- a/src/ast/environment.rs +++ b/src/ast/environment.rs @@ -112,7 +112,8 @@ impl FunctionRegistry for EnvFunctionRegistry { } struct RuntimeMacroEvaluator { - global_names: Rc>>, + root_scopes: Rc>>, + root_slot_count: Rc>, global_types: Rc>>, global_values: Rc>>, fixed_scope_idx: i32, @@ -131,7 +132,10 @@ impl MacroEvaluator for RuntimeMacroEvaluator { } let mut diag = Diagnostics::new(); - let (bound_ast, captures) = Binder::bind_root(self.global_names.clone(), self.fixed_scope_idx, node, &mut diag)?; + let initial_scopes = self.root_scopes.borrow().clone(); + let initial_slot_count = *self.root_slot_count.borrow(); + + let (bound_ast, captures, _, _) = Binder::bind_root(initial_scopes, initial_slot_count, self.fixed_scope_idx, node, &mut diag)?; let bound_ast = crate::ast::compiler::captures::CapturePass::apply(bound_ast, &captures); let checker = TypeChecker::new(self.global_types.clone()); let typed_ast = checker.check(&bound_ast, &[], &mut diag); @@ -219,7 +223,8 @@ impl Environment { fn get_expander(&self) -> MacroExpander { let evaluator = RuntimeMacroEvaluator { - global_names: self.global_names.clone(), + root_scopes: self.root_scopes.clone(), + root_slot_count: self.root_slot_count.clone(), global_types: self.global_types.clone(), global_values: self.global_values.clone(), fixed_scope_idx: self.fixed_scope_idx, @@ -433,8 +438,11 @@ impl Environment { } }; - let (bound_ast, captures) = - match Binder::bind_root(self.global_names.clone(), self.fixed_scope_idx, &expanded_ast, diagnostics) { + let initial_scopes = self.root_scopes.borrow().clone(); + let initial_slot_count = *self.root_slot_count.borrow(); + + let (bound_ast, captures, final_scopes, final_slot_count) = + match Binder::bind_root(initial_scopes, initial_slot_count, self.fixed_scope_idx, &expanded_ast, diagnostics) { Ok(res) => res, Err(e) => { diagnostics.push_error(e, None); @@ -442,6 +450,10 @@ impl Environment { } }; + // Update environment state with new bindings from this script + *self.root_scopes.borrow_mut() = final_scopes; + *self.root_slot_count.borrow_mut() = final_slot_count; + let bound_ast = crate::ast::compiler::captures::CapturePass::apply(bound_ast, &captures); // Pre-allocate global slots to prevent out-of-bounds during specialization/optimization @@ -520,7 +532,7 @@ impl Environment { root_scopes[0].locals.insert( Symbol::from(name), crate::ast::compiler::binder::LocalInfo { - addr: Address::Local(slot), + addr: Address::Global(GlobalIdx(slot.0)), identity, _ty: ty, purity: func.purity, @@ -566,7 +578,7 @@ impl Environment { root_scopes[0].locals.insert( Symbol::from(name), crate::ast::compiler::binder::LocalInfo { - addr: Address::Local(slot), + addr: Address::Global(GlobalIdx(slot.0)), identity, _ty: ty, purity: Purity::Pure,