Skip to content

Commit f09a84e

Browse files
authored
Merge pull request #92 from fglock/fix/comp-proto-test
Fix comp/proto.t test failures
2 parents 0f32ffa + 3b67769 commit f09a84e

2 files changed

Lines changed: 71 additions & 0 deletions

File tree

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

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,61 @@ private static void handlePlusArgument(Parser parser, ListNode args, boolean isO
551551
}
552552
}
553553

554+
/**
555+
* Unwraps unary plus from expressions like +(%hash) or +(@array) for backslash prototypes.
556+
* In Perl, +() is used for disambiguation but should be transparent for \% and \@ prototypes.
557+
*
558+
* @param arg The argument node to potentially unwrap
559+
* @param refType The reference type from the prototype ('%', '@', etc.)
560+
* @return The unwrapped node if applicable, or the original node
561+
*/
562+
private static Node unwrapUnaryPlus(Node arg, char refType) {
563+
// Only unwrap for \% and \@ prototypes
564+
if (refType != '%' && refType != '@') {
565+
return arg;
566+
}
567+
568+
// Check if arg is unary plus: OperatorNode with operator "+"
569+
if (!(arg instanceof OperatorNode plusOp) || !plusOp.operator.equals("+")) {
570+
return arg;
571+
}
572+
573+
// Get the operand of the unary plus
574+
Node operand = plusOp.operand;
575+
576+
// If the operand is a ListNode with a single element, extract it
577+
if (operand instanceof ListNode listNode && listNode.elements.size() == 1) {
578+
operand = listNode.elements.get(0);
579+
}
580+
581+
// Check if the operand is the expected type (hash or array variable)
582+
if (operand instanceof OperatorNode varOp) {
583+
String expectedSigil = (refType == '%') ? "%" : "@";
584+
if (varOp.operator.equals(expectedSigil)) {
585+
return operand;
586+
}
587+
}
588+
589+
// Also check if operand is directly a ListNode containing a hash/array expression
590+
// This handles cases like +(%hash) where the parentheses create a list context
591+
if (operand instanceof ListNode listNode) {
592+
// In Perl +(%hash) should pass the hash itself, not a list
593+
// Look for a single hash or array variable inside the list
594+
if (listNode.elements.size() == 1) {
595+
Node element = listNode.elements.get(0);
596+
if (element instanceof OperatorNode varOp) {
597+
String expectedSigil = (refType == '%') ? "%" : "@";
598+
if (varOp.operator.equals(expectedSigil)) {
599+
return element;
600+
}
601+
}
602+
}
603+
}
604+
605+
// Return original if no match
606+
return arg;
607+
}
608+
554609
private static int handleBackslashArgument(Parser parser, ListNode args, String prototype, int prototypeIndex,
555610
boolean isOptional, boolean needComma) {
556611
if (prototypeIndex >= prototype.length()) {
@@ -572,6 +627,9 @@ private static int handleBackslashArgument(Parser parser, ListNode args, String
572627
// Restore flag
573628
parser.parsingTakeReference = oldParsingTakeReference;
574629
if (referenceArg != null) {
630+
// Handle +(%hash) and +(@array) constructs for \% and \@ prototypes
631+
// The unary + is used for disambiguation but should be transparent for prototypes
632+
referenceArg = unwrapUnaryPlus(referenceArg, refType);
575633
// For \& prototype, check for invalid forms like &foo(), foo(), or bareword foo
576634
if (refType == '&') {
577635
String subName = parser.ctx.symbolTable.getCurrentSubroutine();

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,19 @@ public RuntimeScalar set(RuntimeScalar value) {
119119
return value;
120120
case UNDEF:
121121
return value;
122+
case STRING:
123+
case BYTE_STRING:
124+
// Assigning a string to a stash entry sets the prototype for that symbol
125+
// e.g., $::{foo} = '$$' sets the prototype of &foo to '$$'
126+
RuntimeScalar codeRef = GlobalVariable.getGlobalCodeRef(this.globName);
127+
if (codeRef.type == CODE) {
128+
((RuntimeCode) codeRef.value).prototype = value.toString();
129+
} else {
130+
// Create a new RuntimeCode with the prototype
131+
RuntimeCode code = new RuntimeCode(value.toString(), null);
132+
codeRef.set(new RuntimeScalar(code));
133+
}
134+
return value;
122135
}
123136
throw new IllegalStateException("typeglob assignment not implemented for " + value.type);
124137
}

0 commit comments

Comments
 (0)