diff --git a/src/main/java/org/perlonjava/parser/StringDoubleQuoted.java b/src/main/java/org/perlonjava/parser/StringDoubleQuoted.java index a682d8fe..12dc4a06 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