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
18 changes: 18 additions & 0 deletions src/main/java/org/perlonjava/parser/OperatorParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,24 @@ static OperatorNode parseRequire(Parser parser) {
} else if (token.text.matches("^v\\d+$")) {
consume(parser);
operand = StringParser.parseVstring(parser, token.text, parser.tokenIndex);
} else if (token.type == IDENTIFIER && !ParsePrimary.isIsQuoteLikeOperator(token.text)) {
// `require` bareword module name - parse directly without going through expression parser
// This avoids treating module names like "Encode" as subroutine calls when a sub
// with the same name exists in the current package (e.g., sub Encode in Image::ExifTool)
// But don't intercept quote-like operators like q(), qq(), etc.
String moduleName = IdentifierParser.parseSubroutineIdentifier(parser);
parser.ctx.logDebug("require module name `" + moduleName + "`");
if (moduleName == null) {
throw new PerlCompilerException(parser.tokenIndex, "Syntax error", parser.ctx.errorUtil);
}

// Check if module name starts with ::
if (moduleName.startsWith("::")) {
throw new PerlCompilerException(parser.tokenIndex, "Bareword in require must not start with a double-colon: \"" + moduleName + "\"", parser.ctx.errorUtil);
}

String fileName = NameNormalizer.moduleToFilename(moduleName);
operand = ListNode.makeList(new StringNode(fileName, parser.tokenIndex));
} else {
// Check for the specific pattern: :: followed by identifier (which is invalid for require)
if (token.type == LexerTokenType.OPERATOR && token.text.equals("::")) {
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/org/perlonjava/parser/ParsePrimary.java
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ private static Node parseIdentifier(Parser parser, int startIndex, LexerToken to
// IMPORTANT: Check for lexical subs AFTER CORE::, but before checking for quote-like operators!
// This allows "my sub y" to shadow the "y///" transliteration operator
// But doesn't interfere with CORE:: prefix handling
if (!calledWithCore) {
// ALSO: Don't treat as lexical sub if :: follows - that's a qualified name like Encode::is_utf8
if (!calledWithCore && !nextTokenText.equals("::")) {
String lexicalKey = "&" + operator;
SymbolTable.SymbolEntry lexicalEntry = parser.ctx.symbolTable.getSymbolEntry(lexicalKey);
if (lexicalEntry != null && lexicalEntry.ast() instanceof OperatorNode) {
Expand Down