Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
6f90620
Fix Module::Runtime test failures: #line directive, hints hash, reloa…
fglock Mar 22, 2026
3f75402
Fix base.pm isa check and error message formatting
fglock Mar 22, 2026
d9b3ef9
Fix parent.pm tests: normalize old-style package separator and improv…
fglock Mar 22, 2026
d5c0919
Fix Module::Metadata tests: Unicode regex, File::Spec path handling
fglock Mar 22, 2026
2b8361e
Fix %main:: to include top-level packages in stash enumeration
fglock Mar 22, 2026
a4fd199
Fix substr() with negative offsets that overshoot string start
fglock Mar 22, 2026
a58b4cc
Fix regex /u flag: only enable Unicode character classes when requested
fglock Mar 22, 2026
ce00ee9
Fix cached require error message to include 'Compilation failed'
fglock Mar 22, 2026
dba7944
Fix version qv flag and stringify for decimal versions
fglock Mar 22, 2026
d1cf8a0
Fix version module: strip trailing zeros and reject math ops
fglock Mar 22, 2026
e475e1c
Fix File::Temp: TEMPLATE option and PERMS support
fglock Mar 22, 2026
3d750ed
Fix File::Temp cleanup when chdir'd or using relative paths
fglock Mar 22, 2026
50e7205
Fix anonymous glob slot dereferencing (${*$fh}, %{*$fh}, @{*$fh})
fglock Mar 22, 2026
1f22855
Fix File::Temp tests: fileno, autoflush, template path handling
fglock Mar 22, 2026
9bf62c2
Revert fileno synthetic fd change to fix io/perlio_leaks.t regressions
fglock Mar 22, 2026
252e357
Fix DateTime test failures: overload warnings and custom warning cate…
fglock Mar 22, 2026
1a3a509
Fix indirect object syntax with blocks for undefined barewords
fglock Mar 22, 2026
8aa8d62
Fix warnings::warnif to work with Test::Warnings warning capture
fglock Mar 22, 2026
f0df197
Add warnings scope design doc and infrastructure
fglock Mar 22, 2026
1af9e9e
Implement lexical warning scope propagation for warnif()
fglock Mar 22, 2026
02a1a0a
Fix runtime warning scope check in RuntimeIO
fglock Mar 22, 2026
9fc0431
Add architecture documentation
fglock Mar 23, 2026
38832fe
Fix stringConcatWarnUninitialized to avoid double FETCH on tied scalars
fglock Mar 23, 2026
8d45476
Fix tied variable FETCH/STORE semantics for chained assignments
fglock Mar 23, 2026
1e420c8
Fix regex /i flag to not affect Unicode properties
fglock Mar 23, 2026
ad8b5df
Revert tied variable and regex changes that caused test regressions
fglock Mar 23, 2026
0fe84a1
Restore StringOperators fix for tied scalar concat (fixes op/gmagic.t)
fglock Mar 23, 2026
7c9d597
Fix /i flag handling for Unicode properties in extended character cla…
fglock Mar 23, 2026
e209ba6
Fix extended char class negation with /i flag
fglock Mar 23, 2026
a2ce08d
Fix case-insensitive matching for extended char class ranges and escapes
fglock Mar 23, 2026
3e39116
Fix MM_PerlOnJava test target to include blib/lib in @INC via PERL5LIB
fglock Mar 23, 2026
2d9e752
Add blib.pm and fix MM_PerlOnJava to skip perllocal/packlist writes
fglock Mar 23, 2026
cd220c5
Fix -M module hang and add PERL5LIB to stub Makefile tests
fglock Mar 23, 2026
c76b817
Add deprecate.pm core module
fglock Mar 23, 2026
4276215
Update ExtUtils::MakeMaker to version 7.78
fglock Mar 23, 2026
d79a7d6
Implement lock() builtin as no-op for non-threaded Perl
fglock Mar 23, 2026
d41f340
Fix use statement list context and sort method call parsing (#359)
fglock Mar 23, 2026
5693949
Merge master (includes PR 359 changes)
fglock Mar 23, 2026
8edf5bc
Fix glob-to-code dereference in createCodeReference
fglock Mar 23, 2026
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 @@ -205,6 +205,7 @@ public static RuntimeList executePerlCode(CompilerOptions compilerOptions,

/**
* Executes the given Perl code using a syntax tree and returns the result.
* Uses VOID context by default.
*
* @param ast The abstract syntax tree representing the Perl code.
* @param tokens The list of tokens representing the Perl code.
Expand All @@ -214,6 +215,22 @@ public static RuntimeList executePerlCode(CompilerOptions compilerOptions,
public static RuntimeList executePerlAST(Node ast,
List<LexerToken> tokens,
CompilerOptions compilerOptions) throws Exception {
return executePerlAST(ast, tokens, compilerOptions, RuntimeContextType.VOID);
}

/**
* Executes the given Perl code using a syntax tree with specified context.
*
* @param ast The abstract syntax tree representing the Perl code.
* @param tokens The list of tokens representing the Perl code.
* @param compilerOptions Compiler flags, file name and source code.
* @param contextType The context to use for execution (VOID, SCALAR, LIST).
* @return The result of the Perl code execution.
*/
public static RuntimeList executePerlAST(Node ast,
List<LexerToken> tokens,
CompilerOptions compilerOptions,
int contextType) throws Exception {

// Save the current scope so we can restore it after execution.
ScopedSymbolTable savedCurrentScope = SpecialBlockParser.getCurrentScope();
Expand All @@ -229,7 +246,7 @@ public static RuntimeList executePerlAST(Node ast,
globalSymbolTable.snapShot(),
null,
null,
RuntimeContextType.VOID,
contextType,
true,
null,
compilerOptions,
Expand All @@ -255,8 +272,7 @@ public static RuntimeList executePerlAST(Node ast,
// Compile to executable (compiler or interpreter based on flag)
RuntimeCode runtimeCode = compileToExecutable(ast, ctx);

// executePerlAST is always called from special blocks which use VOID context
return executeCode(runtimeCode, ctx, false, RuntimeContextType.VOID);
return executeCode(runtimeCode, ctx, false, contextType);
} finally {
// Restore the caller's scope
if (savedCurrentScope != null) {
Expand Down
25 changes: 20 additions & 5 deletions src/main/java/org/perlonjava/frontend/parser/ParseMapGrepSort.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,27 @@ static BinaryOperatorNode parseSort(Parser parser, LexerToken token) {
if (nextToken.type == LexerTokenType.IDENTIFIER && !nextToken.text.equals("{")
&& !ParserTables.CORE_PROTOTYPES.containsKey(nextToken.text)
&& !ParsePrimary.isIsQuoteLikeOperator(nextToken.text)) {
// This could be a subroutine name for comparison (sort mysub LIST)
// or a class name for method call (sort MyClass->method)
// Save position and try to determine which
int identStart = parser.tokenIndex;
String subName = IdentifierParser.parseSubroutineIdentifier(parser);
Node var = new OperatorNode("&",
new IdentifierNode(subName, parser.tokenIndex), parser.tokenIndex);
operand = ListParser.parseZeroOrMoreList(parser, 0, false, false, false, false);
operand.handle = var;
if (CompilerOptions.DEBUG_ENABLED) parser.ctx.logDebug("parseSort identifier: " + operand.handle + " : " + operand);

// Check if followed by -> (method call) - if so, backtrack and parse as list
if (peek(parser).text.equals("->")) {
// This is a method call like "sort MyClass->method"
// Backtrack and parse the whole thing as a list expression
parser.tokenIndex = identStart;
operand = ListParser.parseZeroOrMoreList(parser, 0, false, false, false, false);
if (CompilerOptions.DEBUG_ENABLED) parser.ctx.logDebug("parseSort method call: " + operand);
} else {
// This is a comparison subroutine name
Node var = new OperatorNode("&",
new IdentifierNode(subName, parser.tokenIndex), parser.tokenIndex);
operand = ListParser.parseZeroOrMoreList(parser, 0, false, false, false, false);
operand.handle = var;
if (CompilerOptions.DEBUG_ENABLED) parser.ctx.logDebug("parseSort identifier: " + operand.handle + " : " + operand);
}
} else {
try {
operand = ListParser.parseZeroOrMoreList(parser, 1, true, false, false, false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,27 @@ static Node parseSpecialBlock(Parser parser) {

/**
* Executes a special block with the given block phase and block AST.
* Uses VOID context by default.
*
* @param parser The parser instance.
* @param blockPhase The phase of the block (e.g., BEGIN, END).
* @param block The block AST to execute.
* @return A RuntimeList containing the result of the execution.
*/
static RuntimeList runSpecialBlock(Parser parser, String blockPhase, Node block) {
return runSpecialBlock(parser, blockPhase, block, RuntimeContextType.VOID);
}

/**
* Executes a special block with the given block phase, block AST, and context.
*
* @param parser The parser instance.
* @param blockPhase The phase of the block (e.g., BEGIN, END).
* @param block The block AST to execute.
* @param contextType The context to use for execution (VOID, SCALAR, LIST).
* @return A RuntimeList containing the result of the execution.
*/
static RuntimeList runSpecialBlock(Parser parser, String blockPhase, Node block, int contextType) {
int tokenIndex = parser.tokenIndex;

// Create AST nodes for setting up the capture variables and package declaration
Expand Down Expand Up @@ -252,7 +266,8 @@ static RuntimeList runSpecialBlock(Parser parser, String blockPhase, Node block)
result = PerlLanguageProvider.executePerlAST(
new BlockNode(nodes, tokenIndex),
parser.tokens,
parsedArgs);
parsedArgs,
contextType);
} catch (PerlExitException e) {
// exit() inside BEGIN block should terminate the program, not cause compilation error
// Re-throw so it propagates to the CLI (Main.main()) which will call System.exit()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -658,8 +658,10 @@ public static Node parseUseDeclaration(Parser parser, LexerToken token) {
// call Module->import( LIST )
// or Module->unimport( LIST )

// Execute the argument list immediately
RuntimeList args = runSpecialBlock(parser, "BEGIN", list);
// Execute the argument list immediately in LIST context
// This is necessary for expressions like: use lib ($path =~ /^(.*)$/);
// where the regex match must return captured groups, not just success/failure
RuntimeList args = runSpecialBlock(parser, "BEGIN", list, RuntimeContextType.LIST);

if (CompilerOptions.DEBUG_ENABLED) ctx.logDebug("Use statement list: " + args);
if (hasParentheses && args.isEmpty()) {
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -2099,6 +2099,20 @@ public static RuntimeScalar createCodeReference(RuntimeScalar runtimeScalar, Str
}
}

// Handle GLOB type: \&{*glob} - get the code slot directly from the glob
// Globs stringify with a "*" prefix (e.g., "*main::test_sub") which would
// cause normalizeVariableName to look up the wrong name
if (runtimeScalar.type == RuntimeScalarType.GLOB) {
RuntimeGlob glob = (RuntimeGlob) runtimeScalar.value;
RuntimeScalar codeRef = GlobalVariable.getGlobalCodeRef(glob.globName);

// Return a snapshot of the current code reference
RuntimeScalar snapshot = new RuntimeScalar();
snapshot.type = codeRef.type;
snapshot.value = codeRef.value;
return snapshot;
}

String name = NameNormalizer.normalizeVariableName(runtimeScalar.toString(), packageName);
// System.out.println("Creating code reference: " + name + " got: " + GlobalContext.getGlobalCodeRef(name));
RuntimeScalar codeRef = GlobalVariable.getGlobalCodeRef(name);
Expand Down
Loading