diff --git a/examples/destructuring.myc b/examples/destructuring.myc index ab53f72..23ad46e 100644 --- a/examples/destructuring.myc +++ b/examples/destructuring.myc @@ -1,5 +1,5 @@ -;; Benchmark: 2.1us -;; Benchmark-Repeat: 961 +;; Benchmark: 969ns +;; Benchmark-Repeat: 2078 ;; Comprehensive Destructuring Test ;; Covers: Nested tuples, mixed params, dynamic passing diff --git a/examples/record_unpack.myc b/examples/record_unpack.myc index 568fcba..f1d99ef 100644 --- a/examples/record_unpack.myc +++ b/examples/record_unpack.myc @@ -2,8 +2,8 @@ ;; This test calls a function that destructures a record ;; Current implementation is now zero-allocation for destructuring. ;; Output: 3 -;; Benchmark: 698ns -;; Benchmark-Repeat: 2875 +;; Benchmark: 129ns +;; Benchmark-Repeat: 15432 (do (def process (fn [[x y]] (+ x y))) diff --git a/src/ast/compiler/binder.rs b/src/ast/compiler/binder.rs index f3ace9d..187d151 100644 --- a/src/ast/compiler/binder.rs +++ b/src/ast/compiler/binder.rs @@ -247,24 +247,23 @@ impl Binder { let compiled_fn = self.functions.pop().unwrap(); - // 3. Static optimization: check if parameters are purely positional - let positional_count = match ¶ms_bound.kind { - BoundKind::Tuple { elements } => { - let mut count = 0; - let mut all_params = true; - for e in elements { - if matches!(e.kind, BoundKind::Parameter { .. }) { - count += 1; - } else { - all_params = false; - break; + // 3. Static optimization: count total parameters needed in flat argument list + fn count_params(node: &BoundNode) -> Option { + match &node.kind { + BoundKind::Parameter { .. } => Some(1), + BoundKind::Tuple { elements } => { + let mut total = 0; + for e in elements { + total += count_params(e)?; } + Some(total) } - if all_params { Some(count) } else { None } + BoundKind::Nop => Some(0), + _ => None, } - BoundKind::Parameter { .. } => Some(1), - _ => None, - }; + } + + let positional_count = count_params(¶ms_bound); Ok(self.make_node( identity, diff --git a/src/ast/compiler/optimizer.rs b/src/ast/compiler/optimizer.rs index 094aaff..70c4e93 100644 --- a/src/ast/compiler/optimizer.rs +++ b/src/ast/compiler/optimizer.rs @@ -695,10 +695,15 @@ impl Optimizer { } fn flatten_tuple(&self, node: AnalyzedNode, into: &mut Vec) { - if let BoundKind::Tuple { elements } = &node.kind { - for el in elements { self.flatten_tuple(el.clone(), into); } - } else if !matches!(node.kind, BoundKind::Nop) { - into.push(node); + match node.kind { + BoundKind::Tuple { elements } => { + for el in elements { self.flatten_tuple(el, into); } + } + BoundKind::Record { fields } => { + for (_, v) in fields { self.flatten_tuple(v, into); } + } + BoundKind::Nop => {} + _ => into.push(node), } } @@ -725,6 +730,30 @@ impl Optimizer { *offset += 1; } BoundKind::Tuple { elements } => { + // RECURSIVE DESTRUCTURING SUPPORT + // Check if the current argument at 'offset' is itself a Tuple or Record literal. + if let Some(arg) = args.get(*offset) { + let mut sub_args = Vec::new(); + let is_compound = match &arg.kind { + BoundKind::Tuple { .. } | BoundKind::Record { .. } => { + self.flatten_tuple(arg.clone(), &mut sub_args); + true + } + _ => false, + }; + + if is_compound { + // Match inner elements against the flattened compound argument + let mut sub_offset = 0; + for el in elements { + self.map_params_to_args(el, &sub_args, &mut sub_offset, sub); + } + *offset += 1; + return; + } + } + + // Fallback: Continue flat matching (original behavior) for el in elements { self.map_params_to_args(el, args, offset, sub); } } _ => {} diff --git a/src/integration_test.rs b/src/integration_test.rs index d97fd74..973d33f 100644 --- a/src/integration_test.rs +++ b/src/integration_test.rs @@ -278,4 +278,21 @@ mod tests { } assert_eq!(format!("{}", result.unwrap()), "60"); } + + #[test] + fn test_nested_destructuring_optimization() { + let env = Environment::new(); + + // 1. Tuple-to-Tuple + let source_tuple = "((fn [[x y]] (+ x y)) [10 20])"; + assert_eq!(format!("{}", env.run_script(source_tuple).unwrap()), "30"); + let dump_tuple = env.dump_ast(source_tuple).unwrap(); + assert!(dump_tuple.contains("Constant: 30"), "Nested tuple should be folded to 30. Dump:\n{}", dump_tuple); + + // 2. Record-to-Tuple + let source_record = "((fn [[x y]] (+ x y)) {:a 5 :b 7})"; + assert_eq!(format!("{}", env.run_script(source_record).unwrap()), "12"); + let dump_record = env.dump_ast(source_record).unwrap(); + assert!(dump_record.contains("Constant: 12"), "Record-to-Tuple destructuring should be folded to 12. Dump:\n{}", dump_record); + } }