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),
|
||||
},
|
||||
|
||||
+7
-2
@@ -281,7 +281,7 @@ impl VM {
|
||||
globals[idx] = val.clone();
|
||||
Ok(val)
|
||||
}
|
||||
BoundKind::DefDestructure { pattern, value } => {
|
||||
BoundKind::Destructure { pattern, value } => {
|
||||
let val = self.eval_internal(obs, value)?;
|
||||
let mut offset = 0;
|
||||
|
||||
@@ -289,7 +289,7 @@ impl VM {
|
||||
if let Some(vals) = val.as_slice() {
|
||||
self.unpack(pattern, vals, &mut offset)?;
|
||||
} else {
|
||||
self.unpack(pattern, &[val.clone()], &mut offset)?;
|
||||
self.unpack(pattern, std::slice::from_ref(&val), &mut offset)?;
|
||||
}
|
||||
Ok(val)
|
||||
}
|
||||
@@ -704,6 +704,11 @@ impl VM {
|
||||
globals[idx] = val;
|
||||
Ok(())
|
||||
}
|
||||
BoundKind::Set { addr, .. } => {
|
||||
let val = values.get(*offset).cloned().unwrap_or(Value::Void);
|
||||
*offset += 1;
|
||||
self.set_value(*addr, val)
|
||||
}
|
||||
BoundKind::Tuple { elements } => {
|
||||
if let Some(sub_values) = values.get(*offset).and_then(|v| v.as_slice()) {
|
||||
*offset += 1;
|
||||
|
||||
@@ -337,4 +337,36 @@ mod tests {
|
||||
panic!("Expected tuple return from def, got {:?}", res);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_assign_destructuring() {
|
||||
// 1. Simple assignment destructuring
|
||||
{
|
||||
let env = Environment::new();
|
||||
let source_simple = "(do (def a 0) (def b 0) (assign [a b] [10 20]) (+ a b))";
|
||||
assert_eq!(format!("{}", env.run_script(source_simple).unwrap()), "30");
|
||||
}
|
||||
|
||||
// 2. Nested assignment destructuring
|
||||
{
|
||||
let env = Environment::new();
|
||||
let source_nested =
|
||||
"(do (def a 0) (def b 0) (def c 0) (assign [a [b c]] [1 [2 3]]) (+ a (+ b c)))";
|
||||
assert_eq!(format!("{}", env.run_script(source_nested).unwrap()), "6");
|
||||
}
|
||||
|
||||
// 3. Assignment returns the assigned value
|
||||
{
|
||||
let env = Environment::new();
|
||||
let source_return = "(do (def a 0) (def b 0) (assign [a b] [5 6]))";
|
||||
let res = env.run_script(source_return).unwrap();
|
||||
if let Value::Tuple(vals) = res {
|
||||
assert_eq!(vals.len(), 2);
|
||||
assert_eq!(format!("{}", vals[0]), "5");
|
||||
assert_eq!(format!("{}", vals[1]), "6");
|
||||
} else {
|
||||
panic!("Expected tuple return from assign, got {:?}", res);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user