Fixing escape charaters again 😢 #2517

This commit is contained in:
Mike Farah 2025-11-25 10:15:08 +11:00
parent 5d0481c0d2
commit 9e0c5fd3c9
2 changed files with 182 additions and 9 deletions

View File

@ -187,15 +187,69 @@ func parseInt(numberString string) (int, error) {
}
func processEscapeCharacters(original string) string {
value := original
value = strings.ReplaceAll(value, "\\\"", "\"")
value = strings.ReplaceAll(value, "\\n", "\n")
value = strings.ReplaceAll(value, "\\t", "\t")
value = strings.ReplaceAll(value, "\\r", "\r")
value = strings.ReplaceAll(value, "\\f", "\f")
value = strings.ReplaceAll(value, "\\v", "\v")
value = strings.ReplaceAll(value, "\\b", "\b")
value = strings.ReplaceAll(value, "\\a", "\a")
if original == "" {
return original
}
var result strings.Builder
runes := []rune(original)
for i := 0; i < len(runes); i++ {
if runes[i] == '\\' && i < len(runes)-1 {
next := runes[i+1]
switch next {
case '\\':
// Check if followed by opening bracket - if so, preserve both backslashes
// this is required for string interpolation to work correctly.
if i+2 < len(runes) && runes[i+2] == '(' {
// Preserve \\ when followed by (
result.WriteRune('\\')
result.WriteRune('\\')
i++ // Skip the next backslash (we'll process the ( normally on next iteration)
continue
}
// Escaped backslash: \\ -> \
result.WriteRune('\\')
i++ // Skip the next backslash
continue
case '"':
result.WriteRune('"')
i++ // Skip the quote
continue
case 'n':
result.WriteRune('\n')
i++ // Skip the 'n'
continue
case 't':
result.WriteRune('\t')
i++ // Skip the 't'
continue
case 'r':
result.WriteRune('\r')
i++ // Skip the 'r'
continue
case 'f':
result.WriteRune('\f')
i++ // Skip the 'f'
continue
case 'v':
result.WriteRune('\v')
i++ // Skip the 'v'
continue
case 'b':
result.WriteRune('\b')
i++ // Skip the 'b'
continue
case 'a':
result.WriteRune('\a')
i++ // Skip the 'a'
continue
}
}
result.WriteRune(runes[i])
}
value := result.String()
if value != original {
log.Debug("processEscapeCharacters from [%v] to [%v]", original, value)
}

View File

@ -408,3 +408,122 @@ func TestKindString(t *testing.T) {
test.AssertResult(t, "AliasNode", KindString(AliasNode))
test.AssertResult(t, "unknown!", KindString(Kind(999))) // Invalid kind
}
type processEscapeCharactersScenario struct {
input string
expected string
}
var processEscapeCharactersScenarios = []processEscapeCharactersScenario{
{
input: "",
expected: "",
},
{
input: "hello",
expected: "hello",
},
{
input: "\\\"",
expected: "\"",
},
{
input: "hello\\\"world",
expected: "hello\"world",
},
{
input: "\\n",
expected: "\n",
},
{
input: "line1\\nline2",
expected: "line1\nline2",
},
{
input: "\\t",
expected: "\t",
},
{
input: "hello\\tworld",
expected: "hello\tworld",
},
{
input: "\\r",
expected: "\r",
},
{
input: "hello\\rworld",
expected: "hello\rworld",
},
{
input: "\\f",
expected: "\f",
},
{
input: "hello\\fworld",
expected: "hello\fworld",
},
{
input: "\\v",
expected: "\v",
},
{
input: "hello\\vworld",
expected: "hello\vworld",
},
{
input: "\\b",
expected: "\b",
},
{
input: "hello\\bworld",
expected: "hello\bworld",
},
{
input: "\\a",
expected: "\a",
},
{
input: "hello\\aworld",
expected: "hello\aworld",
},
{
input: "\\\"\\n\\t\\r\\f\\v\\b\\a",
expected: "\"\n\t\r\f\v\b\a",
},
{
input: "multiple\\nlines\\twith\\ttabs",
expected: "multiple\nlines\twith\ttabs",
},
{
input: "quote\\\"here",
expected: "quote\"here",
},
{
input: "\\\\",
expected: "\\", // Backslash is processed: "\\\\" becomes "\\"
},
{
input: "\\\"test\\\"",
expected: "\"test\"",
},
{
input: "a\\\\b",
expected: "a\\b", // Tests roundtrip: "a\\\\b" should become "a\\b"
},
{
input: "Hi \\\\(.value)",
expected: "Hi \\\\(.value)",
},
{
input: `a\\b`,
expected: "a\\b",
},
}
func TestProcessEscapeCharacters(t *testing.T) {
for _, tt := range processEscapeCharactersScenarios {
actual := processEscapeCharacters(tt.input)
test.AssertResultComplexWithContext(t, tt.expected, actual, fmt.Sprintf("Input: %q", tt.input))
}
}