Refactor: Use generic Define bound kind
This commit consolidates `DefLocal` and `DefGlobal` into a single `Define` bound kind. This simplifies the AST and makes it more consistent. It also introduces `DeclarationKind` to differentiate between variable and parameter definitions.
This commit is contained in:
+76
-84
@@ -1,4 +1,4 @@
|
||||
use crate::ast::compiler::bound_nodes::{Address, BoundKind, BoundNode};
|
||||
use crate::ast::compiler::bound_nodes::{Address, BoundKind, BoundNode, DeclarationKind};
|
||||
use crate::ast::nodes::{Node, Symbol, UntypedKind};
|
||||
use crate::ast::types::{Identity, StaticType};
|
||||
use std::cell::RefCell;
|
||||
@@ -110,6 +110,26 @@ impl Binder {
|
||||
binder.bind(node)
|
||||
}
|
||||
|
||||
fn declare_variable(
|
||||
&mut self,
|
||||
name: &Symbol,
|
||||
_kind: crate::ast::compiler::bound_nodes::DeclarationKind,
|
||||
) -> Result<Address, String> {
|
||||
if self.functions.len() == 1 {
|
||||
let mut globals = self.globals.borrow_mut();
|
||||
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> {
|
||||
match &node.kind {
|
||||
UntypedKind::Nop => Ok(self.make_node(node.identity.clone(), BoundKind::Nop)),
|
||||
@@ -157,55 +177,33 @@ impl Binder {
|
||||
UntypedKind::Def { target, value } => {
|
||||
// Special case: Single identifier (to support recursion)
|
||||
if let UntypedKind::Parameter(ref name) = target.kind {
|
||||
let slot_or_idx = if self.functions.len() == 1 {
|
||||
let mut globals = self.globals.borrow_mut();
|
||||
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);
|
||||
idx
|
||||
} else {
|
||||
let current_fn = self.functions.last_mut().unwrap();
|
||||
current_fn.scope.define(name)?
|
||||
};
|
||||
|
||||
let addr = self.declare_variable(
|
||||
name,
|
||||
crate::ast::compiler::bound_nodes::DeclarationKind::Variable,
|
||||
)?;
|
||||
let val_node = self.bind(value)?;
|
||||
let captured_by = self
|
||||
.capture_map
|
||||
.get(&target.identity)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
if self.functions.len() == 1 {
|
||||
Ok(self.make_node(
|
||||
node.identity.clone(),
|
||||
BoundKind::DefGlobal {
|
||||
name: name.clone(),
|
||||
global_index: slot_or_idx,
|
||||
value: Box::new(val_node),
|
||||
},
|
||||
))
|
||||
} else {
|
||||
let captured_by = self
|
||||
.capture_map
|
||||
.get(&target.identity)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
Ok(self.make_node(
|
||||
node.identity.clone(),
|
||||
BoundKind::DefLocal {
|
||||
name: name.clone(),
|
||||
slot: slot_or_idx,
|
||||
value: Box::new(val_node),
|
||||
captured_by,
|
||||
},
|
||||
))
|
||||
}
|
||||
Ok(self.make_node(
|
||||
node.identity.clone(),
|
||||
BoundKind::Define {
|
||||
name: name.clone(),
|
||||
addr,
|
||||
kind: crate::ast::compiler::bound_nodes::DeclarationKind::Variable,
|
||||
value: Box::new(val_node),
|
||||
captured_by,
|
||||
},
|
||||
))
|
||||
} else {
|
||||
// Complex Destructuring Pattern
|
||||
// NOTE: Destructuring definitions are NOT recursive by default
|
||||
// (the variables are only available AFTER the definition)
|
||||
let val_node = self.bind(value)?;
|
||||
let target_node = self.bind_pattern(target)?;
|
||||
let target_node = self.bind_pattern(target, DeclarationKind::Variable)?;
|
||||
|
||||
Ok(self.make_node(
|
||||
node.identity.clone(),
|
||||
@@ -246,7 +244,7 @@ impl Binder {
|
||||
self.functions.push(FunctionCompiler::new());
|
||||
|
||||
// 1. Bind the parameter pattern/tuple
|
||||
let params_bound = self.bind_pattern(params)?;
|
||||
let params_bound = self.bind_pattern(params, DeclarationKind::Parameter)?;
|
||||
|
||||
// 2. Bind the body
|
||||
let body_bound = self.bind(body)?;
|
||||
@@ -256,7 +254,10 @@ impl Binder {
|
||||
// 3. Static optimization: count total parameters needed in flat argument list
|
||||
fn count_params(node: &BoundNode) -> Option<u32> {
|
||||
match &node.kind {
|
||||
BoundKind::Parameter { .. } => Some(1),
|
||||
BoundKind::Define {
|
||||
kind: DeclarationKind::Parameter,
|
||||
..
|
||||
} => Some(1),
|
||||
BoundKind::Tuple { elements } => {
|
||||
let mut total = 0;
|
||||
for e in elements {
|
||||
@@ -411,46 +412,35 @@ impl Binder {
|
||||
Err(format!("Undefined variable '{}'", sym.name))
|
||||
}
|
||||
|
||||
fn bind_pattern(&mut self, node: &Node<UntypedKind>) -> Result<BoundNode, String> {
|
||||
fn bind_pattern(
|
||||
&mut self,
|
||||
node: &Node<UntypedKind>,
|
||||
kind: DeclarationKind,
|
||||
) -> Result<BoundNode, String> {
|
||||
match &node.kind {
|
||||
UntypedKind::Parameter(sym) => {
|
||||
if self.functions.len() == 1 {
|
||||
// Global Definition in pattern
|
||||
let mut globals = self.globals.borrow_mut();
|
||||
if globals.contains_key(sym) {
|
||||
return Err(format!(
|
||||
"Global variable '{}' is already defined.",
|
||||
sym.name
|
||||
));
|
||||
}
|
||||
let idx = globals.len() as u32;
|
||||
globals.insert(sym.clone(), idx);
|
||||
// For Global Destructuring, we return a DefGlobal with Nop value as it's a pattern part
|
||||
Ok(self.make_node(
|
||||
node.identity.clone(),
|
||||
BoundKind::DefGlobal {
|
||||
name: sym.clone(),
|
||||
global_index: idx,
|
||||
value: Box::new(self.make_node(node.identity.clone(), BoundKind::Nop)),
|
||||
},
|
||||
))
|
||||
} else {
|
||||
// Local Parameter/Definition in pattern
|
||||
let current_fn = self.functions.last_mut().unwrap();
|
||||
let slot = current_fn.scope.define(sym)?;
|
||||
Ok(self.make_node(
|
||||
node.identity.clone(),
|
||||
BoundKind::Parameter {
|
||||
name: sym.clone(),
|
||||
slot,
|
||||
},
|
||||
))
|
||||
}
|
||||
let addr = self.declare_variable(sym, kind)?;
|
||||
let captured_by = self
|
||||
.capture_map
|
||||
.get(&node.identity)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(self.make_node(
|
||||
node.identity.clone(),
|
||||
BoundKind::Define {
|
||||
name: sym.clone(),
|
||||
addr,
|
||||
kind,
|
||||
value: Box::new(self.make_node(node.identity.clone(), BoundKind::Nop)),
|
||||
captured_by,
|
||||
},
|
||||
))
|
||||
}
|
||||
UntypedKind::Tuple { elements } => {
|
||||
let mut bound_elems = Vec::new();
|
||||
for e in elements {
|
||||
bound_elems.push(self.bind_pattern(e)?);
|
||||
bound_elems.push(self.bind_pattern(e, kind)?);
|
||||
}
|
||||
Ok(self.make_node(
|
||||
node.identity.clone(),
|
||||
@@ -518,18 +508,19 @@ mod tests {
|
||||
let globals = Rc::new(RefCell::new(HashMap::new()));
|
||||
let bound = Binder::bind_root(globals, &untyped).unwrap();
|
||||
|
||||
// Structure: Lambda -> Block -> [ DefLocal(x), DefLocal(f), Get(x) ]
|
||||
// Structure: Lambda -> Block -> [ Define(x), Define(f), Get(x) ]
|
||||
if let BoundKind::Lambda { body, .. } = &bound.kind {
|
||||
if let BoundKind::Block { exprs } = &body.kind {
|
||||
let x_decl = &exprs[0];
|
||||
if let BoundKind::DefLocal { captured_by, .. } = &x_decl.kind {
|
||||
if let BoundKind::Define { captured_by, addr, .. } = &x_decl.kind {
|
||||
assert!(matches!(addr, Address::Local(_)));
|
||||
assert!(
|
||||
!captured_by.is_empty(),
|
||||
"Variable 'x' should have capturers because it is used in lambda 'f'"
|
||||
);
|
||||
} else {
|
||||
panic!(
|
||||
"First expression in block should be DefLocal, got {:?}",
|
||||
"First expression in block should be Define, got {:?}",
|
||||
x_decl.kind
|
||||
);
|
||||
}
|
||||
@@ -553,13 +544,14 @@ mod tests {
|
||||
if let BoundKind::Lambda { body, .. } = &bound.kind {
|
||||
if let BoundKind::Block { exprs } = &body.kind {
|
||||
let x_decl = &exprs[0];
|
||||
if let BoundKind::DefLocal { captured_by, .. } = &x_decl.kind {
|
||||
if let BoundKind::Define { captured_by, addr, .. } = &x_decl.kind {
|
||||
assert!(matches!(addr, Address::Local(_)));
|
||||
assert!(
|
||||
captured_by.is_empty(),
|
||||
"Variable 'x' should NOT have any capturers"
|
||||
);
|
||||
} else {
|
||||
panic!("First expression should be DefLocal");
|
||||
panic!("First expression should be Define");
|
||||
}
|
||||
} else {
|
||||
panic!("Lambda body should be a Block");
|
||||
|
||||
Reference in New Issue
Block a user