Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1477,6 +1477,7 @@ void handleCompoundAssignment(BinaryOperatorNode node) {
case ">>=" -> emit(Opcodes.RIGHT_SHIFT_ASSIGN); // Right shift
case "&&=" -> emit(Opcodes.LOGICAL_AND_ASSIGN); // Logical AND
case "||=" -> emit(Opcodes.LOGICAL_OR_ASSIGN); // Logical OR
case "//=" -> emit(Opcodes.DEFINED_OR_ASSIGN); // Defined-or
default -> {
throwCompilerException("Unknown compound assignment operator: " + op);
currentCallContext = savedContext;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -534,9 +534,11 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c
int rd = bytecode[pc++];
int rs1 = bytecode[pc++];
int rs2 = bytecode[pc++];
RuntimeBase concatLeft = registers[rs1];
RuntimeBase concatRight = registers[rs2];
registers[rd] = StringOperators.stringConcat(
(RuntimeScalar) registers[rs1],
(RuntimeScalar) registers[rs2]
concatLeft instanceof RuntimeScalar ? (RuntimeScalar) concatLeft : concatLeft.scalar(),
concatRight instanceof RuntimeScalar ? (RuntimeScalar) concatRight : concatRight.scalar()
);
break;
}
Expand Down Expand Up @@ -706,6 +708,12 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c
pc = OpcodeHandlerExtended.executeLogicalOrAssign(bytecode, pc, registers);
break;

case Opcodes.DEFINED_OR_ASSIGN:
// Compound assignment: rd //= rs (short-circuit)
// Format: DEFINED_OR_ASSIGN rd rs
pc = OpcodeHandlerExtended.executeDefinedOrAssign(bytecode, pc, registers);
break;

// =================================================================
// SHIFT OPERATIONS
// =================================================================
Expand Down Expand Up @@ -829,6 +837,14 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c
break;
}

case Opcodes.SET_ARRAY_LAST_INDEX: {
int arrayReg = bytecode[pc++];
int valueReg = bytecode[pc++];
RuntimeArray.indexLastElem((RuntimeArray) registers[arrayReg])
.set(((RuntimeScalar) registers[valueReg]));
break;
}

case Opcodes.CREATE_ARRAY: {
// Create array reference from list: rd = new RuntimeArray(rs_list).createReference()
// Array literals always return references in Perl
Expand Down Expand Up @@ -1199,6 +1215,14 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c
pc = OpcodeHandlerExtended.executeStringBitwiseXor(bytecode, pc, registers);
break;

case Opcodes.XOR_LOGICAL: {
int rd = bytecode[pc++];
int rs1 = bytecode[pc++];
int rs2 = bytecode[pc++];
registers[rd] = Operator.xor((RuntimeScalar) registers[rs1], (RuntimeScalar) registers[rs2]);
break;
}

case Opcodes.BITWISE_NOT_BINARY:
// Numeric bitwise NOT: rd = binary~ rs
// Format: BITWISE_NOT_BINARY rd rs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1032,8 +1032,13 @@ public static void compileAssignmentOperator(BytecodeCompiler bytecodeCompiler,
} else {
bytecodeCompiler.throwCompilerException("Assignment to unsupported array dereference");
}
} else if (leftOp.operator.equals("$#")) {
int arrayReg = CompileAssignment.resolveArrayForDollarHash(bytecodeCompiler, leftOp);
bytecodeCompiler.emit(Opcodes.SET_ARRAY_LAST_INDEX);
bytecodeCompiler.emitReg(arrayReg);
bytecodeCompiler.emitReg(valueReg);
bytecodeCompiler.lastResultReg = valueReg;
} else {
// chop/chomp cannot be used as lvalues (matches JVM compiler message)
if (leftOp.operator.equals("chop") || leftOp.operator.equals("chomp")) {
bytecodeCompiler.throwCompilerException("Can't modify " + leftOp.operator + " in scalar assignment");
}
Expand Down Expand Up @@ -1663,4 +1668,44 @@ public static void compileAssignmentOperator(BytecodeCompiler bytecodeCompiler,
bytecodeCompiler.currentCallContext = savedContext;
}
}

static int resolveArrayForDollarHash(BytecodeCompiler bytecodeCompiler, OperatorNode dollarHashOp) {
if (dollarHashOp.operand instanceof OperatorNode operandOp
&& operandOp.operator.equals("@") && operandOp.operand instanceof IdentifierNode idNode) {
String varName = "@" + idNode.name;
if (bytecodeCompiler.hasVariable(varName)) {
return bytecodeCompiler.getVariableRegister(varName);
}
int arrayReg = bytecodeCompiler.allocateRegister();
String globalName = NameNormalizer.normalizeVariableName(idNode.name, bytecodeCompiler.getCurrentPackage());
int nameIdx = bytecodeCompiler.addToStringPool(globalName);
bytecodeCompiler.emit(Opcodes.LOAD_GLOBAL_ARRAY);
bytecodeCompiler.emitReg(arrayReg);
bytecodeCompiler.emit(nameIdx);
return arrayReg;
} else if (dollarHashOp.operand instanceof IdentifierNode idNode) {
String varName = "@" + idNode.name;
if (bytecodeCompiler.hasVariable(varName)) {
return bytecodeCompiler.getVariableRegister(varName);
}
int arrayReg = bytecodeCompiler.allocateRegister();
String globalName = NameNormalizer.normalizeVariableName(idNode.name, bytecodeCompiler.getCurrentPackage());
int nameIdx = bytecodeCompiler.addToStringPool(globalName);
bytecodeCompiler.emit(Opcodes.LOAD_GLOBAL_ARRAY);
bytecodeCompiler.emitReg(arrayReg);
bytecodeCompiler.emit(nameIdx);
return arrayReg;
} else if (dollarHashOp.operand instanceof OperatorNode operandOp && operandOp.operator.equals("$")) {
operandOp.accept(bytecodeCompiler);
int refReg = bytecodeCompiler.lastResultReg;
int arrayReg = bytecodeCompiler.allocateRegister();
bytecodeCompiler.emitWithToken(Opcodes.DEREF_ARRAY, dollarHashOp.getIndex());
bytecodeCompiler.emitReg(arrayReg);
bytecodeCompiler.emitReg(refReg);
return arrayReg;
}
bytecodeCompiler.throwCompilerException("$# assignment requires array variable");
return -1;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,21 @@ static void visitBinaryOperator(BytecodeCompiler bytecodeCompiler, BinaryOperato
return;
}

// Handle I/O and misc binary operators that use MiscOpcodeHandler (filehandle + args → list)
switch (node.operator) {
case "binmode", "seek", "eof", "close", "fileno", "getc", "printf":
compileBinaryAsListOp(bytecodeCompiler, node);
return;
case "tell":
compileTellBinaryOp(bytecodeCompiler, node);
return;
case "join":
compileJoinBinaryOp(bytecodeCompiler, node);
return;
default:
break;
}

// Handle compound assignment operators (+=, -=, *=, /=, %=, .=, &=, |=, ^=, &.=, |.=, ^.=, binary&=, binary|=, binary^=, x=, **=, <<=, >>=, &&=, ||=)
if (node.operator.equals("+=") || node.operator.equals("-=") ||
node.operator.equals("*=") || node.operator.equals("/=") ||
Expand All @@ -94,6 +109,7 @@ static void visitBinaryOperator(BytecodeCompiler bytecodeCompiler, BinaryOperato
node.operator.equals("x=") || node.operator.equals("**=") ||
node.operator.equals("<<=") || node.operator.equals(">>=") ||
node.operator.equals("&&=") || node.operator.equals("||=") ||
node.operator.equals("//=") ||
node.operator.startsWith("binary")) { // Handle binary&=, binary|=, binary^=
bytecodeCompiler.handleCompoundAssignment(node);
return;
Expand Down Expand Up @@ -633,6 +649,98 @@ else if (node.right instanceof BinaryOperatorNode) {
int rd = CompileBinaryOperatorHelper.compileBinaryOperatorSwitch(bytecodeCompiler, node.operator, rs1, rs2, node.getIndex());


bytecodeCompiler.lastResultReg = rd;
}

private static void compileBinaryAsListOp(BytecodeCompiler bytecodeCompiler, BinaryOperatorNode node) {
node.left.accept(bytecodeCompiler);
int fhReg = bytecodeCompiler.lastResultReg;

java.util.List<Integer> argRegs = new java.util.ArrayList<>();
argRegs.add(fhReg);

if (node.right instanceof ListNode argsList) {
for (Node arg : argsList.elements) {
arg.accept(bytecodeCompiler);
argRegs.add(bytecodeCompiler.lastResultReg);
}
} else {
node.right.accept(bytecodeCompiler);
argRegs.add(bytecodeCompiler.lastResultReg);
}

int argsListReg = bytecodeCompiler.allocateRegister();
bytecodeCompiler.emit(Opcodes.CREATE_LIST);
bytecodeCompiler.emitReg(argsListReg);
bytecodeCompiler.emit(argRegs.size());
for (int argReg : argRegs) {
bytecodeCompiler.emitReg(argReg);
}

int opcode = switch (node.operator) {
case "binmode" -> Opcodes.BINMODE;
case "seek" -> Opcodes.SEEK;
case "eof" -> Opcodes.EOF_OP;
case "close" -> Opcodes.CLOSE;
case "fileno" -> Opcodes.FILENO;
case "getc" -> Opcodes.GETC;
case "printf" -> Opcodes.PRINTF;
default -> throw new RuntimeException("Unknown operator: " + node.operator);
};

int rd = bytecodeCompiler.allocateRegister();
bytecodeCompiler.emit(opcode);
bytecodeCompiler.emitReg(rd);
bytecodeCompiler.emitReg(argsListReg);
bytecodeCompiler.emit(bytecodeCompiler.currentCallContext);

bytecodeCompiler.lastResultReg = rd;
}

private static void compileTellBinaryOp(BytecodeCompiler bytecodeCompiler, BinaryOperatorNode node) {
node.left.accept(bytecodeCompiler);
int fhReg = bytecodeCompiler.lastResultReg;

int rd = bytecodeCompiler.allocateRegister();
bytecodeCompiler.emit(Opcodes.TELL);
bytecodeCompiler.emitReg(rd);
bytecodeCompiler.emitReg(fhReg);

bytecodeCompiler.lastResultReg = rd;
}

private static void compileJoinBinaryOp(BytecodeCompiler bytecodeCompiler, BinaryOperatorNode node) {
node.left.accept(bytecodeCompiler);
int separatorReg = bytecodeCompiler.lastResultReg;

int listReg;
if (node.right instanceof ListNode listNode) {
java.util.List<Integer> argRegs = new java.util.ArrayList<>();
for (Node arg : listNode.elements) {
arg.accept(bytecodeCompiler);
argRegs.add(bytecodeCompiler.lastResultReg);
}
listReg = bytecodeCompiler.allocateRegister();
bytecodeCompiler.emit(Opcodes.CREATE_LIST);
bytecodeCompiler.emitReg(listReg);
bytecodeCompiler.emit(argRegs.size());
for (int argReg : argRegs) {
bytecodeCompiler.emitReg(argReg);
}
} else {
int savedContext = bytecodeCompiler.currentCallContext;
bytecodeCompiler.currentCallContext = RuntimeContextType.LIST;
node.right.accept(bytecodeCompiler);
bytecodeCompiler.currentCallContext = savedContext;
listReg = bytecodeCompiler.lastResultReg;
}

int rd = bytecodeCompiler.allocateRegister();
bytecodeCompiler.emit(Opcodes.JOIN);
bytecodeCompiler.emitReg(rd);
bytecodeCompiler.emitReg(separatorReg);
bytecodeCompiler.emitReg(listReg);

bytecodeCompiler.lastResultReg = rd;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,12 @@ public static int compileBinaryOperatorSwitch(BytecodeCompiler bytecodeCompiler,
bytecodeCompiler.emitReg(rs1);
bytecodeCompiler.emitReg(rs2);
}
case "xor", "^^" -> {
bytecodeCompiler.emit(Opcodes.XOR_LOGICAL);
bytecodeCompiler.emitReg(rd);
bytecodeCompiler.emitReg(rs1);
bytecodeCompiler.emitReg(rs2);
}
case "..." -> {
// Flip-flop operator (.. and ...) - per-call-site state via unique ID
// Note: numeric range (..) is handled earlier in visitBinaryOperator for list context;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,18 @@ public static int executeLogicalOrAssign(int[] bytecode, int pc, RuntimeBase[] r
return pc;
}

public static int executeDefinedOrAssign(int[] bytecode, int pc, RuntimeBase[] registers) {
int rd = bytecode[pc++];
int rs = bytecode[pc++];
RuntimeScalar s1 = ((RuntimeBase) registers[rd]).scalar();
if (s1.getDefinedBoolean()) {
return pc;
}
RuntimeScalar s2 = ((RuntimeBase) registers[rs]).scalar();
((RuntimeScalar) registers[rd]).set(s2);
return pc;
}

/**
* Execute string concatenation assign operation.
* Format: STRING_CONCAT_ASSIGN rd rs
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/org/perlonjava/backend/bytecode/Opcodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -1140,5 +1140,14 @@ public class Opcodes {
* Format: HASH_KEYVALUE_SLICE rd hashReg keysListReg */
public static final short HASH_KEYVALUE_SLICE = 344;

/** Set $#array = value: Format: SET_ARRAY_LAST_INDEX arrayReg valueReg */
public static final short SET_ARRAY_LAST_INDEX = 347;

/** Logical xor: rd = left xor right. Format: XOR_LOGICAL rd rs1 rs2 */
public static final short XOR_LOGICAL = 348;

/** Defined-or assignment: rd //= rs. Format: DEFINED_OR_ASSIGN rd rs */
public static final short DEFINED_OR_ASSIGN = 349;

private Opcodes() {} // Utility class - no instantiation
}