From 03c37da7e915544f3b39a4c4544e8d4a27c44194 Mon Sep 17 00:00:00 2001 From: Flavio Soibelmann Glock Date: Wed, 31 Dec 2025 15:33:41 +0100 Subject: [PATCH] Fix 'when' keyword parsing to respect 'use feature switch' The 'when' keyword should only be treated as a reserved keyword when 'use feature "switch"' is active. Without this feature enabled, 'when' should be allowed as a bareword, particularly as a hash key. Changes: - StatementResolver.java: Add feature flag checks for 'given', 'when', and 'default' keywords (similar to 'try' and 'class') - ListParser.java: Add 'when' to the list of keywords that should be treated as barewords when followed by '=>' (fat comma) This fixes parsing errors in ExifTool's XMP.pm where 'when' is used as a hash key: when => { %dateTimeInfo, Groups => { 2 => 'Time' } } Tested with ExifTool 13.44 - XMP-related commands now parse correctly. --- src/main/java/org/perlonjava/parser/ListParser.java | 10 +++++----- .../org/perlonjava/parser/StatementResolver.java | 12 +++++++++--- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/perlonjava/parser/ListParser.java b/src/main/java/org/perlonjava/parser/ListParser.java index 6e8aeed4b..9a3c0dbfe 100644 --- a/src/main/java/org/perlonjava/parser/ListParser.java +++ b/src/main/java/org/perlonjava/parser/ListParser.java @@ -222,11 +222,11 @@ static boolean isListTerminator(Parser parser, LexerToken token) { return false; } - // Special case: and/or/xor before => should be treated as barewords, not terminators - if (token.text.equals("and") || token.text.equals("or") || token.text.equals("xor")) { + // Special case: and/or/xor/when before => should be treated as barewords, not terminators + if (token.text.equals("and") || token.text.equals("or") || token.text.equals("xor") || token.text.equals("when")) { // Look ahead to see if => follows int saveIndex = parser.tokenIndex; - TokenUtils.consume(parser); // consume and/or/xor + TokenUtils.consume(parser); // consume and/or/xor/when LexerToken nextToken = TokenUtils.peek(parser); parser.tokenIndex = saveIndex; // restore if (nextToken.text.equals("=>")) { @@ -297,8 +297,8 @@ public static boolean looksLikeEmptyList(Parser parser) { // Check if this is a list terminator, but we need to restore position for the check boolean isTerminator = false; if (ParserTables.LIST_TERMINATORS.contains(token.text)) { - // Special case: check if and/or/xor followed by => - if (token.text.equals("and") || token.text.equals("or") || token.text.equals("xor")) { + // Special case: check if and/or/xor/when followed by => + if (token.text.equals("and") || token.text.equals("or") || token.text.equals("xor") || token.text.equals("when")) { if (nextToken.text.equals("=>")) { isTerminator = false; // Not a terminator, it's a hash key } else { diff --git a/src/main/java/org/perlonjava/parser/StatementResolver.java b/src/main/java/org/perlonjava/parser/StatementResolver.java index c6777e41b..fbca51260 100644 --- a/src/main/java/org/perlonjava/parser/StatementResolver.java +++ b/src/main/java/org/perlonjava/parser/StatementResolver.java @@ -69,11 +69,17 @@ public static Node parseStatement(Parser parser, String label) { case "while", "until" -> StatementParser.parseWhileStatement(parser, label); - case "given" -> StatementParser.parseGivenStatement(parser); + case "given" -> parser.ctx.symbolTable.isFeatureCategoryEnabled("switch") + ? StatementParser.parseGivenStatement(parser) + : null; - case "when" -> StatementParser.parseWhenStatement(parser); + case "when" -> parser.ctx.symbolTable.isFeatureCategoryEnabled("switch") + ? StatementParser.parseWhenStatement(parser) + : null; - case "default" -> StatementParser.parseDefaultStatement(parser); + case "default" -> parser.ctx.symbolTable.isFeatureCategoryEnabled("switch") + ? StatementParser.parseDefaultStatement(parser) + : null; case "try" -> parser.ctx.symbolTable.isFeatureCategoryEnabled("try") ? StatementParser.parseTryStatement(parser)