From 3315d1b229e37c87448afdc786c5e44145fd62a3 Mon Sep 17 00:00:00 2001 From: John Wostenberg Date: Wed, 4 Dec 2024 12:02:37 -0600 Subject: [PATCH] Support string interpolation with named holes --- src/Fable.FSharp.Logf/logf.fs | 14 +++++++++----- tests/FSharp.Logf.Tests/Program.fs | 22 ++++++++++++++++++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/Fable.FSharp.Logf/logf.fs b/src/Fable.FSharp.Logf/logf.fs index ff78bc3..e62770e 100644 --- a/src/Fable.FSharp.Logf/logf.fs +++ b/src/Fable.FSharp.Logf/logf.fs @@ -28,10 +28,14 @@ type StringFormat<'T, 'Result, 'Tuple> = Format<'T, unit, string, 'Result, 'Tupl // filing an issue would be great! let printfFmtSpecPattern = """%""" - + """(0|-|\+| )*""" // flags - + """[0-9]*""" // width - + """(\.\d+)?""" // precision - + """[a-zA-Z]""" // type + // flags + + """(0|-|\+| )*""" + // width + + """[0-9]*""" + // precision + + """(\.\d+)?""" + // type (interpolated string holes are special -- they show up as "%P()" -- note the extra parens) + + """(P\(\)|[a-zA-Z])""" let netMsgHolePattern = """(?""" @@ -200,7 +204,7 @@ type private LogfEnv<'Result>(continuation: string -> obj[] -> 'Result) = // Valid format specifiers will be captured by the named capture group "a", and lone curly braces will be captured // by the named capture group "b". Later, using the replacement pattern "${a}${b}${b}" causes any occurrences of "a" // (valid format specifiers) to be placed back into the string as-is, while occurrences of "b" will be doubled (having -// the effect of escaping those lone curly braces. +// the effect of escaping those lone curly braces). // Examples: // * Input: "%s{foo}" // * 1st match: "a" = "%s{foo}", "b" = "", "${a}${b}${b}" = "$s{foo}" diff --git a/tests/FSharp.Logf.Tests/Program.fs b/tests/FSharp.Logf.Tests/Program.fs index 1a5ab36..c909a19 100644 --- a/tests/FSharp.Logf.Tests/Program.fs +++ b/tests/FSharp.Logf.Tests/Program.fs @@ -320,6 +320,28 @@ let allTests = $"Hello, {Person}" [] ) + testCase "Interpolated string with one named hole" (fun () -> + let Person = "Sam" + (fun l -> + logfi l $"Hello, {Person}{{Person}}") + |> assertEquivalent + $"Hello, {Person}" + ["Person", "Sam"] + ) + testCase "Interpolated string with many unnamed parameters" (fun () -> + let A, B, C = "foo", 42, false + (fun l -> logfi l $"A is %s{A}, B is %d{B}, C is %b{C}") + |> assertEquivalent + (sprintf $"A is %s{A}, B is %d{B}, C is %b{C}") + [] + ) + testCase "Interpolated string with many named and unnamed parameters" (fun () -> + let A, B, C, D = "foo", 42, false, "bar" + (fun l -> logfi l $"A is %s{A}{{A}}, B is %d{B}, C is %b{C}{{C}}, D is %s{D}") + |> assertEquivalent + $"A is %s{A}, B is %d{B}, C is %b{C}, D is %s{D}" + ["A", "foo"; "C", false] + ) let valuesF = caseData [ 5. / 3.; 50. / 3.; 500. / 3.; -(5. / 3.); -42.; 0.; -0.; 42.; Math.PI * 1_000_000.; -Math.PI * 1_000_000.; Math.PI / 1_000_000.; -Math.PI / 1_000_000. ] let valuesD = caseData [ 0m; 12345.98m; -10m; 0.012m ] let valuesI = caseData [ 0xdeadbeef; 42 ]