Skip to content

Commit 103a5fb

Browse files
committed
fix pack() WIP
1 parent 97ce5f7 commit 103a5fb

File tree

2 files changed

+198
-15
lines changed

2 files changed

+198
-15
lines changed

src/main/java/org/perlonjava/operators/Pack.java

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,18 @@ public static RuntimeScalar pack(RuntimeList args) {
107107
} else if (nextChar == '!') {
108108
// Skip '!' for now
109109
nextPos++;
110+
} else if (nextChar == '[') {
111+
// Parse repeat count in brackets [n]
112+
int j = nextPos + 1;
113+
while (j < template.length() && Character.isDigit(template.charAt(j))) {
114+
j++;
115+
}
116+
if (j >= template.length() || template.charAt(j) != ']') {
117+
throw new PerlCompilerException("No group ending character ']' found in template");
118+
}
119+
groupRepeatCount = Integer.parseInt(template.substring(nextPos + 1, j));
120+
nextPos = j + 1; // Move past ']'
121+
break;
110122
} else if (Character.isDigit(nextChar)) {
111123
// Parse repeat count
112124
int j = nextPos;
@@ -235,7 +247,19 @@ public static RuntimeScalar pack(RuntimeList args) {
235247
// Check for repeat count or '*' AFTER endianness modifier
236248
if (i + 1 < template.length()) {
237249
char nextChar = template.charAt(i + 1);
238-
if (Character.isDigit(nextChar)) {
250+
if (nextChar == '[') {
251+
// Parse repeat count in brackets [n]
252+
int j = i + 2;
253+
while (j < template.length() && Character.isDigit(template.charAt(j))) {
254+
j++;
255+
}
256+
if (j >= template.length() || template.charAt(j) != ']') {
257+
throw new PerlCompilerException("No group ending character ']' found in template");
258+
}
259+
String countStr = template.substring(i + 2, j);
260+
count = Integer.parseInt(countStr);
261+
i = j; // Position at ']'
262+
} else if (Character.isDigit(nextChar)) {
239263
int j = i + 1;
240264
while (j < template.length() && Character.isDigit(template.charAt(j))) {
241265
j++;
@@ -420,16 +444,28 @@ public static RuntimeScalar pack(RuntimeList args) {
420444
case 'C':
421445
output.write(lengthToWrite & 0xFF);
422446
break;
447+
case 's':
448+
PackHelper.writeShortLittleEndian(output, lengthToWrite);
449+
break;
450+
case 'S':
451+
PackHelper.writeShort(output, lengthToWrite);
452+
break;
453+
case 'i':
454+
case 'I':
455+
case 'l':
456+
case 'L':
457+
PackHelper.writeIntLittleEndian(output, lengthToWrite);
458+
break;
423459
case 'Z':
424-
// For Z*/, encode length as null-terminated decimal string
425-
String lengthStr = String.valueOf(lengthToWrite);
426-
try {
427-
output.write(lengthStr.getBytes(StandardCharsets.US_ASCII));
428-
output.write(0); // null terminator
429-
} catch (IOException e) {
430-
throw new RuntimeException(e);
431-
}
432-
break;
460+
// For Z*/, encode length as null-terminated decimal string
461+
String lengthStr = String.valueOf(lengthToWrite);
462+
try {
463+
output.write(lengthStr.getBytes(StandardCharsets.US_ASCII));
464+
output.write(0); // null terminator
465+
} catch (IOException e) {
466+
throw new RuntimeException(e);
467+
}
468+
break;
433469
default:
434470
throw new PerlCompilerException("Invalid length type '" + lengthFormat + "' for '/'");
435471
}
@@ -439,6 +475,26 @@ public static RuntimeScalar pack(RuntimeList args) {
439475
if (stringFormat == 'Z' && stringCount < 0) {
440476
output.write(0); // null terminator
441477
}
478+
} else if (format == '/') {
479+
// ... existing '/' handler code ...
480+
} else if (format == '@') {
481+
// @ is used for absolute positioning
482+
// @n means null-fill or truncate to position n
483+
int targetPosition = count;
484+
int currentPosition = output.size();
485+
486+
if (targetPosition > currentPosition) {
487+
// Pad with nulls to reach target position
488+
for (int k = currentPosition; k < targetPosition; k++) {
489+
output.write(0);
490+
}
491+
} else if (targetPosition < currentPosition) {
492+
// Truncate to target position
493+
byte[] truncated = new byte[targetPosition];
494+
System.arraycopy(output.toByteArray(), 0, truncated, 0, targetPosition);
495+
output.reset();
496+
output.write(truncated, 0, targetPosition);
497+
}
442498
} else {
443499
// Check if this is a numeric format followed by '/' - skip it
444500
if (isNumericFormat(format) && i + 1 < template.length() && template.charAt(i + 1) == '/') {

src/main/java/org/perlonjava/operators/Unpack.java

Lines changed: 132 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)