dcbor_pattern/pattern/structure/array_pattern.rs
1use std::ops::RangeBounds;
2
3use dcbor::prelude::*;
4
5use crate::{
6 Interval,
7 pattern::{
8 Matcher, MetaPattern, Path, Pattern,
9 meta::{RepeatPattern, SequencePattern},
10 vm::Instr,
11 },
12};
13
14/// Pattern for matching CBOR array structures.
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub enum ArrayPattern {
17 /// Matches any array.
18 Any,
19 /// Matches arrays with elements that match the given pattern.
20 Elements(Box<Pattern>),
21 /// Matches arrays with length in the given interval.
22 Length(Interval),
23}
24
25impl ArrayPattern {
26 /// Creates a new `ArrayPattern` that matches any array.
27 pub fn any() -> Self {
28 ArrayPattern::Any
29 }
30
31 /// Creates a new `ArrayPattern` that matches arrays with elements
32 /// that match the given pattern.
33 pub fn with_elements(pattern: Pattern) -> Self {
34 ArrayPattern::Elements(Box::new(pattern))
35 }
36
37 pub fn with_length_range<R: RangeBounds<usize>>(range: R) -> Self {
38 ArrayPattern::Length(Interval::new(range))
39 }
40
41 /// Creates a new `ArrayPattern` that matches arrays with length in the
42 /// given range.
43 pub fn with_length_interval(interval: Interval) -> Self {
44 ArrayPattern::Length(interval)
45 }
46
47 /// Match a complex sequence against array elements using VM-based matching.
48 /// This handles patterns with repeats and other complex constructs that
49 /// require backtracking and proper quantifier evaluation.
50 fn match_complex_sequence(
51 &self,
52 cbor: &CBOR,
53 pattern: &Pattern,
54 ) -> Vec<Path> {
55 // For complex sequences containing repeats, we need to check if the
56 // pattern can match the array elements in sequence.
57 // The key insight is that a sequence pattern like
58 // `(*)*, 42, (*)*` should match if there's any way to
59 // arrange the array elements to satisfy the sequence
60 // requirements.
61
62 match cbor.as_case() {
63 CBORCase::Array(arr) => {
64 // Create a synthetic "element sequence" CBOR value to match
65 // against This represents the array elements as
66 // a sequence that the pattern can evaluate
67
68 // For sequences with repeats, we need to check if the pattern
69 // can be satisfied by the array elements in order
70 if self.can_match_sequence_against_array(pattern, arr) {
71 vec![vec![cbor.clone()]]
72 } else {
73 vec![]
74 }
75 }
76 _ => vec![], // Not an array
77 }
78 }
79
80 /// Check if a sequence pattern can match against array elements.
81 /// This implements the core logic for matching patterns like
82 /// `(*)*, 42, (*)*` against array elements.
83 fn can_match_sequence_against_array(
84 &self,
85 pattern: &Pattern,
86 arr: &[CBOR],
87 ) -> bool {
88 match pattern {
89 Pattern::Meta(MetaPattern::Sequence(seq_pattern)) => {
90 self.match_sequence_patterns_against_array(seq_pattern, arr)
91 }
92 Pattern::Meta(MetaPattern::Repeat(repeat_pattern)) => {
93 // Single repeat pattern: check if it can consume all array
94 // elements
95 self.match_repeat_pattern_against_array(repeat_pattern, arr)
96 }
97 _ => {
98 // For non-sequence patterns, fall back to simple matching
99 // Create an array CBOR value by creating a string
100 // representation and parsing it
101 let elements_str: Vec<String> =
102 arr.iter().map(|item| item.to_string()).collect();
103 let array_str = format!("[{}]", elements_str.join(", "));
104 if let Ok(array_cbor) =
105 dcbor_parse::parse_dcbor_item(&array_str)
106 {
107 pattern.matches(&array_cbor)
108 } else {
109 false
110 }
111 }
112 }
113 }
114
115 /// Match a sequence of patterns against array elements.
116 /// This is the core algorithm for handling sequences with repeats.
117 fn match_sequence_patterns_against_array(
118 &self,
119 seq_pattern: &SequencePattern,
120 arr: &[CBOR],
121 ) -> bool {
122 let patterns = seq_pattern.patterns();
123
124 // For patterns like `(*)*, 42, (*)*`:
125 // - First `(*)*` can consume 0 or more elements from the start
126 // - `42` must match exactly one element (which must be 42)
127 // - Last `(*)*` can consume 0 or more elements from the end
128
129 // Simple case: if no patterns, then empty array should match
130 if patterns.is_empty() {
131 return arr.is_empty();
132 }
133
134 // Try to match the sequence using a backtracking approach
135 self.backtrack_sequence_match(patterns, arr, 0, 0)
136 }
137
138 /// Backtracking algorithm to match sequence patterns against array
139 /// elements. pattern_idx: current pattern index in the sequence
140 /// element_idx: current element index in the array
141 #[allow(clippy::only_used_in_recursion)]
142 fn backtrack_sequence_match(
143 &self,
144 patterns: &[Pattern],
145 arr: &[CBOR],
146 pattern_idx: usize,
147 element_idx: usize,
148 ) -> bool {
149 // Base case: if we've matched all patterns
150 if pattern_idx >= patterns.len() {
151 // Success if we've also consumed all elements
152 return element_idx >= arr.len();
153 }
154
155 let current_pattern = &patterns[pattern_idx];
156
157 match current_pattern {
158 Pattern::Meta(MetaPattern::Repeat(repeat_pattern)) => {
159 let quantifier = repeat_pattern.quantifier();
160 let min_count = quantifier.min();
161 // Fix the infinite loop: limit max_count to reasonable bounds
162 let remaining_elements = arr.len().saturating_sub(element_idx);
163 let max_count = quantifier
164 .max()
165 .unwrap_or(remaining_elements)
166 .min(remaining_elements);
167
168 // Try different numbers of repetitions (greedy: start with max)
169 for rep_count in (min_count..=max_count).rev() {
170 // Check bounds to prevent out-of-bounds access
171 if element_idx + rep_count <= arr.len() {
172 let can_match_reps = if rep_count == 0 {
173 true // Zero repetitions always match for rep_count=0
174 } else {
175 (0..rep_count).all(|i| {
176 repeat_pattern
177 .pattern()
178 .matches(&arr[element_idx + i])
179 })
180 };
181
182 if can_match_reps {
183 // Try to match the rest of the sequence
184 if self.backtrack_sequence_match(
185 patterns,
186 arr,
187 pattern_idx + 1,
188 element_idx + rep_count,
189 ) {
190 return true;
191 }
192 }
193 }
194 }
195 false
196 }
197 _ => {
198 // Non-repeat pattern: must match exactly one element
199 if element_idx < arr.len()
200 && current_pattern.matches(&arr[element_idx])
201 {
202 self.backtrack_sequence_match(
203 patterns,
204 arr,
205 pattern_idx + 1,
206 element_idx + 1,
207 )
208 } else {
209 false
210 }
211 }
212 }
213 }
214
215 /// Match a single repeat pattern against array elements.
216 fn match_repeat_pattern_against_array(
217 &self,
218 repeat_pattern: &RepeatPattern,
219 arr: &[CBOR],
220 ) -> bool {
221 let quantifier = repeat_pattern.quantifier();
222 let min_count = quantifier.min();
223 let max_count = quantifier.max().unwrap_or(arr.len());
224
225 // Check if the array length is within the valid range for this repeat
226 if arr.len() < min_count || arr.len() > max_count {
227 return false;
228 }
229
230 // Check if all elements match the repeated pattern
231 arr.iter()
232 .all(|element| repeat_pattern.pattern().matches(element))
233 }
234
235 /// Handle sequence patterns with captures by manually matching elements
236 /// and collecting captures with proper array context.
237 fn handle_sequence_captures(
238 &self,
239 seq_pattern: &SequencePattern,
240 array_cbor: &CBOR,
241 arr: &[CBOR],
242 ) -> (Vec<Path>, std::collections::HashMap<String, Vec<Path>>) {
243 // Use the existing sequence matching logic to find element assignments
244 if let Some(assignments) =
245 self.find_sequence_element_assignments(seq_pattern, arr)
246 {
247 let mut all_captures = std::collections::HashMap::new();
248
249 // For each pattern in the sequence, collect captures from its
250 // assigned element
251 for (pattern_idx, element_idx) in assignments {
252 let pattern = &seq_pattern.patterns()[pattern_idx];
253 let element = &arr[element_idx];
254
255 // Get captures from this pattern matching this element
256 let (_element_paths, element_captures) =
257 pattern.paths_with_captures(element);
258
259 // Transform captures to include array context
260 for (capture_name, captured_paths) in element_captures {
261 let mut array_context_paths = Vec::new();
262 for captured_path in captured_paths {
263 // Create path: [array] + [element_at_index] +
264 // rest_of_path
265 let mut array_path =
266 vec![array_cbor.clone(), element.clone()];
267 if captured_path.len() > 1 {
268 array_path
269 .extend(captured_path.iter().skip(1).cloned());
270 }
271 array_context_paths.push(array_path);
272 }
273 all_captures
274 .entry(capture_name)
275 .or_insert_with(Vec::new)
276 .extend(array_context_paths);
277 }
278 }
279
280 // Return the array path and all captures
281 (vec![vec![array_cbor.clone()]], all_captures)
282 } else {
283 // Sequence doesn't match the array
284 (vec![], std::collections::HashMap::new())
285 }
286 }
287
288 /// Find which array elements are assigned to which sequence patterns.
289 /// Returns a vector of (pattern_index, element_index) pairs if the sequence
290 /// matches.
291 fn find_sequence_element_assignments(
292 &self,
293 seq_pattern: &SequencePattern,
294 arr: &[CBOR],
295 ) -> Option<Vec<(usize, usize)>> {
296 let patterns = seq_pattern.patterns();
297
298 // Check if we have any repeat patterns that require backtracking
299 let has_repeat_patterns = patterns.iter().any(|p| {
300 matches!(p, Pattern::Meta(crate::pattern::MetaPattern::Repeat(_)))
301 });
302
303 // Simple case: if pattern count equals element count AND no repeat
304 // patterns, try one-to-one matching
305 if patterns.len() == arr.len() && !has_repeat_patterns {
306 let mut assignments = Vec::new();
307 for (pattern_idx, pattern) in patterns.iter().enumerate() {
308 let element = &arr[pattern_idx];
309 if pattern.matches(element) {
310 assignments.push((pattern_idx, pattern_idx));
311 } else {
312 return None; // Pattern doesn't match its corresponding element
313 }
314 }
315 return Some(assignments);
316 }
317
318 // Handle complex cases with repeats and variable assignments
319 // Use the existing backtracking algorithm to find assignments
320 if let Some(assignments) =
321 self.find_sequence_assignments_with_backtracking(patterns, arr)
322 {
323 return Some(assignments);
324 }
325
326 None
327 }
328
329 /// Use backtracking to find sequence pattern assignments.
330 /// This handles cases like `(*)*, @item(42), (*)*` properly.
331 fn find_sequence_assignments_with_backtracking(
332 &self,
333 patterns: &[Pattern],
334 arr: &[CBOR],
335 ) -> Option<Vec<(usize, usize)>> {
336 let mut assignments = Vec::new();
337
338 if self.backtrack_sequence_assignments(
339 patterns,
340 arr,
341 0,
342 0,
343 &mut assignments,
344 ) {
345 Some(assignments)
346 } else {
347 None
348 }
349 }
350
351 /// Backtracking algorithm to find pattern-to-element assignments.
352 #[allow(clippy::only_used_in_recursion)]
353 fn backtrack_sequence_assignments(
354 &self,
355 patterns: &[Pattern],
356 arr: &[CBOR],
357 pattern_idx: usize,
358 element_idx: usize,
359 assignments: &mut Vec<(usize, usize)>,
360 ) -> bool {
361 // Base case: if we've matched all patterns
362 if pattern_idx >= patterns.len() {
363 // Success if we've also consumed all elements
364 return element_idx >= arr.len();
365 }
366
367 let current_pattern = &patterns[pattern_idx];
368
369 match current_pattern {
370 Pattern::Meta(crate::pattern::MetaPattern::Repeat(
371 repeat_pattern,
372 )) => {
373 let quantifier = repeat_pattern.quantifier();
374 let min_count = quantifier.min();
375 let remaining_elements = arr.len().saturating_sub(element_idx);
376 let max_count = quantifier
377 .max()
378 .unwrap_or(remaining_elements)
379 .min(remaining_elements);
380
381 // Try different numbers of repetitions (greedy: start with max)
382 for rep_count in (min_count..=max_count).rev() {
383 if element_idx + rep_count <= arr.len() {
384 let can_match_reps = if rep_count == 0 {
385 true // Zero repetitions always match
386 } else {
387 (0..rep_count).all(|i| {
388 repeat_pattern
389 .pattern()
390 .matches(&arr[element_idx + i])
391 })
392 };
393
394 if can_match_reps {
395 // Record assignments for non-capture repeat
396 // patterns
397 let old_len = assignments.len();
398
399 // Add assignments for elements consumed by this
400 // repeat
401 for i in 0..rep_count {
402 assignments
403 .push((pattern_idx, element_idx + i));
404 }
405
406 // Try to match the rest of the sequence
407 if self.backtrack_sequence_assignments(
408 patterns,
409 arr,
410 pattern_idx + 1,
411 element_idx + rep_count,
412 assignments,
413 ) {
414 return true;
415 }
416
417 // Backtrack: remove the assignments we added
418 assignments.truncate(old_len);
419 }
420 }
421 }
422 false
423 }
424 _ => {
425 // Non-repeat pattern: must match exactly one element
426 if element_idx < arr.len()
427 && current_pattern.matches(&arr[element_idx])
428 {
429 assignments.push((pattern_idx, element_idx));
430
431 if self.backtrack_sequence_assignments(
432 patterns,
433 arr,
434 pattern_idx + 1,
435 element_idx + 1,
436 assignments,
437 ) {
438 true
439 } else {
440 // Backtrack: remove the assignment
441 assignments.pop();
442 false
443 }
444 } else {
445 false
446 }
447 }
448 }
449 }
450}
451
452impl Matcher for ArrayPattern {
453 fn paths(&self, haystack: &CBOR) -> Vec<Path> {
454 // First check if this is an array
455 match haystack.as_case() {
456 CBORCase::Array(arr) => {
457 match self {
458 ArrayPattern::Any => {
459 // Match any array - return the array itself
460 vec![vec![haystack.clone()]]
461 }
462 ArrayPattern::Elements(pattern) => {
463 // For unified syntax, the pattern should match against
464 // the array elements
465 // as a sequence, not against any individual element.
466 //
467 // Examples:
468 // - [42] should match [42] but not [1, 42, 3]
469 // - ["a" , "b"] should match ["a", "b"] but not ["a",
470 // "x", "b"]
471
472 // Check if this is a simple single-element case
473 use crate::pattern::{MetaPattern, Pattern};
474
475 match pattern.as_ref() {
476 // Simple case: single pattern should match array
477 // with exactly one element
478 Pattern::Value(_) | Pattern::Structure(_) => {
479 if arr.len() == 1 {
480 if pattern.matches(&arr[0]) {
481 vec![vec![haystack.clone()]]
482 } else {
483 vec![]
484 }
485 } else {
486 vec![]
487 }
488 }
489
490 // Complex case: sequences, repeats, etc.
491 Pattern::Meta(MetaPattern::Sequence(
492 seq_pattern,
493 )) => {
494 let patterns = seq_pattern.patterns();
495
496 // Check if this sequence contains any repeat
497 // patterns that
498 // require VM-based matching
499 let has_repeat_patterns =
500 patterns.iter().any(|p| {
501 matches!(
502 p,
503 Pattern::Meta(MetaPattern::Repeat(
504 _
505 ))
506 )
507 });
508
509 if has_repeat_patterns {
510 // Use VM-based matching for complex
511 // sequences
512 self.match_complex_sequence(haystack, pattern)
513 } else {
514 // Simple sequence: match each pattern
515 // against consecutive elements
516 if patterns.len() == arr.len() {
517 // Check if each pattern matches the
518 // corresponding array element
519 for (i, element_pattern) in
520 patterns.iter().enumerate()
521 {
522 if !element_pattern.matches(&arr[i])
523 {
524 return vec![];
525 }
526 }
527 vec![vec![haystack.clone()]]
528 } else {
529 vec![]
530 }
531 }
532 }
533
534 // For individual repeat patterns
535 Pattern::Meta(MetaPattern::Repeat(_)) => {
536 // Use VM-based matching for repeat patterns
537 self.match_complex_sequence(haystack, pattern)
538 }
539
540 // For other meta patterns, handle them properly
541 Pattern::Meta(MetaPattern::Capture(
542 capture_pattern,
543 )) => {
544 // Capture patterns should search within array
545 // elements
546 // (This is different from non-capture patterns)
547 let has_matching_element =
548 arr.iter().any(|element| {
549 capture_pattern
550 .pattern()
551 .matches(element)
552 });
553
554 if has_matching_element {
555 vec![vec![haystack.clone()]]
556 } else {
557 vec![]
558 }
559 }
560
561 // For other meta patterns (or, and, etc.), use the
562 // old heuristic
563 // This handles cases like `[(number | text)]`
564 _ => {
565 // Check if the pattern matches the array as a
566 // whole sequence
567 // For now, use a heuristic: if it's a simple
568 // meta pattern,
569 // apply it to each element and require at least
570 // one match
571 // This is not perfect but maintains some
572 // compatibility
573 let mut result = Vec::new();
574 for element in arr {
575 if pattern.matches(element) {
576 result.push(vec![haystack.clone()]);
577 break;
578 }
579 }
580 result
581 }
582 }
583 }
584 ArrayPattern::Length(interval) => {
585 if interval.contains(arr.len()) {
586 vec![vec![haystack.clone()]]
587 } else {
588 vec![]
589 }
590 }
591 }
592 }
593 _ => {
594 // Not an array, no match
595 vec![]
596 }
597 }
598 }
599
600 fn compile(
601 &self,
602 code: &mut Vec<Instr>,
603 literals: &mut Vec<Pattern>,
604 captures: &mut Vec<String>,
605 ) {
606 // Collect capture names from inner patterns
607 self.collect_capture_names(captures);
608
609 // Check if this pattern has captures
610 let mut capture_names = Vec::new();
611 self.collect_capture_names(&mut capture_names);
612
613 if capture_names.is_empty() {
614 // No captures, use the simple MatchStructure approach
615 let idx = literals.len();
616 literals.push(Pattern::Structure(
617 crate::pattern::StructurePattern::Array(self.clone()),
618 ));
619 code.push(Instr::MatchStructure(idx));
620 } else {
621 // Has captures, compile to VM navigation instructions
622 match self {
623 ArrayPattern::Elements(pattern) => {
624 // First check that we have an array
625 let array_check_idx = literals.len();
626 literals.push(Pattern::Structure(
627 crate::pattern::StructurePattern::Array(
628 ArrayPattern::Any,
629 ),
630 ));
631 code.push(Instr::MatchStructure(array_check_idx));
632
633 // Navigate to array elements
634 code.push(Instr::PushAxis(
635 crate::pattern::vm::Axis::ArrayElement,
636 ));
637
638 // Compile the inner pattern with captures
639 pattern.compile(code, literals, captures);
640
641 // Pop back to array level
642 code.push(Instr::Pop);
643 }
644 _ => {
645 // Other array patterns (length-based) don't support
646 // captures in this context Fall back to
647 // MatchStructure
648 let idx = literals.len();
649 literals.push(Pattern::Structure(
650 crate::pattern::StructurePattern::Array(self.clone()),
651 ));
652 code.push(Instr::MatchStructure(idx));
653 }
654 }
655 }
656 }
657
658 fn collect_capture_names(&self, names: &mut Vec<String>) {
659 match self {
660 ArrayPattern::Any => {
661 // No captures in a simple any pattern
662 }
663 ArrayPattern::Elements(pattern) => {
664 // Collect captures from the element pattern
665 pattern.collect_capture_names(names);
666 }
667 ArrayPattern::Length(_) => {
668 // No captures in length range patterns
669 }
670 }
671 }
672
673 fn paths_with_captures(
674 &self,
675 cbor: &CBOR,
676 ) -> (Vec<Path>, std::collections::HashMap<String, Vec<Path>>) {
677 // For simple cases that never have captures, use the fast path
678 match self {
679 ArrayPattern::Any | ArrayPattern::Length(_) => {
680 return (self.paths(cbor), std::collections::HashMap::new());
681 }
682 ArrayPattern::Elements(pattern) => {
683 // Check if this specific pattern has any captures
684 let mut capture_names = Vec::new();
685 pattern.collect_capture_names(&mut capture_names);
686
687 if capture_names.is_empty() {
688 // No captures in the element pattern, use the fast path
689 return (
690 self.paths(cbor),
691 std::collections::HashMap::new(),
692 );
693 }
694
695 // Has captures, continue with complex logic below
696 }
697 }
698
699 match cbor.as_case() {
700 CBORCase::Array(_arr) => {
701 if let ArrayPattern::Elements(pattern) = self {
702 // First check if this array pattern matches at all
703 if self.paths(cbor).is_empty() {
704 return (vec![], std::collections::HashMap::new());
705 }
706
707 // For patterns with captures, we need special handling
708 // depending on the inner pattern type
709 match pattern.as_ref() {
710 Pattern::Meta(
711 crate::pattern::MetaPattern::Sequence(seq_pattern),
712 ) => {
713 // Special handling for SequencePattern with
714 // captures
715 self.handle_sequence_captures(
716 seq_pattern,
717 cbor,
718 _arr,
719 )
720 }
721 Pattern::Meta(
722 crate::pattern::MetaPattern::Capture(
723 _capture_pattern,
724 ),
725 ) => {
726 // For capture patterns like [@item(number)] or
727 // [@item(42)],
728 // use the VM approach for consistency with existing
729 // behavior
730
731 // Use the VM approach for consistent behavior
732 let mut code = Vec::new();
733 let mut literals = Vec::new();
734 let mut captures_list = Vec::new();
735
736 // Compile the entire ArrayPattern (not just the
737 // inner pattern)
738 let array_pattern = Pattern::Structure(
739 crate::pattern::StructurePattern::Array(
740 self.clone(),
741 ),
742 );
743 array_pattern.compile(
744 &mut code,
745 &mut literals,
746 &mut captures_list,
747 );
748 code.push(crate::pattern::vm::Instr::Accept);
749
750 let program = crate::pattern::vm::Program {
751 code,
752 literals,
753 capture_names: captures_list,
754 };
755
756 // Run the VM program against the CBOR
757 crate::pattern::vm::run(&program, cbor)
758 }
759 _ => {
760 // For non-sequence patterns, use the original VM
761 // approach
762 // but start with the main Pattern's VM compilation
763 // for better compatibility
764 let mut code = Vec::new();
765 let mut literals = Vec::new();
766 let mut captures = Vec::new();
767
768 // Compile the entire ArrayPattern (not just the
769 // inner pattern)
770 let array_pattern = Pattern::Structure(
771 crate::pattern::StructurePattern::Array(
772 self.clone(),
773 ),
774 );
775 array_pattern.compile(
776 &mut code,
777 &mut literals,
778 &mut captures,
779 );
780 code.push(crate::pattern::vm::Instr::Accept);
781
782 let program = crate::pattern::vm::Program {
783 code,
784 literals,
785 capture_names: captures,
786 };
787
788 // Run the VM program against the CBOR
789 crate::pattern::vm::run(&program, cbor)
790 }
791 }
792 } else {
793 // Other array patterns (length-based) don't have inner
794 // patterns with captures
795 (self.paths(cbor), std::collections::HashMap::new())
796 }
797 }
798 _ => {
799 // Not an array, no match
800 (vec![], std::collections::HashMap::new())
801 }
802 }
803 }
804}
805
806impl ArrayPattern {
807 // ...existing methods...
808
809 /// Format a pattern for display within array context.
810 /// This handles sequence patterns specially to use commas instead of >.
811 fn format_array_element_pattern(pattern: &Pattern) -> String {
812 match pattern {
813 Pattern::Meta(crate::pattern::MetaPattern::Sequence(
814 seq_pattern,
815 )) => {
816 // For sequence patterns within arrays, use commas instead of >
817 let patterns_str: Vec<String> = seq_pattern
818 .patterns()
819 .iter()
820 .map(Self::format_array_element_pattern)
821 .collect();
822 patterns_str.join(", ")
823 }
824 _ => pattern.to_string(),
825 }
826 }
827}
828
829impl std::fmt::Display for ArrayPattern {
830 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
831 match self {
832 ArrayPattern::Any => write!(f, "[*]"),
833 ArrayPattern::Elements(pattern) => {
834 let formatted_pattern =
835 Self::format_array_element_pattern(pattern);
836 write!(f, "[{}]", formatted_pattern)
837 }
838 ArrayPattern::Length(interval) => {
839 write!(f, "[{}]", interval)
840 }
841 }
842 }
843}