@@ -82,7 +82,7 @@ public static RuntimeList unpack(int ctx, RuntimeBase... args) {
8282 int i = 0 ;
8383 while (i < template .length ()) {
8484 char format = template .charAt (i );
85- System .err .println ("DEBUG: unpack main loop i=" + i + ", format='" + format + "' (code " + (int )format + "), template='" + template + "'" );
85+ System .err .println ("DEBUG: unpack main loop i=" + i + ", format='" + format + "' (code " + (int )format + "), template='" + template + "', remaining='" + template . substring ( i ) + "' " );
8686 boolean isChecksum = false ;
8787 int checksumBits = 16 ; // default
8888
@@ -328,13 +328,9 @@ public static RuntimeList unpack(int ctx, RuntimeBase... args) {
328328 }
329329 String countStr = template .substring (i + 1 , j );
330330 count = Integer .parseInt (countStr );
331- if (format == '@' ) {
332- System .err .println ("DEBUG: @ count string '" + countStr + "' parsed to " + count );
333- }
334331 i = j - 1 ;
335332 }
336333 }
337-
338334 // Get handler and unpack
339335 FormatHandler handler = getHandler (format , startsWithU );
340336 if (handler != null ) {
@@ -494,6 +490,17 @@ private static void processGroup(String groupTemplate, UnpackState state, List<R
494490 break ;
495491 }
496492
493+ // Save position before processing group (for detecting progress with *)
494+ int positionBefore ;
495+ if (state .isCharacterMode ()) {
496+ positionBefore = state .getCurrentCodePointIndex ();
497+ } else {
498+ positionBefore = state .getTotalLength () - state .remainingBytes ();
499+ }
500+
501+ System .err .println ("DEBUG: Group iteration " + rep + " starting at position " + positionBefore );
502+ int valuesBeforeGroup = values .size ();
503+
497504 // Process the group content
498505 for (int j = 0 ; j < groupTemplate .length (); j ++) {
499506 char format = groupTemplate .charAt (j );
@@ -503,6 +510,51 @@ private static void processGroup(String groupTemplate, UnpackState state, List<R
503510 continue ;
504511 }
505512
513+ // Handle nested groups
514+ if (format == '(' ) {
515+ // Find the matching closing parenthesis within the group
516+ int closePos = findMatchingParen (groupTemplate , j );
517+ if (closePos == -1 ) {
518+ throw new PerlCompilerException ("unpack: unmatched parenthesis in template" );
519+ }
520+
521+ // Extract nested group content
522+ String nestedGroupContent = groupTemplate .substring (j + 1 , closePos );
523+
524+ // Check for modifiers and repeat count after the nested group
525+ int nestedNextPos = closePos + 1 ;
526+ int nestedRepeatCount = 1 ;
527+
528+ // Parse modifiers after ')'
529+ while (nestedNextPos < groupTemplate .length ()) {
530+ char nextChar = groupTemplate .charAt (nestedNextPos );
531+ if (nextChar == '<' || nextChar == '>' || nextChar == '!' ) {
532+ nestedNextPos ++;
533+ } else if (nextChar == '*' ) {
534+ nestedRepeatCount = Integer .MAX_VALUE ;
535+ nestedNextPos ++;
536+ break ;
537+ } else if (Character .isDigit (nextChar )) {
538+ int endPos = nestedNextPos ;
539+ while (endPos < groupTemplate .length () && Character .isDigit (groupTemplate .charAt (endPos ))) {
540+ endPos ++;
541+ }
542+ nestedRepeatCount = Integer .parseInt (groupTemplate .substring (nestedNextPos , endPos ));
543+ nestedNextPos = endPos ;
544+ break ;
545+ } else {
546+ break ;
547+ }
548+ }
549+
550+ // Process the nested group with its repeat count
551+ processGroup (nestedGroupContent , state , values , nestedRepeatCount , startsWithU , modeStack );
552+
553+ // Move past the nested group and its modifiers/count
554+ j = nestedNextPos - 1 ; // -1 because loop will increment
555+ continue ;
556+ }
557+
506558 // Check for mode modifiers
507559 if (format == 'C' && j + 1 < groupTemplate .length () && groupTemplate .charAt (j + 1 ) == '0' ) {
508560 state .switchToCharacterMode ();
@@ -514,6 +566,36 @@ private static void processGroup(String groupTemplate, UnpackState state, List<R
514566 continue ;
515567 }
516568
569+ // Handle @ format
570+ if (format == '@' ) {
571+ System .err .println ("DEBUG: Found @ in group at position " + j );
572+ // Parse count for @
573+ int atCount = 0 ;
574+ boolean atHasStar = false ;
575+
576+ if (j + 1 < groupTemplate .length ()) {
577+ char nextChar = groupTemplate .charAt (j + 1 );
578+ if (nextChar == '*' ) {
579+ atHasStar = true ;
580+ j ++;
581+ } else if (Character .isDigit (nextChar )) {
582+ int k = j + 1 ;
583+ while (k < groupTemplate .length () && Character .isDigit (groupTemplate .charAt (k ))) {
584+ k ++;
585+ }
586+ atCount = Integer .parseInt (groupTemplate .substring (j + 1 , k ));
587+ j = k - 1 ;
588+ }
589+ }
590+
591+ // Get handler and unpack
592+ FormatHandler handler = getHandler ('@' , startsWithU );
593+ if (handler != null ) {
594+ handler .unpack (state , values , atCount , atHasStar );
595+ }
596+ continue ;
597+ }
598+
517599 // Handle '/' for counted strings
518600 if (format == '/' ) {
519601 if (values .isEmpty ()) {
@@ -580,6 +662,18 @@ private static void processGroup(String groupTemplate, UnpackState state, List<R
580662 isStarCount = true ;
581663 j ++;
582664 count = getRemainingCount (state , format , startsWithU );
665+ } else if (nextChar == '[' ) {
666+ // Parse repeat count in brackets [n]
667+ int k = j + 2 ;
668+ while (k < groupTemplate .length () && Character .isDigit (groupTemplate .charAt (k ))) {
669+ k ++;
670+ }
671+ if (k >= groupTemplate .length () || groupTemplate .charAt (k ) != ']' ) {
672+ throw new PerlCompilerException ("No group ending character ']' found in template" );
673+ }
674+ String countStr = groupTemplate .substring (j + 2 , k );
675+ count = Integer .parseInt (countStr );
676+ j = k ; // Position at ']'
583677 } else if (Character .isDigit (nextChar )) {
584678 int k = j + 1 ;
585679 while (k < groupTemplate .length () && Character .isDigit (groupTemplate .charAt (k ))) {
@@ -598,6 +692,39 @@ private static void processGroup(String groupTemplate, UnpackState state, List<R
598692 throw new PerlCompilerException ("unpack: unsupported format character: " + format );
599693 }
600694 }
695+
696+ // DEBUG: Show what values were extracted in this iteration
697+ System .err .println ("DEBUG: Group iteration " + rep + " complete, extracted " +
698+ (values .size () - valuesBeforeGroup ) + " values" );
699+ if (values .size () > valuesBeforeGroup ) {
700+ List <String > extracted = new ArrayList <>();
701+ for (int idx = valuesBeforeGroup ; idx < values .size (); idx ++) {
702+ extracted .add (values .get (idx ).toString ());
703+ }
704+ System .err .println ("DEBUG: Values: " + extracted );
705+ }
706+
707+ // Check if we made progress (for * repeat count)
708+ if (repeatCount == Integer .MAX_VALUE ) {
709+ int positionAfter ;
710+ if (state .isCharacterMode ()) {
711+ positionAfter = state .getCurrentCodePointIndex ();
712+ } else {
713+ positionAfter = state .getTotalLength () - state .remainingBytes ();
714+ }
715+
716+ // If we've consumed all data, stop
717+ if (state .remainingBytes () == 0 && positionAfter >= state .getTotalLength ()) {
718+ System .err .println ("DEBUG: All data consumed, stopping group repetition" );
719+ break ;
720+ }
721+
722+ // If no progress was made, stop
723+ if (positionAfter == positionBefore ) {
724+ System .err .println ("DEBUG: No progress made in group with * repeat, stopping" );
725+ break ;
726+ }
727+ }
601728 }
602729
603730 // Restore mode
0 commit comments