diff --git a/src/main/java/org/perlonjava/backend/bytecode/BytecodeCompiler.java b/src/main/java/org/perlonjava/backend/bytecode/BytecodeCompiler.java index a258f6c71..d8b15e446 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/BytecodeCompiler.java +++ b/src/main/java/org/perlonjava/backend/bytecode/BytecodeCompiler.java @@ -3567,18 +3567,29 @@ public void visit(For3Node node) { // Handle bare blocks (simple blocks) differently - they execute once, not loop if (node.isSimpleBlock) { // Simple bare block: { statements; } - // Create a new scope for the block + // Allocate a result register before entering scope when in non-VOID context, + // so the value is accessible after the scope exits (same pattern as BlockNode). + int outerResultReg = -1; + if (currentCallContext != RuntimeContextType.VOID) { + outerResultReg = allocateRegister(); + } enterScope(); try { // Just execute the body once, no loop if (node.body != null) { node.body.accept(this); } - lastResultReg = -1; // Block returns empty + // Save last statement result into outer register before exiting scope + if (outerResultReg >= 0 && lastResultReg >= 0) { + emit(Opcodes.MOVE); + emitReg(outerResultReg); + emitReg(lastResultReg); + } } finally { // Exit scope to clean up lexical variables exitScope(); } + lastResultReg = outerResultReg; return; } diff --git a/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java b/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java index 677ea9669..f0b90bb9a 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java +++ b/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java @@ -2083,6 +2083,19 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c break; } + case Opcodes.POP_PACKAGE: + // Scoped package block exit — restore handled by POP_LOCAL_LEVEL. + break; + + case Opcodes.DO_FILE: { + int rd = bytecode[pc++]; + int fileReg = bytecode[pc++]; + int ctx = bytecode[pc++]; + RuntimeScalar file = ((RuntimeBase) registers[fileReg]).scalar(); + registers[rd] = ModuleOperators.doFile(file, ctx); + break; + } + default: // Unknown opcode int opcodeInt = opcode; diff --git a/src/main/java/org/perlonjava/backend/bytecode/CompileAssignment.java b/src/main/java/org/perlonjava/backend/bytecode/CompileAssignment.java index 407659b08..ea24bdb7f 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/CompileAssignment.java +++ b/src/main/java/org/perlonjava/backend/bytecode/CompileAssignment.java @@ -1,5 +1,6 @@ package org.perlonjava.backend.bytecode; +import org.perlonjava.frontend.analysis.LValueVisitor; import org.perlonjava.frontend.astnode.*; import org.perlonjava.runtime.runtimetypes.NameNormalizer; import org.perlonjava.runtime.runtimetypes.RuntimeContextType; @@ -1457,6 +1458,9 @@ public static void compileAssignmentOperator(BytecodeCompiler bytecodeCompiler, // List assignment: ($a, $b) = ... or () = ... // In scalar context, returns the number of elements on RHS // In list context, returns the RHS list + // Validate lvalue context - throws PerlCompilerException for invalid LHS + // (e.g. "($a ? $x : ($y)) = 5" -> "Assignment to both a list and a scalar") + LValueVisitor.getContext(node.left); ListNode listNode = (ListNode) node.left; // Compile RHS in LIST context to get all elements diff --git a/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java b/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java index 831cda4ea..991f416ae 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java +++ b/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java @@ -2862,6 +2862,20 @@ public static void visitOperator(BytecodeCompiler bytecodeCompiler, OperatorNode bytecodeCompiler.emitReg(patternReg); bytecodeCompiler.emit(bytecodeCompiler.currentCallContext); bytecodeCompiler.lastResultReg = rd; + } else if (op.equals("doFile")) { + // do FILE: executes a Perl file + int savedContext = bytecodeCompiler.currentCallContext; + bytecodeCompiler.currentCallContext = RuntimeContextType.SCALAR; + node.operand.accept(bytecodeCompiler); + bytecodeCompiler.currentCallContext = savedContext; + int fileReg = bytecodeCompiler.lastResultReg; + + int rd = bytecodeCompiler.allocateRegister(); + bytecodeCompiler.emit(Opcodes.DO_FILE); + bytecodeCompiler.emitReg(rd); + bytecodeCompiler.emitReg(fileReg); + bytecodeCompiler.emit(bytecodeCompiler.currentCallContext); + bytecodeCompiler.lastResultReg = rd; } else { bytecodeCompiler.throwCompilerException("Unsupported operator: " + op); } diff --git a/src/main/java/org/perlonjava/backend/bytecode/InterpretedCode.java b/src/main/java/org/perlonjava/backend/bytecode/InterpretedCode.java index fd676f89a..8f2b41f9a 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/InterpretedCode.java +++ b/src/main/java/org/perlonjava/backend/bytecode/InterpretedCode.java @@ -1440,6 +1440,18 @@ public String disassemble() { break; // GENERATED_DISASM_END + case Opcodes.SET_PACKAGE: + sb.append("SET_PACKAGE '").append(stringPool[bytecode[pc++]]).append("'\n"); + break; + case Opcodes.PUSH_PACKAGE: + sb.append("PUSH_PACKAGE '").append(stringPool[bytecode[pc++]]).append("'\n"); + break; + case Opcodes.POP_PACKAGE: + sb.append("POP_PACKAGE\n"); + break; + case Opcodes.DO_FILE: + sb.append("DO_FILE r").append(bytecode[pc++]).append(" = doFile(r").append(bytecode[pc++]).append(") ctx=").append(bytecode[pc++]).append("\n"); + break; default: sb.append("UNKNOWN(").append(opcode).append(")\n"); break; diff --git a/src/main/java/org/perlonjava/backend/bytecode/Opcodes.java b/src/main/java/org/perlonjava/backend/bytecode/Opcodes.java index ad988417b..46d83a45d 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/Opcodes.java +++ b/src/main/java/org/perlonjava/backend/bytecode/Opcodes.java @@ -1111,5 +1111,10 @@ public class Opcodes { * Format: GLOB_OP rd globId patternReg ctx */ public static final short GLOB_OP = 339; + /** Execute a file: rd = ModuleOperators.doFile(fileReg, ctx) + * Implements Perl's do FILE operator. + * Format: DO_FILE rd fileReg ctx */ + public static final short DO_FILE = 340; + private Opcodes() {} // Utility class - no instantiation }