FSharp.Text.RegexProvider


FSharp.Text.RegexProvider

The FSharp.Text.RegexProvider project contains a type provider for regular expressions.

The library can be installed from NuGet:
PM> Install-Package FSharp.Text.RegexProvider

Example

This example demonstrates the use of the type provider:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
// reference the type provider dll
#r "FSharp.Text.RegexProvider.dll"
open System.Globalization
open FSharp.Text.RegexProvider

// Let the type provider do its work
type PhoneRegex = Regex< @"(?<AreaCode>^\d{3})-(?<PhoneNumber>\d{3}-\d{4}$)" >


// now you have typed access to the regex groups and you can browse it via Intellisense
PhoneRegex().TypedMatch("425-123-2345").AreaCode.Value

val it : string = "425"

// you can also get an option for simpler handling in case the rege doesn't match
match PhoneRegex().TryTypedMatch("425-123-2345") with
| Some m -> printfn "Phone number is %s" m.PhoneNumber.Value
| None -> printfn "Phone number unavailable"

alt text

Note that since version 1.0, generated methods are prefixed by Typed by default. You can disable this behaviour using the parameter noMethodPrefix:

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
type MultiplePhoneRegex = Regex< @"\b(?<AreaCode>\d{3})-(?<PhoneNumber>\d{3}-\d{4})\b", noMethodPrefix = true >

// now the generated types are just added as an overload of the existing method name on the `Regex` type
MultiplePhoneRegex().Matches("425-123-2345, 426-123-2346, 427-123-2347")
|> Seq.map (fun x -> x.AreaCode.Value)
|> List.ofSeq

val it : string list = ["425"; "426"; "427"]

To ease the conversion of matched groups to primitives types, the assembly provides the FSharp.Text.RegexExtensions module. It contains extensions methods to convert matched groups to int, uint32, int64, uint64, int16, uint16, byte, sbyte, float, single, decimal, DateTime, DateTimeOffset, TimeSpan, bool and char:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
open FSharp.Text.RegexExtensions

type TempRegex = Regex< @"^(?<Temperature>[\d\.]+)\s*°C$", noMethodPrefix = true >

// the simplest extension gets an option string depending on Success
TempRegex().Match("21.3°C").Temperature.TryValue
val it : string option = Some "21.3"

// conversion to decimal in the happy case
TempRegex().Match("21.3°C").Temperature.AsDecimal
val it : decimal = 21.3M

// When not sure whether the match occured or the format is always correct use TryAs...
// here it's successful
TempRegex().Match("21.3°C").Temperature.TryAsDecimal
val it : decimal option = Some 21.3M

// here it returns None because it's not °C, so it doesn't match
TempRegex().Match("21.3°F").Temperature.TryAsDecimal
val it : decimal option = None

// here it matches, but it is not a valid decimal
TempRegex().Match("21.3.5°F").Temperature.TryAsDecimal
val it : decimal option = None

type DateRegex = Regex< @"^Date:\s*(?<Date>\d{4}-\d{2}-\d{2})$", noMethodPrefix = true >

// for dates, specify a format, especially for local or utc conversion
DateRegex().Match("Date: 2019-06-18").Date.AsDateTime(DateTimeStyles.AssumeUniversal|||DateTimeStyles.AdjustToUniversal)
val it : System.DateTime = 18/06/2019 00:00:00

TypedReplace can be used with a match evaluator that takes a MatchType as an input. Here, the type of m is NumberRegex.MatchType, and has a Number property infered from the regular expression :

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
type NumberRegex = Regex< @"(?<Number>\d+)" >

let song = "
    1, 2 buckle my shoe
    3, 4 knock on the door
    5, 6 pick up sticks
    9, 10 a big fat hen."

let numbers = 
    Map.ofList
        [ 1, "one"; 2, "two"; 3, "three"; 4, "four"; 5, "five"
          6, "six"; 7, "seven"; 8, "eight"; 9, "nine"; 10, "ten"]

// using typed replace, you can use typed match in the provided function
NumberRegex().TypedReplace(song, fun m -> numbers.[m.Number.AsInt] )
val it : string = 
"
    one, two buckle my shoe
    three, four knock on the door
    five, six pick up sticks
    nine, ten a big fat hen."

Contributing and copyright

The project is hosted on GitHub where you can report issues, fork the project and submit pull requests. If you're adding new public API, please also consider adding samples that can be turned into a documentation. You might also want to read library design notes to understand how it works.

The library is available under Public Domain license, which allows modification and redistribution for both commercial and non-commercial purposes. For more information see the License file in the GitHub repository.

Fork me on GitHub
namespace System
namespace System.Globalization
Multiple items
namespace FSharp

--------------------
namespace Microsoft.FSharp
type PhoneRegex = obj
union case Option.Some: Value: 'T -> Option<'T>
val m : obj
val printfn : format:Printf.TextWriterFormat<'T> -> 'T
union case Option.None: Option<'T>
type MultiplePhoneRegex = obj
module Seq

from Microsoft.FSharp.Collections
val map : mapping:('T -> 'U) -> source:seq<'T> -> seq<'U>
val x : obj
Multiple items
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
    interface IReadOnlyList<'T>
    interface IReadOnlyCollection<'T>
    interface IEnumerable
    interface IEnumerable<'T>
    member GetSlice : startIndex:int option * endIndex:int option -> 'T list
    member Head : 'T
    member IsEmpty : bool
    member Item : index:int -> 'T with get
    member Length : int
    member Tail : 'T list
    ...
val ofSeq : source:seq<'T> -> 'T list
type TempRegex = obj
type DateRegex = obj
type DateTimeStyles =
  | None = 0
  | AllowLeadingWhite = 1
  | AllowTrailingWhite = 2
  | AllowInnerWhite = 4
  | AllowWhiteSpaces = 7
  | NoCurrentDateDefault = 8
  | AdjustToUniversal = 16
  | AssumeLocal = 32
  | AssumeUniversal = 64
  | RoundtripKind = 128
field DateTimeStyles.AssumeUniversal: DateTimeStyles = 64
field DateTimeStyles.AdjustToUniversal: DateTimeStyles = 16
type NumberRegex = obj
val song : string
val numbers : Map<int,string>
Multiple items
module Map

from Microsoft.FSharp.Collections

--------------------
type Map<'Key,'Value (requires comparison)> =
  interface IReadOnlyDictionary<'Key,'Value>
  interface IReadOnlyCollection<KeyValuePair<'Key,'Value>>
  interface IEnumerable
  interface IComparable
  interface IEnumerable<KeyValuePair<'Key,'Value>>
  interface ICollection<KeyValuePair<'Key,'Value>>
  interface IDictionary<'Key,'Value>
  new : elements:seq<'Key * 'Value> -> Map<'Key,'Value>
  member Add : key:'Key * value:'Value -> Map<'Key,'Value>
  member ContainsKey : key:'Key -> bool
  ...

--------------------
new : elements:seq<'Key * 'Value> -> Map<'Key,'Value>
val ofList : elements:('Key * 'T) list -> Map<'Key,'T> (requires comparison)