Skip to content

Commit 83e2689

Browse files
committed
Fix test regressions in op/repeat.t and op/tiehash.t
This commit fixes test regressions introduced by commit a039ec0. Test Results: - op/repeat.t: 47/50 passing (was 0/50, now restored to baseline) - op/tiehash.t: 27/27 passing (was 0/27, now fully restored) Fixes Applied: 1. OperatorParser.java - Fix scalar operator parsing - Removed ensureOneOperand() check for scalar operator only - scalar can accept comma expressions like scalar((nil) x 3, 1) - values/keys/each still need single operand check - Fixes op/repeat.t regression 2. RuntimeScalar.java - Add UNDEF autovivification to NonStrict methods - arrayDerefNonStrict() now handles UNDEF case (creates autovivified array) - hashDerefNonStrict() now handles UNDEF case (creates autovivified hash) - Matches behavior of arrayDeref() and hashDeref() for consistency - Required for autovivification when strict refs is disabled 3. RuntimeBaseProxy.java - Fix proxy state synchronization - arrayDeref() and hashDeref() now update proxy type/value after vivification - Added arrayDerefNonStrict() and hashDerefNonStrict() overrides - All four methods now properly sync proxy state with lvalue - Fixes tied hash autovivification in op/tiehash.t Root Cause: Commit a039ec0 added symbolic reference support but had bugs: - Overly strict parser checks on scalar operator - Missing autovivification in NonStrict methods - Incomplete proxy state updates after vivification
1 parent a039ec0 commit 83e2689

File tree

3 files changed

+52
-5
lines changed

3 files changed

+52
-5
lines changed

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -476,14 +476,18 @@ static OperatorNode parseKeys(Parser parser, LexerToken token, int currentIndex)
476476
// Handle operators with a single operand
477477
// For scalar, values, keys, each: parse with precedence that includes postfix operators ([], {}, ->)
478478
// Named unary operators have precedence between 20 and 21 in Perl
479-
// This allows expressions like: values $hashref->%* or keys $hashref->%*
479+
// This allows expressions like: values $hashref->%* or keys $hashref->%* or scalar((nil) x 3, 1)
480480
if (operator.equals("scalar") || operator.equals("values") || operator.equals("keys") || operator.equals("each")) {
481481
operand = parser.parseExpression(parser.getPrecedence("=~")); // precedence 20
482-
// For values/keys/each, check if operand is null (no argument provided)
482+
// Check if operand is null (no argument provided)
483483
if (operand == null) {
484484
throw new PerlCompilerException(currentIndex, "Not enough arguments for " + operator, parser.ctx.errorUtil);
485485
}
486-
operand = ensureOneOperand(parser, token, operand);
486+
// scalar can accept comma expressions like scalar((nil) x 3, 1)
487+
// but values/keys/each need single operand check
488+
if (!operator.equals("scalar")) {
489+
operand = ensureOneOperand(parser, token, operand);
490+
}
487491
} else {
488492
operand = ParsePrimary.parsePrimary(parser);
489493
// Check if operand is null (no argument provided)

src/main/java/org/perlonjava/runtime/RuntimeBaseProxy.java

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,41 @@ public RuntimeScalar set(boolean value) {
7676
@Override
7777
public RuntimeHash hashDeref() {
7878
vivify(); // Ensure the scalar exists in parent hash
79-
return lvalue.hashDeref(); // Delegate to the actual scalar
79+
RuntimeHash result = lvalue.hashDeref(); // Delegate to the actual scalar
80+
// Update proxy's type and value to match lvalue after vivification
81+
this.type = lvalue.type;
82+
this.value = lvalue.value;
83+
return result;
8084
}
8185

8286
@Override
8387
public RuntimeArray arrayDeref() {
8488
vivify(); // Ensure the scalar exists in parent hash
85-
return lvalue.arrayDeref(); // Delegate to the actual scalar
89+
RuntimeArray result = lvalue.arrayDeref(); // Delegate to the actual scalar
90+
// Update proxy's type and value to match lvalue after vivification
91+
this.type = lvalue.type;
92+
this.value = lvalue.value;
93+
return result;
94+
}
95+
96+
@Override
97+
public RuntimeArray arrayDerefNonStrict(String packageName) {
98+
vivify(); // Ensure the scalar exists in parent hash/array
99+
RuntimeArray result = lvalue.arrayDerefNonStrict(packageName); // Delegate to the actual scalar
100+
// Update proxy's type and value to match lvalue after vivification
101+
this.type = lvalue.type;
102+
this.value = lvalue.value;
103+
return result;
104+
}
105+
106+
@Override
107+
public RuntimeHash hashDerefNonStrict(String packageName) {
108+
vivify(); // Ensure the scalar exists in parent hash/array
109+
RuntimeHash result = lvalue.hashDerefNonStrict(packageName); // Delegate to the actual scalar
110+
// Update proxy's type and value to match lvalue after vivification
111+
this.type = lvalue.type;
112+
this.value = lvalue.value;
113+
return result;
86114
}
87115

88116
/**

src/main/java/org/perlonjava/runtime/RuntimeScalar.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,13 @@ public RuntimeHash hashDerefNonStrict(String packageName) {
918918
}
919919

920920
return switch (type) {
921+
case UNDEF -> {
922+
// Don't autovivify read-only scalars (like constants)
923+
if (this instanceof RuntimeScalarReadOnly) {
924+
yield new RuntimeHash();
925+
}
926+
yield AutovivificationHash.createAutovivifiedHash(this);
927+
}
921928
case HASHREFERENCE -> (RuntimeHash) value;
922929
case TIED_SCALAR -> tiedFetch().hashDerefNonStrict(packageName);
923930
default -> {
@@ -946,6 +953,14 @@ public RuntimeArray arrayDerefNonStrict(String packageName) {
946953
}
947954

948955
return switch (type) {
956+
case UNDEF -> {
957+
// Don't autovivify read-only scalars (like constants)
958+
// This matches Perl's behavior where 1->[0] returns undef without error
959+
if (this instanceof RuntimeScalarReadOnly) {
960+
yield new RuntimeArray();
961+
}
962+
yield AutovivificationArray.createAutovivifiedArray(this);
963+
}
949964
case ARRAYREFERENCE -> (RuntimeArray) value;
950965
case TIED_SCALAR -> tiedFetch().arrayDerefNonStrict(packageName);
951966
default -> {

0 commit comments

Comments
 (0)