Refactor: Pass global names to Binder
The `Binder` struct now accepts a `Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>` for global names. This allows the binder to directly access and manage global symbols during the binding process, simplifying the logic and improving efficiency. Additionally, `CompilerScope` now implements `Default`, and a type alias `BindingResult` has been introduced for clarity. The `bind_root` function signature has been updated to accept the `globals` argument.
This commit is contained in:
+56
-14
@@ -21,6 +21,12 @@ pub struct CompilerScope {
|
|||||||
pub locals: HashMap<Symbol, LocalInfo>,
|
pub locals: HashMap<Symbol, LocalInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for CompilerScope {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl CompilerScope {
|
impl CompilerScope {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -108,8 +114,16 @@ pub enum ExprContext {
|
|||||||
Statement,
|
Statement,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type BindingResult = (
|
||||||
|
BoundNode,
|
||||||
|
HashMap<Identity, Vec<Identity>>,
|
||||||
|
Vec<CompilerScope>,
|
||||||
|
u32,
|
||||||
|
);
|
||||||
|
|
||||||
pub struct Binder {
|
pub struct Binder {
|
||||||
functions: Vec<FunctionCompiler>,
|
functions: Vec<FunctionCompiler>,
|
||||||
|
globals: Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>,
|
||||||
capture_map: HashMap<Identity, std::collections::HashSet<Identity>>,
|
capture_map: HashMap<Identity, std::collections::HashSet<Identity>>,
|
||||||
fixed_scope_idx: i32,
|
fixed_scope_idx: i32,
|
||||||
}
|
}
|
||||||
@@ -119,9 +133,11 @@ impl Binder {
|
|||||||
initial_scopes: Vec<CompilerScope>,
|
initial_scopes: Vec<CompilerScope>,
|
||||||
initial_slot_count: u32,
|
initial_slot_count: u32,
|
||||||
fixed_scope_idx: i32,
|
fixed_scope_idx: i32,
|
||||||
|
globals: Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut binder = Self {
|
let mut binder = Self {
|
||||||
functions: Vec::new(),
|
functions: Vec::new(),
|
||||||
|
globals,
|
||||||
capture_map: HashMap::new(),
|
capture_map: HashMap::new(),
|
||||||
fixed_scope_idx,
|
fixed_scope_idx,
|
||||||
};
|
};
|
||||||
@@ -140,10 +156,11 @@ impl Binder {
|
|||||||
initial_scopes: Vec<CompilerScope>,
|
initial_scopes: Vec<CompilerScope>,
|
||||||
initial_slot_count: u32,
|
initial_slot_count: u32,
|
||||||
fixed_scope_idx: i32,
|
fixed_scope_idx: i32,
|
||||||
|
globals: Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>,
|
||||||
node: &Node<UntypedKind>,
|
node: &Node<UntypedKind>,
|
||||||
diagnostics: &mut Diagnostics,
|
diagnostics: &mut Diagnostics,
|
||||||
) -> Result<(BoundNode, HashMap<Identity, Vec<Identity>>, Vec<CompilerScope>, u32), String> {
|
) -> Result<BindingResult, String> {
|
||||||
let mut binder = Self::new(initial_scopes, initial_slot_count, fixed_scope_idx);
|
let mut binder = Self::new(initial_scopes, initial_slot_count, fixed_scope_idx, globals);
|
||||||
let bound = binder.bind(node, ExprContext::Expression, diagnostics);
|
let bound = binder.bind(node, ExprContext::Expression, diagnostics);
|
||||||
|
|
||||||
let final_captures = binder
|
let final_captures = binder
|
||||||
@@ -531,14 +548,25 @@ impl Binder {
|
|||||||
|
|
||||||
// 2. Try enclosing scopes (capture chain)
|
// 2. Try enclosing scopes (capture chain)
|
||||||
for i in (0..current_fn_idx).rev() {
|
for i in (0..current_fn_idx).rev() {
|
||||||
if let Some((info, _)) = self.functions[i].resolve_local(sym) {
|
if let Some((info, scope_idx)) = self.functions[i].resolve_local(sym) {
|
||||||
// If the resolved address is already Global, we don't need to capture it as an upvalue
|
// If it's in the root script and within a frozen scope, translate to Global
|
||||||
|
let is_frozen_root = i == 0 && (scope_idx as i32) <= self.fixed_scope_idx;
|
||||||
|
|
||||||
if let Address::Global(_) = info.addr {
|
if let Address::Global(_) = info.addr {
|
||||||
return Some(info.addr);
|
return Some(info.addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut addr = info.addr;
|
let mut addr = info.addr;
|
||||||
|
|
||||||
|
if is_frozen_root
|
||||||
|
&& 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
|
// Record the capture for each lambda level in between
|
||||||
for k in (i + 1)..=current_fn_idx {
|
for k in (i + 1)..=current_fn_idx {
|
||||||
let lambda_id = self.functions[k].identity.clone();
|
let lambda_id = self.functions[k].identity.clone();
|
||||||
@@ -552,7 +580,13 @@ impl Binder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Global Fallback (search in root level with context removed)
|
// 3. Global Fallback
|
||||||
|
let globals = self.globals.borrow();
|
||||||
|
if let Some((idx, _)) = globals.get(sym) {
|
||||||
|
return Some(Address::Global(*idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Global Fallback (search in root level with context removed)
|
||||||
if sym.context.is_some() {
|
if sym.context.is_some() {
|
||||||
let fallback_sym = Symbol {
|
let fallback_sym = Symbol {
|
||||||
name: sym.name.clone(),
|
name: sym.name.clone(),
|
||||||
@@ -560,12 +594,15 @@ impl Binder {
|
|||||||
};
|
};
|
||||||
// Search again in all functions, primarily we care about Root Scopes
|
// Search again in all functions, primarily we care about Root Scopes
|
||||||
for i in (0..=current_fn_idx).rev() {
|
for i in (0..=current_fn_idx).rev() {
|
||||||
if let Some((info, _)) = self.functions[i].resolve_local(&fallback_sym) {
|
if let Some((info, _)) = self.functions[i].resolve_local(&fallback_sym)
|
||||||
if let Address::Global(_) = info.addr {
|
&& let Address::Global(_) = info.addr
|
||||||
return Some(info.addr);
|
{
|
||||||
}
|
return Some(info.addr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some((idx, _)) = globals.get(&fallback_sym) {
|
||||||
|
return Some(Address::Global(*idx));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
diag.push_error(
|
diag.push_error(
|
||||||
@@ -682,8 +719,9 @@ mod tests {
|
|||||||
let mut parser = Parser::new(source);
|
let mut parser = Parser::new(source);
|
||||||
let untyped = parser.parse_expression();
|
let untyped = parser.parse_expression();
|
||||||
|
|
||||||
|
let globals = Rc::new(RefCell::new(HashMap::new()));
|
||||||
let mut diagnostics = Diagnostics::new();
|
let mut diagnostics = Diagnostics::new();
|
||||||
let (bound, captures, _scopes, _slots) = Binder::bind_root(vec![], 0, 0, &untyped, &mut diagnostics).unwrap();
|
let (bound, captures, _scopes, _slots) = Binder::bind_root(vec![], 0, 0, globals, &untyped, &mut diagnostics).unwrap();
|
||||||
|
|
||||||
if let BoundKind::Lambda { body, .. } = &bound.kind {
|
if let BoundKind::Lambda { body, .. } = &bound.kind {
|
||||||
if let BoundKind::Block { exprs } = &body.kind {
|
if let BoundKind::Block { exprs } = &body.kind {
|
||||||
@@ -711,8 +749,9 @@ mod tests {
|
|||||||
let mut parser = Parser::new(source);
|
let mut parser = Parser::new(source);
|
||||||
let untyped = parser.parse_expression();
|
let untyped = parser.parse_expression();
|
||||||
|
|
||||||
|
let globals = Rc::new(RefCell::new(HashMap::new()));
|
||||||
let mut diagnostics = Diagnostics::new();
|
let mut diagnostics = Diagnostics::new();
|
||||||
let (bound, captures, _scopes, _slots) = Binder::bind_root(vec![], 0, 0, &untyped, &mut diagnostics).unwrap();
|
let (bound, captures, _scopes, _slots) = Binder::bind_root(vec![], 0, 0, globals, &untyped, &mut diagnostics).unwrap();
|
||||||
|
|
||||||
if let BoundKind::Lambda { body, .. } = &bound.kind {
|
if let BoundKind::Lambda { body, .. } = &bound.kind {
|
||||||
if let BoundKind::Block { exprs } = &body.kind {
|
if let BoundKind::Block { exprs } = &body.kind {
|
||||||
@@ -740,8 +779,9 @@ mod tests {
|
|||||||
let mut parser = Parser::new(source);
|
let mut parser = Parser::new(source);
|
||||||
let untyped = parser.parse_expression();
|
let untyped = parser.parse_expression();
|
||||||
|
|
||||||
|
let globals = Rc::new(RefCell::new(HashMap::new()));
|
||||||
let mut diagnostics = Diagnostics::new();
|
let mut diagnostics = Diagnostics::new();
|
||||||
let _ = Binder::bind_root(vec![], 0, 0, &untyped, &mut diagnostics);
|
let _ = Binder::bind_root(vec![], 0, 0, globals, &untyped, &mut diagnostics);
|
||||||
|
|
||||||
assert!(diagnostics.has_errors());
|
assert!(diagnostics.has_errors());
|
||||||
assert!(diagnostics.items.iter().any(|i| i.message.contains("already defined")));
|
assert!(diagnostics.items.iter().any(|i| i.message.contains("already defined")));
|
||||||
@@ -749,16 +789,18 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_repro_global_redefinition() {
|
fn test_repro_global_redefinition() {
|
||||||
|
let globals = Rc::new(RefCell::new(HashMap::new()));
|
||||||
|
|
||||||
let source1 = "(def x 1) 1";
|
let source1 = "(def x 1) 1";
|
||||||
let untyped1 = Parser::new(source1).parse_expression();
|
let untyped1 = Parser::new(source1).parse_expression();
|
||||||
let mut diagnostics = Diagnostics::new();
|
let mut diagnostics = Diagnostics::new();
|
||||||
let (_, _, scopes1, slots1) = Binder::bind_root(vec![], 0, -1, &untyped1, &mut diagnostics).unwrap();
|
let (_, _, scopes1, slots1) = Binder::bind_root(vec![], 0, -1, globals.clone(), &untyped1, &mut diagnostics).unwrap();
|
||||||
|
|
||||||
let source2 = "(def x 2) 2";
|
let source2 = "(def x 2) 2";
|
||||||
let untyped2 = Parser::new(source2).parse_expression();
|
let untyped2 = Parser::new(source2).parse_expression();
|
||||||
let mut diagnostics2 = Diagnostics::new();
|
let mut diagnostics2 = Diagnostics::new();
|
||||||
// Here we simulate frozen scope by passing fixed_scope_idx = 0
|
// Here we simulate frozen scope by passing fixed_scope_idx = 0
|
||||||
let _ = Binder::bind_root(scopes1, slots1, 0, &untyped2, &mut diagnostics2);
|
let _ = Binder::bind_root(scopes1, slots1, 0, globals.clone(), &untyped2, &mut diagnostics2);
|
||||||
|
|
||||||
assert!(diagnostics2.has_errors());
|
assert!(diagnostics2.has_errors());
|
||||||
assert!(diagnostics2.items.iter().any(|i| i.message.contains("frozen/immutable")));
|
assert!(diagnostics2.items.iter().any(|i| i.message.contains("frozen/immutable")));
|
||||||
|
|||||||
@@ -783,7 +783,7 @@ mod tests {
|
|||||||
|
|
||||||
let mut diag = crate::ast::diagnostics::Diagnostics::new();
|
let mut diag = crate::ast::diagnostics::Diagnostics::new();
|
||||||
// fixed_scope_idx = 0 means scope 0 is frozen (Global)
|
// fixed_scope_idx = 0 means scope 0 is frozen (Global)
|
||||||
let result = Binder::bind_root(initial_scopes, 1, 0, &expanded, &mut diag);
|
let result = Binder::bind_root(initial_scopes, 1, 0, Rc::new(RefCell::new(HashMap::new())), &expanded, &mut diag);
|
||||||
assert!(
|
assert!(
|
||||||
result.is_ok(),
|
result.is_ok(),
|
||||||
"Should find global '*' Error: {:?}",
|
"Should find global '*' Error: {:?}",
|
||||||
@@ -807,7 +807,7 @@ mod tests {
|
|||||||
let expanded = expander.expand(untyped).unwrap();
|
let expanded = expander.expand(untyped).unwrap();
|
||||||
|
|
||||||
let mut diag = crate::ast::diagnostics::Diagnostics::new();
|
let mut diag = crate::ast::diagnostics::Diagnostics::new();
|
||||||
let result = Binder::bind_root(vec![], 0, 0, &expanded, &mut diag);
|
let result = Binder::bind_root(vec![], 0, 0, Rc::new(RefCell::new(HashMap::new())), &expanded, &mut diag);
|
||||||
assert!(result.is_ok(), "Hygiene failed: {:?}", result.err());
|
assert!(result.is_ok(), "Hygiene failed: {:?}", result.err());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -827,7 +827,7 @@ mod tests {
|
|||||||
let expanded = expander.expand(untyped).unwrap();
|
let expanded = expander.expand(untyped).unwrap();
|
||||||
|
|
||||||
let mut diag = crate::ast::diagnostics::Diagnostics::new();
|
let mut diag = crate::ast::diagnostics::Diagnostics::new();
|
||||||
let result = Binder::bind_root(vec![], 0, 0, &expanded, &mut diag);
|
let result = Binder::bind_root(vec![], 0, 0, Rc::new(RefCell::new(HashMap::new())), &expanded, &mut diag);
|
||||||
assert!(
|
assert!(
|
||||||
result.is_ok(),
|
result.is_ok(),
|
||||||
"Explicit Hygiene with Backticks failed: {:?}",
|
"Explicit Hygiene with Backticks failed: {:?}",
|
||||||
|
|||||||
@@ -717,7 +717,7 @@ mod tests {
|
|||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.unwrap_err(),
|
result.unwrap_err(),
|
||||||
"Statement 'def' cannot be used as an expression.\nCannot define 'x': Scope 0 is frozen/immutable.\nCannot destructure type int as a tuple/vector"
|
"Statement 'def' cannot be used as an expression.\nCannot destructure type int as a tuple/vector"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+26
-10
@@ -112,6 +112,7 @@ impl FunctionRegistry for EnvFunctionRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct RuntimeMacroEvaluator {
|
struct RuntimeMacroEvaluator {
|
||||||
|
global_names: Rc<RefCell<HashMap<Symbol, (GlobalIdx, Identity)>>>,
|
||||||
root_scopes: Rc<RefCell<Vec<crate::ast::compiler::binder::CompilerScope>>>,
|
root_scopes: Rc<RefCell<Vec<crate::ast::compiler::binder::CompilerScope>>>,
|
||||||
root_slot_count: Rc<RefCell<u32>>,
|
root_slot_count: Rc<RefCell<u32>>,
|
||||||
global_types: Rc<RefCell<HashMap<GlobalIdx, StaticType>>>,
|
global_types: Rc<RefCell<HashMap<GlobalIdx, StaticType>>>,
|
||||||
@@ -135,7 +136,7 @@ impl MacroEvaluator for RuntimeMacroEvaluator {
|
|||||||
let initial_scopes = self.root_scopes.borrow().clone();
|
let initial_scopes = self.root_scopes.borrow().clone();
|
||||||
let initial_slot_count = *self.root_slot_count.borrow();
|
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, captures, _, _) = Binder::bind_root(initial_scopes, initial_slot_count, self.fixed_scope_idx, self.global_names.clone(), node, &mut diag)?;
|
||||||
let bound_ast = crate::ast::compiler::captures::CapturePass::apply(bound_ast, &captures);
|
let bound_ast = crate::ast::compiler::captures::CapturePass::apply(bound_ast, &captures);
|
||||||
let checker = TypeChecker::new(self.global_types.clone());
|
let checker = TypeChecker::new(self.global_types.clone());
|
||||||
let typed_ast = checker.check(&bound_ast, &[], &mut diag);
|
let typed_ast = checker.check(&bound_ast, &[], &mut diag);
|
||||||
@@ -186,6 +187,8 @@ impl Environment {
|
|||||||
|
|
||||||
let mut env = env;
|
let mut env = env;
|
||||||
env.fixed_scope_idx = 0;
|
env.fixed_scope_idx = 0;
|
||||||
|
// Push the first mutable user scope (Level 1)
|
||||||
|
env.root_scopes.borrow_mut().push(crate::ast::compiler::binder::CompilerScope::new());
|
||||||
|
|
||||||
// Automatically add standard search paths (CWD and CWD/rtl)
|
// Automatically add standard search paths (CWD and CWD/rtl)
|
||||||
if let Ok(cwd) = std::env::current_dir() {
|
if let Ok(cwd) = std::env::current_dir() {
|
||||||
@@ -223,6 +226,7 @@ impl Environment {
|
|||||||
|
|
||||||
fn get_expander(&self) -> MacroExpander<RuntimeMacroEvaluator> {
|
fn get_expander(&self) -> MacroExpander<RuntimeMacroEvaluator> {
|
||||||
let evaluator = RuntimeMacroEvaluator {
|
let evaluator = RuntimeMacroEvaluator {
|
||||||
|
global_names: self.global_names.clone(),
|
||||||
root_scopes: self.root_scopes.clone(),
|
root_scopes: self.root_scopes.clone(),
|
||||||
root_slot_count: self.root_slot_count.clone(),
|
root_slot_count: self.root_slot_count.clone(),
|
||||||
global_types: self.global_types.clone(),
|
global_types: self.global_types.clone(),
|
||||||
@@ -398,10 +402,23 @@ impl Environment {
|
|||||||
match &node.kind {
|
match &node.kind {
|
||||||
UntypedKind::Def { target, .. } => {
|
UntypedKind::Def { target, .. } => {
|
||||||
if let UntypedKind::Identifier(sym) = &target.kind {
|
if let UntypedKind::Identifier(sym) = &target.kind {
|
||||||
let mut names = self.global_names.borrow_mut();
|
let mut root_scopes = self.root_scopes.borrow_mut();
|
||||||
if !names.contains_key(sym) {
|
let last_idx = root_scopes.len() - 1;
|
||||||
let idx = GlobalIdx(names.len() as u32);
|
let current_scope = &mut root_scopes[last_idx];
|
||||||
names.insert(sym.clone(), (idx, node.identity.clone()));
|
|
||||||
|
if !current_scope.locals.contains_key(sym) {
|
||||||
|
let mut slot_count = self.root_slot_count.borrow_mut();
|
||||||
|
let slot = crate::ast::compiler::bound_nodes::LocalSlot(*slot_count);
|
||||||
|
current_scope.locals.insert(
|
||||||
|
sym.clone(),
|
||||||
|
crate::ast::compiler::binder::LocalInfo {
|
||||||
|
addr: Address::Local(slot),
|
||||||
|
identity: node.identity.clone(),
|
||||||
|
_ty: StaticType::Any,
|
||||||
|
purity: Purity::Impure,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
*slot_count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -442,7 +459,7 @@ impl Environment {
|
|||||||
let initial_slot_count = *self.root_slot_count.borrow();
|
let initial_slot_count = *self.root_slot_count.borrow();
|
||||||
|
|
||||||
let (bound_ast, captures, final_scopes, final_slot_count) =
|
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) {
|
match Binder::bind_root(initial_scopes, initial_slot_count, self.fixed_scope_idx, self.global_names.clone(), &expanded_ast, diagnostics) {
|
||||||
Ok(res) => res,
|
Ok(res) => res,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
diagnostics.push_error(e, None);
|
diagnostics.push_error(e, None);
|
||||||
@@ -459,10 +476,9 @@ impl Environment {
|
|||||||
// Pre-allocate global slots to prevent out-of-bounds during specialization/optimization
|
// Pre-allocate global slots to prevent out-of-bounds during specialization/optimization
|
||||||
{
|
{
|
||||||
let mut values = self.global_values.borrow_mut();
|
let mut values = self.global_values.borrow_mut();
|
||||||
let names = self.global_names.borrow();
|
let count = *self.root_slot_count.borrow();
|
||||||
let max_idx = names.values().map(|(idx, _)| idx.0).max().unwrap_or(0);
|
if (count as usize) > values.len() {
|
||||||
if (max_idx as usize) >= values.len() {
|
values.resize(count as usize, Value::Void);
|
||||||
values.resize((max_idx + 1) as usize, Value::Void);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user