diff --git a/src/main/java/org/perlonjava/frontend/parser/FileHandle.java b/src/main/java/org/perlonjava/frontend/parser/FileHandle.java index 93112f692..6edc378a4 100644 --- a/src/main/java/org/perlonjava/frontend/parser/FileHandle.java +++ b/src/main/java/org/perlonjava/frontend/parser/FileHandle.java @@ -271,7 +271,8 @@ public static Node parseBarewordHandle(Parser parser, String name) { // Check if this is a known file handle in the global I/O table // This helps distinguish between file handles and other barewords - if (GlobalVariable.existsGlobalIO(name) || isStandardFilehandle(name)) { + if (GlobalVariable.existsGlobalIO(name) || isStandardFilehandle(name) + || isAllDigitGlobName(name)) { // Create a GLOB reference for the file handle, like `\*FH` return new OperatorNode("\\", new OperatorNode("*", @@ -280,6 +281,24 @@ public static Node parseBarewordHandle(Parser parser, String name) { return null; } + /** + * Perl allows numeric typeglob names such as {@code open 0; print <0>}, where filehandle + * {@code 0} shares the {@code *0} stash entry with {@code $0} (program name). + */ + private static boolean isAllDigitGlobName(String normalizedName) { + int idx = normalizedName.lastIndexOf("::"); + String base = idx >= 0 ? normalizedName.substring(idx + 2) : normalizedName; + if (base.isEmpty()) { + return false; + } + for (int i = 0; i < base.length(); i++) { + if (!Character.isDigit(base.charAt(i))) { + return false; + } + } + return true; + } + /** * Checks if a normalized name represents a standard filehandle. * diff --git a/src/main/java/org/perlonjava/frontend/parser/OperatorParser.java b/src/main/java/org/perlonjava/frontend/parser/OperatorParser.java index cdd09961f..278657407 100644 --- a/src/main/java/org/perlonjava/frontend/parser/OperatorParser.java +++ b/src/main/java/org/perlonjava/frontend/parser/OperatorParser.java @@ -139,6 +139,24 @@ static Node parseDiamondOperator(Parser parser, LexerToken token) { return diamond; } + // <0>, <1>, … — numeric filehandles (e.g. Acme::Buffy "open 0" reads $0) + if (operand.type == NUMBER && tokenText.matches("[0-9]+") + && parser.tokens.get(parser.tokenIndex + 1).text.equals(">")) { + parser.tokenIndex++; // consume NUMBER + TokenUtils.consume(parser); // consume '>' + String digitName = operand.text; + GlobalVariable.getGlobalIO(FileHandle.normalizeBarewordHandle(parser, digitName)); + Node globRef = FileHandle.parseBarewordHandle(parser, digitName); + if (globRef != null) { + BinaryOperatorNode readlineNode = new BinaryOperatorNode("readline", + globRef, + new ListNode(parser.tokenIndex), currentTokenIndex); + readlineNode.setAnnotation("handleName", digitName); + return readlineNode; + } + parser.tokenIndex = currentTokenIndex; + } + // Check if the token looks like a Bareword file handle if (operand.type == IDENTIFIER) { Node fileHandle = FileHandle.parseFileHandle(parser);