Skip to content

Commit 4f3da32

Browse files
committed
fix pack() WIP
1 parent 3981e6a commit 4f3da32

File tree

9 files changed

+95
-42
lines changed

9 files changed

+95
-42
lines changed

src/main/java/org/perlonjava/codegen/EmitOperatorNode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public static void emitOperatorNode(EmitterVisitor emitterVisitor, OperatorNode
3030
case "$", "@", "%", "*", "&" -> EmitVariable.handleVariableOperator(emitterVisitor, node);
3131

3232
// Operations that take a list of operands
33-
case "keys", "values", "pack", "unpack", "mkdir", "opendir", "seekdir", "crypt", "vec", "read", "sysopen", "chmod" ->
33+
case "keys", "values", "pack", "mkdir", "opendir", "seekdir", "crypt", "vec", "read", "sysopen", "chmod" ->
3434
EmitOperator.handleOpWithList(emitterVisitor, node);
3535

3636
case "each" ->

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ public record OperatorHandler(String className, String methodName, int methodTyp
175175
put("link", "link", "org/perlonjava/nativ/NativeUtils", "(I[Lorg/perlonjava/runtime/RuntimeBase;)Lorg/perlonjava/runtime/RuntimeScalar;");
176176
put("symlink", "symlink", "org/perlonjava/nativ/NativeUtils", "(I[Lorg/perlonjava/runtime/RuntimeBase;)Lorg/perlonjava/runtime/RuntimeScalar;");
177177

178-
put("unpack", "unpack", "org/perlonjava/operators/Unpack", "(Lorg/perlonjava/runtime/RuntimeList;)Lorg/perlonjava/runtime/RuntimeList;");
178+
put("unpack", "unpack", "org/perlonjava/operators/Unpack", "(I[Lorg/perlonjava/runtime/RuntimeBase;)Lorg/perlonjava/runtime/RuntimeList;");
179179
put("pack", "pack", "org/perlonjava/operators/Pack", "(Lorg/perlonjava/runtime/RuntimeList;)Lorg/perlonjava/runtime/RuntimeScalar;");
180180
put("read", "read", "org/perlonjava/operators/Readline", "(Lorg/perlonjava/runtime/RuntimeList;)Lorg/perlonjava/runtime/RuntimeScalar;");
181181

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

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
package org.perlonjava.operators;
22

33
import org.perlonjava.operators.unpack.*;
4-
import org.perlonjava.runtime.PerlCompilerException;
5-
import org.perlonjava.runtime.RuntimeBase;
6-
import org.perlonjava.runtime.RuntimeList;
7-
import org.perlonjava.runtime.RuntimeScalar;
4+
import org.perlonjava.runtime.*;
85

96
import java.util.*;
107

8+
import static org.perlonjava.runtime.RuntimeScalarCache.scalarUndef;
9+
1110
/**
1211
* Provides functionality to unpack binary data into a list of scalars
1312
* based on a specified template, similar to Perl's unpack function.
@@ -41,20 +40,22 @@ public class Unpack {
4140
handlers.put('H', new HexStringFormatHandler('H'));
4241
handlers.put('W', new WFormatHandler());
4342
handlers.put('x', new XFormatHandler());
43+
handlers.put('X', new XBackwardHandler()); // Add this line
4444
handlers.put('w', new WBERFormatHandler());
4545
handlers.put('p', new PointerFormatHandler());
4646
handlers.put('u', new UuencodeFormatHandler());
4747
handlers.put('@', new AtFormatHandler()); // Add this line
4848
// Note: U handler is created dynamically based on startsWithU
4949
}
5050

51-
public static RuntimeList unpack(RuntimeList args) {
52-
if (args.elements.size() < 2) {
53-
throw new PerlCompilerException("unpack: not enough arguments");
54-
}
55-
56-
RuntimeScalar templateScalar = (RuntimeScalar) args.elements.get(0);
57-
RuntimeScalar packedData = args.elements.get(1).scalar();
51+
/** unpack(template, data)
52+
*
53+
* @param args
54+
* @return
55+
*/
56+
public static RuntimeList unpack(int ctx, RuntimeBase... args) {
57+
RuntimeScalar templateScalar = (RuntimeScalar) args[0];
58+
RuntimeScalar packedData = args.length > 1 ? args[1].scalar() : scalarUndef;
5859

5960
String template = templateScalar.toString();
6061
String dataString = packedData.toString();
@@ -210,13 +211,16 @@ public static RuntimeList unpack(RuntimeList args) {
210211
boolean hasStarAfterSlash = false;
211212
if (i + 1 < template.length() && template.charAt(i + 1) == '*') {
212213
hasStarAfterSlash = true;
213-
i++; // consume the '*'
214+
i++; // Move to the '*'
214215
}
215216

216217
// Unpack the string with the count from the previous numeric value
217218
FormatHandler stringHandler = handlers.get(stringFormat);
218-
// Pass slashCount as the count, and hasStarAfterSlash as isStarCount
219219
stringHandler.unpack(state, values, slashCount, hasStarAfterSlash);
220+
221+
// IMPORTANT: Skip past all characters we've processed
222+
// The continue statement will skip the normal i++ at the end of the loop
223+
i++; // Move past the last character we processed
220224
continue;
221225
}
222226

@@ -251,14 +255,21 @@ public static RuntimeList unpack(RuntimeList args) {
251255
while (j < template.length() && Character.isDigit(template.charAt(j))) {
252256
j++;
253257
}
254-
count = Integer.parseInt(template.substring(i + 1, j));
258+
String countStr = template.substring(i + 1, j);
259+
count = Integer.parseInt(countStr);
260+
if (format == '@') {
261+
System.err.println("DEBUG: @ count string '" + countStr + "' parsed to " + count);
262+
}
255263
i = j - 1;
256264
}
257265
}
258266

259267
// Get handler and unpack
260268
FormatHandler handler = getHandler(format, startsWithU);
261269
if (handler != null) {
270+
if (format == '@') {
271+
System.err.println("DEBUG: Calling @ handler with count=" + count);
272+
}
262273
// For 'p' format, check and consume endianness modifiers
263274
if (format == 'p' && i + 1 < template.length()) {
264275
char nextChar = template.charAt(i + 1);

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,4 +192,18 @@ public int getTotalLength() {
192192
return originalBytes.length;
193193
}
194194
}
195+
196+
/**
197+
* Gets the current code point index (for character mode).
198+
*/
199+
public int getCurrentCodePointIndex() {
200+
return codePointIndex;
201+
}
202+
203+
/**
204+
* Sets the code point index directly (for character mode).
205+
*/
206+
public void setCodePointIndex(int index) {
207+
codePointIndex = Math.max(0, Math.min(index, codePoints.length));
208+
}
195209
}

src/main/java/org/perlonjava/operators/unpack/AtFormatHandler.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,18 @@
1010
*/
1111
public class AtFormatHandler implements FormatHandler {
1212
@Override
13-
public void unpack(UnpackState state, List<RuntimeBase> values, int count, boolean isStarCount) {
14-
// @ doesn't produce a value, it just moves the position
15-
System.err.println("DEBUG: AtFormatHandler called with count=" + count + ", isStarCount=" + isStarCount);
16-
if (isStarCount) {
17-
// @* means go to end of string
18-
state.setPosition(state.getTotalLength());
19-
} else {
20-
// @N means go to position N (0-based)
21-
state.setPosition(count);
22-
}
23-
System.err.println("DEBUG: After setPosition, codePointIndex should be " + count);
13+
public void unpack(UnpackState state, List<RuntimeBase> output, int count, boolean isStarCount) {
14+
System.err.println("DEBUG: AtFormatHandler.unpack called with count=" + count);
15+
System.err.println("DEBUG: Current position before setPosition: " +
16+
(state.isCharacterMode() ? state.getCurrentCodePointIndex() : state.getBuffer().position()));
17+
18+
// Set absolute position
19+
state.setPosition(count);
20+
21+
System.err.println("DEBUG: Current position after setPosition: " +
22+
(state.isCharacterMode() ? state.getCurrentCodePointIndex() : state.getBuffer().position()));
23+
24+
// @ doesn't produce any output values
2425
}
2526

2627
@Override

src/main/java/org/perlonjava/operators/unpack/StringFormatHandler.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,19 @@ public StringFormatHandler(char format) {
2121
@Override
2222
public void unpack(UnpackState state, List<RuntimeBase> output, int count, boolean isStarCount) {
2323
if (state.isCharacterMode()) {
24+
// Debug: check current position
25+
System.err.println("DEBUG: StringFormatHandler in character mode, current position: " +
26+
state.getCurrentCodePointIndex());
27+
2428
// In character mode, read characters directly
2529
StringBuilder sb = new StringBuilder();
2630
int charsToRead = Math.min(count, state.remainingCodePoints());
2731

2832
for (int i = 0; i < charsToRead; i++) {
2933
if (state.hasMoreCodePoints()) {
30-
sb.appendCodePoint(state.nextCodePoint());
34+
int cp = state.nextCodePoint();
35+
System.err.println("DEBUG: Read code point: " + cp + " ('" + (char)cp + "')");
36+
sb.appendCodePoint(cp);
3137
} else {
3238
break;
3339
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package org.perlonjava.operators.unpack;
2+
3+
import org.perlonjava.operators.UnpackState;
4+
import org.perlonjava.runtime.RuntimeBase;
5+
6+
import java.nio.ByteBuffer;
7+
import java.util.List;
8+
9+
/**
10+
* Handles 'X' format - backup (move backward).
11+
*/
12+
public class XBackwardHandler implements FormatHandler {
13+
@Override
14+
public void unpack(UnpackState state, List<RuntimeBase> output, int count, boolean isStarCount) {
15+
// X format moves backward but doesn't add any values to the result
16+
if (state.isCharacterMode()) {
17+
// In character mode, move backward by count code points
18+
int currentPos = state.getCurrentCodePointIndex();
19+
int newPos = Math.max(0, currentPos - count);
20+
state.setCodePointIndex(newPos);
21+
} else {
22+
// In byte mode, move backward in the buffer
23+
ByteBuffer buffer = state.getBuffer();
24+
int currentPos = buffer.position();
25+
int newPos = Math.max(0, currentPos - count);
26+
buffer.position(newPos);
27+
}
28+
// Don't add anything to output - X just moves backward
29+
}
30+
31+
@Override
32+
public int getFormatSize() {
33+
return 1;
34+
}
35+
}

src/main/java/org/perlonjava/parser/CoreOperatorResolver.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ public static Node parseCoreOperator(Parser parser, LexerToken token, int startI
6464
case "select" -> OperatorParser.parseSelect(parser, token, currentIndex);
6565
case "stat", "lstat" -> OperatorParser.parseStat(parser, token, currentIndex);
6666
case "readpipe" -> OperatorParser.parseReadpipe(parser);
67-
case "unpack" -> OperatorParser.parseUnpack(parser, token);
6867
case "bless" -> OperatorParser.parseBless(parser, currentIndex);
6968
case "split" -> OperatorParser.parseSplit(parser, token, currentIndex);
7069
case "push", "unshift", "join", "sprintf" ->

src/main/java/org/perlonjava/parser/OperatorParser.java

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -396,19 +396,6 @@ static OperatorNode parseDelete(Parser parser, LexerToken token, int currentInde
396396
return new OperatorNode(token.text, operand, currentIndex);
397397
}
398398

399-
static OperatorNode parseUnpack(Parser parser, LexerToken token) {
400-
ListNode operand;
401-
// Handle 'unpack' operator with one mandatory and one optional argument
402-
operand = ListParser.parseZeroOrMoreList(parser, 1, false, true, false, false);
403-
if (operand.elements.size() == 1) {
404-
// Create `$_` variable if only one argument is provided
405-
operand.elements.add(
406-
ParserNodeUtils.scalarUnderscore(parser)
407-
);
408-
}
409-
return new OperatorNode(token.text, operand, parser.tokenIndex);
410-
}
411-
412399
static BinaryOperatorNode parseBless(Parser parser, int currentIndex) {
413400
// Handle 'bless' operator with special handling for class name
414401
Node ref;

0 commit comments

Comments
 (0)