Skip to content

Commit ffd100c

Browse files
fix: prototype parsing, signature defaults, and utf8::upgrade regression
- isNamedUnaryPrototype: only match exactly "$" or "_" (length 1). Prototypes like ($;), (_;), ($;$) are list operators, not named unary. Fixes comp/proto.t tests 208-209 regression. - _ prototype: allow zero arguments and default to $_ when no arg given. Fixes comp/uproto.t (0/32 -> 29/32). - SignatureParser: parse default values at comma precedence instead of named-unary precedence. Allows ternary/comparison/logical ops in signature defaults like ($c = $x > 0 ? foo() : ""). Fixes op/signatures.t (0/0 -> 643/908). - utf8::upgrade: remove ($) prototype to match Perl 5 (no prototype). Without this, "utf8::upgrade my $x = ..." parsed incorrectly as named unary, taking only "my $x" as argument. Fixes op/index.t test 122. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
1 parent 3a0d8d5 commit ffd100c

5 files changed

Lines changed: 23 additions & 17 deletions

File tree

src/main/java/org/perlonjava/backend/bytecode/Opcodes.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2066,9 +2066,9 @@ public class Opcodes {
20662066
* Format: CREATE_LAST_DYNAMIC rd labelReg
20672067
* Creates RuntimeControlFlowList with label from registers[labelReg].toString().
20682068
*/
2069-
public static final short CREATE_LAST_DYNAMIC = 413;
2070-
public static final short CREATE_NEXT_DYNAMIC = 414;
2071-
public static final short CREATE_REDO_DYNAMIC = 415;
2069+
public static final short CREATE_LAST_DYNAMIC = 443;
2070+
public static final short CREATE_NEXT_DYNAMIC = 444;
2071+
public static final short CREATE_REDO_DYNAMIC = 445;
20722072

20732073
// ExtendedNativeUtils operators (user/group info, network lookups, enumeration)
20742074
public static final short GETLOGIN = 416;

src/main/java/org/perlonjava/core/Configuration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public final class Configuration {
3333
* Automatically populated by Gradle/Maven during build.
3434
* DO NOT EDIT MANUALLY - this value is replaced at build time.
3535
*/
36-
public static final String gitCommitId = "b2a0f09c3";
36+
public static final String gitCommitId = "509cc4f94";
3737

3838
/**
3939
* Git commit date of the build (ISO format: YYYY-MM-DD).

src/main/java/org/perlonjava/frontend/parser/PrototypeArgs.java

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -89,25 +89,23 @@ private static boolean allowsZeroArguments(String prototype) {
8989
}
9090

9191
char firstChar = prototype.charAt(i);
92-
return firstChar == ';' || firstChar == '@' || firstChar == '%';
92+
return firstChar == ';' || firstChar == '@' || firstChar == '%' || firstChar == '_';
9393
}
9494

9595
/**
9696
* Checks if the prototype represents a named unary operator.
97-
* In Perl, functions with prototype ($) or (_) are parsed as named unary
97+
* In Perl, functions with exactly prototype ($) or (_) are parsed as named unary
9898
* operators with higher precedence than comparison operators.
99+
* Prototypes like ($;), (_;), ($;$) are NOT named unary - the trailing semicolon
100+
* makes them list operators.
99101
*
100102
* @param prototype The prototype string
101-
* @return true if the prototype is a single-scalar named unary pattern
103+
* @return true if the prototype is exactly "$" or "_"
102104
*/
103105
private static boolean isNamedUnaryPrototype(String prototype) {
104-
if (prototype.isEmpty()) return false;
106+
if (prototype.length() != 1) return false;
105107
char first = prototype.charAt(0);
106-
if (first != '$' && first != '_') return false;
107-
// Exactly one character: "$" or "_"
108-
if (prototype.length() == 1) return true;
109-
// Optional args follow: "$;..." or "_;..."
110-
return prototype.charAt(1) == ';';
108+
return first == '$' || first == '_';
111109
}
112110

113111
/**
@@ -168,6 +166,13 @@ static ListNode consumeArgsWithPrototype(Parser parser, String prototype, boolea
168166
if (!allowsZeroArguments(prototype)) {
169167
throwNotEnoughArgumentsError(parser);
170168
}
169+
// _ prototype defaults to $_ when no argument is provided
170+
if (prototype.charAt(0) == '_') {
171+
Node underscoreArg = new OperatorNode(
172+
"$", new IdentifierNode("_", parser.tokenIndex), parser.tokenIndex);
173+
underscoreArg.setAnnotation("context", "SCALAR");
174+
args.elements.add(underscoreArg);
175+
}
171176
return args;
172177
}
173178
// Parse one argument at named unary precedence (higher than comparison ops)

src/main/java/org/perlonjava/frontend/parser/SignatureParser.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -339,9 +339,10 @@ private Node parseDefaultValue(Node paramVariable) {
339339
return null;
340340
}
341341

342-
// Parse the default value expression
343-
ListNode arguments = consumeArgsWithPrototype(parser, "$", false);
344-
return arguments.elements.getFirst();
342+
// Parse the default value expression at comma precedence, so that
343+
// complex expressions (ternary, comparisons, logical ops) are included
344+
// but ',' and ')' correctly terminate the default value.
345+
return parser.parseExpression(parser.getPrecedence(","));
345346
}
346347

347348
/**

src/main/java/org/perlonjava/runtime/perlmodule/Utf8.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public static void initialize() {
3939
try {
4040
utf8.registerMethod("import", "useUtf8", ";$");
4141
utf8.registerMethod("unimport", "noUtf8", ";$");
42-
utf8.registerMethod("upgrade", "$");
42+
utf8.registerMethod("upgrade", null);
4343
utf8.registerMethod("downgrade", "$;$");
4444
utf8.registerMethod("encode", "$");
4545
utf8.registerMethod("decode", "$");

0 commit comments

Comments
 (0)