Refactor Destructure to handle assignments
Renames `DefDestructure` to `Destructure` to better reflect its use in both definitions and assignments. Introduces `bind_assign_pattern` to handle assignment destructuring in the binder. Adds `test_assign_destructuring` to verify assignment destructuring functionality.
This commit is contained in:
@@ -178,11 +178,11 @@ impl<'a> Analyzer<'a> {
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::DefDestructure { pattern, value } => {
|
||||
BoundKind::Destructure { pattern, value } => {
|
||||
let pat_m = self.visit(Rc::new((**pattern).clone()));
|
||||
let val_m = self.visit(Rc::new((**value).clone()));
|
||||
(
|
||||
BoundKind::DefDestructure {
|
||||
BoundKind::Destructure {
|
||||
pattern: Box::new(pat_m),
|
||||
value: Box::new(val_m),
|
||||
},
|
||||
|
||||
@@ -209,7 +209,7 @@ impl Binder {
|
||||
|
||||
Ok(self.make_node(
|
||||
node.identity.clone(),
|
||||
BoundKind::DefDestructure {
|
||||
BoundKind::Destructure {
|
||||
pattern: Box::new(target_node),
|
||||
value: Box::new(val_node),
|
||||
},
|
||||
@@ -230,7 +230,14 @@ impl Binder {
|
||||
},
|
||||
))
|
||||
} else {
|
||||
Err("Assignment target must be an identifier".to_string())
|
||||
let target_node = self.bind_assign_pattern(target)?;
|
||||
Ok(self.make_node(
|
||||
node.identity.clone(),
|
||||
BoundKind::Destructure {
|
||||
pattern: Box::new(target_node),
|
||||
value: Box::new(val_node),
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,6 +463,34 @@ impl Binder {
|
||||
}
|
||||
}
|
||||
|
||||
fn bind_assign_pattern(&mut self, node: &Node<UntypedKind>) -> Result<BoundNode, String> {
|
||||
match &node.kind {
|
||||
UntypedKind::Identifier(sym) => {
|
||||
let addr = self.resolve_variable(sym)?;
|
||||
Ok(self.make_node(
|
||||
node.identity.clone(),
|
||||
BoundKind::Set {
|
||||
addr,
|
||||
value: Box::new(self.make_node(node.identity.clone(), BoundKind::Nop)),
|
||||
},
|
||||
))
|
||||
}
|
||||
UntypedKind::Tuple { elements } => {
|
||||
let mut bound_elems = Vec::new();
|
||||
for e in elements {
|
||||
bound_elems.push(self.bind_assign_pattern(e)?);
|
||||
}
|
||||
Ok(self.make_node(
|
||||
node.identity.clone(),
|
||||
BoundKind::Tuple {
|
||||
elements: bound_elems,
|
||||
},
|
||||
))
|
||||
}
|
||||
_ => Err(format!("Invalid node in assignment pattern: {:?}", node.kind)),
|
||||
}
|
||||
}
|
||||
|
||||
fn make_node(&self, identity: Identity, kind: BoundKind<()>) -> BoundNode {
|
||||
Node {
|
||||
identity,
|
||||
|
||||
@@ -82,8 +82,8 @@ pub enum BoundKind<T = ()> {
|
||||
value: Box<BoundNode<T>>,
|
||||
},
|
||||
|
||||
/// A destructuring definition (can be local or global depending on the pattern)
|
||||
DefDestructure {
|
||||
/// A destructuring operation (can be a definition or an assignment depending on the pattern)
|
||||
Destructure {
|
||||
pattern: Box<BoundNode<T>>,
|
||||
value: Box<BoundNode<T>>,
|
||||
},
|
||||
@@ -194,11 +194,11 @@ where
|
||||
},
|
||||
) => na == nb && ga == gb && va == vb,
|
||||
(
|
||||
BoundKind::DefDestructure {
|
||||
BoundKind::Destructure {
|
||||
pattern: pa,
|
||||
value: va,
|
||||
},
|
||||
BoundKind::DefDestructure {
|
||||
BoundKind::Destructure {
|
||||
pattern: pb,
|
||||
value: vb,
|
||||
},
|
||||
@@ -265,7 +265,7 @@ impl<T> BoundKind<T> {
|
||||
BoundKind::DefGlobal {
|
||||
name, global_index, ..
|
||||
} => format!("DEF_GLOBAL({}, Idx:{})", name.name, global_index),
|
||||
BoundKind::DefDestructure { .. } => "DEF_DESTRUCTURE".to_string(),
|
||||
BoundKind::Destructure { .. } => "DESTRUCTURE".to_string(),
|
||||
BoundKind::Lambda {
|
||||
params, upvalues, ..
|
||||
} => {
|
||||
|
||||
@@ -120,8 +120,8 @@ impl Dumper {
|
||||
self.indent -= 1;
|
||||
}
|
||||
|
||||
BoundKind::DefDestructure { pattern, value } => {
|
||||
self.log("DefDestructure", node);
|
||||
BoundKind::Destructure { pattern, value } => {
|
||||
self.log("Destructure", node);
|
||||
self.indent += 1;
|
||||
self.write_indent();
|
||||
self.output.push_str("Pattern:\n");
|
||||
|
||||
@@ -95,6 +95,34 @@ impl Optimizer {
|
||||
current
|
||||
}
|
||||
|
||||
fn collect_pattern_usage(&self, node: &AnalyzedNode, info: &mut UsageInfo) {
|
||||
match &node.kind {
|
||||
BoundKind::Parameter { slot, .. } => {
|
||||
info.assigned_locals.insert(*slot);
|
||||
}
|
||||
BoundKind::DefGlobal { global_index, .. } => {
|
||||
info.assigned_globals.insert(*global_index);
|
||||
}
|
||||
BoundKind::Set { addr, .. } => {
|
||||
match addr {
|
||||
Address::Local(slot) => {
|
||||
info.assigned_locals.insert(*slot);
|
||||
}
|
||||
Address::Global(idx) => {
|
||||
info.assigned_globals.insert(*idx);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
BoundKind::Tuple { elements } => {
|
||||
for el in elements {
|
||||
self.collect_pattern_usage(el, info);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_node(
|
||||
&self,
|
||||
node: AnalyzedNode,
|
||||
@@ -561,6 +589,31 @@ impl Optimizer {
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Destructure {
|
||||
ref pattern,
|
||||
ref value,
|
||||
} => {
|
||||
let val_opt = Box::new(self.visit_node((**value).clone(), sub, path));
|
||||
let pat_opt = Box::new(self.visit_node((**pattern).clone(), sub, path));
|
||||
|
||||
let mut info = UsageInfo::default();
|
||||
self.collect_pattern_usage(&pat_opt, &mut info);
|
||||
for slot in info.assigned_locals {
|
||||
sub.locals.remove(&slot);
|
||||
}
|
||||
for idx in info.assigned_globals {
|
||||
sub.globals.remove(&idx);
|
||||
}
|
||||
|
||||
(
|
||||
BoundKind::Destructure {
|
||||
pattern: pat_opt,
|
||||
value: val_opt,
|
||||
},
|
||||
node.ty.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::Tuple { elements } => {
|
||||
let elements = elements
|
||||
.into_iter()
|
||||
@@ -808,6 +861,10 @@ impl Optimizer {
|
||||
|
||||
fn collect_usage(&self, node: &AnalyzedNode, info: &mut UsageInfo) {
|
||||
match &node.kind {
|
||||
BoundKind::Destructure { pattern, value } => {
|
||||
self.collect_pattern_usage(pattern, info);
|
||||
self.collect_usage(value, info);
|
||||
}
|
||||
BoundKind::Constant(v) => {
|
||||
if let Value::Object(obj) = v
|
||||
&& let Some(closure) = obj.as_any().downcast_ref::<Closure>()
|
||||
|
||||
@@ -118,7 +118,7 @@ impl TCO {
|
||||
global_index: *global_index,
|
||||
value: Box::new(Self::transform(Rc::new((**value).clone()), false)),
|
||||
},
|
||||
BoundKind::DefDestructure { pattern, value } => BoundKind::DefDestructure {
|
||||
BoundKind::Destructure { pattern, value } => BoundKind::Destructure {
|
||||
pattern: Box::new(Self::transform(Rc::new((**pattern).clone()), false)),
|
||||
value: Box::new(Self::transform(Rc::new((**value).clone()), false)),
|
||||
},
|
||||
|
||||
@@ -163,6 +163,23 @@ impl TypeChecker {
|
||||
specialized_ty.clone(),
|
||||
)
|
||||
}
|
||||
BoundKind::Set { addr, value: _value } => {
|
||||
// In an assignment pattern, 'value' is just a Nop placeholder from the Binder.
|
||||
// We update the type of the address (if it's a local or global) to match the specialized type.
|
||||
// Note: For now, we assume assignments are compatible if types were already inferred,
|
||||
// or we could add a check here against existing type.
|
||||
(
|
||||
BoundKind::Set {
|
||||
addr,
|
||||
value: Box::new(Node {
|
||||
identity: node.identity.clone(),
|
||||
kind: BoundKind::Nop,
|
||||
ty: specialized_ty.clone(),
|
||||
}),
|
||||
},
|
||||
specialized_ty.clone(),
|
||||
)
|
||||
}
|
||||
BoundKind::Tuple { elements } => {
|
||||
let mut typed_elements = Vec::new();
|
||||
let mut elem_types = Vec::new();
|
||||
@@ -282,12 +299,12 @@ impl TypeChecker {
|
||||
)
|
||||
}
|
||||
|
||||
BoundKind::DefDestructure { pattern, value } => {
|
||||
BoundKind::Destructure { pattern, value } => {
|
||||
let val_typed = self.check_node(*value, ctx)?;
|
||||
let pat_typed = self.check_params(*pattern, &val_typed.ty, ctx)?;
|
||||
let ty = val_typed.ty.clone();
|
||||
(
|
||||
BoundKind::DefDestructure {
|
||||
BoundKind::Destructure {
|
||||
pattern: Box::new(pat_typed),
|
||||
value: Box::new(val_typed),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user