Skip to content

Commit c680acc

Browse files
committed
fix pack() WIP
1 parent cc5722f commit c680acc

File tree

1 file changed

+110
-19
lines changed
  • src/main/java/org/perlonjava/operators

1 file changed

+110
-19
lines changed

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

Lines changed: 110 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -169,10 +169,34 @@ public static RuntimeScalar pack(RuntimeList args) {
169169
continue;
170170
}
171171

172-
// Check if this is a numeric format followed by '/' - skip it entirely
173-
if (isNumericFormat(format) && i + 1 < template.length() && template.charAt(i + 1) == '/') {
174-
// System.err.println("DEBUG: skipping format '" + format + "' because it's followed by '/', valueIndex=" + valueIndex);
175-
continue;
172+
// Check if this is a numeric/Z format followed by '/' - skip it entirely
173+
if ((isNumericFormat(format) || format == 'Z') && i + 1 < template.length()) {
174+
// Look ahead, skipping modifiers and counts
175+
int lookAhead = i + 1;
176+
177+
// Skip modifiers
178+
while (lookAhead < template.length() &&
179+
(template.charAt(lookAhead) == '<' ||
180+
template.charAt(lookAhead) == '>' ||
181+
template.charAt(lookAhead) == '!')) {
182+
lookAhead++;
183+
}
184+
185+
// Skip count or *
186+
if (lookAhead < template.length() && template.charAt(lookAhead) == '*') {
187+
lookAhead++;
188+
} else if (lookAhead < template.length() && Character.isDigit(template.charAt(lookAhead))) {
189+
while (lookAhead < template.length() && Character.isDigit(template.charAt(lookAhead))) {
190+
lookAhead++;
191+
}
192+
}
193+
194+
// Check if followed by '/'
195+
if (lookAhead < template.length() && template.charAt(lookAhead) == '/') {
196+
// Skip this entire format sequence - it's used for length encoding
197+
i = lookAhead - 1; // -1 because loop will increment
198+
continue;
199+
}
176200
}
177201

178202
// Parse modifiers BEFORE parsing counts
@@ -257,22 +281,60 @@ public static RuntimeScalar pack(RuntimeList args) {
257281
}
258282
} else if (format == '/') {
259283
// System.err.println("DEBUG: entering '/' handler, valueIndex=" + valueIndex);
260-
// '/' must follow a numeric type
284+
// '/' must follow a numeric type or Z
261285
if (i == 0) {
262286
throw new PerlCompilerException("Invalid type '/'");
263287
}
264288

265-
// Find the numeric format that precedes '/'
266-
// Need to look back, skipping any modifiers
289+
// Find the format that precedes '/'
290+
// Need to look back, skipping any modifiers and repeat counts
267291
int numericPos = i - 1;
292+
293+
// Skip back over repeat counts and '*'
294+
if (numericPos > 0 && (template.charAt(numericPos) == '*' || Character.isDigit(template.charAt(numericPos)))) {
295+
if (template.charAt(numericPos) == '*') {
296+
// Skip the '*' to get to the format
297+
numericPos--;
298+
} else {
299+
// Skip digits
300+
while (numericPos > 0 && Character.isDigit(template.charAt(numericPos))) {
301+
numericPos--;
302+
}
303+
// Now numericPos points to the format character
304+
}
305+
}
306+
307+
// Skip back over any modifiers
268308
while (numericPos > 0 && (template.charAt(numericPos) == '<' ||
269309
template.charAt(numericPos) == '>' ||
270310
template.charAt(numericPos) == '!')) {
271311
numericPos--;
272312
}
273313

274-
char lengthFormat = template.charAt(numericPos);
275-
if (!isNumericFormat(lengthFormat)) {
314+
// If we hit a ')', we need to look at what's inside the group
315+
char lengthFormat;
316+
if (numericPos >= 0 && template.charAt(numericPos) == ')') {
317+
// Find the matching '(' and get the last numeric format in the group
318+
int openPos = numericPos - 1;
319+
int depth = 1;
320+
while (openPos >= 0 && depth > 0) {
321+
if (template.charAt(openPos) == ')') depth++;
322+
else if (template.charAt(openPos) == '(') depth--;
323+
openPos--;
324+
}
325+
openPos++; // Move back to the '('
326+
327+
// Find the last numeric format in the group
328+
lengthFormat = findLastNumericFormat(template.substring(openPos + 1, numericPos));
329+
if (lengthFormat == '\0') {
330+
throw new PerlCompilerException("'/' must follow a numeric type");
331+
}
332+
} else if (numericPos >= 0) {
333+
lengthFormat = template.charAt(numericPos);
334+
if (!isNumericFormat(lengthFormat) && lengthFormat != 'Z') {
335+
throw new PerlCompilerException("'/' must follow a numeric type");
336+
}
337+
} else {
276338
throw new PerlCompilerException("'/' must follow a numeric type");
277339
}
278340

@@ -355,8 +417,15 @@ public static RuntimeScalar pack(RuntimeList args) {
355417
output.write(lengthToWrite & 0xFF);
356418
break;
357419
case 'Z':
358-
output.write(lengthToWrite & 0xFF);
359-
break;
420+
// For Z*/, encode length as null-terminated decimal string
421+
String lengthStr = String.valueOf(lengthToWrite);
422+
try {
423+
output.write(lengthStr.getBytes(StandardCharsets.US_ASCII));
424+
output.write(0); // null terminator
425+
} catch (IOException e) {
426+
throw new RuntimeException(e);
427+
}
428+
break;
360429
default:
361430
throw new PerlCompilerException("Invalid length type '" + lengthFormat + "' for '/'");
362431
}
@@ -660,7 +729,7 @@ private static boolean hasConflictingEndianness(String groupContent, char groupE
660729
char modifier = groupContent.charAt(checkPos);
661730
if (modifier == '<' || modifier == '>') {
662731
if ((modifier == '<' && groupEndian == '>') ||
663-
(modifier == '>' && groupEndian == '<')) {
732+
(modifier == '>' && groupEndian == '<')) {
664733
return true;
665734
}
666735
break;
@@ -710,9 +779,9 @@ private static int countValuesNeeded(String template) {
710779
i = closePos;
711780
// Skip modifiers
712781
while (i + 1 < template.length() &&
713-
(template.charAt(i + 1) == '<' ||
714-
template.charAt(i + 1) == '>' ||
715-
template.charAt(i + 1) == '!')) {
782+
(template.charAt(i + 1) == '<' ||
783+
template.charAt(i + 1) == '>' ||
784+
template.charAt(i + 1) == '!')) {
716785
i++;
717786
}
718787

@@ -744,9 +813,9 @@ private static int countValuesNeeded(String template) {
744813

745814
// Skip modifiers
746815
while (i + 1 < template.length() &&
747-
(template.charAt(i + 1) == '<' ||
748-
template.charAt(i + 1) == '>' ||
749-
template.charAt(i + 1) == '!')) {
816+
(template.charAt(i + 1) == '<' ||
817+
template.charAt(i + 1) == '>' ||
818+
template.charAt(i + 1) == '!')) {
750819
i++;
751820
}
752821

@@ -777,4 +846,26 @@ private static int countValuesNeeded(String template) {
777846
}
778847
return count;
779848
}
780-
}
849+
850+
private static char findLastNumericFormat(String template) {
851+
char lastNumeric = '\0';
852+
for (int i = 0; i < template.length(); i++) {
853+
char c = template.charAt(i);
854+
if (isNumericFormat(c) || c == 'Z') {
855+
lastNumeric = c;
856+
} else if (c == '(') {
857+
// Skip the group
858+
int closePos = findMatchingParen(template, i);
859+
if (closePos != -1) {
860+
// Check inside the group
861+
char groupNumeric = findLastNumericFormat(template.substring(i + 1, closePos));
862+
if (groupNumeric != '\0') {
863+
lastNumeric = groupNumeric;
864+
}
865+
i = closePos;
866+
}
867+
}
868+
}
869+
return lastNumeric;
870+
}
871+
}

0 commit comments

Comments
 (0)