FsRegEx


FsRegEx

The FsRegEx library provides composable F# functionality for nearly all the capabilites of the .NET Regex class, supporting uniform pipe forward |> composability and all Regex features except timeouts. Optionally you can compose F# verbal expressions in natural language. Lazy evaluation ensures natural language composition imposes no performance penalty.

The FsRegEx library can be installed from NuGet:
PM> Install-Package FsRegEx

Introduction

The FsRegEx library brings a composable regular expression experience to F#.

  • Composable functions representing all available Regex functionality (except timeouts) with the target input string uniformly the last parameter to better support pipe forward |> composition and partial application.

  • Collections returned as F# arrays rather than Regex special collections for better composability (but sacrificing lazy match evaluation and timeout support).

  • Short-cut functions like capture on group name make common multi-step processes a single function.

    • FsRegEx - A composable F# wrapper type over System.Text.RegularExpressions.Regex for natural language regular expression composition.

  • FsMatch - A composable F# wrapper type over System.Text.RegularExpressions.Match.

  • FsGroup - A composable F# wrapper type over System.Text.RegularExpressions.Group.

  • Basic functional operations on FsRegEx (exists, fold, foldBack, iter, and map), among other composable functions.

  • FsRegEx speciall collections are returned as arrays. This does lose the advantange of lazy match evaluation the special collections provide. The underlying object can always be returned from the wrapper.

Better F# Composition

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
open FsRegEx

let carRegExp = @"(\w+)\s+(car)"

sprintf "%s %s %s" "One car" "red car" "blue car"
|> matches carRegExp
|> Array.iter(fun m -> printfn "%s" m.Value)

// One car
// red car
// blue car

Short-cut common procedures to a single function

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
let groupNumberRegExp = @"COD(?<GroupNumber>[0-9]{3})END"
let groupNumberName = "GroupNumber"

sprintf "%s" "COD123END"
|> capture groupNumberRegExp groupNumberName
|> printfn "%s"

// 123

Natural Language Regular Expressions

The library also provides the composable Verbal Expressions language for constructing simple regular expressions in a readable fashion.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
open FsRegEx
open System.Text.RegularExpressions
open VerbalExpressions

let groupName =  "GroupNumber"
 
FsRegEx()
|> add "COD"
|> beginCaptureNamed groupName
|> any "0-9"
|> repeatPrevious 3
|> endCapture
|> then' "END"
|> capture "COD123END" groupName
|> printfn "%s"

// 123

A comparison of using Regex natively, and Verbal Expressions to retrieve the database parameter from a connection string:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
let TestConnString = 
    """<add name="MyConnString" connectionString="Server=MyServer;Database=MyDatabase;User ID=me;Password=secret;Encrypt=True;TrustServerCertificate=True;Enlist=False;" />"""

let databaseFromRegex connectionString =
        
    Regex.Match(connectionString, "[dD]atabase=(?<database>.*?);").Groups.["database"].Value

let databaseFromFsRegEx connectionString =

    FsRegEx()
    |> add "[dD]atabase=(?<database>.*?);"
    |> capture connectionString "database"

(databaseFromRegex TestConnString) = (databaseFromFsRegEx TestConnString) 
|> printfn "%b"

// true

This example shows the more verbose and descriptive Verbal Expressions:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
let databaseFromVerboseFsRegEx connectionString =

    FsRegEx()
    |> anyOf "dD"
    |> then' "atabase="
    |> beginCaptureNamed "database"
    |> add ".*?"
    |> endCapture
    |> then' ";"
    |> capture connectionString "database"

(databaseFromRegex TestConnString) = (databaseFromVerboseFsRegEx TestConnString) 
|> printfn "%b"

// true

Documentation

FsRegEx comes with comprehensive API documentation and a tutorial.

  • API Reference contains documentation for all types, modules, and functions in the library.

  • F# Verbal Expressions Tutorial contains further explanation of the natural language DSL syntax and many more usage examples.

Versioning

FsRegEx adheres to Semantic Versioning. So long as the project is pre-1.0.0 minor versions may be breaking. Once the project reaches 1.0.0 minor enhancements will be backwards-compatible.

Contributing and copyright

FsRegEx is hosted on GitHub where you can report issues, fork the project, and submit pull requests, so long as pull requests:

  • include excellent unit test coverage
  • include correct intellisense documentation
  • adhere to the concepts of composability

FsRegEx 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.

val carRegExp : string
val sprintf : format:Printf.StringFormat<'T> -> 'T
module Array

from Microsoft.FSharp.Collections
val iter : action:('T -> unit) -> array:'T [] -> unit
val m : obj
val printfn : format:Printf.TextWriterFormat<'T> -> 'T
val groupNumberRegExp : string
val groupNumberName : string
namespace System
namespace System.Text
namespace System.Text.RegularExpressions
val groupName : string
val TestConnString : string
val databaseFromRegex : connectionString:string -> string
val connectionString : string
Multiple items
type Regex =
  new : pattern:string -> Regex + 2 overloads
  member GetGroupNames : unit -> string[]
  member GetGroupNumbers : unit -> int[]
  member GroupNameFromNumber : i:int -> string
  member GroupNumberFromName : name:string -> int
  member IsMatch : input:string -> bool + 1 overload
  member Match : input:string -> Match + 2 overloads
  member MatchTimeout : TimeSpan
  member Matches : input:string -> MatchCollection + 1 overload
  member Options : RegexOptions
  ...

--------------------
Regex(pattern: string) : Regex
Regex(pattern: string, options: RegexOptions) : Regex
Regex(pattern: string, options: RegexOptions, matchTimeout: System.TimeSpan) : Regex
Regex.Match(input: string, pattern: string) : Match
Regex.Match(input: string, pattern: string, options: RegexOptions) : Match
Regex.Match(input: string, pattern: string, options: RegexOptions, matchTimeout: System.TimeSpan) : Match
val databaseFromFsRegEx : connectionString:'a -> 'b
val connectionString : 'a
val databaseFromVerboseFsRegEx : connectionString:'a -> 'b
Fork me on GitHub