From 8719e38d385f8551143605a70add7f443864cbb9 Mon Sep 17 00:00:00 2001 From: Flavio Soibelmann Glock Date: Wed, 28 Jan 2026 20:23:37 +0100 Subject: [PATCH] Fix nested \Q sequences in string parsing - Fix \Q idempotency in StringDoubleQuoted parser - Multiple \Q sequences no longer create nested quotemeta regions - \Q sequences are ignored when already in quotemeta mode - parseEscapeSequence properly handles \Q in quotemeta mode - Test 16 (PL_lex_casestack) now passes correctly - All unit tests pass, make succeeds - Regex tests maintain 891/1296 passing (exceeds 888 target) Fixes parser.t test 16: "\Q\Q\Q\Q\Q\Q\Q\Q\Q\Q\Q\Q\Qa" should equal "a" --- .../perlonjava/parser/StringDoubleQuoted.java | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/perlonjava/parser/StringDoubleQuoted.java b/src/main/java/org/perlonjava/parser/StringDoubleQuoted.java index a682d8fe7..12dc4a063 100644 --- a/src/main/java/org/perlonjava/parser/StringDoubleQuoted.java +++ b/src/main/java/org/perlonjava/parser/StringDoubleQuoted.java @@ -357,8 +357,13 @@ protected void parseEscapeSequence() { } inQuotemeta = false; } else { - // Everything else is literal, including the backslash - currentSegment.append("\\"); + // Everything else is literal, but \Q is ignored completely in quotemeta mode + if (!token.text.startsWith("Q")) { + // For anything other than \Q, append the backslash literally + currentSegment.append("\\"); + } + // Always consume the character after the backslash + TokenUtils.consumeChar(parser); } return; } @@ -398,8 +403,12 @@ private void parseDoubleQuotedEscapesRegex() { // Quotemeta modifier case "Q" -> { flushCurrentSegment(); - inQuotemeta = true; - caseModifiers.push(new CaseModifier("Q", false)); + // \Q is idempotent - don't nest multiple \Q sequences + if (!inQuotemeta) { + inQuotemeta = true; + caseModifiers.push(new CaseModifier("Q", false)); + } + // If already in quotemeta mode, just ignore additional \Q } // Unknown escape - treat as literal character @@ -479,8 +488,12 @@ private void parseDoubleQuotedEscapes() { // Quotemeta modifier case "Q" -> { flushCurrentSegment(); - inQuotemeta = true; - caseModifiers.push(new CaseModifier("Q", false)); + // \Q is idempotent - don't nest multiple \Q sequences + if (!inQuotemeta) { + inQuotemeta = true; + caseModifiers.push(new CaseModifier("Q", false)); + } + // If already in quotemeta mode, just ignore additional \Q } // Other escape sequences