Skip to content

Commit ee64841

Browse files
committed
fix(pretty): escape quoted/interpolated string output
1 parent e27cdc1 commit ee64841

File tree

4 files changed

+30
-2
lines changed

4 files changed

+30
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ All notable changes to FScript are documented in this file.
44

55
## [Unreleased]
66

7+
- Escaped pretty-printed string values (including map string keys) so interpolated strings with embedded quotes render unambiguously in CLI/REPL output.
8+
79
## [0.54.2]
810

911

src/FScript.Language/Pretty.fs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,21 @@ module Pretty =
1010
| _ -> acc
1111
loop [ firstArg ] body
1212

13+
let private escapeStringLiteral (value: string) =
14+
value
15+
.Replace("\\", "\\\\")
16+
.Replace("\"", "\\\"")
17+
.Replace("\r", "\\r")
18+
.Replace("\n", "\\n")
19+
.Replace("\t", "\\t")
20+
1321
let rec valueToString v =
1422
match v with
1523
| VUnit -> "()"
1624
| VInt i -> string i
1725
| VFloat f -> string f
1826
| VBool b -> if b then "true" else "false"
19-
| VString s -> sprintf "\"%s\"" s
27+
| VString s -> sprintf "\"%s\"" (escapeStringLiteral s)
2028
| VList xs ->
2129
xs |> List.map valueToString |> String.concat ";" |> sprintf "[%s]"
2230
| VTuple xs ->
@@ -30,7 +38,7 @@ module Pretty =
3038
| VMap fields ->
3139
let mapKeyToString key =
3240
match key with
33-
| MKString s -> $"\"{s}\""
41+
| MKString s -> $"\"{escapeStringLiteral s}\""
3442
| MKInt i -> string i
3543
fields
3644
|> Map.toList

tests/FScript.Language.Tests/EvalTests.fs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,12 @@ type EvalTests () =
508508
| VString "missing" -> ()
509509
| _ -> Assert.Fail("Expected interpolation with string-literal placeholder expression")
510510

511+
[<Test>]
512+
member _.``Evaluates interpolation containing escaped quotes around placeholder`` () =
513+
match Helpers.eval "let value = \"'\"\n$\"Path=\\\"{value}\\\"\"" with
514+
| VString "Path=\"'\"" -> ()
515+
| _ -> Assert.Fail("Expected interpolation value with quoted placeholder content")
516+
511517
[<Test>]
512518
member _.``Evaluates typeof for explicit recursive type`` () =
513519
match Helpers.eval "type rec Node = { Value: int; Next: Node option }\ntypeof Node" with

tests/FScript.Language.Tests/PrettyTests.fs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,15 @@ type PrettyTests () =
2727
[<Test>]
2828
member _.``Formats extern values with name and application progress`` () =
2929
Helpers.evalToString "print" |> should equal "<extern print>"
30+
31+
[<Test>]
32+
member _.``Escapes quoted interpolation output in strings`` () =
33+
Helpers.evalToString "let value = \"'\"\n$\"Path=\\\"{value}\\\"\"" |> should equal "\"Path=\\\"'\\\"\""
34+
35+
[<Test>]
36+
member _.``Escapes special characters in rendered strings`` () =
37+
Helpers.evalToString "\"a\\\"b\\\\c\\n\\t\"" |> should equal "\"a\\\"b\\\\c\\n\\t\""
38+
39+
[<Test>]
40+
member _.``Escapes quoted map string keys`` () =
41+
Helpers.evalToString "{ [\"a\\\"b\"] = 1 }" |> should equal "map { \"a\\\"b\" => 1 }"

0 commit comments

Comments
 (0)