From e30cb052ed4a1c7cf2a247c05b227168c97ad5aa Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Thu, 29 May 2025 20:49:23 +0800 Subject: [PATCH 01/32] Support putting two format specifiers next to each other --- src/FSharpPlus/Parsing.fs | 59 +++++++++++++++++++++++-------- tests/FSharpPlus.Tests/Parsing.fs | 24 +++++++------ 2 files changed, 59 insertions(+), 24 deletions(-) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index fab78fc1a..524bb8300 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -11,20 +11,51 @@ module Parsing = open FSharpPlus.Internals open FSharpPlus.Internals.Prelude - let inline private getGroups (pf: PrintfFormat<_,_,_,_,_>) s = - let formatters = [|"%A"; "%b"; "%B"; "%c"; "%d"; "%e"; "%E"; "%f"; "%F"; "%g"; "%G"; "%i"; "%M"; "%o"; "%O"; "%s"; "%u"; "%x"; "%X"|] - let formatStr = replace "%%" "%" pf.Value - let constants = split formatters formatStr - let regex = Regex ("^" + String.Join ("(.*?)", constants |> Array.map Regex.Escape) + "$") - let getGroup x = - let groups = - regex.Match(x).Groups - |> Seq.cast - |> Seq.skip 1 - groups - |> Seq.map (fun g -> g.Value) - |> Seq.toArray - (getGroup s, getGroup pf.Value) ||> Array.zipShortest + let inline private getGroups (pf: PrintfFormat<_,_,_,_,_>) str = + let format = pf.Value + let regex = System.Text.StringBuilder "^" + let mutable groups = FSharp.Core.CompilerServices.ArrayCollector() + let mutable i = 0 + while i < String.length format do + match format[i] with + | '%' -> + let mutable j = i + 1 + while + match format[j] with + | ' ' | '+' | '-' | '*' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' -> true + | _ -> false + do j <- j + 1 + if format[j] <> '%' then groups.Add format[i..j] // %% does not capture a group + i <- j + match format[j] with + | 'A' | 'O' -> "(.*?)" + | 'b' -> "([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])" + | 'B' -> "([01]+)" + | 'c' -> "(.)" + | 'd' | 'i' -> "([+-]?[0-9]+)" + | 'e' | 'E' | 'f' | 'F' | 'g' | 'G' | 'M' -> "([+-]?[0-9.]+(?:[eE][+-]?[0-9]+)?)" + | 'o' -> "([0-7]+)" + | 'u' -> "([0-9]+)" + | 's' -> "(.*?)" + | 'x' | 'X' -> "([0-9a-fA-F]+)" + | '%' -> "%" + | x -> failwith $"Unknown specifier: {x}" + |> regex.Append + | '\\' | '*' | '+' | '?' | '|' | '{' | '[' | '(' | ')' | '^' | '$' | '.' | '#' | ' ' as escape -> + regex.Append('\\').Append escape + | c -> regex.Append c + |> ignore + i <- i + 1 + regex.Append '$' + |> string + |> Regex + |> _.Match(str) + |> _.Groups + |> Seq.cast + |> Seq.skip 1 + |> Seq.map _.Value + |> Seq.toArray + |> Array.zip <| groups.Close() let inline private conv (destType: System.Type) (b: int) (s: string) = match destType with diff --git a/tests/FSharpPlus.Tests/Parsing.fs b/tests/FSharpPlus.Tests/Parsing.fs index 37f0029e2..f19c048ed 100644 --- a/tests/FSharpPlus.Tests/Parsing.fs +++ b/tests/FSharpPlus.Tests/Parsing.fs @@ -102,16 +102,20 @@ module Parsing = let _zzz = sscanf "(%%%s)" "(%hello)" let (_x1,_y1,_z1) = sscanf "%s--%s-%s" "test--this-string" - - let _f1 = trySscanf "(%%%s)" "(%hello)" - let _f2 = trySscanf "%s--%s-%s" "test--this-gg" - let _f3 = trySscanf "%f %F %g %G %e %E %c %c" "1 2.1 3.4 .3 43.2e32 0 f f" - let _f4 = trySscanf "%f %F %g %G %e %E %c %c %c" "1 2.1 3.4 .3 43.2e32 0 f f f" - let _f5 = trySscanf "%f %F %g %G %e %E %c %c %c %c" "1 2.1 3.4 .3 43.2e32 0 f f f f" - let _f6 = trySscanf "%f %F %g %G %e %E %c %c %c %c %c %c %c %c %c" "1 2.1 3.4 .3 43.2e32 0 f f f f f f f f" - let _f7 = trySscanf "%f %F %g %G %e %E %c %c %c %c %c %c %c %c %c %i" "1 2.1 3.4 .3 43.2e32 0 f f f f f f f f f 16" - let _f8 = trySscanf "%f %F %g %G %e %E %c %c %c %c %c %c %c %c %c %i %f" "1 2.1 3.4 .3 43.2e32 0 f f f f f f f f f 16 17" - + let inline (|Like|_|) format = FSharpPlus.Parsing.trySscanf format + match "(%hello)" with Like "(%%%s)" "hello" -> () | _ -> failwith "didn't match" + match "test--this-gg" with Like "%s--%s-%s" ("test", "this", "gg") -> () | _ -> failwith "didn't match" + match "1 2.1 3.4 .3 43.2e32 0 f f" with Like "%f %F %g %G %e %E %c %c" (1f, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f') -> () | _ -> failwith "didn't match" + match "1 2.1 3.4 .3 43.2e32 0 f f f" with Like "%f %F %g %G %e %E %c %c %c" (1f, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f') -> () | _ -> failwith "didn't match" + match "1 2.1 3.4 .3 43.2e32 0 f f ff" with Like "%f %F %g %G %e %E %c %c %c%c" (1f, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" + match "1 2.1 3.4 .3 43.2e32 0 f f fff" with Like "%f %F %g %G %e %E %c %c %c%c%c" (1f, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" + match "1 2.1 3.4 .3 43.2e32 0 f f fff 16" with Like "%f %F %g %G %e %E %c %c %c%c%c%i" (1f, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16) -> () | _ -> failwith "didn't match" + match "1 2.1 3.4 .3 43.2e32 0 f f fff 16" with Like "%f %F %g %G %e %E %c %c %c%c%c%i %f" (1f, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16, 17.) -> () | _ -> failwith "didn't match" + match "13 43 AA 77A" with Like "%x %X %x %o%X" (0x13, 0x43, 0xAA, 0o77, 0xA) -> () | _ -> failwith "didn't match" + match "13 43 AA 77A" with Like "%B%x %X %x %o%X" (0b1, 0x3, 0x43, 0xAA, 0o77, 0xA) -> () | _ -> failwith "didn't match" + match "111AAA" with Like "%B%s" (0b111, "AAA") -> () | _ -> failwith "didn't match" + match "100700 100 100" with Like "%B%o %x %X" (0b100, 0o700, 0x100, 0x100) -> () | _ -> failwith "didn't match" + let _date: (DayOfWeek * string * uint16 * int) option = trySscanf "%A %A %A %A" "Saturday March 25 1989" let x = trySscanf "%X %x" "13 43" From ee47fdcd6e18348c1524b164ca82bd3010e250e3 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Thu, 29 May 2025 21:01:32 +0800 Subject: [PATCH 02/32] Update Parsing.fs --- tests/FSharpPlus.Tests/Parsing.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/FSharpPlus.Tests/Parsing.fs b/tests/FSharpPlus.Tests/Parsing.fs index f19c048ed..ae0b3f861 100644 --- a/tests/FSharpPlus.Tests/Parsing.fs +++ b/tests/FSharpPlus.Tests/Parsing.fs @@ -110,7 +110,7 @@ module Parsing = match "1 2.1 3.4 .3 43.2e32 0 f f ff" with Like "%f %F %g %G %e %E %c %c %c%c" (1f, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" match "1 2.1 3.4 .3 43.2e32 0 f f fff" with Like "%f %F %g %G %e %E %c %c %c%c%c" (1f, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" match "1 2.1 3.4 .3 43.2e32 0 f f fff 16" with Like "%f %F %g %G %e %E %c %c %c%c%c%i" (1f, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16) -> () | _ -> failwith "didn't match" - match "1 2.1 3.4 .3 43.2e32 0 f f fff 16" with Like "%f %F %g %G %e %E %c %c %c%c%c%i %f" (1f, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16, 17.) -> () | _ -> failwith "didn't match" + match "1 2.1 3.4 .3 43.2e32 0 f f fff 16 17" with Like "%f %F %g %G %e %E %c %c %c%c%c%i %f" (1f, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16, 17.) -> () | _ -> failwith "didn't match" match "13 43 AA 77A" with Like "%x %X %x %o%X" (0x13, 0x43, 0xAA, 0o77, 0xA) -> () | _ -> failwith "didn't match" match "13 43 AA 77A" with Like "%B%x %X %x %o%X" (0b1, 0x3, 0x43, 0xAA, 0o77, 0xA) -> () | _ -> failwith "didn't match" match "111AAA" with Like "%B%s" (0b111, "AAA") -> () | _ -> failwith "didn't match" @@ -126,4 +126,4 @@ module Parsing = areEqual (Some (19, 67)) x areEqual (Some 8) o areEqual (Some 5) b - areEqual (Some (4, 64, 256, 256)) a \ No newline at end of file + areEqual (Some (4, 64, 256, 256)) a From 666f5d0c6f1c889b27c2942ad297844a31c46f94 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Thu, 29 May 2025 21:02:35 +0800 Subject: [PATCH 03/32] Update Parsing.fs --- src/FSharpPlus/Parsing.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index 524bb8300..b30c31934 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -33,7 +33,7 @@ module Parsing = | 'B' -> "([01]+)" | 'c' -> "(.)" | 'd' | 'i' -> "([+-]?[0-9]+)" - | 'e' | 'E' | 'f' | 'F' | 'g' | 'G' | 'M' -> "([+-]?[0-9.]+(?:[eE][+-]?[0-9]+)?)" + | 'e' | 'E' | 'f' | 'F' | 'g' | 'G' | 'M' -> "([+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?)" | 'o' -> "([0-7]+)" | 'u' -> "([0-9]+)" | 's' -> "(.*?)" From c74f1b833f13618d8e952997728cd866442d322d Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Thu, 29 May 2025 21:05:04 +0800 Subject: [PATCH 04/32] Update Parsing.fs --- src/FSharpPlus/Parsing.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index b30c31934..4ffc75e89 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -33,7 +33,7 @@ module Parsing = | 'B' -> "([01]+)" | 'c' -> "(.)" | 'd' | 'i' -> "([+-]?[0-9]+)" - | 'e' | 'E' | 'f' | 'F' | 'g' | 'G' | 'M' -> "([+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?)" + | 'e' | 'E' | 'f' | 'F' | 'g' | 'G' | 'M' -> "([+-]?[0-9]+\.?[0-9]*(?:[eE][+-]?[0-9]+)?)" | 'o' -> "([0-7]+)" | 'u' -> "([0-9]+)" | 's' -> "(.*?)" From ce008cb3fb29e03972657b44c5bf82b4e1a308c0 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Thu, 29 May 2025 21:06:06 +0800 Subject: [PATCH 05/32] Update Parsing.fs --- src/FSharpPlus/Parsing.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index 4ffc75e89..1b3bea66b 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -33,7 +33,7 @@ module Parsing = | 'B' -> "([01]+)" | 'c' -> "(.)" | 'd' | 'i' -> "([+-]?[0-9]+)" - | 'e' | 'E' | 'f' | 'F' | 'g' | 'G' | 'M' -> "([+-]?[0-9]+\.?[0-9]*(?:[eE][+-]?[0-9]+)?)" + | 'e' | 'E' | 'f' | 'F' | 'g' | 'G' | 'M' -> "([+-]?(?:[0-9]+\.?|\.?[0-9]+)(?:[eE][+-]?[0-9]+)?)" | 'o' -> "([0-7]+)" | 'u' -> "([0-9]+)" | 's' -> "(.*?)" From 9c73bbb916ca8ba9d70f354bf2fabd7a482e9611 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Thu, 29 May 2025 21:07:23 +0800 Subject: [PATCH 06/32] Update Parsing.fs --- src/FSharpPlus/Parsing.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index 1b3bea66b..a104ad6c2 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -33,7 +33,7 @@ module Parsing = | 'B' -> "([01]+)" | 'c' -> "(.)" | 'd' | 'i' -> "([+-]?[0-9]+)" - | 'e' | 'E' | 'f' | 'F' | 'g' | 'G' | 'M' -> "([+-]?(?:[0-9]+\.?|\.?[0-9]+)(?:[eE][+-]?[0-9]+)?)" + | 'e' | 'E' | 'f' | 'F' | 'g' | 'G' | 'M' -> "([+-]?(?:[0-9]+(?:\.[0-9]*)?|\.?[0-9]+)(?:[eE][+-]?[0-9]+)?)" | 'o' -> "([0-7]+)" | 'u' -> "([0-9]+)" | 's' -> "(.*?)" From 64dd5391f13397a863fd597484dac112c0895f63 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Thu, 29 May 2025 21:08:20 +0800 Subject: [PATCH 07/32] Update Parsing.fs --- src/FSharpPlus/Parsing.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index a104ad6c2..99dd00abb 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -33,7 +33,7 @@ module Parsing = | 'B' -> "([01]+)" | 'c' -> "(.)" | 'd' | 'i' -> "([+-]?[0-9]+)" - | 'e' | 'E' | 'f' | 'F' | 'g' | 'G' | 'M' -> "([+-]?(?:[0-9]+(?:\.[0-9]*)?|\.?[0-9]+)(?:[eE][+-]?[0-9]+)?)" + | 'e' | 'E' | 'f' | 'F' | 'g' | 'G' | 'M' -> "([+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?)" | 'o' -> "([0-7]+)" | 'u' -> "([0-9]+)" | 's' -> "(.*?)" From a88a7f4e9419d5eb0fdac60462c90507ad9a5db8 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Wed, 11 Jun 2025 00:22:52 +0800 Subject: [PATCH 08/32] Fixed the entire feature --- src/FSharpPlus/Parsing.fs | 10 ++++++---- tests/FSharpPlus.Tests/Parsing.fs | 17 ++++++++++++----- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index 99dd00abb..d12096a5d 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -11,7 +11,7 @@ module Parsing = open FSharpPlus.Internals open FSharpPlus.Internals.Prelude - let inline private getGroups (pf: PrintfFormat<_,_,_,_,_>) str = + let getGroups (pf: PrintfFormat<_,_,_,_,_>) str = let format = pf.Value let regex = System.Text.StringBuilder "^" let mutable groups = FSharp.Core.CompilerServices.ArrayCollector() @@ -33,7 +33,7 @@ module Parsing = | 'B' -> "([01]+)" | 'c' -> "(.)" | 'd' | 'i' -> "([+-]?[0-9]+)" - | 'e' | 'E' | 'f' | 'F' | 'g' | 'G' | 'M' -> "([+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?)" + | 'e' | 'E' | 'f' | 'F' | 'g' | 'G' | 'M' -> "([+-]?[0-9.]+(?:[eE][+-]?[0-9]+)?)" | 'o' -> "([0-7]+)" | 'u' -> "([0-9]+)" | 's' -> "(.*?)" @@ -50,12 +50,14 @@ module Parsing = |> string |> Regex |> _.Match(str) - |> _.Groups + |> fun m -> + if not m.Success then [||] else + m.Groups |> Seq.cast |> Seq.skip 1 |> Seq.map _.Value |> Seq.toArray - |> Array.zip <| groups.Close() + |> Array.zip <| groups.Close() let inline private conv (destType: System.Type) (b: int) (s: string) = match destType with diff --git a/tests/FSharpPlus.Tests/Parsing.fs b/tests/FSharpPlus.Tests/Parsing.fs index ae0b3f861..f198f9a86 100644 --- a/tests/FSharpPlus.Tests/Parsing.fs +++ b/tests/FSharpPlus.Tests/Parsing.fs @@ -100,17 +100,24 @@ module Parsing = let _zzz = sscanf "(%%%s)" "(%hello)" + let _zzz1 = sscanf "%%(%s)" "%(hello)" let (_x1,_y1,_z1) = sscanf "%s--%s-%s" "test--this-string" let inline (|Like|_|) format = FSharpPlus.Parsing.trySscanf format - match "(%hello)" with Like "(%%%s)" "hello" -> () | _ -> failwith "didn't match" + match "ab" with Like "%c" _ -> failwith "wrong match" | Like "%c%c" ('a', 'b') -> () | _ -> failwith "didn't match" + match "abc" with Like "%c%c" ('a', 'b') -> failwith "wrong match" | Like "%c%c%c%s" ('a', 'b', 'c', "") -> () | _ -> failwith "didn't match" + match "(%hello)" with + | Like "%d" _ | Like "%f" _ | Like "%x" _ -> failwith "wrong match" + | Like "%%(%%%s)" _ | Like "(%%%sa" _ | Like "(%%hel%c" _ | Like "%%h%cllo)" _ -> failwith "wrong match" + | Like "(%%%s)" "hello" -> () + | _ -> failwith "didn't match" match "test--this-gg" with Like "%s--%s-%s" ("test", "this", "gg") -> () | _ -> failwith "didn't match" match "1 2.1 3.4 .3 43.2e32 0 f f" with Like "%f %F %g %G %e %E %c %c" (1f, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f') -> () | _ -> failwith "didn't match" match "1 2.1 3.4 .3 43.2e32 0 f f f" with Like "%f %F %g %G %e %E %c %c %c" (1f, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f') -> () | _ -> failwith "didn't match" - match "1 2.1 3.4 .3 43.2e32 0 f f ff" with Like "%f %F %g %G %e %E %c %c %c%c" (1f, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" - match "1 2.1 3.4 .3 43.2e32 0 f f fff" with Like "%f %F %g %G %e %E %c %c %c%c%c" (1f, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" - match "1 2.1 3.4 .3 43.2e32 0 f f fff 16" with Like "%f %F %g %G %e %E %c %c %c%c%c%i" (1f, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16) -> () | _ -> failwith "didn't match" - match "1 2.1 3.4 .3 43.2e32 0 f f fff 16 17" with Like "%f %F %g %G %e %E %c %c %c%c%c%i %f" (1f, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16, 17.) -> () | _ -> failwith "didn't match" + match "1 2.1 3.4 .3 43.2e32 0 f f ff" with Like "%B %F %g %G %e %E %c %c %c%c" (1, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" + match "1 2.1 3.4 .3 43.2e32 0 f f fff" with Like "%o %F %g %G %e %E %c %c %c%c%c" (1, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" + match "1 2.1 3.4 .3 43.2e32 0 f f fff16" with Like "%x %F %g %G %e %E %c %c %c%c%c%i" (1, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16) -> () | _ -> failwith "didn't match" + match "1 2.1 3.4 .3 43.2e32 0 f f fff16 17" with Like "%X %F %g %G %e %E %c %c %c%c%c%i %f" (1, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16, 17.) -> () | _ -> failwith "didn't match" match "13 43 AA 77A" with Like "%x %X %x %o%X" (0x13, 0x43, 0xAA, 0o77, 0xA) -> () | _ -> failwith "didn't match" match "13 43 AA 77A" with Like "%B%x %X %x %o%X" (0b1, 0x3, 0x43, 0xAA, 0o77, 0xA) -> () | _ -> failwith "didn't match" match "111AAA" with Like "%B%s" (0b111, "AAA") -> () | _ -> failwith "didn't match" From 058bb8c8d211b4781388a349686a559a4a8efeef Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Wed, 11 Jun 2025 01:03:59 +0800 Subject: [PATCH 09/32] More variations in tests --- tests/FSharpPlus.Tests/Parsing.fs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/FSharpPlus.Tests/Parsing.fs b/tests/FSharpPlus.Tests/Parsing.fs index f198f9a86..bd6790974 100644 --- a/tests/FSharpPlus.Tests/Parsing.fs +++ b/tests/FSharpPlus.Tests/Parsing.fs @@ -112,12 +112,12 @@ module Parsing = | Like "(%%%s)" "hello" -> () | _ -> failwith "didn't match" match "test--this-gg" with Like "%s--%s-%s" ("test", "this", "gg") -> () | _ -> failwith "didn't match" - match "1 2.1 3.4 .3 43.2e32 0 f f" with Like "%f %F %g %G %e %E %c %c" (1f, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f') -> () | _ -> failwith "didn't match" - match "1 2.1 3.4 .3 43.2e32 0 f f f" with Like "%f %F %g %G %e %E %c %c %c" (1f, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f') -> () | _ -> failwith "didn't match" + match "1 2.1 3.4 .3 43.2e32 0 f f" with Like "%f %F %g %G %e %E %c %c" (1f, 2.1, 3.4m, 0.3, 43.2e32, 0., 'f', 'f') -> () | _ -> failwith "didn't match" + match "1 2.1 3.4 .3 43.2e32 0 f f f" with Like "%f %F %g %G %e %E %c %c %c" (1m, 2.1, 3.4, 0.3m, 43.2e32, 0., 'f', 'f', 'f') -> () | _ -> failwith "didn't match" match "1 2.1 3.4 .3 43.2e32 0 f f ff" with Like "%B %F %g %G %e %E %c %c %c%c" (1, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" - match "1 2.1 3.4 .3 43.2e32 0 f f fff" with Like "%o %F %g %G %e %E %c %c %c%c%c" (1, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" - match "1 2.1 3.4 .3 43.2e32 0 f f fff16" with Like "%x %F %g %G %e %E %c %c %c%c%c%i" (1, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16) -> () | _ -> failwith "didn't match" - match "1 2.1 3.4 .3 43.2e32 0 f f fff16 17" with Like "%X %F %g %G %e %E %c %c %c%c%c%i %f" (1, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16, 17.) -> () | _ -> failwith "didn't match" + match "1 2.1 3.4 .3 43.2e32 0 f f fff" with Like "%o %F %g %G %e %E %c %c %c%c%c" (1y, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" + match "1 2.1 3.4 .3 43.2e32 0 f f fff16" with Like "%x %F %g %G %e %E %c %c %c%c%c%i" (1us, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16) -> () | _ -> failwith "didn't match" + match "1 2.1 3.4 .3 43.2e32 0 f f fff16 17" with Like "%X %F %g %G %e %E %c %c %c%c%c%i %f" (1s, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16L, 17.) -> () | _ -> failwith "didn't match" match "13 43 AA 77A" with Like "%x %X %x %o%X" (0x13, 0x43, 0xAA, 0o77, 0xA) -> () | _ -> failwith "didn't match" match "13 43 AA 77A" with Like "%B%x %X %x %o%X" (0b1, 0x3, 0x43, 0xAA, 0o77, 0xA) -> () | _ -> failwith "didn't match" match "111AAA" with Like "%B%s" (0b111, "AAA") -> () | _ -> failwith "didn't match" From 86b87da7e14a2ec071bf1685b625194c6b366a75 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Wed, 11 Jun 2025 23:36:49 +0800 Subject: [PATCH 10/32] inline? --- src/FSharpPlus/Parsing.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index d12096a5d..22bc2f282 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -11,7 +11,7 @@ module Parsing = open FSharpPlus.Internals open FSharpPlus.Internals.Prelude - let getGroups (pf: PrintfFormat<_,_,_,_,_>) str = + let inline private getGroups (pf: PrintfFormat<_,_,_,_,_>) str = let format = pf.Value let regex = System.Text.StringBuilder "^" let mutable groups = FSharp.Core.CompilerServices.ArrayCollector() From 10f3d379e035c99a29496f7726a243d1c26dca9f Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Wed, 11 Jun 2025 22:00:58 +0000 Subject: [PATCH 11/32] Support consuming spaces before and after --- src/FSharpPlus/Parsing.fs | 95 ++++++++++++++++--------------- tests/FSharpPlus.Tests/Parsing.fs | 18 +++++- 2 files changed, 63 insertions(+), 50 deletions(-) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index 22bc2f282..e629fa747 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -4,12 +4,11 @@ [] module Parsing = - open System open System.Text.RegularExpressions open FSharpPlus open FSharpPlus.Internals - open FSharpPlus.Internals.Prelude + open Prelude let inline private getGroups (pf: PrintfFormat<_,_,_,_,_>) str = let format = pf.Value @@ -19,15 +18,16 @@ module Parsing = while i < String.length format do match format[i] with | '%' -> - let mutable j = i + 1 + let mutable consumeSpacesAfter = false // consume spaces after if '-' specified while - match format[j] with - | ' ' | '+' | '-' | '*' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' -> true + match format[i] with + | ' ' -> regex.Append @"\s*" |> ignore; true // consume spaces before if ' ' specified + | '-' -> consumeSpacesAfter <- true; true + | '+' | '*' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' -> true | _ -> false - do j <- j + 1 - if format[j] <> '%' then groups.Add format[i..j] // %% does not capture a group - i <- j - match format[j] with + do i <- i + 1 + if format[i] <> '%' then groups.Add format[i] // %% does not capture a group + match format[i] with | 'A' | 'O' -> "(.*?)" | 'b' -> "([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])" | 'B' -> "([01]+)" @@ -40,7 +40,8 @@ module Parsing = | 'x' | 'X' -> "([0-9a-fA-F]+)" | '%' -> "%" | x -> failwith $"Unknown specifier: {x}" - |> regex.Append + |> regex.Append |> ignore + if consumeSpacesAfter then regex.Append @"\s*" else regex | '\\' | '*' | '+' | '?' | '|' | '{' | '[' | '(' | ')' | '^' | '$' | '.' | '#' | ' ' as escape -> regex.Append('\\').Append escape | c -> regex.Append c @@ -71,29 +72,29 @@ module Parsing = | t when t = typeof -> Convert.ToInt64 (s, b) |> box | _ -> invalidOp (sprintf "Type conversion from string to type %A with base %i is not supported" destType b) - let inline private parse (s: string, f: string) : 'r = + let inline private parse (s: string, f: char) : 'r = match f with - | "%B" -> conv typeof<'r> 2 s |> string |> parse - | "%o" -> conv typeof<'r> 8 s |> string |> parse - | "%x" | "%X" -> conv typeof<'r> 16 s |> string |> parse + | 'B' -> conv typeof<'r> 2 s |> string |> parse + | 'o' -> conv typeof<'r> 8 s |> string |> parse + | 'x' | 'X' -> conv typeof<'r> 16 s |> string |> parse | _ -> parse s - let inline private tryParse (s: string, f: string) : 'r option = + let inline private tryParse (s: string, f: char) : 'r option = match f with - | "%B" -> Option.protect (conv typeof<'r> 2) s |> Option.map string |> Option.bind tryParse - | "%o" -> Option.protect (conv typeof<'r> 8) s |> Option.map string |> Option.bind tryParse - | "%x" | "%X" -> Option.protect (conv typeof<'r> 16) s |> Option.map string |> Option.bind tryParse + | 'B' -> Option.protect (conv typeof<'r> 2) s |> Option.map string |> Option.bind tryParse + | 'o' -> Option.protect (conv typeof<'r> 8) s |> Option.map string |> Option.bind tryParse + | 'x' | 'X' -> Option.protect (conv typeof<'r> 16) s |> Option.map string |> Option.bind tryParse | _ -> tryParse s type ParseArray = - static member inline ParseArray (_: 't , _: obj) = fun (g: (string * string) []) -> (parse (g.[0])) : 't + static member inline ParseArray (_: 't , _: obj) = fun (g: (string * char) []) -> (parse (g.[0])) : 't - static member inline Invoke (g: (string * string) []) = + static member inline Invoke (g: (string * char) []) = let inline call_2 (a: ^a, b: ^b) = ((^a or ^b) : (static member ParseArray: _*_ -> _) b, a) g let inline call (a: 'a) = call_2 (a, Unchecked.defaultof<'r>) : 'r call Unchecked.defaultof - static member inline ParseArray (t: 't, _: ParseArray) = fun (g: (string * string) []) -> + static member inline ParseArray (t: 't, _: ParseArray) = fun (g: (string * char) []) -> let _f _ = Constraints.whenNestedTuple t : ('t1*'t2*'t3*'t4*'t5*'t6*'t7*'tr) let (t1: 't1) = parse (g.[0]) let (t2: 't2) = parse (g.[1]) @@ -105,29 +106,29 @@ module Parsing = let (tr: 'tr) = ParseArray.Invoke (g.[7..]) Tuple<_,_,_,_,_,_,_,_> (t1, t2, t3, t4, t5, t6, t7, tr) |> retype : 't - static member inline ParseArray (_: unit , _: ParseArray) = fun (_: (string * string) []) -> () - static member inline ParseArray (_: Tuple<'t1> , _: ParseArray) = fun (g: (string * string) []) -> Tuple<_> (parse g.[0]) : Tuple<'t1> - static member inline ParseArray (_: Id<'t1> , _: ParseArray) = fun (g: (string * string) []) -> Id<_> (parse g.[0]) - static member inline ParseArray (_: 't1*'t2 , _: ParseArray) = fun (g: (string * string) []) -> parse g.[0], parse g.[1] - static member inline ParseArray (_: 't1*'t2'*'t3 , _: ParseArray) = fun (g: (string * string) []) -> parse g.[0], parse g.[1], parse g.[2] - static member inline ParseArray (_: 't1*'t2'*'t3*'t4 , _: ParseArray) = fun (g: (string * string) []) -> parse g.[0], parse g.[1], parse g.[2], parse g.[3] - static member inline ParseArray (_: 't1*'t2'*'t3*'t4*'t5 , _: ParseArray) = fun (g: (string * string) []) -> parse g.[0], parse g.[1], parse g.[2], parse g.[3], parse g.[4] - static member inline ParseArray (_: 't1*'t2'*'t3*'t4*'t5*'t6 , _: ParseArray) = fun (g: (string * string) []) -> parse g.[0], parse g.[1], parse g.[2], parse g.[3], parse g.[4], parse g.[5] - static member inline ParseArray (_: 't1*'t2'*'t3*'t4*'t5*'t6*'t7, _: ParseArray) = fun (g: (string * string) []) -> parse g.[0], parse g.[1], parse g.[2], parse g.[3], parse g.[4], parse g.[5], parse g.[6] - - let inline private tryParseElemAt i (g: (string * string) []) = + static member inline ParseArray (_: unit , _: ParseArray) = fun (_: (string * char) []) -> () + static member inline ParseArray (_: Tuple<'t1> , _: ParseArray) = fun (g: (string * char) []) -> Tuple<_> (parse g.[0]) : Tuple<'t1> + static member inline ParseArray (_: Id<'t1> , _: ParseArray) = fun (g: (string * char) []) -> Id<_> (parse g.[0]) + static member inline ParseArray (_: 't1*'t2 , _: ParseArray) = fun (g: (string * char) []) -> parse g.[0], parse g.[1] + static member inline ParseArray (_: 't1*'t2'*'t3 , _: ParseArray) = fun (g: (string * char) []) -> parse g.[0], parse g.[1], parse g.[2] + static member inline ParseArray (_: 't1*'t2'*'t3*'t4 , _: ParseArray) = fun (g: (string * char) []) -> parse g.[0], parse g.[1], parse g.[2], parse g.[3] + static member inline ParseArray (_: 't1*'t2'*'t3*'t4*'t5 , _: ParseArray) = fun (g: (string * char) []) -> parse g.[0], parse g.[1], parse g.[2], parse g.[3], parse g.[4] + static member inline ParseArray (_: 't1*'t2'*'t3*'t4*'t5*'t6 , _: ParseArray) = fun (g: (string * char) []) -> parse g.[0], parse g.[1], parse g.[2], parse g.[3], parse g.[4], parse g.[5] + static member inline ParseArray (_: 't1*'t2'*'t3*'t4*'t5*'t6*'t7, _: ParseArray) = fun (g: (string * char) []) -> parse g.[0], parse g.[1], parse g.[2], parse g.[3], parse g.[4], parse g.[5], parse g.[6] + + let inline private tryParseElemAt i (g: (string * char) []) = if i < Array.length g then tryParse (g.[i]) else None type TryParseArray = - static member inline TryParseArray (_:'t, _:obj) = fun (g: (string * string) []) -> tryParseElemAt 0 g : 't option + static member inline TryParseArray (_:'t, _:obj) = fun (g: (string * char) []) -> tryParseElemAt 0 g : 't option - static member inline Invoke (g: (string * string) []) = + static member inline Invoke (g: (string * char) []) = let inline call_2 (a: ^a, b: ^b) = ((^a or ^b) : (static member TryParseArray: _*_ -> _) b, a) g let inline call (a: 'a) = call_2 (a, Unchecked.defaultof<'r>) : 'r option call Unchecked.defaultof - static member inline TryParseArray (t: 't, _: TryParseArray) = fun (g: (string * string) []) -> + static member inline TryParseArray (t: 't, _: TryParseArray) = fun (g: (string * char) []) -> let _f _ = Constraints.whenNestedTuple t : ('t1*'t2*'t3*'t4*'t5*'t6*'t7*'tr) let (t1: 't1 option) = tryParseElemAt 0 g let (t2: 't2 option) = tryParseElemAt 1 g @@ -140,20 +141,20 @@ module Parsing = match t1, t2, t3, t4, t5, t6, t7, tr with | Some t1, Some t2, Some t3, Some t4, Some t5, Some t6, Some t7, Some tr -> Some (Tuple<_,_,_,_,_,_,_,_> (t1, t2, t3, t4, t5, t6, t7, tr) |> retype : 't) | _ -> None - - static member inline TryParseArray (_: unit , _: TryParseArray) = fun (_: (string * string) []) -> () - static member inline TryParseArray (_: Tuple<'t1> , _: TryParseArray) = fun (g: (string * string) []) -> Tuple<_> tryParseElemAt 0 g : Tuple<'t1> option - static member inline TryParseArray (_: Id<'t1> , _: TryParseArray) = fun (g: (string * string) []) -> Id<_> tryParseElemAt 0 g - static member inline TryParseArray (_: 't1*'t2 , _: TryParseArray) = fun (g: (string * string) []) -> tuple2 tryParseElemAt 0 g <*> tryParseElemAt 1 g - static member inline TryParseArray (_: 't1*'t2'*'t3 , _: TryParseArray) = fun (g: (string * string) []) -> tuple3 tryParseElemAt 0 g <*> tryParseElemAt 1 g <*> tryParseElemAt 2 g - static member inline TryParseArray (_: 't1*'t2'*'t3*'t4 , _: TryParseArray) = fun (g: (string * string) []) -> tuple4 tryParseElemAt 0 g <*> tryParseElemAt 1 g <*> tryParseElemAt 2 g <*> tryParseElemAt 3 g - static member inline TryParseArray (_: 't1*'t2'*'t3*'t4*'t5 , _: TryParseArray) = fun (g: (string * string) []) -> tuple5 tryParseElemAt 0 g <*> tryParseElemAt 1 g <*> tryParseElemAt 2 g <*> tryParseElemAt 3 g <*> tryParseElemAt 4 g - static member inline TryParseArray (_: 't1*'t2'*'t3*'t4*'t5*'t6 , _: TryParseArray) = fun (g: (string * string) []) -> tuple6 tryParseElemAt 0 g <*> tryParseElemAt 1 g <*> tryParseElemAt 2 g <*> tryParseElemAt 3 g <*> tryParseElemAt 4 g <*> tryParseElemAt 5 g - static member inline TryParseArray (_: 't1*'t2'*'t3*'t4*'t5*'t6*'t7, _: TryParseArray) = fun (g: (string * string) []) -> tuple7 tryParseElemAt 0 g <*> tryParseElemAt 1 g <*> tryParseElemAt 2 g <*> tryParseElemAt 3 g <*> tryParseElemAt 4 g <*> tryParseElemAt 5 g <*> tryParseElemAt 6 g + + static member inline TryParseArray (_: unit , _: TryParseArray) = fun (_: (string * char) []) -> () + static member inline TryParseArray (_: Tuple<'t1> , _: TryParseArray) = fun (g: (string * char) []) -> Tuple<_> tryParseElemAt 0 g : Tuple<'t1> option + static member inline TryParseArray (_: Id<'t1> , _: TryParseArray) = fun (g: (string * char) []) -> Id<_> tryParseElemAt 0 g + static member inline TryParseArray (_: 't1*'t2 , _: TryParseArray) = fun (g: (string * char) []) -> tuple2 tryParseElemAt 0 g <*> tryParseElemAt 1 g + static member inline TryParseArray (_: 't1*'t2'*'t3 , _: TryParseArray) = fun (g: (string * char) []) -> tuple3 tryParseElemAt 0 g <*> tryParseElemAt 1 g <*> tryParseElemAt 2 g + static member inline TryParseArray (_: 't1*'t2'*'t3*'t4 , _: TryParseArray) = fun (g: (string * char) []) -> tuple4 tryParseElemAt 0 g <*> tryParseElemAt 1 g <*> tryParseElemAt 2 g <*> tryParseElemAt 3 g + static member inline TryParseArray (_: 't1*'t2'*'t3*'t4*'t5 , _: TryParseArray) = fun (g: (string * char) []) -> tuple5 tryParseElemAt 0 g <*> tryParseElemAt 1 g <*> tryParseElemAt 2 g <*> tryParseElemAt 3 g <*> tryParseElemAt 4 g + static member inline TryParseArray (_: 't1*'t2'*'t3*'t4*'t5*'t6 , _: TryParseArray) = fun (g: (string * char) []) -> tuple6 tryParseElemAt 0 g <*> tryParseElemAt 1 g <*> tryParseElemAt 2 g <*> tryParseElemAt 3 g <*> tryParseElemAt 4 g <*> tryParseElemAt 5 g + static member inline TryParseArray (_: 't1*'t2'*'t3*'t4*'t5*'t6*'t7, _: TryParseArray) = fun (g: (string * char) []) -> tuple7 tryParseElemAt 0 g <*> tryParseElemAt 1 g <*> tryParseElemAt 2 g <*> tryParseElemAt 3 g <*> tryParseElemAt 4 g <*> tryParseElemAt 5 g <*> tryParseElemAt 6 g /// Gets a tuple with the result of parsing each element of a string array. - let inline parseArray (source: string []) : '``(T1 * T2 * ... * Tn)`` = ParseArray.Invoke (Array.map (fun x -> (x, "")) source) + let inline parseArray (source: string []) : '``(T1 * T2 * ... * Tn)`` = ParseArray.Invoke (Array.map (fun x -> (x, '\000')) source) /// Gets a tuple with the result of parsing each element of a formatted text. let inline sscanf (pf: PrintfFormat<_,_,_,_,'``(T1 * T2 * ... * Tn)``>) s : '``(T1 * T2 * ... * Tn)`` = getGroups pf s |> ParseArray.Invoke @@ -162,7 +163,7 @@ module Parsing = let inline scanfn pf : '``(T1 * T2 * ... * Tn)`` = sscanf pf (Console.ReadLine ()) /// Gets a tuple with the result of parsing each element of a string array. Returns None in case of failure. - let inline tryParseArray (source: string []) : '``(T1 * T2 * ... * Tn)`` option = TryParseArray.Invoke (Array.map (fun x -> (x, "")) source) + let inline tryParseArray (source: string []) : '``(T1 * T2 * ... * Tn)`` option = TryParseArray.Invoke (Array.map (fun x -> (x, '\000')) source) /// Gets a tuple with the result of parsing each element of a formatted text. Returns None in case of failure. let inline trySscanf (pf: PrintfFormat<_,_,_,_,'``(T1 * T2 * ... * Tn)``>) s : '``(T1 * T2 * ... * Tn)`` option = getGroups pf s |> TryParseArray.Invoke diff --git a/tests/FSharpPlus.Tests/Parsing.fs b/tests/FSharpPlus.Tests/Parsing.fs index bd6790974..e88646cf3 100644 --- a/tests/FSharpPlus.Tests/Parsing.fs +++ b/tests/FSharpPlus.Tests/Parsing.fs @@ -87,7 +87,10 @@ module Parsing = let _ccx: int * uint32 * float * float32 * int * uint32 * float * float32 * int * uint32 * float * float32 * int * uint32 * float * float32 * int = parseArray [|"34"; "24"; "34"; "4"; "5"; "6"; "7"; "8"; "9"; "10"; "11"; "12"; "13"; "14"; "15"; "16"; "17"|] let _t = sscanf "(%i-%i-%f-%i-%i-%i-%i-%i-%i)" "(32-66-888-4-5-6-7-8-9)" - let (_a,_b) = sscanf "(%%%s,%M)" "(%hello, 4.53)" + let (_a,_b) = sscanf "(%%%s,%M)" "(%hello,4.53)" + let (_a1,_b1) = sscanf "(%%%s,% M)" "(%hello, 4.53)" + let (_a2,_b2) = sscanf "(%%%s,%-M)" "(%hello,4.53 )" + let (_a3,_b3) = sscanf "(%%%s,% -M)" "(%hello, 4.53 )" let (_x,_y,_z) = sscanf "%s-%s-%s" "test-this-string" let (_j,_k,_l,_m,_n,_o,_p) = sscanf "%f %F %g %G %e %E %c" "1 2.1 3.4 .3 43.2e32 0 f" @@ -111,10 +114,19 @@ module Parsing = | Like "%%(%%%s)" _ | Like "(%%%sa" _ | Like "(%%hel%c" _ | Like "%%h%cllo)" _ -> failwith "wrong match" | Like "(%%%s)" "hello" -> () | _ -> failwith "didn't match" + match " 3" with Like "% d" 3 -> () | _ -> failwith "didn't match" + match " 3" with Like "% d" 3 -> () | _ -> failwith "didn't match" + match " 3" with Like "% d" 3 -> () | _ -> failwith "didn't match" // em space + match "3 " with Like "%-d" 3 -> () | _ -> failwith "didn't match" + match "3 " with Like "%-d" 3 -> () | _ -> failwith "didn't match" + match "3 " with Like "%-d" 3 -> () | _ -> failwith "didn't match" // em space + match " 3 " with Like "% -d" 3 -> () | _ -> failwith "didn't match" + match " 3 " with Like "% -d" 3 -> () | _ -> failwith "didn't match" + match " 3 " with Like "% -d" 3 -> () | _ -> failwith "didn't match" // em space match "test--this-gg" with Like "%s--%s-%s" ("test", "this", "gg") -> () | _ -> failwith "didn't match" match "1 2.1 3.4 .3 43.2e32 0 f f" with Like "%f %F %g %G %e %E %c %c" (1f, 2.1, 3.4m, 0.3, 43.2e32, 0., 'f', 'f') -> () | _ -> failwith "didn't match" - match "1 2.1 3.4 .3 43.2e32 0 f f f" with Like "%f %F %g %G %e %E %c %c %c" (1m, 2.1, 3.4, 0.3m, 43.2e32, 0., 'f', 'f', 'f') -> () | _ -> failwith "didn't match" - match "1 2.1 3.4 .3 43.2e32 0 f f ff" with Like "%B %F %g %G %e %E %c %c %c%c" (1, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" + match "1 2.1 3.4 .3 43.2e32 0 f f f" with Like "%f% F %g %G %e %E %c %c %c" (1m, 2.1, 3.4, 0.3m, 43.2e32, 0., 'f', 'f', 'f') -> () | _ -> failwith "didn't match" + match "1 2.1 3.4.3 43.2e32 0 f f ff" with Like "%B %F %-g %G %e %E %c %c %c%c" (1, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" match "1 2.1 3.4 .3 43.2e32 0 f f fff" with Like "%o %F %g %G %e %E %c %c %c%c%c" (1y, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" match "1 2.1 3.4 .3 43.2e32 0 f f fff16" with Like "%x %F %g %G %e %E %c %c %c%c%c%i" (1us, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16) -> () | _ -> failwith "didn't match" match "1 2.1 3.4 .3 43.2e32 0 f f fff16 17" with Like "%X %F %g %G %e %E %c %c %c%c%c%i %f" (1s, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16L, 17.) -> () | _ -> failwith "didn't match" From e407fdbbddcc3ee674aee7dce29fccd172f879d7 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Wed, 11 Jun 2025 22:14:32 +0000 Subject: [PATCH 12/32] Align spaces --- src/FSharpPlus/Parsing.fs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index e629fa747..77f2787ed 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -74,15 +74,15 @@ module Parsing = let inline private parse (s: string, f: char) : 'r = match f with - | 'B' -> conv typeof<'r> 2 s |> string |> parse - | 'o' -> conv typeof<'r> 8 s |> string |> parse + | 'B' -> conv typeof<'r> 2 s |> string |> parse + | 'o' -> conv typeof<'r> 8 s |> string |> parse | 'x' | 'X' -> conv typeof<'r> 16 s |> string |> parse | _ -> parse s let inline private tryParse (s: string, f: char) : 'r option = match f with - | 'B' -> Option.protect (conv typeof<'r> 2) s |> Option.map string |> Option.bind tryParse - | 'o' -> Option.protect (conv typeof<'r> 8) s |> Option.map string |> Option.bind tryParse + | 'B' -> Option.protect (conv typeof<'r> 2) s |> Option.map string |> Option.bind tryParse + | 'o' -> Option.protect (conv typeof<'r> 8) s |> Option.map string |> Option.bind tryParse | 'x' | 'X' -> Option.protect (conv typeof<'r> 16) s |> Option.map string |> Option.bind tryParse | _ -> tryParse s From 0d4a9429ed1e398642854318e6250b72bca59f00 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Thu, 12 Jun 2025 09:00:53 +0800 Subject: [PATCH 13/32] This should pass --- src/FSharpPlus/Parsing.fs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index 77f2787ed..53ad55194 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -18,6 +18,7 @@ module Parsing = while i < String.length format do match format[i] with | '%' -> + i <- i + 1 let mutable consumeSpacesAfter = false // consume spaces after if '-' specified while match format[i] with @@ -168,6 +169,9 @@ module Parsing = /// Gets a tuple with the result of parsing each element of a formatted text. Returns None in case of failure. let inline trySscanf (pf: PrintfFormat<_,_,_,_,'``(T1 * T2 * ... * Tn)``>) s : '``(T1 * T2 * ... * Tn)`` option = getGroups pf s |> TryParseArray.Invoke + /// Matches a formatted text with the result of parsing each element. Will not match in case of failure. + let inline (|Scan|_|) (pf: PrintfFormat<_,_,_,_,'``(T1 * T2 * ... * Tn)``>) s : '``(T1 * T2 * ... * Tn)`` option = trySscanf pf s + /// Gets a tuple with the result of parsing each element of a formatted text from the Console. Returns None in case of failure. let inline tryScanfn pf : '``(T1 * T2 * ... * Tn)`` option = trySscanf pf (Console.ReadLine ()) From 49932ac3c4c30fcacf88dc35b1eed9e272e580a5 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Thu, 12 Jun 2025 09:01:44 +0800 Subject: [PATCH 14/32] Test Scan --- tests/FSharpPlus.Tests/Parsing.fs | 51 +++++++++++++++---------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/tests/FSharpPlus.Tests/Parsing.fs b/tests/FSharpPlus.Tests/Parsing.fs index e88646cf3..916c1e18a 100644 --- a/tests/FSharpPlus.Tests/Parsing.fs +++ b/tests/FSharpPlus.Tests/Parsing.fs @@ -106,34 +106,33 @@ module Parsing = let _zzz1 = sscanf "%%(%s)" "%(hello)" let (_x1,_y1,_z1) = sscanf "%s--%s-%s" "test--this-string" - let inline (|Like|_|) format = FSharpPlus.Parsing.trySscanf format - match "ab" with Like "%c" _ -> failwith "wrong match" | Like "%c%c" ('a', 'b') -> () | _ -> failwith "didn't match" - match "abc" with Like "%c%c" ('a', 'b') -> failwith "wrong match" | Like "%c%c%c%s" ('a', 'b', 'c', "") -> () | _ -> failwith "didn't match" + match "ab" with Scan "%c" _ -> failwith "wrong match" | Scan "%c%c" ('a', 'b') -> () | _ -> failwith "didn't match" + match "abc" with Scan "%c%c" ('a', 'b') -> failwith "wrong match" | Scan "%c%c%c%s" ('a', 'b', 'c', "") -> () | _ -> failwith "didn't match" match "(%hello)" with - | Like "%d" _ | Like "%f" _ | Like "%x" _ -> failwith "wrong match" - | Like "%%(%%%s)" _ | Like "(%%%sa" _ | Like "(%%hel%c" _ | Like "%%h%cllo)" _ -> failwith "wrong match" - | Like "(%%%s)" "hello" -> () + | Scan "%d" _ | Scan "%f" _ | Scan "%x" _ -> failwith "wrong match" + | Scan "%%(%%%s)" _ | Scan "(%%%sa" _ | Scan "(%%hel%c" _ | Scan "%%h%cllo)" _ -> failwith "wrong match" + | Scan "(%%%s)" "hello" -> () | _ -> failwith "didn't match" - match " 3" with Like "% d" 3 -> () | _ -> failwith "didn't match" - match " 3" with Like "% d" 3 -> () | _ -> failwith "didn't match" - match " 3" with Like "% d" 3 -> () | _ -> failwith "didn't match" // em space - match "3 " with Like "%-d" 3 -> () | _ -> failwith "didn't match" - match "3 " with Like "%-d" 3 -> () | _ -> failwith "didn't match" - match "3 " with Like "%-d" 3 -> () | _ -> failwith "didn't match" // em space - match " 3 " with Like "% -d" 3 -> () | _ -> failwith "didn't match" - match " 3 " with Like "% -d" 3 -> () | _ -> failwith "didn't match" - match " 3 " with Like "% -d" 3 -> () | _ -> failwith "didn't match" // em space - match "test--this-gg" with Like "%s--%s-%s" ("test", "this", "gg") -> () | _ -> failwith "didn't match" - match "1 2.1 3.4 .3 43.2e32 0 f f" with Like "%f %F %g %G %e %E %c %c" (1f, 2.1, 3.4m, 0.3, 43.2e32, 0., 'f', 'f') -> () | _ -> failwith "didn't match" - match "1 2.1 3.4 .3 43.2e32 0 f f f" with Like "%f% F %g %G %e %E %c %c %c" (1m, 2.1, 3.4, 0.3m, 43.2e32, 0., 'f', 'f', 'f') -> () | _ -> failwith "didn't match" - match "1 2.1 3.4.3 43.2e32 0 f f ff" with Like "%B %F %-g %G %e %E %c %c %c%c" (1, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" - match "1 2.1 3.4 .3 43.2e32 0 f f fff" with Like "%o %F %g %G %e %E %c %c %c%c%c" (1y, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" - match "1 2.1 3.4 .3 43.2e32 0 f f fff16" with Like "%x %F %g %G %e %E %c %c %c%c%c%i" (1us, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16) -> () | _ -> failwith "didn't match" - match "1 2.1 3.4 .3 43.2e32 0 f f fff16 17" with Like "%X %F %g %G %e %E %c %c %c%c%c%i %f" (1s, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16L, 17.) -> () | _ -> failwith "didn't match" - match "13 43 AA 77A" with Like "%x %X %x %o%X" (0x13, 0x43, 0xAA, 0o77, 0xA) -> () | _ -> failwith "didn't match" - match "13 43 AA 77A" with Like "%B%x %X %x %o%X" (0b1, 0x3, 0x43, 0xAA, 0o77, 0xA) -> () | _ -> failwith "didn't match" - match "111AAA" with Like "%B%s" (0b111, "AAA") -> () | _ -> failwith "didn't match" - match "100700 100 100" with Like "%B%o %x %X" (0b100, 0o700, 0x100, 0x100) -> () | _ -> failwith "didn't match" + match " 3" with Scan "% d" 3 -> () | _ -> failwith "didn't match" + match " 3" with Scan "% d" 3 -> () | _ -> failwith "didn't match" + match " 3" with Scan "% d" 3 -> () | _ -> failwith "didn't match" // em space + match "3 " with Scan "%-d" 3 -> () | _ -> failwith "didn't match" + match "3 " with Scan "%-d" 3 -> () | _ -> failwith "didn't match" + match "3 " with Scan "%-d" 3 -> () | _ -> failwith "didn't match" // em space + match " 3 " with Scan "% -d" 3 -> () | _ -> failwith "didn't match" + match " 3 " with Scan "% -d" 3 -> () | _ -> failwith "didn't match" + match " 3 " with Scan "% -d" 3 -> () | _ -> failwith "didn't match" // em space + match "test--this-gg" with Scan "%s--%s-%s" ("test", "this", "gg") -> () | _ -> failwith "didn't match" + match "1 2.1 3.4 .3 43.2e32 0 f f" with Scan "%f %F %g %G %e %E %c %c" (1f, 2.1, 3.4m, 0.3, 43.2e32, 0., 'f', 'f') -> () | _ -> failwith "didn't match" + match "1 2.1 3.4 .3 43.2e32 0 f f f" with Scan "%f% F %g %G %e %E %c %c %c" (1m, 2.1, 3.4, 0.3m, 43.2e32, 0., 'f', 'f', 'f') -> () | _ -> failwith "didn't match" + match "1 2.1 3.4.3 43.2e32 0 f f ff" with Scan "%B %F %-g %G %e %E %c %c %c%c" (1, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" + match "1 2.1 3.4 .3 43.2e32 0 f f fff" with Scan "%o %F %g %G %e %E %c %c %c%c%c" (1y, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" + match "1 2.1 3.4 .3 43.2e32 0 f f fff16" with Scan "%x %F %g %G %e %E %c %c %c%c%c%i" (1us, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16) -> () | _ -> failwith "didn't match" + match "1 2.1 3.4 .3 43.2e32 0 f f fff16 17" with Scan "%X %F %g %G %e %E %c %c %c%c%c%i %f" (1s, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16L, 17.) -> () | _ -> failwith "didn't match" + match "13 43 AA 77A" with Scan "%x %X %x %o%X" (0x13, 0x43, 0xAA, 0o77, 0xA) -> () | _ -> failwith "didn't match" + match "13 43 AA 77A" with Scan "%B%x %X %x %o%X" (0b1, 0x3, 0x43, 0xAA, 0o77, 0xA) -> () | _ -> failwith "didn't match" + match "111AAA" with Scan "%B%s" (0b111, "AAA") -> () | _ -> failwith "didn't match" + match "100700 100 100" with Scan "%B%o %x %X" (0b100, 0o700, 0x100, 0x100) -> () | _ -> failwith "didn't match" let _date: (DayOfWeek * string * uint16 * int) option = trySscanf "%A %A %A %A" "Saturday March 25 1989" From 35374b7441d860414be2090d6d6440c48edc970a Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Thu, 12 Jun 2025 22:30:09 +0800 Subject: [PATCH 15/32] Fix test --- tests/FSharpPlus.Tests/Parsing.fs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/FSharpPlus.Tests/Parsing.fs b/tests/FSharpPlus.Tests/Parsing.fs index 916c1e18a..2558eab14 100644 --- a/tests/FSharpPlus.Tests/Parsing.fs +++ b/tests/FSharpPlus.Tests/Parsing.fs @@ -125,10 +125,10 @@ module Parsing = match "test--this-gg" with Scan "%s--%s-%s" ("test", "this", "gg") -> () | _ -> failwith "didn't match" match "1 2.1 3.4 .3 43.2e32 0 f f" with Scan "%f %F %g %G %e %E %c %c" (1f, 2.1, 3.4m, 0.3, 43.2e32, 0., 'f', 'f') -> () | _ -> failwith "didn't match" match "1 2.1 3.4 .3 43.2e32 0 f f f" with Scan "%f% F %g %G %e %E %c %c %c" (1m, 2.1, 3.4, 0.3m, 43.2e32, 0., 'f', 'f', 'f') -> () | _ -> failwith "didn't match" - match "1 2.1 3.4.3 43.2e32 0 f f ff" with Scan "%B %F %-g %G %e %E %c %c %c%c" (1, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" - match "1 2.1 3.4 .3 43.2e32 0 f f fff" with Scan "%o %F %g %G %e %E %c %c %c%c%c" (1y, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" - match "1 2.1 3.4 .3 43.2e32 0 f f fff16" with Scan "%x %F %g %G %e %E %c %c %c%c%c%i" (1us, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16) -> () | _ -> failwith "didn't match" - match "1 2.1 3.4 .3 43.2e32 0 f f fff16 17" with Scan "%X %F %g %G %e %E %c %c %c%c%c%i %f" (1s, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16L, 17.) -> () | _ -> failwith "didn't match" + match "1 2.1 3.4.3 43.2e32 0 f f ff" with Scan "%B %F %-g%G %e %E %c %c %c%c" (1, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" + match "1 2.1 3.4.3 43.2e32 0 f f fff" with Scan "%o %F % g%-G %e %E %c %c %c%c%c" (1y, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" + match "1 2.1 3.4.3 43.2e32 0 f f fff16" with Scan "%x %F %- g%- G %e %E %c %c %c%c%c%i" (1us, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16) -> () | _ -> failwith "didn't match" + match "1 2.1 3.4.3 43.2e32 0 f f fff16 17" with Scan "%X %F %g %G %e %E %c %c %c%c%c%i %f" (1s, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16L, 17.) -> () | _ -> failwith "didn't match" match "13 43 AA 77A" with Scan "%x %X %x %o%X" (0x13, 0x43, 0xAA, 0o77, 0xA) -> () | _ -> failwith "didn't match" match "13 43 AA 77A" with Scan "%B%x %X %x %o%X" (0b1, 0x3, 0x43, 0xAA, 0o77, 0xA) -> () | _ -> failwith "didn't match" match "111AAA" with Scan "%B%s" (0b111, "AAA") -> () | _ -> failwith "didn't match" From d4f495941d68124dd0501cf64839b2102be2d158 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Thu, 12 Jun 2025 23:05:59 +0800 Subject: [PATCH 16/32] Update Parsing.fs --- tests/FSharpPlus.Tests/Parsing.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FSharpPlus.Tests/Parsing.fs b/tests/FSharpPlus.Tests/Parsing.fs index 2558eab14..e73068950 100644 --- a/tests/FSharpPlus.Tests/Parsing.fs +++ b/tests/FSharpPlus.Tests/Parsing.fs @@ -128,7 +128,7 @@ module Parsing = match "1 2.1 3.4.3 43.2e32 0 f f ff" with Scan "%B %F %-g%G %e %E %c %c %c%c" (1, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" match "1 2.1 3.4.3 43.2e32 0 f f fff" with Scan "%o %F % g%-G %e %E %c %c %c%c%c" (1y, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" match "1 2.1 3.4.3 43.2e32 0 f f fff16" with Scan "%x %F %- g%- G %e %E %c %c %c%c%c%i" (1us, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16) -> () | _ -> failwith "didn't match" - match "1 2.1 3.4.3 43.2e32 0 f f fff16 17" with Scan "%X %F %g %G %e %E %c %c %c%c%c%i %f" (1s, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16L, 17.) -> () | _ -> failwith "didn't match" + match "1 2.1 3.4.3 43.2e32 0 f f fff16 17" with Scan "%X %F %g% G %e %E %c %c %c%c%c%i %f" (1s, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16L, 17.) -> () | _ -> failwith "didn't match" match "13 43 AA 77A" with Scan "%x %X %x %o%X" (0x13, 0x43, 0xAA, 0o77, 0xA) -> () | _ -> failwith "didn't match" match "13 43 AA 77A" with Scan "%B%x %X %x %o%X" (0b1, 0x3, 0x43, 0xAA, 0o77, 0xA) -> () | _ -> failwith "didn't match" match "111AAA" with Scan "%B%s" (0b111, "AAA") -> () | _ -> failwith "didn't match" From 7432c287e24df305532eef2b2de345fa2c4cdb55 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Thu, 12 Jun 2025 23:12:41 +0800 Subject: [PATCH 17/32] Update Parsing.fs --- src/FSharpPlus/Parsing.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index 53ad55194..b5a490862 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -34,7 +34,7 @@ module Parsing = | 'B' -> "([01]+)" | 'c' -> "(.)" | 'd' | 'i' -> "([+-]?[0-9]+)" - | 'e' | 'E' | 'f' | 'F' | 'g' | 'G' | 'M' -> "([+-]?[0-9.]+(?:[eE][+-]?[0-9]+)?)" + | 'e' | 'E' | 'f' | 'F' | 'g' | 'G' | 'M' -> "([+-]?(?:[0-9]+\.?[0-9]*|\.[0-9]+)?(?:[eE][+-]?[0-9]+)?)" | 'o' -> "([0-7]+)" | 'u' -> "([0-9]+)" | 's' -> "(.*?)" From 4bbb1d66fa41c60f3d7e7c53c1e83514a2ee7b92 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Thu, 12 Jun 2025 23:14:03 +0800 Subject: [PATCH 18/32] Update Parsing.fs --- src/FSharpPlus/Parsing.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index b5a490862..c2d7fcf4d 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -34,7 +34,7 @@ module Parsing = | 'B' -> "([01]+)" | 'c' -> "(.)" | 'd' | 'i' -> "([+-]?[0-9]+)" - | 'e' | 'E' | 'f' | 'F' | 'g' | 'G' | 'M' -> "([+-]?(?:[0-9]+\.?[0-9]*|\.[0-9]+)?(?:[eE][+-]?[0-9]+)?)" + | 'e' | 'E' | 'f' | 'F' | 'g' | 'G' | 'M' -> @"([+-]?(?:[0-9]+\.?[0-9]*|\.[0-9]+)?(?:[eE][+-]?[0-9]+)?)" | 'o' -> "([0-7]+)" | 'u' -> "([0-9]+)" | 's' -> "(.*?)" From b46db24fc44907e790c2d96c4a836e87398eb6ea Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Thu, 12 Jun 2025 23:16:28 +0800 Subject: [PATCH 19/32] Update Parsing.fs --- src/FSharpPlus/Parsing.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index c2d7fcf4d..540bc4109 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -34,7 +34,7 @@ module Parsing = | 'B' -> "([01]+)" | 'c' -> "(.)" | 'd' | 'i' -> "([+-]?[0-9]+)" - | 'e' | 'E' | 'f' | 'F' | 'g' | 'G' | 'M' -> @"([+-]?(?:[0-9]+\.?[0-9]*|\.[0-9]+)?(?:[eE][+-]?[0-9]+)?)" + | 'e' | 'E' | 'f' | 'F' | 'g' | 'G' | 'M' -> @"([+-]?(?:[0-9]+\.?[0-9]*|\.[0-9]+)(?:[eE][+-]?[0-9]+)?)" | 'o' -> "([0-7]+)" | 'u' -> "([0-9]+)" | 's' -> "(.*?)" From 6a94e1efeafe73fc0d0e24b4791ab1e718084ed7 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Thu, 12 Jun 2025 23:19:58 +0800 Subject: [PATCH 20/32] Update Parsing.fs --- tests/FSharpPlus.Tests/Parsing.fs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/FSharpPlus.Tests/Parsing.fs b/tests/FSharpPlus.Tests/Parsing.fs index e73068950..cab67132b 100644 --- a/tests/FSharpPlus.Tests/Parsing.fs +++ b/tests/FSharpPlus.Tests/Parsing.fs @@ -91,6 +91,7 @@ module Parsing = let (_a1,_b1) = sscanf "(%%%s,% M)" "(%hello, 4.53)" let (_a2,_b2) = sscanf "(%%%s,%-M)" "(%hello,4.53 )" let (_a3,_b3) = sscanf "(%%%s,% -M)" "(%hello, 4.53 )" + let (_a4,_b4) = sscanf "(%%%s,% d%-M)" "(%hello, 4.53 )" let (_x,_y,_z) = sscanf "%s-%s-%s" "test-this-string" let (_j,_k,_l,_m,_n,_o,_p) = sscanf "%f %F %g %G %e %E %c" "1 2.1 3.4 .3 43.2e32 0 f" From e3ff33776f012629897c024fe44f9299e04a6705 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Thu, 12 Jun 2025 23:21:08 +0800 Subject: [PATCH 21/32] Update Parsing.fs --- tests/FSharpPlus.Tests/Parsing.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FSharpPlus.Tests/Parsing.fs b/tests/FSharpPlus.Tests/Parsing.fs index cab67132b..782d1c1c9 100644 --- a/tests/FSharpPlus.Tests/Parsing.fs +++ b/tests/FSharpPlus.Tests/Parsing.fs @@ -91,7 +91,7 @@ module Parsing = let (_a1,_b1) = sscanf "(%%%s,% M)" "(%hello, 4.53)" let (_a2,_b2) = sscanf "(%%%s,%-M)" "(%hello,4.53 )" let (_a3,_b3) = sscanf "(%%%s,% -M)" "(%hello, 4.53 )" - let (_a4,_b4) = sscanf "(%%%s,% d%-M)" "(%hello, 4.53 )" + let (_a4,_b4,_ab) = sscanf "(%%%s,% d%-M)" "(%hello, 4.53 )" let (_x,_y,_z) = sscanf "%s-%s-%s" "test-this-string" let (_j,_k,_l,_m,_n,_o,_p) = sscanf "%f %F %g %G %e %E %c" "1 2.1 3.4 .3 43.2e32 0 f" From 0868dcdb5afa7fd0397937d54dc3ae0628601b1b Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Fri, 13 Jun 2025 23:14:07 +0800 Subject: [PATCH 22/32] Support + specifier to consume plus in front --- src/FSharpPlus/Parsing.fs | 34 ++++++++++++++++++++++--------- tests/FSharpPlus.Tests/Parsing.fs | 31 ++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 10 deletions(-) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index 540bc4109..ca18fadc6 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -10,7 +10,7 @@ module Parsing = open FSharpPlus.Internals open Prelude - let inline private getGroups (pf: PrintfFormat<_,_,_,_,_>) str = + let getGroups (pf: PrintfFormat<_,_,_,_,_>) str = let format = pf.Value let regex = System.Text.StringBuilder "^" let mutable groups = FSharp.Core.CompilerServices.ArrayCollector() @@ -20,25 +20,39 @@ module Parsing = | '%' -> i <- i + 1 let mutable consumeSpacesAfter = false // consume spaces after if '-' specified + let mutable consumeNumericPlus = false // consume plus for numeric values after if '+' specified while match format[i] with | ' ' -> regex.Append @"\s*" |> ignore; true // consume spaces before if ' ' specified | '-' -> consumeSpacesAfter <- true; true - | '+' | '*' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' -> true + | '+' -> consumeNumericPlus <- true; true + | '*' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' -> true | _ -> false do i <- i + 1 if format[i] <> '%' then groups.Add format[i] // %% does not capture a group match format[i] with | 'A' | 'O' -> "(.*?)" | 'b' -> "([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])" - | 'B' -> "([01]+)" + | 'B' -> + if consumeNumericPlus then regex.Append @"\+?" |> ignore + "([01]+)" | 'c' -> "(.)" - | 'd' | 'i' -> "([+-]?[0-9]+)" - | 'e' | 'E' | 'f' | 'F' | 'g' | 'G' | 'M' -> @"([+-]?(?:[0-9]+\.?[0-9]*|\.[0-9]+)(?:[eE][+-]?[0-9]+)?)" - | 'o' -> "([0-7]+)" - | 'u' -> "([0-9]+)" + | 'd' | 'i' -> + regex.Append (if consumeNumericPlus then "([+-]?" else "(-?") |> ignore + "[0-9]+)" + | 'e' | 'E' | 'f' | 'F' | 'g' | 'G' | 'M' -> + regex.Append (if consumeNumericPlus then "([+-]?" else "(-?") |> ignore + @"(?:[0-9]+\.?[0-9]*|\.[0-9]+)(?:[eE][+-]?[0-9]+)?)" + | 'o' -> + if consumeNumericPlus then regex.Append @"\+?" |> ignore + "([0-7]+)" + | 'u' -> + if consumeNumericPlus then regex.Append @"\+?" |> ignore + "([0-9]+)" | 's' -> "(.*?)" - | 'x' | 'X' -> "([0-9a-fA-F]+)" + | 'x' | 'X' -> + if consumeNumericPlus then regex.Append @"\+?" |> ignore + "([0-9a-fA-F]+)" | '%' -> "%" | x -> failwith $"Unknown specifier: {x}" |> regex.Append |> ignore @@ -142,7 +156,7 @@ module Parsing = match t1, t2, t3, t4, t5, t6, t7, tr with | Some t1, Some t2, Some t3, Some t4, Some t5, Some t6, Some t7, Some tr -> Some (Tuple<_,_,_,_,_,_,_,_> (t1, t2, t3, t4, t5, t6, t7, tr) |> retype : 't) | _ -> None - + static member inline TryParseArray (_: unit , _: TryParseArray) = fun (_: (string * char) []) -> () static member inline TryParseArray (_: Tuple<'t1> , _: TryParseArray) = fun (g: (string * char) []) -> Tuple<_> tryParseElemAt 0 g : Tuple<'t1> option static member inline TryParseArray (_: Id<'t1> , _: TryParseArray) = fun (g: (string * char) []) -> Id<_> tryParseElemAt 0 g @@ -175,4 +189,4 @@ module Parsing = /// Gets a tuple with the result of parsing each element of a formatted text from the Console. Returns None in case of failure. let inline tryScanfn pf : '``(T1 * T2 * ... * Tn)`` option = trySscanf pf (Console.ReadLine ()) -#endif +#endif \ No newline at end of file diff --git a/tests/FSharpPlus.Tests/Parsing.fs b/tests/FSharpPlus.Tests/Parsing.fs index 782d1c1c9..8df8237c4 100644 --- a/tests/FSharpPlus.Tests/Parsing.fs +++ b/tests/FSharpPlus.Tests/Parsing.fs @@ -135,7 +135,38 @@ module Parsing = match "111AAA" with Scan "%B%s" (0b111, "AAA") -> () | _ -> failwith "didn't match" match "100700 100 100" with Scan "%B%o %x %X" (0b100, 0o700, 0x100, 0x100) -> () | _ -> failwith "didn't match" + match "1+1-2+2-8+8" with + | Scan "%s%o" _ -> failwith "wrong match" + | Scan "%+u%+u%+d%+u%+u%+u" _ -> failwith "wrong match" + | Scan "%+u%+u%+d%+u%+d%o" _ -> failwith "wrong match" + | Scan "%+u%+u%+d%+u%-d%u" _ -> failwith "wrong match" + | Scan "%+u%+u%+d%+u%u%+d" _ -> failwith "wrong match" + | Scan "%+u%+u%+d%+u%+o%+u" _ -> failwith "wrong match" + | Scan "%+u%+u%+d%+u%+B%+u" _ -> failwith "wrong match" + | Scan "%+u%+u%+d%+u%+x%+X" _ -> failwith "wrong match" + | Scan "%+u%+u%+d%+u%+d%+X" (a, b, c, d, e, f) -> + areEqual (a |> box |> unbox) 1 + areEqual (b |> box |> unbox) 1 + areEqual (c |> box |> unbox) -2 + areEqual (d |> box |> unbox) 2 + areEqual (e |> box |> unbox) -8 + areEqual (f |> box |> unbox) 8 + | _ -> failwith "didn't match" + match "1+1-2+2-8+8" with Scan "%+-d%+d%+-d%+d%+-d%+d" (1,1,-2,2,-8,8) -> () | _ -> failwith "didn't match" + match "1+1-2+2-8+8" with Scan "%d+%d%d%+d%d%+d" (1,1,-2,2,-8,8) -> () | _ -> failwith "didn't match" + match "1+1-2+2-8+8" with Scan "%+B%+B-%+o%+o-%+X%+X" (1,1,2,2,8,8) -> () | _ -> failwith "didn't match" + match "1+1-2+2-8+8e" with Scan "%+f%+F%+e%+E%+g%+G%+X" (1f,1.,-2m,2f,-8.,8M,0xE) -> () | _ -> failwith "didn't match" + match "1+1-2+2-8+8e1a" with Scan "%+f%+F%+e%+E%+g%+G%+X" (1f,1.,-2m,2f,-8.,80M,0xA) -> () | _ -> failwith "didn't match" + match "1+1-2+2-8+8e-1a" with Scan "%+f%+F%+e%+E%+g%+G%+X" (1f,1.,-2m,2f,-8.,0.8M,0xA) -> () | _ -> failwith "didn't match" + match "1+1-2+2-8+8ea" with Scan "%+-f%+-F%+-e%+-E%+-g%+-G%+-X" (1f,1.,-2m,2f,-8.,8M,0xEA) -> () | _ -> failwith "didn't match" + let _date: (DayOfWeek * string * uint16 * int) option = trySscanf "%A %A %A %A" "Saturday March 25 1989" + let _date1: DateTime option = trySscanf "%A" "Saturday March 25 1989" + + match "12:34" with Scan "%A" (x: TimeSpan) -> areEqual (TimeSpan(12, 34, 0)) x | _ -> failwith "Pattern match failed" + match "12:34:56" with Scan "%O" (x: TimeSpan) -> areEqual (TimeSpan(12, 34, 56)) x | _ -> failwith "Pattern match failed" + match "9876-5-4 3:2:1" with Scan "%A" (x: DateTime) -> areEqual (DateTime(9876,5,4,3,2,1)) x | _ -> failwith "Pattern match failed" + match "9876-5-4 3:2:1 a" with Scan "%O %x" (x: DateTime, y) -> areEqual (DateTime(9876,5,4,3,2,1)) x; areEqual 0xA y | _ -> failwith "Pattern match failed" let x = trySscanf "%X %x" "13 43" let o = trySscanf "%o" "10" From a5aa84787522032cec01ab8e9f10abdeb5d677a3 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Fri, 13 Jun 2025 23:17:19 +0800 Subject: [PATCH 23/32] Update Parsing.fs --- src/FSharpPlus/Parsing.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index ca18fadc6..ae5cc1523 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -20,7 +20,7 @@ module Parsing = | '%' -> i <- i + 1 let mutable consumeSpacesAfter = false // consume spaces after if '-' specified - let mutable consumeNumericPlus = false // consume plus for numeric values after if '+' specified + let mutable consumeNumericPlus = false // consume plus before numeric values if '+' specified while match format[i] with | ' ' -> regex.Append @"\s*" |> ignore; true // consume spaces before if ' ' specified @@ -189,4 +189,4 @@ module Parsing = /// Gets a tuple with the result of parsing each element of a formatted text from the Console. Returns None in case of failure. let inline tryScanfn pf : '``(T1 * T2 * ... * Tn)`` option = trySscanf pf (Console.ReadLine ()) -#endif \ No newline at end of file +#endif From 62378c9881fbea46e1758b1c7bb39163bf971232 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Fri, 13 Jun 2025 23:33:29 +0800 Subject: [PATCH 24/32] Update Parsing.fs --- src/FSharpPlus/Parsing.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index ae5cc1523..e0e1ea686 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -10,7 +10,7 @@ module Parsing = open FSharpPlus.Internals open Prelude - let getGroups (pf: PrintfFormat<_,_,_,_,_>) str = + let inline private getGroups (pf: PrintfFormat<_,_,_,_,_>) str = let format = pf.Value let regex = System.Text.StringBuilder "^" let mutable groups = FSharp.Core.CompilerServices.ArrayCollector() From 17bf00f924633687e43684b46b8420839e0814f0 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Sat, 14 Jun 2025 00:04:42 +0800 Subject: [PATCH 25/32] Performance optimizations --- src/FSharpPlus/Parsing.fs | 89 ++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 48 deletions(-) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index e0e1ea686..881208065 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -10,17 +10,17 @@ module Parsing = open FSharpPlus.Internals open Prelude - let inline private getGroups (pf: PrintfFormat<_,_,_,_,_>) str = + let inline private getGroups (pf: PrintfFormat<_,_,_,_,_>) = let format = pf.Value let regex = System.Text.StringBuilder "^" - let mutable groups = FSharp.Core.CompilerServices.ArrayCollector() + let groups = ResizeArray(format.Length / 2) // worst case, every character is a group let mutable i = 0 while i < String.length format do match format[i] with | '%' -> i <- i + 1 let mutable consumeSpacesAfter = false // consume spaces after if '-' specified - let mutable consumeNumericPlus = false // consume plus before numeric values if '+' specified + let mutable consumeNumericPlus = false // consume plus for numeric values after if '+' specified while match format[i] with | ' ' -> regex.Append @"\s*" |> ignore; true // consume spaces before if ' ' specified @@ -62,18 +62,11 @@ module Parsing = | c -> regex.Append c |> ignore i <- i + 1 - regex.Append '$' - |> string - |> Regex - |> _.Match(str) - |> fun m -> - if not m.Success then [||] else - m.Groups - |> Seq.cast - |> Seq.skip 1 - |> Seq.map _.Value - |> Seq.toArray - |> Array.zip <| groups.Close() + let regex = regex.Append '$' |> string + fun str -> + let m = Regex.Match(str, regex) + if not m.Success then [||] else + Array.init (m.Groups.Count - 1) <| fun i -> struct(m.Groups[i + 1].Value, groups[i + 1]) let inline private conv (destType: System.Type) (b: int) (s: string) = match destType with @@ -87,14 +80,14 @@ module Parsing = | t when t = typeof -> Convert.ToInt64 (s, b) |> box | _ -> invalidOp (sprintf "Type conversion from string to type %A with base %i is not supported" destType b) - let inline private parse (s: string, f: char) : 'r = + let inline private parse struct(s: string, f: char) : 'r = match f with | 'B' -> conv typeof<'r> 2 s |> string |> parse | 'o' -> conv typeof<'r> 8 s |> string |> parse | 'x' | 'X' -> conv typeof<'r> 16 s |> string |> parse | _ -> parse s - let inline private tryParse (s: string, f: char) : 'r option = + let inline private tryParse struct(s: string, f: char) : 'r option = match f with | 'B' -> Option.protect (conv typeof<'r> 2) s |> Option.map string |> Option.bind tryParse | 'o' -> Option.protect (conv typeof<'r> 8) s |> Option.map string |> Option.bind tryParse @@ -102,14 +95,14 @@ module Parsing = | _ -> tryParse s type ParseArray = - static member inline ParseArray (_: 't , _: obj) = fun (g: (string * char) []) -> (parse (g.[0])) : 't + static member inline ParseArray struct(_: 't , _: obj) = fun (g: struct(string * char) []) -> (parse (g.[0])) : 't - static member inline Invoke (g: (string * char) []) = + static member inline Invoke (g: struct(string * char) []) = let inline call_2 (a: ^a, b: ^b) = ((^a or ^b) : (static member ParseArray: _*_ -> _) b, a) g let inline call (a: 'a) = call_2 (a, Unchecked.defaultof<'r>) : 'r call Unchecked.defaultof - static member inline ParseArray (t: 't, _: ParseArray) = fun (g: (string * char) []) -> + static member inline ParseArray (t: 't, _: ParseArray) = fun (g: struct(string * char) []) -> let _f _ = Constraints.whenNestedTuple t : ('t1*'t2*'t3*'t4*'t5*'t6*'t7*'tr) let (t1: 't1) = parse (g.[0]) let (t2: 't2) = parse (g.[1]) @@ -121,29 +114,29 @@ module Parsing = let (tr: 'tr) = ParseArray.Invoke (g.[7..]) Tuple<_,_,_,_,_,_,_,_> (t1, t2, t3, t4, t5, t6, t7, tr) |> retype : 't - static member inline ParseArray (_: unit , _: ParseArray) = fun (_: (string * char) []) -> () - static member inline ParseArray (_: Tuple<'t1> , _: ParseArray) = fun (g: (string * char) []) -> Tuple<_> (parse g.[0]) : Tuple<'t1> - static member inline ParseArray (_: Id<'t1> , _: ParseArray) = fun (g: (string * char) []) -> Id<_> (parse g.[0]) - static member inline ParseArray (_: 't1*'t2 , _: ParseArray) = fun (g: (string * char) []) -> parse g.[0], parse g.[1] - static member inline ParseArray (_: 't1*'t2'*'t3 , _: ParseArray) = fun (g: (string * char) []) -> parse g.[0], parse g.[1], parse g.[2] - static member inline ParseArray (_: 't1*'t2'*'t3*'t4 , _: ParseArray) = fun (g: (string * char) []) -> parse g.[0], parse g.[1], parse g.[2], parse g.[3] - static member inline ParseArray (_: 't1*'t2'*'t3*'t4*'t5 , _: ParseArray) = fun (g: (string * char) []) -> parse g.[0], parse g.[1], parse g.[2], parse g.[3], parse g.[4] - static member inline ParseArray (_: 't1*'t2'*'t3*'t4*'t5*'t6 , _: ParseArray) = fun (g: (string * char) []) -> parse g.[0], parse g.[1], parse g.[2], parse g.[3], parse g.[4], parse g.[5] - static member inline ParseArray (_: 't1*'t2'*'t3*'t4*'t5*'t6*'t7, _: ParseArray) = fun (g: (string * char) []) -> parse g.[0], parse g.[1], parse g.[2], parse g.[3], parse g.[4], parse g.[5], parse g.[6] - - let inline private tryParseElemAt i (g: (string * char) []) = + static member inline ParseArray (_: unit , _: ParseArray) = fun (_: struct(string * char) []) -> () + static member inline ParseArray (_: Tuple<'t1> , _: ParseArray) = fun (g: struct(string * char) []) -> Tuple<_> (parse g.[0]) : Tuple<'t1> + static member inline ParseArray (_: Id<'t1> , _: ParseArray) = fun (g: struct(string * char) []) -> Id<_> (parse g.[0]) + static member inline ParseArray (_: 't1*'t2 , _: ParseArray) = fun (g: struct(string * char) []) -> parse g.[0], parse g.[1] + static member inline ParseArray (_: 't1*'t2'*'t3 , _: ParseArray) = fun (g: struct(string * char) []) -> parse g.[0], parse g.[1], parse g.[2] + static member inline ParseArray (_: 't1*'t2'*'t3*'t4 , _: ParseArray) = fun (g: struct(string * char) []) -> parse g.[0], parse g.[1], parse g.[2], parse g.[3] + static member inline ParseArray (_: 't1*'t2'*'t3*'t4*'t5 , _: ParseArray) = fun (g: struct(string * char) []) -> parse g.[0], parse g.[1], parse g.[2], parse g.[3], parse g.[4] + static member inline ParseArray (_: 't1*'t2'*'t3*'t4*'t5*'t6 , _: ParseArray) = fun (g: struct(string * char) []) -> parse g.[0], parse g.[1], parse g.[2], parse g.[3], parse g.[4], parse g.[5] + static member inline ParseArray (_: 't1*'t2'*'t3*'t4*'t5*'t6*'t7, _: ParseArray) = fun (g: struct(string * char) []) -> parse g.[0], parse g.[1], parse g.[2], parse g.[3], parse g.[4], parse g.[5], parse g.[6] + + let inline private tryParseElemAt i (g: struct(string * char) []) = if i < Array.length g then tryParse (g.[i]) else None type TryParseArray = - static member inline TryParseArray (_:'t, _:obj) = fun (g: (string * char) []) -> tryParseElemAt 0 g : 't option + static member inline TryParseArray (_:'t, _:obj) = fun (g: struct(string * char) []) -> tryParseElemAt 0 g : 't option - static member inline Invoke (g: (string * char) []) = + static member inline Invoke (g: struct(string * char) []) = let inline call_2 (a: ^a, b: ^b) = ((^a or ^b) : (static member TryParseArray: _*_ -> _) b, a) g let inline call (a: 'a) = call_2 (a, Unchecked.defaultof<'r>) : 'r option call Unchecked.defaultof - static member inline TryParseArray (t: 't, _: TryParseArray) = fun (g: (string * char) []) -> + static member inline TryParseArray (t: 't, _: TryParseArray) = fun (g: struct(string * char) []) -> let _f _ = Constraints.whenNestedTuple t : ('t1*'t2*'t3*'t4*'t5*'t6*'t7*'tr) let (t1: 't1 option) = tryParseElemAt 0 g let (t2: 't2 option) = tryParseElemAt 1 g @@ -157,36 +150,36 @@ module Parsing = | Some t1, Some t2, Some t3, Some t4, Some t5, Some t6, Some t7, Some tr -> Some (Tuple<_,_,_,_,_,_,_,_> (t1, t2, t3, t4, t5, t6, t7, tr) |> retype : 't) | _ -> None - static member inline TryParseArray (_: unit , _: TryParseArray) = fun (_: (string * char) []) -> () - static member inline TryParseArray (_: Tuple<'t1> , _: TryParseArray) = fun (g: (string * char) []) -> Tuple<_> tryParseElemAt 0 g : Tuple<'t1> option - static member inline TryParseArray (_: Id<'t1> , _: TryParseArray) = fun (g: (string * char) []) -> Id<_> tryParseElemAt 0 g - static member inline TryParseArray (_: 't1*'t2 , _: TryParseArray) = fun (g: (string * char) []) -> tuple2 tryParseElemAt 0 g <*> tryParseElemAt 1 g - static member inline TryParseArray (_: 't1*'t2'*'t3 , _: TryParseArray) = fun (g: (string * char) []) -> tuple3 tryParseElemAt 0 g <*> tryParseElemAt 1 g <*> tryParseElemAt 2 g - static member inline TryParseArray (_: 't1*'t2'*'t3*'t4 , _: TryParseArray) = fun (g: (string * char) []) -> tuple4 tryParseElemAt 0 g <*> tryParseElemAt 1 g <*> tryParseElemAt 2 g <*> tryParseElemAt 3 g - static member inline TryParseArray (_: 't1*'t2'*'t3*'t4*'t5 , _: TryParseArray) = fun (g: (string * char) []) -> tuple5 tryParseElemAt 0 g <*> tryParseElemAt 1 g <*> tryParseElemAt 2 g <*> tryParseElemAt 3 g <*> tryParseElemAt 4 g - static member inline TryParseArray (_: 't1*'t2'*'t3*'t4*'t5*'t6 , _: TryParseArray) = fun (g: (string * char) []) -> tuple6 tryParseElemAt 0 g <*> tryParseElemAt 1 g <*> tryParseElemAt 2 g <*> tryParseElemAt 3 g <*> tryParseElemAt 4 g <*> tryParseElemAt 5 g - static member inline TryParseArray (_: 't1*'t2'*'t3*'t4*'t5*'t6*'t7, _: TryParseArray) = fun (g: (string * char) []) -> tuple7 tryParseElemAt 0 g <*> tryParseElemAt 1 g <*> tryParseElemAt 2 g <*> tryParseElemAt 3 g <*> tryParseElemAt 4 g <*> tryParseElemAt 5 g <*> tryParseElemAt 6 g + static member inline TryParseArray (_: unit , _: TryParseArray) = fun (_: struct(string * char) []) -> () + static member inline TryParseArray (_: Tuple<'t1> , _: TryParseArray) = fun (g: struct(string * char) []) -> Tuple<_> tryParseElemAt 0 g : Tuple<'t1> option + static member inline TryParseArray (_: Id<'t1> , _: TryParseArray) = fun (g: struct(string * char) []) -> Id<_> tryParseElemAt 0 g + static member inline TryParseArray (_: 't1*'t2 , _: TryParseArray) = fun (g: struct(string * char) []) -> tuple2 tryParseElemAt 0 g <*> tryParseElemAt 1 g + static member inline TryParseArray (_: 't1*'t2'*'t3 , _: TryParseArray) = fun (g: struct(string * char) []) -> tuple3 tryParseElemAt 0 g <*> tryParseElemAt 1 g <*> tryParseElemAt 2 g + static member inline TryParseArray (_: 't1*'t2'*'t3*'t4 , _: TryParseArray) = fun (g: struct(string * char) []) -> tuple4 tryParseElemAt 0 g <*> tryParseElemAt 1 g <*> tryParseElemAt 2 g <*> tryParseElemAt 3 g + static member inline TryParseArray (_: 't1*'t2'*'t3*'t4*'t5 , _: TryParseArray) = fun (g: struct(string * char) []) -> tuple5 tryParseElemAt 0 g <*> tryParseElemAt 1 g <*> tryParseElemAt 2 g <*> tryParseElemAt 3 g <*> tryParseElemAt 4 g + static member inline TryParseArray (_: 't1*'t2'*'t3*'t4*'t5*'t6 , _: TryParseArray) = fun (g: struct(string * char) []) -> tuple6 tryParseElemAt 0 g <*> tryParseElemAt 1 g <*> tryParseElemAt 2 g <*> tryParseElemAt 3 g <*> tryParseElemAt 4 g <*> tryParseElemAt 5 g + static member inline TryParseArray (_: 't1*'t2'*'t3*'t4*'t5*'t6*'t7, _: TryParseArray) = fun (g: struct(string * char) []) -> tuple7 tryParseElemAt 0 g <*> tryParseElemAt 1 g <*> tryParseElemAt 2 g <*> tryParseElemAt 3 g <*> tryParseElemAt 4 g <*> tryParseElemAt 5 g <*> tryParseElemAt 6 g /// Gets a tuple with the result of parsing each element of a string array. let inline parseArray (source: string []) : '``(T1 * T2 * ... * Tn)`` = ParseArray.Invoke (Array.map (fun x -> (x, '\000')) source) /// Gets a tuple with the result of parsing each element of a formatted text. - let inline sscanf (pf: PrintfFormat<_,_,_,_,'``(T1 * T2 * ... * Tn)``>) s : '``(T1 * T2 * ... * Tn)`` = getGroups pf s |> ParseArray.Invoke + let inline sscanf (pf: PrintfFormat<_,_,_,_,'``(T1 * T2 * ... * Tn)``>) : string -> '``(T1 * T2 * ... * Tn)`` = getGroups pf >> ParseArray.Invoke /// Gets a tuple with the result of parsing each element of a formatted text from the Console. let inline scanfn pf : '``(T1 * T2 * ... * Tn)`` = sscanf pf (Console.ReadLine ()) /// Gets a tuple with the result of parsing each element of a string array. Returns None in case of failure. - let inline tryParseArray (source: string []) : '``(T1 * T2 * ... * Tn)`` option = TryParseArray.Invoke (Array.map (fun x -> (x, '\000')) source) + let inline tryParseArray (source: string []) : '``(T1 * T2 * ... * Tn)`` option = TryParseArray.Invoke (Array.map (fun x -> x, '\000') source) /// Gets a tuple with the result of parsing each element of a formatted text. Returns None in case of failure. - let inline trySscanf (pf: PrintfFormat<_,_,_,_,'``(T1 * T2 * ... * Tn)``>) s : '``(T1 * T2 * ... * Tn)`` option = getGroups pf s |> TryParseArray.Invoke + let inline trySscanf (pf: PrintfFormat<_,_,_,_,'``(T1 * T2 * ... * Tn)``>) : string -> '``(T1 * T2 * ... * Tn)`` option = getGroups pf >> TryParseArray.Invoke /// Matches a formatted text with the result of parsing each element. Will not match in case of failure. - let inline (|Scan|_|) (pf: PrintfFormat<_,_,_,_,'``(T1 * T2 * ... * Tn)``>) s : '``(T1 * T2 * ... * Tn)`` option = trySscanf pf s + let inline (|Scan|_|) (pf: PrintfFormat<_,_,_,_,'``(T1 * T2 * ... * Tn)``>) : string -> '``(T1 * T2 * ... * Tn)`` option = trySscanf pf /// Gets a tuple with the result of parsing each element of a formatted text from the Console. Returns None in case of failure. let inline tryScanfn pf : '``(T1 * T2 * ... * Tn)`` option = trySscanf pf (Console.ReadLine ()) -#endif +#endif \ No newline at end of file From 9e5264084b5f2b4417126f6151262d7a4ba041ae Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Sat, 14 Jun 2025 00:06:16 +0800 Subject: [PATCH 26/32] Better error message --- src/FSharpPlus/Parsing.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index 881208065..051bfb2cc 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -54,7 +54,7 @@ module Parsing = if consumeNumericPlus then regex.Append @"\+?" |> ignore "([0-9a-fA-F]+)" | '%' -> "%" - | x -> failwith $"Unknown specifier: {x}" + | x -> failwith $"Unknown format specifier: {x}" |> regex.Append |> ignore if consumeSpacesAfter then regex.Append @"\s*" else regex | '\\' | '*' | '+' | '?' | '|' | '{' | '[' | '(' | ')' | '^' | '$' | '.' | '#' | ' ' as escape -> @@ -182,4 +182,4 @@ module Parsing = /// Gets a tuple with the result of parsing each element of a formatted text from the Console. Returns None in case of failure. let inline tryScanfn pf : '``(T1 * T2 * ... * Tn)`` option = trySscanf pf (Console.ReadLine ()) -#endif \ No newline at end of file +#endif From c7272e5c800b0f8f32741ea6fdb273c962f827c1 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Sat, 14 Jun 2025 00:15:51 +0800 Subject: [PATCH 27/32] Update Parsing.fs --- src/FSharpPlus/Parsing.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index 051bfb2cc..f2e11fee2 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -13,7 +13,7 @@ module Parsing = let inline private getGroups (pf: PrintfFormat<_,_,_,_,_>) = let format = pf.Value let regex = System.Text.StringBuilder "^" - let groups = ResizeArray(format.Length / 2) // worst case, every character is a group + let groups = ResizeArray(format.Length / 2) // worst case, there are only format specifiers let mutable i = 0 while i < String.length format do match format[i] with From 9ca9a54f7f9debdc00219ae7bd890facfe52e0c9 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Sat, 14 Jun 2025 00:16:34 +0800 Subject: [PATCH 28/32] Update Parsing.fs --- src/FSharpPlus/Parsing.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index f2e11fee2..c6635251d 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -66,7 +66,7 @@ module Parsing = fun str -> let m = Regex.Match(str, regex) if not m.Success then [||] else - Array.init (m.Groups.Count - 1) <| fun i -> struct(m.Groups[i + 1].Value, groups[i + 1]) + Array.init (m.Groups.Count - 1) <| fun i -> struct(m.Groups[i + 1].Value, groups[i]) let inline private conv (destType: System.Type) (b: int) (s: string) = match destType with From 2edf1633f87177883055351d4827b966870f86f3 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Sat, 14 Jun 2025 00:49:57 +0800 Subject: [PATCH 29/32] Whoops --- src/FSharpPlus/Parsing.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index c6635251d..2b1d5bebd 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -95,7 +95,7 @@ module Parsing = | _ -> tryParse s type ParseArray = - static member inline ParseArray struct(_: 't , _: obj) = fun (g: struct(string * char) []) -> (parse (g.[0])) : 't + static member inline ParseArray (_: 't , _: obj) = fun (g: struct(string * char) []) -> (parse (g.[0])) : 't static member inline Invoke (g: struct(string * char) []) = let inline call_2 (a: ^a, b: ^b) = ((^a or ^b) : (static member ParseArray: _*_ -> _) b, a) g From e5a841196f3064ff71b84dd7ac8be3d76fbee67f Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Sat, 14 Jun 2025 01:16:14 +0800 Subject: [PATCH 30/32] Fix comment --- src/FSharpPlus/Parsing.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index 2b1d5bebd..38abef8d5 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -20,7 +20,7 @@ module Parsing = | '%' -> i <- i + 1 let mutable consumeSpacesAfter = false // consume spaces after if '-' specified - let mutable consumeNumericPlus = false // consume plus for numeric values after if '+' specified + let mutable consumeNumericPlus = false // consume plus before numeric values if '+' specified while match format[i] with | ' ' -> regex.Append @"\s*" |> ignore; true // consume spaces before if ' ' specified From af42dadb3d1ca342ea37a71fe009cacf70fd95da Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Sat, 14 Jun 2025 07:45:11 +0800 Subject: [PATCH 31/32] Scan -> Parsedf --- src/FSharpPlus/Parsing.fs | 2 +- tests/FSharpPlus.Tests/Parsing.fs | 90 +++++++++++++++---------------- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index 38abef8d5..bc17acfb0 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -177,7 +177,7 @@ module Parsing = let inline trySscanf (pf: PrintfFormat<_,_,_,_,'``(T1 * T2 * ... * Tn)``>) : string -> '``(T1 * T2 * ... * Tn)`` option = getGroups pf >> TryParseArray.Invoke /// Matches a formatted text with the result of parsing each element. Will not match in case of failure. - let inline (|Scan|_|) (pf: PrintfFormat<_,_,_,_,'``(T1 * T2 * ... * Tn)``>) : string -> '``(T1 * T2 * ... * Tn)`` option = trySscanf pf + let inline (|Parsedf|_|) (pf: PrintfFormat<_,_,_,_,'``(T1 * T2 * ... * Tn)``>) : string -> '``(T1 * T2 * ... * Tn)`` option = trySscanf pf /// Gets a tuple with the result of parsing each element of a formatted text from the Console. Returns None in case of failure. let inline tryScanfn pf : '``(T1 * T2 * ... * Tn)`` option = trySscanf pf (Console.ReadLine ()) diff --git a/tests/FSharpPlus.Tests/Parsing.fs b/tests/FSharpPlus.Tests/Parsing.fs index 8df8237c4..cbf1efd94 100644 --- a/tests/FSharpPlus.Tests/Parsing.fs +++ b/tests/FSharpPlus.Tests/Parsing.fs @@ -107,44 +107,44 @@ module Parsing = let _zzz1 = sscanf "%%(%s)" "%(hello)" let (_x1,_y1,_z1) = sscanf "%s--%s-%s" "test--this-string" - match "ab" with Scan "%c" _ -> failwith "wrong match" | Scan "%c%c" ('a', 'b') -> () | _ -> failwith "didn't match" - match "abc" with Scan "%c%c" ('a', 'b') -> failwith "wrong match" | Scan "%c%c%c%s" ('a', 'b', 'c', "") -> () | _ -> failwith "didn't match" + match "ab" with Parsedf "%c" _ -> failwith "wrong match" | Parsedf "%c%c" ('a', 'b') -> () | _ -> failwith "didn't match" + match "abc" with Parsedf "%c%c" ('a', 'b') -> failwith "wrong match" | Parsedf "%c%c%c%s" ('a', 'b', 'c', "") -> () | _ -> failwith "didn't match" match "(%hello)" with - | Scan "%d" _ | Scan "%f" _ | Scan "%x" _ -> failwith "wrong match" - | Scan "%%(%%%s)" _ | Scan "(%%%sa" _ | Scan "(%%hel%c" _ | Scan "%%h%cllo)" _ -> failwith "wrong match" - | Scan "(%%%s)" "hello" -> () + | Parsedf "%d" _ | Parsedf "%f" _ | Parsedf "%x" _ -> failwith "wrong match" + | Parsedf "%%(%%%s)" _ | Parsedf "(%%%sa" _ | Parsedf "(%%hel%c" _ | Parsedf "%%h%cllo)" _ -> failwith "wrong match" + | Parsedf "(%%%s)" "hello" -> () | _ -> failwith "didn't match" - match " 3" with Scan "% d" 3 -> () | _ -> failwith "didn't match" - match " 3" with Scan "% d" 3 -> () | _ -> failwith "didn't match" - match " 3" with Scan "% d" 3 -> () | _ -> failwith "didn't match" // em space - match "3 " with Scan "%-d" 3 -> () | _ -> failwith "didn't match" - match "3 " with Scan "%-d" 3 -> () | _ -> failwith "didn't match" - match "3 " with Scan "%-d" 3 -> () | _ -> failwith "didn't match" // em space - match " 3 " with Scan "% -d" 3 -> () | _ -> failwith "didn't match" - match " 3 " with Scan "% -d" 3 -> () | _ -> failwith "didn't match" - match " 3 " with Scan "% -d" 3 -> () | _ -> failwith "didn't match" // em space - match "test--this-gg" with Scan "%s--%s-%s" ("test", "this", "gg") -> () | _ -> failwith "didn't match" - match "1 2.1 3.4 .3 43.2e32 0 f f" with Scan "%f %F %g %G %e %E %c %c" (1f, 2.1, 3.4m, 0.3, 43.2e32, 0., 'f', 'f') -> () | _ -> failwith "didn't match" - match "1 2.1 3.4 .3 43.2e32 0 f f f" with Scan "%f% F %g %G %e %E %c %c %c" (1m, 2.1, 3.4, 0.3m, 43.2e32, 0., 'f', 'f', 'f') -> () | _ -> failwith "didn't match" - match "1 2.1 3.4.3 43.2e32 0 f f ff" with Scan "%B %F %-g%G %e %E %c %c %c%c" (1, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" - match "1 2.1 3.4.3 43.2e32 0 f f fff" with Scan "%o %F % g%-G %e %E %c %c %c%c%c" (1y, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" - match "1 2.1 3.4.3 43.2e32 0 f f fff16" with Scan "%x %F %- g%- G %e %E %c %c %c%c%c%i" (1us, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16) -> () | _ -> failwith "didn't match" - match "1 2.1 3.4.3 43.2e32 0 f f fff16 17" with Scan "%X %F %g% G %e %E %c %c %c%c%c%i %f" (1s, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16L, 17.) -> () | _ -> failwith "didn't match" - match "13 43 AA 77A" with Scan "%x %X %x %o%X" (0x13, 0x43, 0xAA, 0o77, 0xA) -> () | _ -> failwith "didn't match" - match "13 43 AA 77A" with Scan "%B%x %X %x %o%X" (0b1, 0x3, 0x43, 0xAA, 0o77, 0xA) -> () | _ -> failwith "didn't match" - match "111AAA" with Scan "%B%s" (0b111, "AAA") -> () | _ -> failwith "didn't match" - match "100700 100 100" with Scan "%B%o %x %X" (0b100, 0o700, 0x100, 0x100) -> () | _ -> failwith "didn't match" + match " 3" with Parsedf "% d" 3 -> () | _ -> failwith "didn't match" + match " 3" with Parsedf "% d" 3 -> () | _ -> failwith "didn't match" + match " 3" with Parsedf "% d" 3 -> () | _ -> failwith "didn't match" // em space + match "3 " with Parsedf "%-d" 3 -> () | _ -> failwith "didn't match" + match "3 " with Parsedf "%-d" 3 -> () | _ -> failwith "didn't match" + match "3 " with Parsedf "%-d" 3 -> () | _ -> failwith "didn't match" // em space + match " 3 " with Parsedf "% -d" 3 -> () | _ -> failwith "didn't match" + match " 3 " with Parsedf "% -d" 3 -> () | _ -> failwith "didn't match" + match " 3 " with Parsedf "% -d" 3 -> () | _ -> failwith "didn't match" // em space + match "test--this-gg" with Parsedf "%s--%s-%s" ("test", "this", "gg") -> () | _ -> failwith "didn't match" + match "1 2.1 3.4 .3 43.2e32 0 f f" with Parsedf "%f %F %g %G %e %E %c %c" (1f, 2.1, 3.4m, 0.3, 43.2e32, 0., 'f', 'f') -> () | _ -> failwith "didn't match" + match "1 2.1 3.4 .3 43.2e32 0 f f f" with Parsedf "%f% F %g %G %e %E %c %c %c" (1m, 2.1, 3.4, 0.3m, 43.2e32, 0., 'f', 'f', 'f') -> () | _ -> failwith "didn't match" + match "1 2.1 3.4.3 43.2e32 0 f f ff" with Parsedf "%B %F %-g%G %e %E %c %c %c%c" (1, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" + match "1 2.1 3.4.3 43.2e32 0 f f fff" with Parsedf "%o %F % g%-G %e %E %c %c %c%c%c" (1y, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f') -> () | _ -> failwith "didn't match" + match "1 2.1 3.4.3 43.2e32 0 f f fff16" with Parsedf "%x %F %- g%- G %e %E %c %c %c%c%c%i" (1us, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16) -> () | _ -> failwith "didn't match" + match "1 2.1 3.4.3 43.2e32 0 f f fff16 17" with Parsedf "%X %F %g% G %e %E %c %c %c%c%c%i %f" (1s, 2.1, 3.4, 0.3, 43.2e32, 0., 'f', 'f', 'f', 'f', 'f', 16L, 17.) -> () | _ -> failwith "didn't match" + match "13 43 AA 77A" with Parsedf "%x %X %x %o%X" (0x13, 0x43, 0xAA, 0o77, 0xA) -> () | _ -> failwith "didn't match" + match "13 43 AA 77A" with Parsedf "%B%x %X %x %o%X" (0b1, 0x3, 0x43, 0xAA, 0o77, 0xA) -> () | _ -> failwith "didn't match" + match "111AAA" with Parsedf "%B%s" (0b111, "AAA") -> () | _ -> failwith "didn't match" + match "100700 100 100" with Parsedf "%B%o %x %X" (0b100, 0o700, 0x100, 0x100) -> () | _ -> failwith "didn't match" match "1+1-2+2-8+8" with - | Scan "%s%o" _ -> failwith "wrong match" - | Scan "%+u%+u%+d%+u%+u%+u" _ -> failwith "wrong match" - | Scan "%+u%+u%+d%+u%+d%o" _ -> failwith "wrong match" - | Scan "%+u%+u%+d%+u%-d%u" _ -> failwith "wrong match" - | Scan "%+u%+u%+d%+u%u%+d" _ -> failwith "wrong match" - | Scan "%+u%+u%+d%+u%+o%+u" _ -> failwith "wrong match" - | Scan "%+u%+u%+d%+u%+B%+u" _ -> failwith "wrong match" - | Scan "%+u%+u%+d%+u%+x%+X" _ -> failwith "wrong match" - | Scan "%+u%+u%+d%+u%+d%+X" (a, b, c, d, e, f) -> + | Parsedf "%s%o" _ -> failwith "wrong match" + | Parsedf "%+u%+u%+d%+u%+u%+u" _ -> failwith "wrong match" + | Parsedf "%+u%+u%+d%+u%+d%o" _ -> failwith "wrong match" + | Parsedf "%+u%+u%+d%+u%-d%u" _ -> failwith "wrong match" + | Parsedf "%+u%+u%+d%+u%u%+d" _ -> failwith "wrong match" + | Parsedf "%+u%+u%+d%+u%+o%+u" _ -> failwith "wrong match" + | Parsedf "%+u%+u%+d%+u%+B%+u" _ -> failwith "wrong match" + | Parsedf "%+u%+u%+d%+u%+x%+X" _ -> failwith "wrong match" + | Parsedf "%+u%+u%+d%+u%+d%+X" (a, b, c, d, e, f) -> areEqual (a |> box |> unbox) 1 areEqual (b |> box |> unbox) 1 areEqual (c |> box |> unbox) -2 @@ -152,21 +152,21 @@ module Parsing = areEqual (e |> box |> unbox) -8 areEqual (f |> box |> unbox) 8 | _ -> failwith "didn't match" - match "1+1-2+2-8+8" with Scan "%+-d%+d%+-d%+d%+-d%+d" (1,1,-2,2,-8,8) -> () | _ -> failwith "didn't match" - match "1+1-2+2-8+8" with Scan "%d+%d%d%+d%d%+d" (1,1,-2,2,-8,8) -> () | _ -> failwith "didn't match" - match "1+1-2+2-8+8" with Scan "%+B%+B-%+o%+o-%+X%+X" (1,1,2,2,8,8) -> () | _ -> failwith "didn't match" - match "1+1-2+2-8+8e" with Scan "%+f%+F%+e%+E%+g%+G%+X" (1f,1.,-2m,2f,-8.,8M,0xE) -> () | _ -> failwith "didn't match" - match "1+1-2+2-8+8e1a" with Scan "%+f%+F%+e%+E%+g%+G%+X" (1f,1.,-2m,2f,-8.,80M,0xA) -> () | _ -> failwith "didn't match" - match "1+1-2+2-8+8e-1a" with Scan "%+f%+F%+e%+E%+g%+G%+X" (1f,1.,-2m,2f,-8.,0.8M,0xA) -> () | _ -> failwith "didn't match" - match "1+1-2+2-8+8ea" with Scan "%+-f%+-F%+-e%+-E%+-g%+-G%+-X" (1f,1.,-2m,2f,-8.,8M,0xEA) -> () | _ -> failwith "didn't match" + match "1+1-2+2-8+8" with Parsedf "%+-d%+d%+-d%+d%+-d%+d" (1,1,-2,2,-8,8) -> () | _ -> failwith "didn't match" + match "1+1-2+2-8+8" with Parsedf "%d+%d%d%+d%d%+d" (1,1,-2,2,-8,8) -> () | _ -> failwith "didn't match" + match "1+1-2+2-8+8" with Parsedf "%+B%+B-%+o%+o-%+X%+X" (1,1,2,2,8,8) -> () | _ -> failwith "didn't match" + match "1+1-2+2-8+8e" with Parsedf "%+f%+F%+e%+E%+g%+G%+X" (1f,1.,-2m,2f,-8.,8M,0xE) -> () | _ -> failwith "didn't match" + match "1+1-2+2-8+8e1a" with Parsedf "%+f%+F%+e%+E%+g%+G%+X" (1f,1.,-2m,2f,-8.,80M,0xA) -> () | _ -> failwith "didn't match" + match "1+1-2+2-8+8e-1a" with Parsedf "%+f%+F%+e%+E%+g%+G%+X" (1f,1.,-2m,2f,-8.,0.8M,0xA) -> () | _ -> failwith "didn't match" + match "1+1-2+2-8+8ea" with Parsedf "%+-f%+-F%+-e%+-E%+-g%+-G%+-X" (1f,1.,-2m,2f,-8.,8M,0xEA) -> () | _ -> failwith "didn't match" let _date: (DayOfWeek * string * uint16 * int) option = trySscanf "%A %A %A %A" "Saturday March 25 1989" let _date1: DateTime option = trySscanf "%A" "Saturday March 25 1989" - match "12:34" with Scan "%A" (x: TimeSpan) -> areEqual (TimeSpan(12, 34, 0)) x | _ -> failwith "Pattern match failed" - match "12:34:56" with Scan "%O" (x: TimeSpan) -> areEqual (TimeSpan(12, 34, 56)) x | _ -> failwith "Pattern match failed" - match "9876-5-4 3:2:1" with Scan "%A" (x: DateTime) -> areEqual (DateTime(9876,5,4,3,2,1)) x | _ -> failwith "Pattern match failed" - match "9876-5-4 3:2:1 a" with Scan "%O %x" (x: DateTime, y) -> areEqual (DateTime(9876,5,4,3,2,1)) x; areEqual 0xA y | _ -> failwith "Pattern match failed" + match "12:34" with Parsedf "%A" (x: TimeSpan) -> areEqual (TimeSpan(12, 34, 0)) x | _ -> failwith "Pattern match failed" + match "12:34:56" with Parsedf "%O" (x: TimeSpan) -> areEqual (TimeSpan(12, 34, 56)) x | _ -> failwith "Pattern match failed" + match "9876-5-4 3:2:1" with Parsedf "%A" (x: DateTime) -> areEqual (DateTime(9876,5,4,3,2,1)) x | _ -> failwith "Pattern match failed" + match "9876-5-4 3:2:1 a" with Parsedf "%O %x" (x: DateTime, y) -> areEqual (DateTime(9876,5,4,3,2,1)) x; areEqual 0xA y | _ -> failwith "Pattern match failed" let x = trySscanf "%X %x" "13 43" let o = trySscanf "%o" "10" From d7902f702cf00437e858c17c2f54084406aa2846 Mon Sep 17 00:00:00 2001 From: Hadrian Tang Date: Sat, 14 Jun 2025 17:57:08 +0800 Subject: [PATCH 32/32] Remove trySscanf active pattern --- src/FSharpPlus/Parsing.fs | 3 --- tests/FSharpPlus.Tests/Parsing.fs | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index bc17acfb0..a694c1075 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -176,9 +176,6 @@ module Parsing = /// Gets a tuple with the result of parsing each element of a formatted text. Returns None in case of failure. let inline trySscanf (pf: PrintfFormat<_,_,_,_,'``(T1 * T2 * ... * Tn)``>) : string -> '``(T1 * T2 * ... * Tn)`` option = getGroups pf >> TryParseArray.Invoke - /// Matches a formatted text with the result of parsing each element. Will not match in case of failure. - let inline (|Parsedf|_|) (pf: PrintfFormat<_,_,_,_,'``(T1 * T2 * ... * Tn)``>) : string -> '``(T1 * T2 * ... * Tn)`` option = trySscanf pf - /// Gets a tuple with the result of parsing each element of a formatted text from the Console. Returns None in case of failure. let inline tryScanfn pf : '``(T1 * T2 * ... * Tn)`` option = trySscanf pf (Console.ReadLine ()) diff --git a/tests/FSharpPlus.Tests/Parsing.fs b/tests/FSharpPlus.Tests/Parsing.fs index cbf1efd94..234534083 100644 --- a/tests/FSharpPlus.Tests/Parsing.fs +++ b/tests/FSharpPlus.Tests/Parsing.fs @@ -107,6 +107,7 @@ module Parsing = let _zzz1 = sscanf "%%(%s)" "%(hello)" let (_x1,_y1,_z1) = sscanf "%s--%s-%s" "test--this-string" + let inline (|Parsedf|_|) pf = trySscanf pf match "ab" with Parsedf "%c" _ -> failwith "wrong match" | Parsedf "%c%c" ('a', 'b') -> () | _ -> failwith "didn't match" match "abc" with Parsedf "%c%c" ('a', 'b') -> failwith "wrong match" | Parsedf "%c%c%c%s" ('a', 'b', 'c', "") -> () | _ -> failwith "didn't match" match "(%hello)" with