Skip to content

Commit 10f00a7

Browse files
committed
Fix op/incfilter.t: Implement generator-style CODE references
- Support 'do \&generator' where generator is called repeatedly - Loop until CODE ref returns false (EOF indicator) - Accumulate content from $_ across multiple calls - Add support for 'do [\&coderef]' array reference syntax - Fixes tests 1-3 in op/incfilter.t - Unblocks 2 additional tests
1 parent d1a4eec commit 10f00a7

File tree

1 file changed

+49
-12
lines changed

1 file changed

+49
-12
lines changed

src/main/java/org/perlonjava/operators/ModuleOperators.java

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,29 @@ private static RuntimeBase doFile(RuntimeScalar runtimeScalar, boolean setINC, b
3636
String code = null;
3737
String actualFileName = null;
3838

39+
// Check if the argument is an ARRAY reference (for @INC filter with state support)
40+
if (runtimeScalar.type == RuntimeScalarType.REFERENCE &&
41+
runtimeScalar.value instanceof RuntimeArray) {
42+
// `do` ARRAY reference - array should contain [coderef, state...]
43+
RuntimeArray arr = (RuntimeArray) runtimeScalar.value;
44+
if (arr.size() > 0) {
45+
RuntimeScalar firstElem = arr.get(0);
46+
// The first element should be a CODE ref or filehandle
47+
if (firstElem.type == RuntimeScalarType.CODE ||
48+
(firstElem.type == RuntimeScalarType.REFERENCE &&
49+
firstElem.scalarDeref() != null &&
50+
firstElem.scalarDeref().type == RuntimeScalarType.CODE) ||
51+
firstElem.type == RuntimeScalarType.GLOB ||
52+
firstElem.type == RuntimeScalarType.GLOBREFERENCE) {
53+
// Recursively handle the first element
54+
// Pass remaining array elements as potential state
55+
RuntimeBase result = doFile(firstElem, setINC, isRequire, ctx);
56+
return result;
57+
}
58+
}
59+
}
3960
// Check if the argument is a CODE reference (for @INC filter support)
40-
if (runtimeScalar.type == RuntimeScalarType.CODE ||
61+
else if (runtimeScalar.type == RuntimeScalarType.CODE ||
4162
(runtimeScalar.type == RuntimeScalarType.REFERENCE &&
4263
runtimeScalar.scalarDeref() != null &&
4364
runtimeScalar.scalarDeref().type == RuntimeScalarType.CODE)) {
@@ -66,20 +87,36 @@ private static RuntimeBase doFile(RuntimeScalar runtimeScalar, boolean setINC, b
6687
} else {
6788
// Save current $_
6889
RuntimeScalar savedDefaultVar = GlobalVariable.getGlobalVariable("main::_");
69-
GlobalVariable.getGlobalVariable("main::_").set("");
90+
StringBuilder accumulatedCode = new StringBuilder();
7091

7192
try {
72-
// Call the CODE reference with no arguments
93+
// Call the CODE reference repeatedly as a generator
94+
// Each call should populate $_ with the next chunk of code
95+
// Continue until it returns 0/false (EOF)
7396
RuntimeArray args = new RuntimeArray();
74-
RuntimeBase result = codeRef.apply(args, RuntimeContextType.SCALAR);
75-
76-
// Get the content from $_
77-
RuntimeScalar defaultVar = GlobalVariable.getGlobalVariable("main::_");
78-
code = defaultVar.toString();
79-
80-
// Return value of 0 means EOF (no MORE content after this call)
81-
// But the content in $_ is still valid and should be used!
82-
// Only set code to null if $_ is actually empty
97+
boolean continueReading = true;
98+
99+
while (continueReading) {
100+
GlobalVariable.getGlobalVariable("main::_").set("");
101+
102+
// Call the CODE reference
103+
RuntimeBase result = codeRef.apply(args, RuntimeContextType.SCALAR);
104+
105+
// Get the content from $_
106+
RuntimeScalar defaultVar = GlobalVariable.getGlobalVariable("main::_");
107+
String chunk = defaultVar.toString();
108+
109+
// Accumulate the chunk if not empty
110+
if (!chunk.isEmpty()) {
111+
accumulatedCode.append(chunk);
112+
}
113+
114+
// Check if we should continue
115+
// Return value of 0/false means EOF
116+
continueReading = result.scalar().getBoolean();
117+
}
118+
119+
code = accumulatedCode.toString();
83120
if (code.isEmpty()) {
84121
code = null;
85122
}

0 commit comments

Comments
 (0)