Skip to content

Instantly share code, notes, and snippets.

@mausch
Forked from anonymous/RegexExtensions.fs
Created January 2, 2012 22:29
Show Gist options
  • Save mausch/1552406 to your computer and use it in GitHub Desktop.
Save mausch/1552406 to your computer and use it in GitHub Desktop.
F# regex active patterns proposal #2 for fsharpx
namespace Extensions
open System.Text.RegularExpressions
///Regex extensions
module Regex =
let replaceWithAcc folder state input (rx: Regex) =
let acc = ref state
let evaluator (m: Match) =
let newState, result = folder !acc m
acc := newState
result
let replacement: string = rx.Replace(input, evaluator)
!acc, replacement
let replaceWith replacements input (rx: Regex) =
let folder replacements (matx: Match) =
match replacements with
| [] -> [], matx.Value
| x::xs -> xs, x
replaceWithAcc folder replacements input rx |> snd
type ActiveMatch =
{
Match: Match
MatchValue: string
Groups: Group list
OptionalGroups: (Group option) list
GroupValues: string list
OptionalGroupValues: (string option) list
}
let tryMatchWithOptions flags pattern input =
match input with
| null -> None //Regex.Match will throw with null input, we return None instead
| _ ->
//using the static Regex.Match takes advantage of Regex caching
match Regex.Match(input, pattern, flags) with
| m when m.Success ->
//n.b. the head value of m.Groups is the match itself, which we discard
//n.b. if a group is optional and doesn't match, it's Value is ""
let groups = [for x in m.Groups -> x].Tail
let optionalGroups = groups |> List.map (fun x -> if x.Success then Some(x) else None)
let groupValues = groups |> List.map (fun x -> x.Value)
let optionalGroupValues = optionalGroups |> List.map (function None -> None | Some(x) -> Some(x.Value))
Some({ Match=m
MatchValue=m.Value
Groups=groups
OptionalGroups=optionalGroups
GroupValues=groupValues
OptionalGroupValues=optionalGroupValues })
| _ -> None
let inline tryMatch x = tryMatchWithOptions RegexOptions.None x
let inline (|Match|_|) x = tryMatchWithOptions x
module Compiled =
//note: if we need to support Silverlight and other reduced runtimes that don't support RegexOptions.Compiled,
//then it would be nice for us to detect that and fall back on RegexOptions.None here (compiling is just an
//optimization detail, doesn't change behavior of regex otherwise, so doing this fall back allows library
//users to share code between their full vs. silverlight applications more easily).
let (|Match|_|) = (|Match|_|) RegexOptions.Compiled
module Interpreted =
let (|Match|_|) = (|Match|_|) RegexOptions.None
module RegexTests =
let ``tryMatch success``() =
let m = "John Smith" |> Regex.tryMatch "(?i)^john .*" |> Option.map (fun m -> { m with MatchValue = m.MatchValue.ToLowerInvariant() })
match m with
| Some { MatchValue = "john doe" } -> false
| Some { MatchValue = "john smith" } -> true
| _ -> false
let ``replace with accumulator``() =
let count, r =
Regex "%."
|> Regex.replaceWithAcc (fun s _ -> let s = s+1 in s, sprintf "@p%d" s) 0 "values (%d, %s)"
count = 2 && r = "values (@p1, @p2)"
let ``replace with fixed number of replacements``() =
let r =
Regex "%."
|> Regex.replaceWith ["@p1"; "@p2"] "values (%d, %s, %i)"
r = "values (@p1, @p2, %i)"
let ``MatchWithGroupValues null input OK`` =
match null with
| Regex.Match RegexOptions.None "^(\w*) (\w* )?(\w*)$" { GroupValues=["john"; ""; "smith"] } ->
false
| _ -> true
//unlike in other version
let ``MatchWithGroupValues no groups IS OK`` =
try
match "john smith" with
| Regex.Match RegexOptions.None "^john smith$" { MatchValue="john smith"; GroupValues=_ } -> true
| _ -> true
with
| :? System.ArgumentException -> false
let ``MatchWithGroupValues success`` =
match "john smith" with
| Regex.Match RegexOptions.None "^(\w*) (\w* )?(\w*)$" { MatchValue="john smith"; GroupValues=["john"; ""; "smith"] } ->
true
| _ -> false
let ``MatchWithGroups success`` =
match "john smith" with
| Regex.Match RegexOptions.None "^(\w*) (\w* )?(\w*)$" { MatchValue="john smith"; OptionalGroupValues=[Some "john"; None; Some "smith"] } ->
true
| _ -> false
let ``GroupValues success`` =
match "john smith" with
| Regex.Match RegexOptions.None "^(\w*) (\w* )?(\w*)$" { GroupValues=["john"; ""; "smith"] }->
true
| _ -> false
let ``Groups success`` =
match "john smith" with
| Regex.Match RegexOptions.None "^(\w*) (\w* )?(\w*)$" { OptionalGroupValues=[Some "john"; None; Some "smith"] }->
true
| _ -> false
let ``Match success`` =
match "john smith" with
| Regex.Match RegexOptions.None "^(\w*) (\w* )?(\w*)$" { MatchValue="john smith" } ->
true
| _ -> false
let ``Compiled.GroupValues success`` =
match "john smith" with
| Regex.Compiled.Match "^(\w*) (\w* )?(\w*)$" { GroupValues=["john"; ""; "smith"] } ->
true
| _ -> false
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment