Skip to content

Commit 3e32458

Browse files
authored
Add support for %o %x and %X to scan functions (#443)
1 parent 4b87bd7 commit 3e32458

File tree

2 files changed

+71
-38
lines changed

2 files changed

+71
-38
lines changed

src/FSharpPlus/Parsing.fs

Lines changed: 65 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,52 @@ module Parsing =
2424
let formatStr = replace "%%" "%" pf.Value
2525
let constants = split formatters formatStr
2626
let regex = Regex ("^" + String.Join ("(.*?)", constants |> Array.map Regex.Escape) + "$")
27-
let groups =
28-
regex.Match(s).Groups
29-
|> Seq.cast<Group>
30-
|> Seq.skip 1
31-
groups
32-
|> Seq.map (fun g -> g.Value)
33-
|> Seq.toArray
27+
let getGroup x =
28+
let groups =
29+
regex.Match(x).Groups
30+
|> Seq.cast<Group>
31+
|> Seq.skip 1
32+
groups
33+
|> Seq.map (fun g -> g.Value)
34+
|> Seq.toArray
35+
(getGroup s, getGroup pf.Value) ||> Array.zipShortest
36+
37+
38+
39+
let conv (destType: System.Type) (b: int) (s: string) =
40+
match destType with
41+
| t when t = typeof<byte> -> Convert.ToByte (s, b) |> box
42+
| t when t = typeof<uint16> -> Convert.ToUInt16 (s, b) |> box
43+
| t when t = typeof<uint32> -> Convert.ToUInt32 (s, b) |> box
44+
| t when t = typeof<uint64> -> Convert.ToUInt64 (s, b) |> box
45+
| t when t = typeof<sbyte> -> Convert.ToSByte (s, b) |> box
46+
| t when t = typeof<int16> -> Convert.ToInt16 (s, b) |> box
47+
| t when t = typeof<int> -> Convert.ToInt32 (s, b) |> box
48+
| t when t = typeof<int64> -> Convert.ToInt64 (s, b) |> box
49+
| _ -> invalidOp (sprintf "Type conversion from string to type %A with base %i is not supported" destType b)
50+
51+
52+
let inline parse (s: string, f: string) : 'r =
53+
match f with
54+
| "%o" -> conv typeof<'r> 8 s |> string |> parse
55+
| "%x" | "%X" -> conv typeof<'r> 16 s |> string |> parse
56+
| _ -> parse s
57+
58+
let inline tryParse (s: string, f: string) : 'r option =
59+
match f with
60+
| "%o" -> Option.protect (conv typeof<'r> 8) s |> Option.map string |> Option.bind tryParse
61+
| "%x" | "%X" -> Option.protect (conv typeof<'r> 16) s |> Option.map string |> Option.bind tryParse
62+
| _ -> tryParse s
3463

3564
type ParseArray =
36-
static member inline ParseArray (_: 't , _: obj) = fun (g: string []) -> (parse (g.[0])) : 't
65+
static member inline ParseArray (_: 't , _: obj) = fun (g: (string * string) []) -> (parse (g.[0])) : 't
3766

38-
static member inline Invoke (g: string []) =
67+
static member inline Invoke (g: (string * string) []) =
3968
let inline call_2 (a: ^a, b: ^b) = ((^a or ^b) : (static member ParseArray: _*_ -> _) b, a) g
4069
let inline call (a: 'a) = call_2 (a, Unchecked.defaultof<'r>) : 'r
4170
call Unchecked.defaultof<ParseArray>
4271

43-
static member inline ParseArray (t: 't, _: ParseArray) = fun (g: string []) ->
72+
static member inline ParseArray (t: 't, _: ParseArray) = fun (g: (string * string) []) ->
4473
let _f _ = Constraints.whenNestedTuple t : ('t1*'t2*'t3*'t4*'t5*'t6*'t7*'tr)
4574
let (t1: 't1) = parse (g.[0])
4675
let (t2: 't2) = parse (g.[1])
@@ -52,29 +81,29 @@ module Parsing =
5281
let (tr: 'tr) = ParseArray.Invoke (g.[7..])
5382
Tuple<_,_,_,_,_,_,_,_> (t1, t2, t3, t4, t5, t6, t7, tr) |> retype : 't
5483

55-
static member inline ParseArray (_: unit , _: ParseArray) = fun (_: string []) -> ()
56-
static member inline ParseArray (_: Tuple<'t1> , _: ParseArray) = fun (g: string []) -> Tuple<_> (parse g.[0]) : Tuple<'t1>
57-
static member inline ParseArray (_: Id<'t1> , _: ParseArray) = fun (g: string []) -> Id<_> (parse g.[0])
58-
static member inline ParseArray (_: 't1*'t2 , _: ParseArray) = fun (g: string []) -> parse g.[0], parse g.[1]
59-
static member inline ParseArray (_: 't1*'t2'*'t3 , _: ParseArray) = fun (g: string []) -> parse g.[0], parse g.[1], parse g.[2]
60-
static member inline ParseArray (_: 't1*'t2'*'t3*'t4 , _: ParseArray) = fun (g: string []) -> parse g.[0], parse g.[1], parse g.[2], parse g.[3]
61-
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]
62-
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]
63-
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]
64-
65-
let inline tryParseElemAt i (g: string []) =
84+
static member inline ParseArray (_: unit , _: ParseArray) = fun (_: (string * string) []) -> ()
85+
static member inline ParseArray (_: Tuple<'t1> , _: ParseArray) = fun (g: (string * string) []) -> Tuple<_> (parse g.[0]) : Tuple<'t1>
86+
static member inline ParseArray (_: Id<'t1> , _: ParseArray) = fun (g: (string * string) []) -> Id<_> (parse g.[0])
87+
static member inline ParseArray (_: 't1*'t2 , _: ParseArray) = fun (g: (string * string) []) -> parse g.[0], parse g.[1]
88+
static member inline ParseArray (_: 't1*'t2'*'t3 , _: ParseArray) = fun (g: (string * string) []) -> parse g.[0], parse g.[1], parse g.[2]
89+
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]
90+
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]
91+
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]
92+
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]
93+
94+
let inline tryParseElemAt i (g: (string * string) []) =
6695
if i < Array.length g then tryParse (g.[i])
6796
else None
6897

6998
type TryParseArray =
70-
static member inline TryParseArray (_:'t, _:obj) = fun (g: string []) -> tryParseElemAt 0 g : 't option
99+
static member inline TryParseArray (_:'t, _:obj) = fun (g: (string * string) []) -> tryParseElemAt 0 g : 't option
71100

72-
static member inline Invoke (g: string []) =
101+
static member inline Invoke (g: (string * string) []) =
73102
let inline call_2 (a: ^a, b: ^b) = ((^a or ^b) : (static member TryParseArray: _*_ -> _) b, a) g
74103
let inline call (a: 'a) = call_2 (a, Unchecked.defaultof<'r>) : 'r option
75104
call Unchecked.defaultof<TryParseArray>
76105

77-
static member inline TryParseArray (t: 't, _: TryParseArray) = fun (g: string []) ->
106+
static member inline TryParseArray (t: 't, _: TryParseArray) = fun (g: (string * string) []) ->
78107
let _f _ = Constraints.whenNestedTuple t : ('t1*'t2*'t3*'t4*'t5*'t6*'t7*'tr)
79108
let (t1: 't1 option) = tryParseElemAt 0 g
80109
let (t2: 't2 option) = tryParseElemAt 1 g
@@ -88,26 +117,26 @@ module Parsing =
88117
| 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)
89118
| _ -> None
90119

91-
static member inline TryParseArray (_: unit , _: TryParseArray) = fun (_: string []) -> ()
120+
static member inline TryParseArray (_: unit , _: TryParseArray) = fun (_: (string * string) []) -> ()
92121

93-
static member inline TryParseArray (_: Tuple<'t1> , _: TryParseArray) = fun (g: string []) -> Tuple<_> <!> tryParseElemAt 0 g : Tuple<'t1> option
94-
static member inline TryParseArray (_: Id<'t1> , _: TryParseArray) = fun (g: string []) -> Id<_> <!> tryParseElemAt 0 g
95-
static member inline TryParseArray (_: 't1*'t2 , _: TryParseArray) = fun (g: string []) -> tuple2 <!> tryParseElemAt 0 g <*> tryParseElemAt 1 g
96-
static member inline TryParseArray (_: 't1*'t2'*'t3 , _: TryParseArray) = fun (g: string []) -> tuple3 <!> tryParseElemAt 0 g <*> tryParseElemAt 1 g <*> tryParseElemAt 2 g
97-
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
98-
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
99-
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
100-
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
122+
static member inline TryParseArray (_: Tuple<'t1> , _: TryParseArray) = fun (g: (string * string) []) -> Tuple<_> <!> tryParseElemAt 0 g : Tuple<'t1> option
123+
static member inline TryParseArray (_: Id<'t1> , _: TryParseArray) = fun (g: (string * string) []) -> Id<_> <!> tryParseElemAt 0 g
124+
static member inline TryParseArray (_: 't1*'t2 , _: TryParseArray) = fun (g: (string * string) []) -> tuple2 <!> tryParseElemAt 0 g <*> tryParseElemAt 1 g
125+
static member inline TryParseArray (_: 't1*'t2'*'t3 , _: TryParseArray) = fun (g: (string * string) []) -> tuple3 <!> tryParseElemAt 0 g <*> tryParseElemAt 1 g <*> tryParseElemAt 2 g
126+
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
127+
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
128+
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
129+
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
101130

102131

103132
open Internals
104133
open System
105134

106135
/// Gets a tuple with the result of parsing each element of a string array.
107-
let inline parseArray (source: string[]) : '``(T1 * T2 * ... * Tn)`` = ParseArray.Invoke source
136+
let inline parseArray (source: string []) : '``(T1 * T2 * ... * Tn)`` = ParseArray.Invoke (Array.map (fun x -> (x, "")) source)
108137

109138
/// Gets a tuple with the result of parsing each element of a formatted text.
110-
let inline sscanf (pf: PrintfFormat<_,_,_,_,'``(T1 * T2 * ... * Tn)``>) s : '``(T1 * T2 * ... * Tn)`` = getGroups pf s |> parseArray
139+
let inline sscanf (pf: PrintfFormat<_,_,_,_,'``(T1 * T2 * ... * Tn)``>) s : '``(T1 * T2 * ... * Tn)`` = getGroups pf s |> ParseArray.Invoke
111140

112141
/// Gets a tuple with the result of parsing each element of a formatted text from the Console.
113142
let inline scanfn pf : '``(T1 * T2 * ... * Tn)`` = sscanf pf (Console.ReadLine ())
@@ -121,4 +150,4 @@ module Parsing =
121150
/// Gets a tuple with the result of parsing each element of a formatted text from the Console. Returns None in case of failure.
122151
let inline tryScanfn pf : '``(T1 * T2 * ... * Tn)`` option = trySscanf pf (Console.ReadLine ())
123152

124-
#endif
153+
#endif

tests/FSharpPlus.Tests/General.fs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2185,8 +2185,12 @@ module Parsing =
21852185
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"
21862186

21872187
let _date: (DayOfWeek * string * uint16 * int) option = trySscanf "%A %A %A %A" "Saturday March 25 1989"
2188-
2189-
()
2188+
2189+
let x = trySscanf "%X %x" "13 43"
2190+
let o = trySscanf "%o" "10"
2191+
2192+
areEqual (Some (19, 67)) x
2193+
areEqual (Some 8) o
21902194

21912195

21922196
module Conversions =

0 commit comments

Comments
 (0)