Refactor function compiler scope handling

Introduce a `ScopeKind` enum to distinguish between root and local
scopes.
This allows the `FunctionCompiler` to correctly handle global variable
declarations in the root scope and local variables in other scopes.
The `define_variable` method is introduced to encapsulate this logic.
This commit is contained in:
Michael Schimmel
2026-02-25 11:19:08 +01:00
parent d109cb2018
commit 82daf03522
+40 -19
View File
@@ -52,16 +52,49 @@ impl CompilerScope {
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ScopeKind {
Root,
Local,
}
struct FunctionCompiler { struct FunctionCompiler {
scope: CompilerScope, scope: CompilerScope,
upvalues: Vec<Address>, upvalues: Vec<Address>,
kind: ScopeKind,
} }
impl FunctionCompiler { impl FunctionCompiler {
fn new() -> Self { fn new(kind: ScopeKind) -> Self {
Self { Self {
scope: CompilerScope::new(), scope: CompilerScope::new(),
upvalues: Vec::new(), upvalues: Vec::new(),
kind,
}
}
fn define_variable(
&mut self,
name: &Symbol,
globals: &Rc<RefCell<HashMap<Symbol, u32>>>,
) -> Result<Address, String> {
match self.kind {
ScopeKind::Root => {
let mut globals_map = globals.borrow_mut();
if globals_map.contains_key(name) {
return Err(format!(
"Global variable '{}' is already defined.",
name.name
));
}
let idx = globals_map.len() as u32;
globals_map.insert(name.clone(), idx);
Ok(Address::Global(idx))
}
ScopeKind::Local => {
let slot = self.scope.define(name)?;
Ok(Address::Local(slot))
}
} }
} }
@@ -97,7 +130,9 @@ impl Binder {
globals, globals,
capture_map: captures, capture_map: captures,
}; };
binder.functions.push(FunctionCompiler::new()); binder
.functions
.push(FunctionCompiler::new(ScopeKind::Root));
binder binder
} }
@@ -115,22 +150,8 @@ impl Binder {
name: &Symbol, name: &Symbol,
_kind: crate::ast::compiler::bound_nodes::DeclarationKind, _kind: crate::ast::compiler::bound_nodes::DeclarationKind,
) -> Result<Address, String> { ) -> Result<Address, String> {
if self.functions.len() == 1 { let current_fn = self.functions.last_mut().unwrap();
let mut globals = self.globals.borrow_mut(); current_fn.define_variable(name, &self.globals)
if globals.contains_key(name) {
return Err(format!(
"Global variable '{}' is already defined.",
name.name
));
}
let idx = globals.len() as u32;
globals.insert(name.clone(), idx);
Ok(Address::Global(idx))
} else {
let current_fn = self.functions.last_mut().unwrap();
let slot = current_fn.scope.define(name)?;
Ok(Address::Local(slot))
}
} }
pub fn bind(&mut self, node: &Node<UntypedKind>) -> Result<BoundNode, String> { pub fn bind(&mut self, node: &Node<UntypedKind>) -> Result<BoundNode, String> {
@@ -244,7 +265,7 @@ impl Binder {
UntypedKind::Lambda { params, body } => { UntypedKind::Lambda { params, body } => {
let identity = node.identity.clone(); let identity = node.identity.clone();
self.functions.push(FunctionCompiler::new()); self.functions.push(FunctionCompiler::new(ScopeKind::Local));
// 1. Bind the parameter pattern/tuple // 1. Bind the parameter pattern/tuple
let params_bound = self.bind_pattern(params, DeclarationKind::Parameter)?; let params_bound = self.bind_pattern(params, DeclarationKind::Parameter)?;