Skip to content

Commit 0fb24a8

Browse files
fix: add ~ operator overload support and ;$ prototype named-unary parsing
- BitwiseOperators: check for overloaded '(~' on blessed objects in bitwiseNot() and integerBitwiseNot() before performing bitwise NOT - PrototypeArgs: treat ;$ and ;_ prototypes as named unary operators so that expressions like ArrayRef[Int] | HashRef[Int] parse correctly (| terminates the first argument instead of being consumed by it) Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
1 parent dbbc469 commit 0fb24a8

3 files changed

Lines changed: 31 additions & 6 deletions

File tree

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

Lines changed: 2 additions & 2 deletions
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 = "85273cb6a";
36+
public static final String gitCommitId = "dbbc469f5";
3737

3838
/**
3939
* Git commit date of the build (ISO format: YYYY-MM-DD).
@@ -48,7 +48,7 @@ public final class Configuration {
4848
* Parsed by App::perlbrew and other tools via: perl -V | grep "Compiled at"
4949
* DO NOT EDIT MANUALLY - this value is replaced at build time.
5050
*/
51-
public static final String buildTimestamp = "Apr 9 2026 21:49:03";
51+
public static final String buildTimestamp = "Apr 9 2026 22:02:12";
5252

5353
// Prevent instantiation
5454
private Configuration() {

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

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,21 @@ private static boolean allowsZeroArguments(String prototype) {
100100
* makes them list operators.
101101
*
102102
* @param prototype The prototype string
103-
* @return true if the prototype is exactly "$" or "_"
103+
* @return true if the prototype is "$", "_", ";$", or ";_"
104104
*/
105105
private static boolean isNamedUnaryPrototype(String prototype) {
106-
if (prototype.length() != 1) return false;
107-
char first = prototype.charAt(0);
108-
return first == '$' || first == '_';
106+
if (prototype.length() == 1) {
107+
char first = prototype.charAt(0);
108+
return first == '$' || first == '_';
109+
}
110+
// ";$" and ";_" are also named unary (optional single scalar argument).
111+
// This matters for expressions like ArrayRef[Int] | HashRef[Int] where
112+
// the | must NOT be consumed as part of ArrayRef's argument.
113+
if (prototype.length() == 2 && prototype.charAt(0) == ';') {
114+
char second = prototype.charAt(1);
115+
return second == '$' || second == '_';
116+
}
117+
return false;
109118
}
110119

111120
/**

src/main/java/org/perlonjava/runtime/operators/BitwiseOperators.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,14 @@ public static RuntimeScalar bitwiseXorBinary(RuntimeScalar runtimeScalar, Runtim
215215
* @return A new RuntimeScalar with the result of the bitwise NOT operation.
216216
*/
217217
public static RuntimeScalar bitwiseNot(RuntimeScalar runtimeScalar) {
218+
// Check for overloaded '~' operator on blessed objects
219+
int blessId = blessedId(runtimeScalar);
220+
if (blessId < 0) {
221+
RuntimeScalar result = OverloadContext.tryOneArgumentOverload(
222+
runtimeScalar, blessId, "(~", "~", BitwiseOperators::bitwiseNot);
223+
if (result != null) return result;
224+
}
225+
218226
// Fetch tied/readonly scalar once to avoid redundant FETCH calls
219227
RuntimeScalar val = runtimeScalar.type < RuntimeScalarType.TIED_SCALAR ? runtimeScalar :
220228
runtimeScalar.type == RuntimeScalarType.TIED_SCALAR ? runtimeScalar.tiedFetch() :
@@ -257,6 +265,14 @@ public static RuntimeScalar bitwiseNotBinary(RuntimeScalar runtimeScalar) {
257265
* @return A new RuntimeScalar with the result of the integer bitwise NOT operation.
258266
*/
259267
public static RuntimeScalar integerBitwiseNot(RuntimeScalar runtimeScalar) {
268+
// Check for overloaded '~' operator on blessed objects
269+
int blessId = blessedId(runtimeScalar);
270+
if (blessId < 0) {
271+
RuntimeScalar result = OverloadContext.tryOneArgumentOverload(
272+
runtimeScalar, blessId, "(~", "~", BitwiseOperators::integerBitwiseNot);
273+
if (result != null) return result;
274+
}
275+
260276
// Fetch tied/readonly scalar once to avoid redundant FETCH calls
261277
RuntimeScalar val = runtimeScalar.type < RuntimeScalarType.TIED_SCALAR ? runtimeScalar :
262278
runtimeScalar.type == RuntimeScalarType.TIED_SCALAR ? runtimeScalar.tiedFetch() :

0 commit comments

Comments
 (0)