From d6ff68132af70ad50cb54479d002ce49deddab51 Mon Sep 17 00:00:00 2001 From: Gustavo Leon <1261319+gusty@users.noreply.github.com> Date: Tue, 18 May 2021 12:32:25 +0200 Subject: [PATCH 1/4] Add support for %o %x and %X to scan functions --- src/FSharpPlus/Parsing.fs | 101 ++++++++++++++++++++++++-------------- 1 file changed, 65 insertions(+), 36 deletions(-) diff --git a/src/FSharpPlus/Parsing.fs b/src/FSharpPlus/Parsing.fs index 71faf92c0..6a93925a2 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -24,23 +24,52 @@ module Parsing = let formatStr = replace "%%" "%" pf.Value let constants = split formatters formatStr let regex = Regex ("^" + String.Join ("(.*?)", constants |> Array.map Regex.Escape) + "$") - let groups = - regex.Match(s).Groups - |> Seq.cast - |> Seq.skip 1 - groups - |> Seq.map (fun g -> g.Value) - |> Seq.toArray + 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.zip + + + + let conv (destType: System.Type) (b: int) (s: string) = + match destType with + | t when t = typeof -> Convert.ToByte (s, b) |> box + | t when t = typeof -> Convert.ToUInt16 (s, b) |> box + | t when t = typeof -> Convert.ToUInt32 (s, b) |> box + | t when t = typeof -> Convert.ToUInt64 (s, b) |> box + | t when t = typeof -> Convert.ToSByte (s, b) |> box + | t when t = typeof -> Convert.ToInt16 (s, b) |> box + | t when t = typeof -> Convert.ToInt32 (s, b) |> box + | 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 parse (s: string, f: string) : 'r = + match f with + | "%o" -> conv typeof<'r> 8 s |> string |> parse + | "%x" | "%X" -> conv typeof<'r> 16 s |> string |> parse + | _ -> parse s + + let inline tryParse (s: string, f: string) : 'r option = + match f with + | "%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 []) -> (parse (g.[0])) : 't + static member inline ParseArray (_: 't , _: obj) = fun (g: (string * string) []) -> (parse (g.[0])) : 't - static member inline Invoke (g: string []) = + static member inline Invoke (g: (string * string) []) = 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 []) -> + static member inline ParseArray (t: 't, _: ParseArray) = fun (g: (string * string) []) -> 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]) @@ -52,29 +81,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 []) -> () - static member inline ParseArray (_: Tuple<'t1> , _: ParseArray) = fun (g: string []) -> Tuple<_> (parse g.[0]) : Tuple<'t1> - static member inline ParseArray (_: Id<'t1> , _: ParseArray) = fun (g: string []) -> Id<_> (parse g.[0]) - static member inline ParseArray (_: 't1*'t2 , _: ParseArray) = fun (g: string []) -> parse g.[0], parse g.[1] - static member inline ParseArray (_: 't1*'t2'*'t3 , _: ParseArray) = fun (g: string []) -> parse g.[0], parse g.[1], parse g.[2] - static member inline ParseArray (_: 't1*'t2'*'t3*'t4 , _: ParseArray) = fun (g: 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 []) -> 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 []) -> 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 []) -> parse g.[0], parse g.[1], parse g.[2], parse g.[3], parse g.[4], parse g.[5], parse g.[6] - - let inline tryParseElemAt i (g: string []) = + 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 tryParseElemAt i (g: (string * string) []) = if i < Array.length g then tryParse (g.[i]) else None type TryParseArray = - static member inline TryParseArray (_:'t, _:obj) = fun (g: string []) -> tryParseElemAt 0 g : 't option + static member inline TryParseArray (_:'t, _:obj) = fun (g: (string * string) []) -> tryParseElemAt 0 g : 't option - static member inline Invoke (g: string []) = + static member inline Invoke (g: (string * string) []) = 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 []) -> + static member inline TryParseArray (t: 't, _: TryParseArray) = fun (g: (string * string) []) -> 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 @@ -88,26 +117,26 @@ 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 []) -> () + static member inline TryParseArray (_: unit , _: TryParseArray) = fun (_: (string * string) []) -> () - static member inline TryParseArray (_: Tuple<'t1> , _: TryParseArray) = fun (g: string []) -> Tuple<_> tryParseElemAt 0 g : Tuple<'t1> option - static member inline TryParseArray (_: Id<'t1> , _: TryParseArray) = fun (g: string []) -> Id<_> tryParseElemAt 0 g - static member inline TryParseArray (_: 't1*'t2 , _: TryParseArray) = fun (g: string []) -> tuple2 tryParseElemAt 0 g <*> tryParseElemAt 1 g - static member inline TryParseArray (_: 't1*'t2'*'t3 , _: TryParseArray) = fun (g: string []) -> tuple3 tryParseElemAt 0 g <*> tryParseElemAt 1 g <*> tryParseElemAt 2 g - static member inline TryParseArray (_: 't1*'t2'*'t3*'t4 , _: TryParseArray) = fun (g: 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 []) -> 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 []) -> 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 []) -> 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 (_: 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 open Internals open System /// Gets a tuple with the result of parsing each element of a string array. - let inline parseArray (source: string[]) : '``(T1 * T2 * ... * Tn)`` = ParseArray.Invoke source + let inline parseArray (source: string []) : '``(T1 * T2 * ... * Tn)`` = ParseArray.Invoke (Array.map (fun x -> (x, "")) 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 + let inline sscanf (pf: PrintfFormat<_,_,_,_,'``(T1 * T2 * ... * Tn)``>) s : '``(T1 * T2 * ... * Tn)`` = getGroups pf s |> 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 ()) @@ -121,4 +150,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 5e18160f6e16d7be9445175699c5e358b32ef1ac Mon Sep 17 00:00:00 2001 From: Gustavo Leon <1261319+gusty@users.noreply.github.com> Date: Tue, 18 May 2021 17:35:42 +0200 Subject: [PATCH 2/4] Use shortest group --- 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 6a93925a2..7a3a97c7d 100644 --- a/src/FSharpPlus/Parsing.fs +++ b/src/FSharpPlus/Parsing.fs @@ -32,7 +32,7 @@ module Parsing = groups |> Seq.map (fun g -> g.Value) |> Seq.toArray - (getGroup s, getGroup pf.Value) ||> Array.zip + (getGroup s, getGroup pf.Value) ||> Array.zipShortest From d813cb099ad98e2d52e2b63764c0028ab38af298 Mon Sep 17 00:00:00 2001 From: Gustavo Leon <1261319+gusty@users.noreply.github.com> Date: Tue, 18 May 2021 20:30:22 +0200 Subject: [PATCH 3/4] + tests --- tests/FSharpPlus.Tests/General.fs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/FSharpPlus.Tests/General.fs b/tests/FSharpPlus.Tests/General.fs index 81f2df4a9..9dfda6572 100644 --- a/tests/FSharpPlus.Tests/General.fs +++ b/tests/FSharpPlus.Tests/General.fs @@ -2185,8 +2185,12 @@ module Parsing = 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 _date: (DayOfWeek * string * uint16 * int) option = trySscanf "%A %A %A %A" "Saturday March 25 1989" - - () + + let x = trySscanf "%X %x" "13 43" + let o = trySscanf "%o" "10" + + areEqual (Some 8) x + areEqual (Some (19, 67)) o module Conversions = From 987a99c3c70fad8657ed8ea612237b6efa8e3689 Mon Sep 17 00:00:00 2001 From: Gustavo Leon <1261319+gusty@users.noreply.github.com> Date: Tue, 18 May 2021 21:06:27 +0200 Subject: [PATCH 4/4] Fix order --- tests/FSharpPlus.Tests/General.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/FSharpPlus.Tests/General.fs b/tests/FSharpPlus.Tests/General.fs index 9dfda6572..2210ff4af 100644 --- a/tests/FSharpPlus.Tests/General.fs +++ b/tests/FSharpPlus.Tests/General.fs @@ -2189,8 +2189,8 @@ module Parsing = let x = trySscanf "%X %x" "13 43" let o = trySscanf "%o" "10" - areEqual (Some 8) x - areEqual (Some (19, 67)) o + areEqual (Some (19, 67)) x + areEqual (Some 8) o module Conversions =