text:
(*** hide ***)
// This block of code is omitted in the generated HTML documentation. Use
// it to define helpers that you do not want to show in the documentation.
#I "../../bin/FsRegEx/net47"

(**
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.
*)

(*** hide ***)
#r "FsRegEx.dll"

(**
Better F# Composition
------------------------------------
*)
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
------------------------------------------------
*)
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.
*)
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:
*)

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:
*)

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](reference/index.html) contains documentation for all types, modules, and functions in the library.

 * [F# Verbal Expressions Tutorial](tutorial.html) contains further explanation of the natural language DSL syntax and many more usage examples.

Versioning
-----------------------

 FsRegEx adheres to [Semantic Versioning](http://semver.org/ "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][gh] where you can [report issues][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][license] in the GitHub repository.

  [content]: http://github.com/jackfoxy/FsRegEx/tree/master/docs/content
  [gh]: http://github.com/jackfoxy/FsRegEx
  [issues]: http://github.com/jackfoxy/FsRegEx/issues
  [readme]: http://github.com/jackfoxy/FsRegEx/blob/master/README.md
  [license]: http://github.com/jackfoxy/FsRegEx/blob/master/LICENSE.txt
*)


text:
(*** hide ***)
// This block of code is omitted in the generated HTML documentation. Use
// it to define helpers that you do not want to show in the documentation.
#I "../../bin/FsRegEx/net47"

(**
Replacing and Splitting
=======================

Replace
-------

The `System.Text.RegularExpressions.Regex` Class provides several overloads of `replace`. The same overloads are available in the `FsRegEx` class and as individually
named functions, including overloads and functions using `RegexOptions`. `Timeout` is not supported at this time.
*)

(*** hide ***)
#r "FsRegEx.dll"

(**
### simple replacement of all occurence ###
*)
open System
open System.Text.RegularExpressions

"This is   text with   far  too   much   " +
                "whitespace."
|> FsRegEx.replace "\\s+" " "
|>printfn "Replacement String: %s"

// Replacement String: This is text with far too much whitespace.
(**
### replace max time starting at ###

Replaces a specified maximum number of strings starting at location that match a regular expression pattern with a specified replacement string.
*)
"Instantiating a New Type\n" +
    "Generally, there are two ways that an\n" +
    "instance of a class or structure can\n" +
    "be instantiated. "
|> FsRegEx.replaceMaxTimesStartAtOpt "^.*$" RegexOptions.Multiline "\n$&"  -1 1
|> printfn "%s"

// Instantiating a New Type
//
// Generally, there are two ways that an
//
// instance of a class or structure can
//
// be instntiated.
(**
### replace with MatchEvaluator ###
*)
let capText (m : Match) =
    // Get the matched string.
    let x = m.Value
    // If the first char is lower case...
    if (Char.IsLower(x.[0])) then
        // Capitalize it.
        (Char.ToUpper(x.[0]).ToString()) + (x.Substring(1, x.Length - 1))
    else
        x

"four score and seven years ago"
|> FsRegEx.replaceByMatch @"\w+" (new MatchEvaluator(capText))
|> printfn "result=[%s]"

// result=[Four Score And Seven Years Ago]
(**
Splitting
---------

The `System.Text.RegularExpressions.Regex` Class provides several overloads of `split` to split strings into arrays. The same overloads are available in the `FsRegEx` class and as individually
named functions, including overloads and functions using `RegexOptions`. `Timeout` is not supported at this time.

### split with Regex option ###
*)
"Abc1234Def5678Ghi9012Jklm"
|> FsRegEx.splitOpt "[a-z]+" RegexOptions.IgnoreCase
|> Array.iter (fun x -> printfn "'%s'" x)

// ''
// '1234'
// '5678'
// '9012'
// ''

repo_name: jackfoxy/FsRegEx

repo_language: F#

mime_type: text/plain


text:
(*** hide ***)
// This block of code is omitted in the generated HTML documentation. Use
// it to define helpers that you do not want to show in the documentation.
#I "../../bin/FsRegEx/net47"
open FsRegEx

(**
Tutorial: F# Verbal Expressions
=================================

The `VerbalExpressions` module includes the `FsRegEx` type which wraps the familiar .NET `RegEx` in a type with useful functional members.
Multiple constructors start with a regular expression in the constructor.
*)

let fsRegEx = new FsRegEx(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}")

(**
This the module is an experimental DSL that allows you to compose regular expressions in natural language
using the immutable `FsRegEx` type. The remainder of this tutorial is concerned with the experimental DSL which is, frankly, not that practical.

For practical examples of using the core `FsRegEx` module for composability, see the following examples:

* [Matching](matching.html)

* [Matching Groups](group.html)

* [Replace and Split](replacesplit.html)

* [Capture](capture.html)

Verbal Expressions DSL
----------------------

You can compose values of the FsRegEx type with the |> operator, including creating a new regular expression by logical or on 2 existing FsRegExs.

*)
#r "FsRegEx.dll"
open FsRegEx
open System.Text.RegularExpressions
open VerbalExpressions

let v =
    CommonFsRegEx.Email
    |> fsRegExOrFsRegEx RegexOptions.None CommonFsRegEx.Url

let foundEmail =
    v
    |> isMatch "test@github.com"

let foundUrl =
    v
    |> isMatch "http://www.google.com"

printfn "%b" foundEmail
printfn "%b" foundUrl

// true
// true

(**
Natural language composition consists of building up a new FsRegEx from an old by functions which append special characters, groups, modifiers, and other attributes of the regular expression language.

function : 'T -> FsRegEx -> FsRegEx

See the API documentation for all the regular expression functions available.
*)

open VerbalExpressions

let foundFromGithub =
    FsRegEx()
    |> startOfLine
    |> something
    |> then' "github.com"
    |> endOfLine
    |> isMatch "test@github.com"

printfn "%b" foundFromGithub

// true

(**
You do not have to worry about escaping special characters in your regular expression.
*)

let foundSomethingSpecial =
    FsRegEx()
    |> startOfLine
    |> something
    |> then' "*+?"
    |> anything
    |> isMatch "blah blah blah*+?yackety yack"

printfn "%b" foundSomethingSpecial

// true

(**
Sometimes you may need more power than the natural language provides, or you just need to include a snippet of native regular expression. The add function lets you do that.
*)

let foundSpecialInMultiline =
    FsRegEx()
    |> add @"phrase1\*\+\?"
    |> anything
    |> isMatch @"phrase1*+?RestOfLine\n"

printfn "%b" foundSpecialInMultiline

// true

(**
FsRegExs posses all the power of the .Net RegEx class in a composable form.
*)

let n =
    FsRegEx()
    |> word
    |> matches "three words here"

printfn "%i" n.Length

// 3

let betterFormat =
    FsRegEx()
    |> add "\s+"
    |> or' "whitespace"
    |> replace "This     is   text with   far  too   much   whitespace" " "

printfn "%s" betterFormat

// This is text with far too much

let groupName =  "GroupNumber"

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

// 123

(**
FsRegEx comes with first class support for unicode, including unicode general categories and .Net extension blocks.
*)

FsRegEx()
|> beginCaptureNamed "upper"
|> unicodeCategory Unicode.UnicodeGeneralCategory.LetterUppercase
|> add "+"
|> endCapture
|> capture "some mixed case WORDS" "upper"
|> printfn "%s"

// WORDS


text:
This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to <http://unlicense.org/>


text:
(*** hide ***)
// This block of code is omitted in the generated HTML documentation. Use
// it to define helpers that you do not want to show in the documentation.
#I "../../bin/FsRegEx/net47"

(**
Matching
========

IsMatch
-------

The `System.Text.RegularExpressions.Regex` Class provides several overloads of `isMatch`. The same overloads are available in the `FsRegEx` class and as individually
named functions, including overloads and functions using `RegexOptions`. `Timeout` is not supported at this time.
*)

(*** hide ***)
#r "FsRegEx.dll"

(**
### is match on first occurence ###
*)
open FsRegEx
module F = FsRegEx

let partNumbers = [| "1298-673-4192"; "A08Z-931-468A";
                    "_A90-123-129X"; "12345-KKA-1230";
                    "0919-2893-1256" |]

for partNumber in partNumbers do
    printfn "%s %s a valid part number."
        partNumber
        (if (F.isMatch @"^[a-zA-Z0-9]\d{2}[a-zA-Z0-9](-\d{3}){2}[A-Za-z0-9]$" partNumber ) then "is"
            else "is not")

let fsRegEx = new FsRegEx(@"^[a-zA-Z0-9]\d{2}[a-zA-Z0-9](-\d{3}){2}[A-Za-z0-9]$")

for partNumber in partNumbers do
    printfn "%s %s a valid part number."
        partNumber
        (if (fsRegEx.IsMatch(partNumber)) then "is"
            else "is not")

// 1298-673-4192 is a valid part number.
// A08Z-931-468A is a valid part number.
// _A90-123-129X is not a valid part number.
// 12345-KKA-1230 is not a valid part number.
// 0919-2893-1256 is not a valid part number.
(**
### is match starting at position ###
*)
let labeledPartNumbers = [| "Part Number: 1298-673-4192"; "Part No: A08Z-931-468A";
                        "_A90-123-129X"; "123K-000-1230";
                        "SKU: 0919-2893-1256" |]

for partNumber in labeledPartNumbers do
    let start = partNumber.IndexOf(':')
    if (start >= 0) then
        printfn "%s %s a valid part number."
            partNumber
            (if (F.isMatchAt @"^[a-zA-Z0-9]\d{2}[a-zA-Z0-9](-\d{3}){2}[A-Za-z0-9]$" start partNumber) then "is" else "is not")
    else
        printfn "Cannot find starting position in %s." partNumber

for partNumber in labeledPartNumbers do
    let start = partNumber.IndexOf(':')
    if (start >= 0) then
        printfn "%s %s a valid part number."
            partNumber
            (if (fsRegEx.IsMatch(partNumber, start)) then "is" else "is not")
    else
        printfn "Cannot find starting position in %s." partNumber

// Part Number: 1298-673-4192 is a valid part number.
// Part No: A08Z-931-468A is a valid part number.
// Cannot find starting position in _A90-123-129X.
// Cannot find starting position in 123K-000-1230.
// SKU: 0919-2893-1256 is not a valid part number.
(**
FsMatch
-------

`FsMatch` provides the functionality in the `System.Text.RegularExpressions.Match` Class, but returning arrays of objects instead of special collections.

### iterate over matches ###
*)
"Who writes these notes?"
|> FsRegEx.matches @"\b\w+es\b"
|> Array.iter (fun m -> printfn "Found '%s' at position %i" m.Value m.Index)

// Found 'writes' at position 4
// Found 'notes' at position 17
(**
### first match and iteration ###
*)
let regExpr = @"\b\w+es\b"
let sentence = "Who writes these notes and uses our paper?"

let m = FsRegEx.firstMatch regExpr sentence

if m.Success then
    printfn "Found first 'es' in '%s' at position %i" m.Value m.Index
    sentence
    |> FsRegEx.matchesAt regExpr (m.Index + m.Length)
    |> Array.iter (fun m -> printfn "Also found '%s' at position %i" m.Value m.Index)

// Found first 'es' in 'writes' at position 4
// Also found 'notes' at position 17
// Also found 'uses' at position 27


text:
(*** hide ***)
// This block of code is omitted in the generated HTML documentation. Use
// it to define helpers that you do not want to show in the documentation.
#I "../../bin/FsRegEx/net47"

(**
Capture Groups
==============

FsGroup
-------

`FsGroup` provides the functionality in the `System.Text.RegularExpressions.Group` Class, but returning arrays of objects instead of special collections and makes the group name a property.
*)

(*** hide ***)
#r "FsRegEx.dll"

(**
### anonymous groups ###
*)

let pattern = @"(\b(\w+?)[,:;]?\s?)+[?.!]"
let input = "This is one sentence. This is a second sentence."

let m = FsRegEx.firstMatch pattern input
printfn "Match: %s" m.Value

m.Groups()
|> Array.iteri (fun i g ->
    printfn "   Group %i: '%s'" i g.Value
    g.Captures()
    |> Array.iteri (fun i c ->
        printfn "      Capture %i: '%s'" i c.Value
        )
    )

// Match: This is one sentence.
//    Group 0: 'This is one sentence.'
//        Capture 0: 'This is one sentence.'
//    Group 1: 'sentence'
//        Capture 0: 'This '
//        Capture 1: 'is '
//        Capture 2: 'one '
//        Capture 3: 'sentence'
//    Group 2: 'sentence'
//        Capture 0: 'This'
//        Capture 1: 'is'
//        Capture 2: 'one'
//        Capture 3: 'sentence'
(**
### named groups ###
*)
let pattern' = @"\b(?<FirstWord>\w+)\s?((\w+)\s)*(?<LastWord>\w+)?(?<Punctuation>\p{Po})"
let input' = "The cow jumped over the moon."

let m' = FsRegEx.firstMatch pattern' input'

printfn "Named Groups:"

m'.Groups()
|> Array.iter (fun g -> printfn "   %s: '%s'" g.Name g.Value)

// Named Groups:
//    0: 'The cow jumped over the moon.'
//    1: 'the '
//    2: 'the'
//    FirstWord: 'The'
//    LastWord: 'moon'
//    Punctuation: '.'
(**
### capturing groups ###
*)
let regExpr = @"\b(\w+?)([\u00AE\u2122])"

let matches =
    "Microsoft® Office Professional Edition combines several office " +
        "productivity products, including Word, Excel®, Access®, Outlook®, " +
        "PowerPoint®, and several others. Some guidelines for creating " +
        "corporate documents using these productivity tools are available " +
        "from the documents created using Silverlight™ on the corporate " +
        "intranet site."
    |> FsRegEx.matches regExpr

matches
|> Array.iter (fun m ->
    let groups = m.Groups()
    printfn "%s: %s" groups.[2].Value groups.[1].Value)

printfn ""
printfn "Found %i trademarks or registered trademarks." matches.Length

// ®: Microsoft
// ®: Excel
// ®: Access
// ®: Outlook
// ®: PowerPoint
// ™: Silverlight
//
// Found 6 trademarks or registered trademarks.
(**
### group numbers and names ###
*)
open System

let pattern'' = @"\b((?<word>\w+)\s*)+(?<end>[.?!])"
let input'' = "This is a sentence. This is a second sentence."

let groupNumbers' = FsRegEx.groupNumbers pattern''
let groupNames = FsRegEx.groupNames pattern''

let m'' = FsRegEx.firstMatch pattern'' input''

printfn "Match: %s" m''.Value

Array.zip groupNumbers' groupNames
|> Array.iter (fun (n, name) ->
    let isNumber, _ = Int32.TryParse(name)
    printfn "   Group %i%s: '%s'"
        n
        (if (not isNumber) then
            " (" + name + ")"
         else String.Empty)
        (m'.Groups()).[n].Value
    )

// Match: This is a sentence.
//    Group 0: 'This is a sentence.'
//    Group 1: 'sentence'
//    Group 2 (word): 'sentence'
//    Group 3 (end): '.'


text:
(*** hide ***)
// This block of code is omitted in the generated HTML documentation. Use
// it to define helpers that you do not want to show in the documentation.
#I "../../bin/FsRegEx/net47"

(**
Capturing
=========

Capturing is a shortcut through matching to a named group or the group's captured value.
*)

(*** hide ***)
#r "FsRegEx.dll"

(**
### capture ###
*)
open FsRegEx

let groupNumberRegExp = @"COD(?<GroupNumber>[0-9]{3})END"
let groupNumberName = "GroupNumber"

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

// 123
(**
### capture group ###
*)
let group = captureGroup groupNumberRegExp groupNumberName "COD123END"

printfn "%s" group.Value

// 123


text:
(**
On the Futility of Email Regex Validation
=========================================

Using regular expressions to validate email addresses is notoriously error prone.
The complexity of the address format specification makes constructing a machine readable
grammar correctly discerning the difference between valid and invalid addresses hard.

Evolution of the Internet Message Format
----------------------------------------

[RFC 561](http://www.ietf.org/rfc/rfc0561.txt) Standardizing Network Mail Headers, September 5, 1973

[RFC 680](http://www.ietf.org/rfc/rfc0680.txt) Message Transmission Protocol, April 30, 1975

[RFC 724](http://www.ietf.org/rfc/rfc0724.txt) Proposed Official Standard for the Format of ARPA Network Messages, May 12, 1977

[RFC 733](http://www.ietf.org/rfc/rfc0733.txt) STANDARD FOR THE FORMAT OF ARPA NETWORK TEXT MESSAGES, November 21, 1977

Obsoletes:  RFC 561, RFC 680, RFC 724

[RFC 822](http://www.ietf.org/rfc/rfc0822.txt) STANDARD FOR THE FORMAT OF ARPA INTERNET TEXT MESSAGES, August 13, 1982

Obsoletes:  RFC 733

[RFC 2822](http://www.ietf.org/rfc/rfc2822.txt) Internet Message Format, April 2001

Obsoletes: RFC 822

[RFC 5322](http://www.ietf.org/rfc/rfc5322.txt) Internet Message Format, October 2008

Obsoletes: RFC 2822

Testing Regular Expressions that Parse Email Addresses
------------------------------------------------------

The tests are derived from a suite of tests available at [RFC 822 Email Address Parser in PHP](http://code.iamcal.com/php/rfc822/),
which we have converted to run under FsRegEx. The PHP parser author claims a much better success rate than we were able to achieve with
any regular expression, although still not perfect. The site also provides more background on the "muddy" internet specification.

The suite consists of 280 validataion tests, which we converted to [Expecto](https://github.com/haf/expecto) tests
[here](http://github.com/jackfoxy/FsRegEx/blob/master/tests/Email.Tests/RFC822.fs). Run with the
[Email.Tests](http://github.com/jackfoxy/FsRegEx/blob/master/tests/Email.Tests/Email.Tests.fsproj) console app.

We chose some email parsing regular expressions available on the internet for testing:

[Simple](http://github.com/jackfoxy/FsRegEx/blob/master/tests/Email.Tests/SimpleEmail.fs), from [emailregex.com](http://emailregex.com/).

[Moderate](http://github.com/jackfoxy/FsRegEx/blob/master/tests/Email.Tests/ModerateEmail.fs), from [emailregex.com](http://emailregex.com/).

[Complex](http://github.com/jackfoxy/FsRegEx/blob/master/tests/Email.Tests/ComplexEmail.fs), from [emailregex.com](http://emailregex.com/).

[Ultimate](http://github.com/jackfoxy/FsRegEx/blob/master/tests/Email.Tests/Ultimate.fs), from
[Mail::RFC822::Address: regexp-based address validation](http://www.ex-parrot.com/pdw/Mail-RFC822-Address.html). This regular expression was machine generated.

Note that [emailregex.com](http://emailregex.com/) headlines "Email Address Regular Expression That 99.99% Works". The site offers many different
email address regular expressions. Apparently we never found the one that works 99.99% on our test suite.

We also tested a much simpler (non-regular expression) checker, that simply tests string for a single at sign that is not wrapped in quotes and is not either
the first or last character in the string [here](http://github.com/jackfoxy/FsRegEx/blob/master/tests/Email.Tests/OneAtSign.fs).

Results
-------

**Simple**:

132 passed, 148 failed, 98 false negatives

**Moderate**:

190 passed, 90 failed, 72 false negatives

**Complex**:

172 passed, 108 failed, 60 false negatives

**Ultimate**:

170 passed, 110 failed, 11 false negatives

**One At Sign**:

162 passed, 118 failed, 1 false negative (test166)

Conclusion
----------

The evolution of the Internet Email Address specification and way it is documented make it impossible to settle on a completely deterministic grammar covering all cases.
With that in mind it is already impossible to make any complete parser, let alone one based on a regular expression. That being said, the test results do not reflect
the likelihood of meeting any of the 280 test cases in the wild.

Simply determining if there is a correctly placed at sign is perhaps the best filter for most practical purposes, given the importance of this criterion.
(Note that even our attempt at rejecting multiple at signs resulted in a false negative.)
*)


text:
<!DOCTYPE html>

    <meta charset="utf-8"/>
    <title>@Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <meta name="description" content="@Description"/>
    <meta name="author" content="@Properties["project-author"]"/>

    <link href="https://netdna.bootstrapcdn.com/twitter-bootstrap/2.2.1/css/bootstrap-combined.min.css" rel="stylesheet"/>

    <link type="text/css" rel="stylesheet" href="@Root/content/style.css" />

fsharp.org

github page

@Properties["project-name"]

          @RenderBody()

@Properties["project-name"]

Home page

Get Library via NuGet

Source Code on GitHub

License

Release Notes

Examples

Matching

Match Groups

Replace and Split

Capture

Documentation

API Reference

More...

On the Futility of Email Regex Validation

F# Verbal Expressions Tutorial


text:
namespace FsRegEx.Tests

open Expecto

module RunTests =

    [<EntryPoint>]
    let main args =

        Tests.runTestsWithArgs defaultConfig args FsRegEx.testMatches |> ignore
        Tests.runTestsWithArgs defaultConfig args FsRegEx.testReplace |> ignore
        Tests.runTestsWithArgs defaultConfig args FsRegEx.testSplit |> ignore
        Tests.runTestsWithArgs defaultConfig args FsRegEx.testCapture |> ignore
        Tests.runTestsWithArgs defaultConfig args FsRegEx.testGroup |> ignore
        Tests.runTestsWithArgs defaultConfig args FsRegEx.testIsMatch |> ignore

        Tests.runTestsWithArgs defaultConfig args VerbalExpressions.testAdd |> ignore
        Tests.runTestsWithArgs defaultConfig args VerbalExpressions.testCapture |> ignore
        Tests.runTestsWithArgs defaultConfig args VerbalExpressions.testEndOfLine |> ignore
        Tests.runTestsWithArgs defaultConfig args VerbalExpressions.testMatch |> ignore
        Tests.runTestsWithArgs defaultConfig args VerbalExpressions.testMaybe |> ignore
        Tests.runTestsWithArgs defaultConfig args VerbalExpressions.testMisc |> ignore
        Tests.runTestsWithArgs defaultConfig args VerbalExpressions.testMultiple |> ignore
        Tests.runTestsWithArgs defaultConfig args VerbalExpressions.testOr' |> ignore
        Tests.runTestsWithArgs defaultConfig args VerbalExpressions.testRange |> ignore
        Tests.runTestsWithArgs defaultConfig args VerbalExpressions.testRegexOptions |> ignore
        Tests.runTestsWithArgs defaultConfig args VerbalExpressions.testRepeatPrevious |> ignore
        Tests.runTestsWithArgs defaultConfig args VerbalExpressions.testSomethingBut |> ignore
        Tests.runTestsWithArgs defaultConfig args VerbalExpressions.testSomething |> ignore
        Tests.runTestsWithArgs defaultConfig args VerbalExpressions.testStartOfLine |> ignore
        Tests.runTestsWithArgs defaultConfig args VerbalExpressions.testThen' |> ignore
        Tests.runTestsWithArgs defaultConfig args VerbalExpressions.testTutorial |> ignore

        0


text:
FSharp.Core

Expecto
Expecto.BenchmarkDotNet
Expecto.FsCheck
Expecto.VisualStudio.TestAdapter


text:
namespace FsRegEx.Tests

open Expecto
open FsRegEx
open VerbalExpressions
open System
open System.Text.RegularExpressions

module VerbalExpressions =

    [<Tests>]
    let testAdd =
        testList "VerbalExpressions.Add " [
            testCase "special characters unescaped" <| fun () ->
                let value =
                    FsRegEx(RegexOptions.Multiline)
                    |> add "Line1\*+?"
                    |> isMatch "Line1\*+?Line2"
                Expect.isFalse value "Expected False"

            testCase "special characters escaped" <| fun () ->
                let value =
                    FsRegEx(RegexOptions.Multiline)
                    |> add "Line1\*\+\?"
                    |> isMatch "Line1*+?Line2"
                Expect.isTrue value "Expected True"

            testCase "Does Not Match GoogleCom With Escaped Dot" <| fun () ->
                let value =
                    FsRegEx(RegexOptions.Multiline)
                    |> add "\.com"
                    |> isMatch "http://www.googlecom/"
                Expect.isFalse value "Expected False"

            testCase "Does Not Match GoogleCom With Dot" <| fun () ->
                let value =
                    FsRegEx(RegexOptions.Multiline)
                    |> add ".com"
                    |> isMatch "http://www.googlecom/"
                Expect.isTrue value "Expected True"
        ]

    [<Tests>]
    let testCapture =
        let duplicateIdentifier =
            FsRegEx()
            |> beginCapture
            |> word
            |> endCapture
            |> whiteSpace
            |> beginCapture
            |> backReference 1
            |> endCapture

        let groupName =  "GroupNumber"

        let v =
            FsRegEx()
            |> add "COD"
            |> beginCaptureNamed groupName
            |> any "0-9"
            |> repeatPrevious 3
            |> endCapture
            |> add "END"

        testList "VerbalExpressions.Capture" [
            testCase "begin and end" <| fun () ->
                let value =
                    FsRegEx()
                    |> beginCapture
                    |> add "com"
                    |> or' "org"
                    |> endCapture
                    |> isMatch "((com)|(org))"
                Expect.isTrue value "Expected True"

            testCase "Duplicates Identifier string value" <| fun () ->
                let value =
                    duplicateIdentifier
                    |> toString
                Expect.isTrue (value = @"(\w+)\s(\1)") "Expected True"

            testCase "Duplicates Identifier Does Match" <| fun () ->
                Expect.isTrue (duplicateIdentifier.IsMatch "He said that that was the the correct answer." ) "Expected True"

            testCase "Duplicates Identifier Does Not Match" <| fun () ->
                Expect.isFalse (duplicateIdentifier.IsMatch "He said that was the correct answer." ) "Expected False"

            testCase "Group Names" <| fun () ->
                let value =
                    duplicateIdentifier
                    |> groupNames
                Expect.isTrue (value = [|"0"; "1"; "2"|]) "Expected True"

            testCase "Group Numbers" <| fun () ->
                let value =
                    duplicateIdentifier
                    |> groupNumbers
                Expect.isTrue (value = [|0; 1; 2|]) "Expected True"

            testCase "Group Name ToString" <| fun () ->
                let value =
                    v
                    |> toString
                Expect.isTrue (value = @"COD(?<GroupNumber>[0-9]{3})END") "Expected True"

            testCase "Group Name capture" <| fun () ->
                let value =
                    v
                    |> capture "COD123END" groupName
                Expect.isTrue (value = "123") "Expected True"

            testCase "Group Names2" <| fun () ->
                let value =
                    v
                    |> groupNames
                Expect.isTrue (value = [|"0"; "GroupNumber"|]) "Expected True"

            testCase "Nmaed Backreference" <| fun () ->
                let groupName = "char"
                let value =
                    FsRegEx()
                    |> add @"(?<char>\w)"
                    |> namedBackReference "char"
                    |> capture "trellis llama webbing dresser swagger" groupName
                Expect.isTrue (value = "l") "Expected True"

            testCase "Match has 1 capture" <| fun () ->
                let value =
                    FsRegEx()
                    |> word
                    |> matches "three words here"
                Expect.isTrue (value.[0].Captures().Length = 1) "Expected True"

            testCase "Match has 1 group" <| fun () ->
                let value =
                    FsRegEx()
                    |> word
                    |> matches "three words here"
                Expect.isTrue (value.[0].Groups().Length = 1) "Expected True"

            testCase "Group captures" <| fun () ->
                let testString = "This is a sentence. This is another sentence."
                let m =
                    FsRegEx()
                    |> add @"\b"
                    |> beginCapture
                    |> word
                    |> whiteSpace
                    |> add "*"
                    |> endCapture
                    |> add @"+\."
                    |> matches testString
                let g =
                    m.[0].Groups().[1]

                let testStringBegins =
                    ("", g.Captures())
                    ||> Array.fold (fun s t ->
                        if s.Length = 0 then t.Value
                        else (s + t.Value))

                Expect.isTrue (testString.StartsWith testStringBegins) "Expected True"
        ]

    [<Tests>]
    let testEndOfLine =
        testList "VerbalExpressions.EndOfLine" [
            testCase "Match Dot Com In End" <| fun () ->
                let value =
                    FsRegEx()
                    |> add ".com"
                    |> endOfLine
                    |> isMatch "www.google.com"
                Expect.isTrue value "Expected True"

            testCase "Does Not Match Forward Slash In End" <| fun () ->
                let value =
                    FsRegEx()
                    |> add ".com"
                    |> endOfLine
                    |> isMatch "www.google.com/"
                Expect.isFalse value "Expected False"
        ]

    [<Tests>]
    let testMatch =
        testList "VerbalExpressions.Match" [
            testCase "Returns Expected Number Of Matches" <| fun () ->
                let value =
                    FsRegEx()
                    |> word
                    |> matches "three words here"
                Expect.isTrue (value.Length = 3) "Expected True"

            testCase "No Matches returned" <| fun () ->
                let value =
                    FsRegEx()
                    |> word
                    |> matches "$#% )(*! {}"
                Expect.isTrue (value.Length = 0) "Expected True"
        ]

    [<Tests>]
    let testMaybe =
        let maybeSSL =
            FsRegEx()
            |> find "http"
            |> maybe "s"
            |> anythingBut " "
            |> then' "://"

        testList "VerbalExpressions.Maybe" [
            testCase "https present" <| fun () ->
                let value =
                    maybeSSL
                    |> isMatch "https://www.google.com"
                Expect.isTrue value "Expected True"

            testCase "http present" <| fun () ->
                let value =
                    maybeSSL
                    |> isMatch "http://www.google.com"
                Expect.isTrue value "Expected True"
        ]

    [<Tests>]
    let testMisc =
        testList "VerbalExpressions.Misc" [
            testCase "valid url" <| fun () ->
                let value =
                    FsRegEx()
                    |> startOfLine
                    |> then' "http"
                    |> maybe "s"
                    |> then' "://"
                    |> maybe "www."
                    |> anythingBut " "
                    |> endOfLine
                    |> isMatch "https://www.google.com"
                Expect.isTrue value "Expected True"

            testCase "does match anything" <| fun () ->
                let value =
                    FsRegEx()
                    |> startOfLine
                    |> anything
                    |> isMatch "'!@#$%¨&*()__+{}'"
                Expect.isTrue value "Expected True"

            testCase "lineBreak" <| fun () ->
                let value =
                    FsRegEx()
                    |> lineBreak
                    |> isMatch (sprintf "testing with %s line break" Environment.NewLine)
                Expect.isTrue value "Expected True"

            testCase "br" <| fun () ->
                let value =
                    FsRegEx()
                    |> br
                    |> isMatch (sprintf "testing with %s line break" Environment.NewLine)
                Expect.isTrue value "Expected True"

            testCase "tab" <| fun () ->
                let value =
                    FsRegEx()
                    |> tab
                    |> isMatch (sprintf "testing with %s tab" "\t")
                Expect.isTrue value "Expected True"
        ]

    [<Tests>]
    let testMultiple =
        let v =
            FsRegEx()
            |> add "y"
            |> multiple "aho"
            |> add "u"

        testList "VerbalExpressions.Multiple" [
            testCase "Match One Or Multiple Values Given ToString" <| fun () ->
                let value =
                    v
                    |> toString
                Expect.isTrue (value = "y(aho)+u") "Expected True"

            testCase "Match One Or Multiple Values Given" <| fun () ->
                let value =
                    v
                    |> isMatch "testesting 123 yahoahoahou another test"
                Expect.isTrue value "Expected True"
        ]

    [<Tests>]
    let testOr' =
        let v =
            FsRegEx()
            |> add "com"
            |> or' "org"

        let vAll =
            FsRegEx()
            |> or' "com"
            |> or' "org"

        testList "VerbalExpressions.Or'" [
            testCase "Matches Com And Org ToString" <| fun () ->
                let value =
                    v
                    |> toString
                Expect.isTrue (value = "com|org") "Expected True"

            testCase "Matches Com And Org" <| fun () ->
                let value1 =
                    v
                    |> isMatch "org"

                let value2 =
                    v
                    |> isMatch "com"
                Expect.isTrue (value1 && value2) "Expected True"

            testCase "Matches Com And Org all or ToString" <| fun () ->
                let value =
                    vAll
                    |> toString
                Expect.isTrue (value = "com|org") "Expected True"

            testCase "Matches Com And Org all or" <| fun () ->
                let value1 =
                    vAll
                    |> isMatch "org"
                let value2 =
                    vAll
                    |> isMatch "com"
                Expect.isTrue (value1 && value2) "Expected True"

            testCase "fsRegEx  email or url" <| fun () ->
                let value =
                    CommonFsRegEx.Email
                    |> fsRegExOrFsRegEx RegexOptions.None CommonFsRegEx.Url
                Expect.isTrue ((value.IsMatch "test@github.com") && (value.IsMatch "http://www.google.com")) "Expected True"

            testCase "fsRegEx  or regular expression" <| fun () ->
                let value =
                    CommonFsRegEx.Email
                    |> or' "org"
                Expect.isTrue ((value.IsMatch "test@github.com") && (value.IsMatch "org")) "Expected True"
        ]

    [<Tests>]
    let testRange =
        let vOdd =
            FsRegEx()
            |> range [1; 6; 7]

        let vEven =
            FsRegEx()
            |> range [|"1"; "6"; "a"; "c"|]

        testList "VerbalExpressions.Range" [
            testCase "Odd Number Of Items Append LastElement With Or Clause toString" <| fun () ->
                let value =
                    vOdd
                    |> toString
                Expect.isTrue (value = "[1-6]|7") "Expected True"

            testCase "Odd Number Of Items Append LastElement With Or Clause" <| fun () ->
                let value =
                    vOdd
                    |> isMatch "abcd7sdadqascdaswde"
                Expect.isTrue value "Expected True"

            testCase "Even Number Of Items toString" <| fun () ->
                let value =
                    vEven
                    |> toString
                Expect.isTrue (value = "[1-6][a-c]") "Expected True"

            testCase "Even Number Of Items isMatch" <| fun () ->
                let value =
                    vEven
                    |> isMatch "2b"
                Expect.isTrue value "Expected True"

            testCase "Has Only One Value  Throw Exception" <| fun () ->
                let value =
                    try
                        FsRegEx() |> range [1] |> ignore
                        ""
                    with e ->
                        e.Message
                Expect.isTrue (value = "one-element range\r\nParameter name: range") "Expected True"
        ]

    [<Tests>]
    let testRegexOptions =
        let testSingleLineUpperCase v =
            v
            |> add "First string"
            |> anything
            |> add "Second string"
            |> isMatch ("FIRST STRING" + Environment.NewLine + "SECOND STRING")

        let vSingleLine = FsRegEx(RegexOptions.Singleline)
        let vSingleLineIgnoreCase = new FsRegEx(RegexOptions.Singleline ||| RegexOptions.IgnoreCase)

        testList "VerbalExpressions.RegexOptions" [
            testCase "Multiline" <| fun () ->
                let value =
                    FsRegEx(RegexOptions.Multiline)
                    |> add "Line1"
                    |> endOfLine
                    |> isMatch "Line1\nLine2"
                Expect.isTrue value "Expected True"

            testCase "Multiline None" <| fun () ->
                let value =
                    FsRegEx()
                    |> add "Line1"
                    |> endOfLine
                    |> isMatch @"Line1\nLine2"
                Expect.isFalse value "Expected False"

            testCase "IgnoreCase" <| fun () ->
                let value =
                    FsRegEx(RegexOptions.IgnoreCase)
                    |> add "teststring"
                    |> endOfLine
                    |> isMatch "TESTSTRING"
                Expect.isTrue value "Expected True"

            testCase "IgnoreCase None" <| fun () ->
                let value =
                    FsRegEx()
                    |> add "teststring"
                    |> endOfLine
                    |> isMatch "TESTSTRING"
                Expect.isFalse value "Expected False"

            testCase "Singleline" <| fun () ->
                let value =
                    FsRegEx(RegexOptions.Singleline)
                    |> add "First string"
                    |> anything
                    |> add "Second string"
                    |> isMatch ("First string" + Environment.NewLine + "Second string")
                Expect.isTrue value "Expected True"

            testCase "Singleline None" <| fun () ->
                let value =
                    FsRegEx()
                    |> add "First string"
                    |> anything
                    |> add "Second string"
                    |> isMatch ("First string" + Environment.NewLine + "Second string")
                Expect.isFalse value "Expected False"

            testCase "Singleline IgnoreCase" <| fun () ->
                let value =
                    FsRegEx(RegexOptions.Singleline ||| RegexOptions.IgnoreCase)
                    |> testSingleLineUpperCase
                Expect.isTrue value "Expected True"

            testCase "Singleline IgnoreCase None" <| fun () ->
                let value =
                    FsRegEx(RegexOptions.Singleline)
                    |> testSingleLineUpperCase
                Expect.isFalse value "Expected False"

            testCase "resetRegexOptions single line" <| fun () ->
                let value =
                    vSingleLine
                    |> testSingleLineUpperCase
                Expect.isFalse value "Expected False"

            testCase "resetRegexOptions single line ignore case" <| fun () ->
                let value =
                    (Some (RegexOptions.Singleline ||| RegexOptions.IgnoreCase), vSingleLine)
                    ||> resetRegexOptions
                    |> testSingleLineUpperCase
                Expect.isTrue value "Expected True"

            testCase "identify options ignore case" <| fun () ->
                let value =
                    vSingleLineIgnoreCase.RegexOptions.HasFlag(RegexOptions.IgnoreCase)
                Expect.isTrue value "Expected True"

            testCase "identify options explicit capture" <| fun () ->
                let value =
                    vSingleLineIgnoreCase.RegexOptions.HasFlag(RegexOptions.ExplicitCapture)
                Expect.isFalse value "Expected False"
        ]

    [<Tests>]
    let testRepeatPrevious =
        testList "VerbalExpressions.RepeatPrevious" [
            testCase "toString" <| fun () ->
                let value =
                    FsRegEx()
                    |> add "A"
                    |> repeatPrevious 3
                    |> toString
                Expect.isTrue (value = "A{3}") "Expected True"

            testCase "between 2 and 4" <| fun () ->
                let value =
                    FsRegEx()
                    |> add "A"
                    |> repeatBetweenPrevious 3 4
                    |> toString
                Expect.isTrue (value = "A{3,4}") "Expected True"
        ]

    [<Tests>]
    let testSomethingBut =
        testList "VerbalExpressions.SomethingBut" [
            testCase "all characters in test" <| fun () ->
                let value =
                    FsRegEx()
                    |> somethingBut "Test"
                    |> isMatch "Test"
                Expect.isFalse value "Expected False"

            testCase "a characters out of test" <| fun () ->
                let value =
                    FsRegEx()
                    |> somethingBut "Test"
                    |> isMatch "TestA"
                Expect.isTrue value "Expected True"
        ]

    [<Tests>]
    let testSomething =
        testList "VerbalExpressions.Something" [
            testCase "something there" <| fun () ->
                let value =
                    FsRegEx()
                    |> something
                    |> isMatch "Test string should not be empty"
                Expect.isTrue value "Expected True"

            testCase "nothing there" <| fun () ->
                let value =
                    FsRegEx()
                    |> something
                    |> isMatch ""
                Expect.isFalse value "Expected False"
        ]

    [<Tests>]
    let testStartOfLine =
        testList "VerbalExpressions.StartOfLine" [
            testCase "line start" <| fun () ->
                let value =
                    FsRegEx()
                    |> startOfLine
                    |> toString
                Expect.isTrue (value = "^") "Expected True"

            testCase "Prefix At Beginning Of The Expression" <| fun () ->
                let value =
                    FsRegEx()
                    |> add "test"
                    |> add "ing"
                    |> startOfLine
                    |> isMatch "testing1234"
                Expect.isTrue value "Expected True"

            testCase "match correct at start" <| fun () ->
                let value =
                    FsRegEx()
                    |> startOfLine
                    |> then' "http"
                    |> maybe "www"
                    |> startOfLine
                    |> isMatch "http://xxx.test.xxx"
                Expect.isTrue value "Expected True"

            testCase "no match not at start" <| fun () ->
                let value =
                    FsRegEx()
                    |> startOfLine
                    |> then' "http"
                    |> maybe "www"
                    |> startOfLine
                    |> isMatch "www.test.com"
                Expect.isFalse value "Expected False"
        ]

    [<Tests>]
    let testThen' =
        testList "VerbalExpressions.Then'" [
            testCase "Does Match" <| fun () ->
                let value =
                    FsRegEx()
                    |> startOfLine
                    |> something
                    |> then' "github.com"
                    |> isMatch "test@github.com"
                Expect.isTrue value "Expected True"

            testCase "Does Not Match" <| fun () ->
                let value =
                    FsRegEx()
                    |> startOfLine
                    |> then' "github.com"
                    |> isMatch "test@"
                Expect.isFalse value "Expected False"

            testCase "special characters" <| fun () ->
                let value =
                    FsRegEx()
                    |> then' "*+?"
                    |> isMatch "Line1*+?Line2"
                Expect.isTrue value "Expected True"

            testCase "Does Not Match GoogleCom Without Dot" <| fun () ->
                let value =
                    FsRegEx()
                    |> then' ".com"
                    |> isMatch "http://www.googlecom/"
                Expect.isFalse value "Expected False"
        ]

    [<Tests>]
    let testTutorial =
        testList "VerbalExpressions.Tutorial" [
            testCase "Tutorial 1" <| fun () ->
                let v =
                    CommonFsRegEx.Email
                    |> fsRegExOrFsRegEx RegexOptions.None CommonFsRegEx.Url

                let foundEmail =
                    v
                    |> VerbalExpressions.isMatch "test@github.com"

                let foundUrl =
                    v
                    |> VerbalExpressions.isMatch "http://www.google.com"
                Expect.isTrue (foundEmail && foundUrl) "Expected True"

            testCase "Tutorial 2" <| fun () ->
                let foundFromGithub =
                    FsRegEx()
                    |> startOfLine
                    |> something
                    |> then' "github.com"
                    |> endOfLine
                    |> isMatch "test@github.com"
                Expect.isTrue foundFromGithub "Expected True"

            testCase "Tutorial 3" <| fun () ->
                let foundSomethingSpecial =
                    FsRegEx()
                    |> startOfLine
                    |> something
                    |> then' "*+?"
                    |> anything
                    |> isMatch "blah blah blah*+?yackety yack"
                Expect.isTrue foundSomethingSpecial "Expected True"

            testCase "Tutorial 4" <| fun () ->
                let foundSpecialInMultiline =
                    FsRegEx()
                    |> add @"phrase1\*\+\?"
                    |> anything
                    |> isMatch @"phrase1*+?RestOfLine\n"
                Expect.isTrue foundSpecialInMultiline "Expected True"

            testCase "Tutorial 5" <| fun () ->
                let n =
                    FsRegEx()
                    |> word
                    |> matches "three words here"
                Expect.isTrue (n.Length = 3) "Expected True"

            testCase "Tutorial 6" <| fun () ->
                let betterFormat =
                    FsRegEx()
                    |> add "\s+"
                    |> or' "whitespace"
                    |> replace "This     is   text with   far  too   much   whitespace" " "
                Expect.isTrue (betterFormat = "This is text with far too much  ") "Expected True"

            testCase "Tutorial 7" <| fun () ->
                let groupName =  "GroupNumber"

                let value =
                    FsRegEx()
                    |> add "COD"
                    |> beginCaptureNamed groupName
                    |> any "0-9"
                    |> repeatPrevious 3
                    |> endCapture
                    |> then' "END"
                    |> capture "COD123END" groupName
                Expect.isTrue (value = "123") "Expected True"

            testCase "Tutorial 8" <| fun () ->
                let value =
                    FsRegEx()
                    |> beginCaptureNamed "upper"
                    |> unicodeCategory  Unicode.UnicodeGeneralCategory.LetterUppercase
                    |> add "+"
                    |> endCapture
                    |> capture "some mixed case WORDS" "upper"
                Expect.isTrue (value = "WORDS") "Expected True"
        ]


text:
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net47</TargetFramework>
    <RootNamespace>FsRegEx.Tests</RootNamespace>
    <AssemblyName>FsRegEx.Tests</AssemblyName>
    <Name>FsRegEx.Tests</Name>
    <OutputType>Exe</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="FsRegEx.fs" />
    <Compile Include="VerbalExpressions.fs" />
    <Compile Include="RunTests.fs" />
    <None Include="paket.references" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\..\src\FsRegEx\FsRegEx.fsproj"></ProjectReference>
  </ItemGroup>
  <Import Project="..\..\.paket\Paket.Restore.targets" />
</Project>


text:
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Expecto.VisualStudio.TestAdapter" version="5.0.1" />
</packages>


text:
namespace FsRegEx.Tests

open Expecto
open FsRegEx
open System.Text.RegularExpressions

module FsRegEx =

    let carText = "One car red car blue car"
    let carRegExp = @"(\w+)\s+(car)"
    let eval = new MatchEvaluator(fun _ -> "blah")

    let splitText = "123ABCDE456FGHIJKL789MNOPQ012"
    let splitRegExp = @"\d+"

    let groupNumberRegExp = @"COD(?<GroupNumber>[0-9]{3})END"
    let groupNumberName = "GroupNumber"

    let partNumberRegExp = @"^[a-zA-Z0-9]\d{2}[a-zA-Z0-9](-\d{3}){2}[A-Za-z0-9]$"

    [<Tests>]
    let testMatches =

        testList "FsRegEx.Match" [
            testCase "firstMatch" <| fun () ->
                let fsMatch = firstMatch carRegExp carText
                Expect.isTrue (fsMatch.Value = "One car") "Expected True"

            testCase "matchAt" <| fun () ->
                let fsMatch = matchAt carRegExp 3 carText
                Expect.isTrue (fsMatch.Value = "red car") "Expected True"

            testCase "matchAtFor" <| fun () ->
                let fsMatch = matchAtFor carRegExp 3 15 carText
                Expect.isTrue (fsMatch.Value = "red car") "Expected True"

            testCase "matches" <| fun () ->
                let value =
                    matches carRegExp carText
                    |> Array.map (fun m -> m.Value)
                Expect.isTrue (value = [|"One car"; "red car"; "blue car"|]) "Expected True"

            testCase "matchesAt" <| fun () ->
                let value =
                    matchesAt carRegExp 3 carText
                    |> Array.map (fun m -> m.Value)
                Expect.isTrue (value = [|"red car"; "blue car"|]) "Expected True"
        ]

    [<Tests>]
    let testReplace =
        testList "FsRegEx.Replace" [
            testCase "" <| fun () ->
                let value = replace carRegExp "blah" carText
                Expect.isTrue (value = "blah blah blah") "Expected True"

            testCase "replaceByMatch" <| fun () ->
                let value = replaceByMatch carRegExp eval carText
                Expect.isTrue (value = "blah blah blah") "Expected True"

            testCase "replaceMaxTimes" <| fun () ->
                let value = replaceMaxTimes carRegExp "blah" 2 carText
                Expect.isTrue (value = "blah blah blue car") "Expected True"

            testCase "replaceByMatchMaxTimes" <| fun () ->
                let value = replaceByMatchMaxTimes carRegExp eval 2 carText
                Expect.isTrue (value = "blah blah blue car") "Expected True"

            testCase "replaceMaxTimesStartAt" <| fun () ->
                let value = replaceMaxTimesStartAt carRegExp "blah" 1 3 carText
                Expect.isTrue (value = "One car blah blue car") "Expected True"

            testCase "replaceByMatchMaxTimesStartAt" <| fun () ->
                let value = replaceByMatchMaxTimesStartAt carRegExp eval 1 3 carText
                Expect.isTrue (value = "One car blah blue car") "Expected True"
        ]

    [<Tests>]
    let testSplit =
        testList "FsRegEx.Split" [
            testCase "split" <| fun () ->
                let value = split splitRegExp splitText
                Expect.isTrue (value = [|""; "ABCDE"; "FGHIJKL"; "MNOPQ"; ""|]) "Expected True"

            testCase "splitMaxTimes" <| fun () ->
                let value = splitMaxTimes splitRegExp 3 splitText
                Expect.isTrue (value = [|""; "ABCDE"; "FGHIJKL789MNOPQ012"|]) "Expected True"

            testCase "splitMaxTimesStartAt" <| fun () ->
                let value = splitMaxTimesStartAt splitRegExp 3 5 splitText
                Expect.isTrue (value = [|"123ABCDE"; "FGHIJKL"; "MNOPQ012"|]) "Expected True"
        ]

    [<Tests>]
    let testCapture =
        testList "FsRegEx.Capture" [
            testCase "capture" <| fun () ->
                let value = capture groupNumberRegExp groupNumberName "COD123END"
                Expect.isTrue (value = "123") "Expected True"

            testCase "captureGroup" <| fun () ->
                let group = captureGroup groupNumberRegExp groupNumberName "COD123END"
                Expect.isTrue (group.Value = "123") "Expected True"
        ]

    [<Tests>]
    let testGroup =
        testList "FsRegEx.Group" [
            testCase "groupNames" <| fun () ->
                let value = groupNames groupNumberRegExp
                Expect.isTrue (value = [|"0"; groupNumberName|]) "Expected True"

            testCase "groupNumbers" <| fun () ->
                let value = groupNumbers groupNumberRegExp
                Expect.isTrue (value = [|0; 1|]) "Expected True"

            testCase "fsGroupName" <| fun () ->
                let pattern = @"\b(?<FirstWord>\w+)\s?((\w+)\s)*(?<LastWord>\w+)?(?<Punctuation>\p{Po})"
                let input = "The cow jumped over the moon."

                let m = FsRegEx.firstMatch pattern input

                let names =
                    m.Groups()
                    |> Array.map (fun x -> x.Name)
                Expect.isTrue (names = [|"0"; "1"; "2"; "FirstWord"; "LastWord"; "Punctuation"|]) "Expected True"
        ]

    [<Tests>]
    let testIsMatch =
        testList "FsRegEx.IsMatch" [
            testCase "isMatch" <| fun () ->
                Expect.isTrue (isMatch partNumberRegExp "1298-673-4192") "Expected True"

            testCase "isMatchAt" <| fun () ->
                let partNumber = "Part Number: 1298-673-4192"
                let start = partNumber.IndexOf(":")
                Expect.isTrue (isMatchAt @"[a-zA-Z0-9]\d{2}[a-zA-Z0-9](-\d{3}){2}[A-Za-z0-9]$" start partNumber) "Expected True"
        ]


text:
namespace Email.Tests

open Expecto

module SimpleEmail =

    [<Tests>]
    let testSimpleEmail =
        testList "SimpleEmail" [
            RFC822.test1 CommonFsRegEx.Email.IsMatch
            RFC822.test2 CommonFsRegEx.Email.IsMatch
            RFC822.test3 CommonFsRegEx.Email.IsMatch
            RFC822.test4 CommonFsRegEx.Email.IsMatch
            RFC822.test5 CommonFsRegEx.Email.IsMatch
            RFC822.test6 CommonFsRegEx.Email.IsMatch
            RFC822.test7 CommonFsRegEx.Email.IsMatch
            RFC822.test8 CommonFsRegEx.Email.IsMatch
            RFC822.test9 CommonFsRegEx.Email.IsMatch
            RFC822.test10 CommonFsRegEx.Email.IsMatch
            RFC822.test11 CommonFsRegEx.Email.IsMatch
            RFC822.test12 CommonFsRegEx.Email.IsMatch
            RFC822.test13 CommonFsRegEx.Email.IsMatch
            RFC822.test14 CommonFsRegEx.Email.IsMatch
            RFC822.test15 CommonFsRegEx.Email.IsMatch
            RFC822.test16 CommonFsRegEx.Email.IsMatch
            RFC822.test17 CommonFsRegEx.Email.IsMatch
            RFC822.test18 CommonFsRegEx.Email.IsMatch
            RFC822.test19 CommonFsRegEx.Email.IsMatch
            RFC822.test20 CommonFsRegEx.Email.IsMatch
            RFC822.test21 CommonFsRegEx.Email.IsMatch
            RFC822.test22 CommonFsRegEx.Email.IsMatch
            RFC822.test23 CommonFsRegEx.Email.IsMatch
            RFC822.test24 CommonFsRegEx.Email.IsMatch
            RFC822.test25 CommonFsRegEx.Email.IsMatch
            RFC822.test26 CommonFsRegEx.Email.IsMatch
            RFC822.test27 CommonFsRegEx.Email.IsMatch
            RFC822.test28 CommonFsRegEx.Email.IsMatch
            RFC822.test29 CommonFsRegEx.Email.IsMatch
            RFC822.test30 CommonFsRegEx.Email.IsMatch
            RFC822.test31 CommonFsRegEx.Email.IsMatch
            RFC822.test32 CommonFsRegEx.Email.IsMatch
            RFC822.test33 CommonFsRegEx.Email.IsMatch
            RFC822.test34 CommonFsRegEx.Email.IsMatch
            RFC822.test35 CommonFsRegEx.Email.IsMatch
            RFC822.test36 CommonFsRegEx.Email.IsMatch
            RFC822.test37 CommonFsRegEx.Email.IsMatch
            RFC822.test38 CommonFsRegEx.Email.IsMatch
            RFC822.test39 CommonFsRegEx.Email.IsMatch
            RFC822.test40 CommonFsRegEx.Email.IsMatch
            RFC822.test41 CommonFsRegEx.Email.IsMatch
            RFC822.test42 CommonFsRegEx.Email.IsMatch
            RFC822.test43 CommonFsRegEx.Email.IsMatch
            RFC822.test44 CommonFsRegEx.Email.IsMatch
            RFC822.test45 CommonFsRegEx.Email.IsMatch
            RFC822.test46 CommonFsRegEx.Email.IsMatch
            RFC822.test47 CommonFsRegEx.Email.IsMatch
            RFC822.test48 CommonFsRegEx.Email.IsMatch
            RFC822.test49 CommonFsRegEx.Email.IsMatch
            RFC822.test50 CommonFsRegEx.Email.IsMatch
            RFC822.test51 CommonFsRegEx.Email.IsMatch
            RFC822.test52 CommonFsRegEx.Email.IsMatch
            RFC822.test53 CommonFsRegEx.Email.IsMatch
            RFC822.test54 CommonFsRegEx.Email.IsMatch
            RFC822.test55 CommonFsRegEx.Email.IsMatch
            RFC822.test56 CommonFsRegEx.Email.IsMatch
            RFC822.test57 CommonFsRegEx.Email.IsMatch
            RFC822.test58 CommonFsRegEx.Email.IsMatch
            RFC822.test59 CommonFsRegEx.Email.IsMatch
            RFC822.test60 CommonFsRegEx.Email.IsMatch
            RFC822.test61 CommonFsRegEx.Email.IsMatch
            RFC822.test62 CommonFsRegEx.Email.IsMatch
            RFC822.test63 CommonFsRegEx.Email.IsMatch
            RFC822.test64 CommonFsRegEx.Email.IsMatch
            RFC822.test65 CommonFsRegEx.Email.IsMatch
            RFC822.test66 CommonFsRegEx.Email.IsMatch
            RFC822.test67 CommonFsRegEx.Email.IsMatch
            RFC822.test68 CommonFsRegEx.Email.IsMatch
            RFC822.test69 CommonFsRegEx.Email.IsMatch
            RFC822.test70 CommonFsRegEx.Email.IsMatch
            RFC822.test71 CommonFsRegEx.Email.IsMatch
            RFC822.test72 CommonFsRegEx.Email.IsMatch
            RFC822.test73 CommonFsRegEx.Email.IsMatch
            RFC822.test74 CommonFsRegEx.Email.IsMatch
            RFC822.test75 CommonFsRegEx.Email.IsMatch
            RFC822.test76 CommonFsRegEx.Email.IsMatch
            RFC822.test77 CommonFsRegEx.Email.IsMatch
            RFC822.test78 CommonFsRegEx.Email.IsMatch
            RFC822.test79 CommonFsRegEx.Email.IsMatch
            RFC822.test80 CommonFsRegEx.Email.IsMatch
            RFC822.test81 CommonFsRegEx.Email.IsMatch
            RFC822.test82 CommonFsRegEx.Email.IsMatch
            RFC822.test83 CommonFsRegEx.Email.IsMatch
            RFC822.test84 CommonFsRegEx.Email.IsMatch
            RFC822.test85 CommonFsRegEx.Email.IsMatch
            RFC822.test86 CommonFsRegEx.Email.IsMatch
            RFC822.test87 CommonFsRegEx.Email.IsMatch
            RFC822.test88 CommonFsRegEx.Email.IsMatch
            RFC822.test89 CommonFsRegEx.Email.IsMatch
            RFC822.test90 CommonFsRegEx.Email.IsMatch
            RFC822.test91 CommonFsRegEx.Email.IsMatch
            RFC822.test92 CommonFsRegEx.Email.IsMatch
            RFC822.test93 CommonFsRegEx.Email.IsMatch
            RFC822.test94 CommonFsRegEx.Email.IsMatch
            RFC822.test95 CommonFsRegEx.Email.IsMatch
            RFC822.test96 CommonFsRegEx.Email.IsMatch
            RFC822.test97 CommonFsRegEx.Email.IsMatch
            RFC822.test98 CommonFsRegEx.Email.IsMatch
            RFC822.test99 CommonFsRegEx.Email.IsMatch
            RFC822.test100 CommonFsRegEx.Email.IsMatch
            RFC822.test101 CommonFsRegEx.Email.IsMatch
            RFC822.test102 CommonFsRegEx.Email.IsMatch
            RFC822.test103 CommonFsRegEx.Email.IsMatch
            RFC822.test104 CommonFsRegEx.Email.IsMatch
            RFC822.test105 CommonFsRegEx.Email.IsMatch
            RFC822.test106 CommonFsRegEx.Email.IsMatch
            RFC822.test107 CommonFsRegEx.Email.IsMatch
            RFC822.test108 CommonFsRegEx.Email.IsMatch
            RFC822.test109 CommonFsRegEx.Email.IsMatch
            RFC822.test110 CommonFsRegEx.Email.IsMatch
            RFC822.test111 CommonFsRegEx.Email.IsMatch
            RFC822.test112 CommonFsRegEx.Email.IsMatch
            RFC822.test113 CommonFsRegEx.Email.IsMatch
            RFC822.test114 CommonFsRegEx.Email.IsMatch
            RFC822.test115 CommonFsRegEx.Email.IsMatch
            RFC822.test116 CommonFsRegEx.Email.IsMatch
            RFC822.test117 CommonFsRegEx.Email.IsMatch
            RFC822.test118 CommonFsRegEx.Email.IsMatch
            RFC822.test119 CommonFsRegEx.Email.IsMatch
            RFC822.test120 CommonFsRegEx.Email.IsMatch
            RFC822.test121 CommonFsRegEx.Email.IsMatch
            RFC822.test122 CommonFsRegEx.Email.IsMatch
            RFC822.test123 CommonFsRegEx.Email.IsMatch
            RFC822.test124 CommonFsRegEx.Email.IsMatch
            RFC822.test125 CommonFsRegEx.Email.IsMatch
            RFC822.test126 CommonFsRegEx.Email.IsMatch
            RFC822.test127 CommonFsRegEx.Email.IsMatch
            RFC822.test128 CommonFsRegEx.Email.IsMatch
            RFC822.test129 CommonFsRegEx.Email.IsMatch
            RFC822.test130 CommonFsRegEx.Email.IsMatch
            RFC822.test131 CommonFsRegEx.Email.IsMatch
            RFC822.test132 CommonFsRegEx.Email.IsMatch
            RFC822.test133 CommonFsRegEx.Email.IsMatch
            RFC822.test134 CommonFsRegEx.Email.IsMatch
            RFC822.test135 CommonFsRegEx.Email.IsMatch
            RFC822.test136 CommonFsRegEx.Email.IsMatch
            RFC822.test137 CommonFsRegEx.Email.IsMatch
            RFC822.test138 CommonFsRegEx.Email.IsMatch
            RFC822.test139 CommonFsRegEx.Email.IsMatch
            RFC822.test140 CommonFsRegEx.Email.IsMatch
            RFC822.test141 CommonFsRegEx.Email.IsMatch
            RFC822.test142 CommonFsRegEx.Email.IsMatch
            RFC822.test143 CommonFsRegEx.Email.IsMatch
            RFC822.test144 CommonFsRegEx.Email.IsMatch
            RFC822.test145 CommonFsRegEx.Email.IsMatch
            RFC822.test146 CommonFsRegEx.Email.IsMatch
            RFC822.test147 CommonFsRegEx.Email.IsMatch
            RFC822.test148 CommonFsRegEx.Email.IsMatch
            RFC822.test149 CommonFsRegEx.Email.IsMatch
            RFC822.test150 CommonFsRegEx.Email.IsMatch
            RFC822.test151 CommonFsRegEx.Email.IsMatch
            RFC822.test152 CommonFsRegEx.Email.IsMatch
            RFC822.test153 CommonFsRegEx.Email.IsMatch
            RFC822.test154 CommonFsRegEx.Email.IsMatch
            RFC822.test155 CommonFsRegEx.Email.IsMatch
            RFC822.test156 CommonFsRegEx.Email.IsMatch
            RFC822.test157 CommonFsRegEx.Email.IsMatch
            RFC822.test158 CommonFsRegEx.Email.IsMatch
            RFC822.test159 CommonFsRegEx.Email.IsMatch
            RFC822.test160 CommonFsRegEx.Email.IsMatch
            RFC822.test161 CommonFsRegEx.Email.IsMatch
            RFC822.test162 CommonFsRegEx.Email.IsMatch
            RFC822.test163 CommonFsRegEx.Email.IsMatch
            RFC822.test164 CommonFsRegEx.Email.IsMatch
            RFC822.test165 CommonFsRegEx.Email.IsMatch
            RFC822.test166 CommonFsRegEx.Email.IsMatch
            RFC822.test167 CommonFsRegEx.Email.IsMatch
            RFC822.test168 CommonFsRegEx.Email.IsMatch
            RFC822.test169 CommonFsRegEx.Email.IsMatch
            RFC822.test170 CommonFsRegEx.Email.IsMatch
            RFC822.test171 CommonFsRegEx.Email.IsMatch
            RFC822.test172 CommonFsRegEx.Email.IsMatch
            RFC822.test173 CommonFsRegEx.Email.IsMatch
            RFC822.test174 CommonFsRegEx.Email.IsMatch
            RFC822.test175 CommonFsRegEx.Email.IsMatch
            RFC822.test176 CommonFsRegEx.Email.IsMatch
            RFC822.test177 CommonFsRegEx.Email.IsMatch
            RFC822.test178 CommonFsRegEx.Email.IsMatch
            RFC822.test179 CommonFsRegEx.Email.IsMatch
            RFC822.test180 CommonFsRegEx.Email.IsMatch
            RFC822.test181 CommonFsRegEx.Email.IsMatch
            RFC822.test182 CommonFsRegEx.Email.IsMatch
            RFC822.test183 CommonFsRegEx.Email.IsMatch
            RFC822.test184 CommonFsRegEx.Email.IsMatch
            RFC822.test185 CommonFsRegEx.Email.IsMatch
            RFC822.test186 CommonFsRegEx.Email.IsMatch
            RFC822.test187 CommonFsRegEx.Email.IsMatch
            RFC822.test188 CommonFsRegEx.Email.IsMatch
            RFC822.test189 CommonFsRegEx.Email.IsMatch
            RFC822.test190 CommonFsRegEx.Email.IsMatch
            RFC822.test191 CommonFsRegEx.Email.IsMatch
            RFC822.test192 CommonFsRegEx.Email.IsMatch
            RFC822.test193 CommonFsRegEx.Email.IsMatch
            RFC822.test194 CommonFsRegEx.Email.IsMatch
            RFC822.test195 CommonFsRegEx.Email.IsMatch
            RFC822.test196 CommonFsRegEx.Email.IsMatch
            RFC822.test197 CommonFsRegEx.Email.IsMatch
            RFC822.test198 CommonFsRegEx.Email.IsMatch
            RFC822.test199 CommonFsRegEx.Email.IsMatch
            RFC822.test200 CommonFsRegEx.Email.IsMatch
            RFC822.test201 CommonFsRegEx.Email.IsMatch
            RFC822.test202 CommonFsRegEx.Email.IsMatch
            RFC822.test203 CommonFsRegEx.Email.IsMatch
            RFC822.test204 CommonFsRegEx.Email.IsMatch
            RFC822.test205 CommonFsRegEx.Email.IsMatch
            RFC822.test206 CommonFsRegEx.Email.IsMatch
            RFC822.test207 CommonFsRegEx.Email.IsMatch
            RFC822.test208 CommonFsRegEx.Email.IsMatch
            RFC822.test209 CommonFsRegEx.Email.IsMatch
            RFC822.test210 CommonFsRegEx.Email.IsMatch
            RFC822.test211 CommonFsRegEx.Email.IsMatch
            RFC822.test212 CommonFsRegEx.Email.IsMatch
            RFC822.test213 CommonFsRegEx.Email.IsMatch
            RFC822.test214 CommonFsRegEx.Email.IsMatch
            RFC822.test215 CommonFsRegEx.Email.IsMatch
            RFC822.test216 CommonFsRegEx.Email.IsMatch
            RFC822.test217 CommonFsRegEx.Email.IsMatch
            RFC822.test218 CommonFsRegEx.Email.IsMatch
            RFC822.test219 CommonFsRegEx.Email.IsMatch
            RFC822.test220 CommonFsRegEx.Email.IsMatch
            RFC822.test221 CommonFsRegEx.Email.IsMatch
            RFC822.test222 CommonFsRegEx.Email.IsMatch
            RFC822.test223 CommonFsRegEx.Email.IsMatch
            RFC822.test224 CommonFsRegEx.Email.IsMatch
            RFC822.test225 CommonFsRegEx.Email.IsMatch
            RFC822.test226 CommonFsRegEx.Email.IsMatch
            RFC822.test227 CommonFsRegEx.Email.IsMatch
            RFC822.test228 CommonFsRegEx.Email.IsMatch
            RFC822.test229 CommonFsRegEx.Email.IsMatch
            RFC822.test230 CommonFsRegEx.Email.IsMatch
            RFC822.test231 CommonFsRegEx.Email.IsMatch
            RFC822.test232 CommonFsRegEx.Email.IsMatch
            RFC822.test233 CommonFsRegEx.Email.IsMatch
            RFC822.test234 CommonFsRegEx.Email.IsMatch
            RFC822.test235 CommonFsRegEx.Email.IsMatch
            RFC822.test236 CommonFsRegEx.Email.IsMatch
            RFC822.test237 CommonFsRegEx.Email.IsMatch
            RFC822.test238 CommonFsRegEx.Email.IsMatch
            RFC822.test239 CommonFsRegEx.Email.IsMatch
            RFC822.test240 CommonFsRegEx.Email.IsMatch
            RFC822.test241 CommonFsRegEx.Email.IsMatch
            RFC822.test242 CommonFsRegEx.Email.IsMatch
            RFC822.test243 CommonFsRegEx.Email.IsMatch
            RFC822.test244 CommonFsRegEx.Email.IsMatch
            RFC822.test245 CommonFsRegEx.Email.IsMatch
            RFC822.test246 CommonFsRegEx.Email.IsMatch
            RFC822.test247 CommonFsRegEx.Email.IsMatch
            RFC822.test248 CommonFsRegEx.Email.IsMatch
            RFC822.test249 CommonFsRegEx.Email.IsMatch
            RFC822.test250 CommonFsRegEx.Email.IsMatch
            RFC822.test251 CommonFsRegEx.Email.IsMatch
            RFC822.test252 CommonFsRegEx.Email.IsMatch
            RFC822.test253 CommonFsRegEx.Email.IsMatch
            RFC822.test254 CommonFsRegEx.Email.IsMatch
            RFC822.test255 CommonFsRegEx.Email.IsMatch
            RFC822.test256 CommonFsRegEx.Email.IsMatch
            RFC822.test257 CommonFsRegEx.Email.IsMatch
            RFC822.test258 CommonFsRegEx.Email.IsMatch
            RFC822.test259 CommonFsRegEx.Email.IsMatch
            RFC822.test260 CommonFsRegEx.Email.IsMatch
            RFC822.test261 CommonFsRegEx.Email.IsMatch
            RFC822.test262 CommonFsRegEx.Email.IsMatch
            RFC822.test263 CommonFsRegEx.Email.IsMatch
            RFC822.test264 CommonFsRegEx.Email.IsMatch
            RFC822.test265 CommonFsRegEx.Email.IsMatch
            RFC822.test266 CommonFsRegEx.Email.IsMatch
            RFC822.test267 CommonFsRegEx.Email.IsMatch
            RFC822.test268 CommonFsRegEx.Email.IsMatch
            RFC822.test269 CommonFsRegEx.Email.IsMatch
            RFC822.test270 CommonFsRegEx.Email.IsMatch
            RFC822.test271 CommonFsRegEx.Email.IsMatch
            RFC822.test272 CommonFsRegEx.Email.IsMatch
            RFC822.test273 CommonFsRegEx.Email.IsMatch
            RFC822.test274 CommonFsRegEx.Email.IsMatch
            RFC822.test275 CommonFsRegEx.Email.IsMatch
            RFC822.test276 CommonFsRegEx.Email.IsMatch
            RFC822.test277 CommonFsRegEx.Email.IsMatch
            RFC822.test278 CommonFsRegEx.Email.IsMatch
            RFC822.test279 CommonFsRegEx.Email.IsMatch
            RFC822.test280 CommonFsRegEx.Email.IsMatch
            ]


text:
namespace Email.Tests

open Expecto
open FsRegEx

module OneAtSign =

    let oneAtSign (email : string) =
        if email.StartsWith("@") || email.EndsWith("@") then
            false
        else
            let s = email.Split '@'

            if s.Length = 1 then
                false
            else
                if s.Length = 2 then
                    true
                else
                    let x =
                        Array.take (s.Length - 1) s
                        |> Array.filter (fun x -> x.Contains("\"") )
                    x.Length = (s.Length - 1)

    [<Tests>]
    let testOneAtSign =
        testList "OneAtSign" [
            RFC822.test1 oneAtSign
            RFC822.test2 oneAtSign
            RFC822.test3 oneAtSign
            RFC822.test4 oneAtSign
            RFC822.test5 oneAtSign
            RFC822.test6 oneAtSign
            RFC822.test7 oneAtSign
            RFC822.test8 oneAtSign
            RFC822.test9 oneAtSign
            RFC822.test10 oneAtSign
            RFC822.test11 oneAtSign
            RFC822.test12 oneAtSign
            RFC822.test13 oneAtSign
            RFC822.test14 oneAtSign
            RFC822.test15 oneAtSign
            RFC822.test16 oneAtSign
            RFC822.test17 oneAtSign
            RFC822.test18 oneAtSign
            RFC822.test19 oneAtSign
            RFC822.test20 oneAtSign
            RFC822.test21 oneAtSign
            RFC822.test22 oneAtSign
            RFC822.test23 oneAtSign
            RFC822.test24 oneAtSign
            RFC822.test25 oneAtSign
            RFC822.test26 oneAtSign
            RFC822.test27 oneAtSign
            RFC822.test28 oneAtSign
            RFC822.test29 oneAtSign
            RFC822.test30 oneAtSign
            RFC822.test31 oneAtSign
            RFC822.test32 oneAtSign
            RFC822.test33 oneAtSign
            RFC822.test34 oneAtSign
            RFC822.test35 oneAtSign
            RFC822.test36 oneAtSign
            RFC822.test37 oneAtSign
            RFC822.test38 oneAtSign
            RFC822.test39 oneAtSign
            RFC822.test40 oneAtSign
            RFC822.test41 oneAtSign
            RFC822.test42 oneAtSign
            RFC822.test43 oneAtSign
            RFC822.test44 oneAtSign
            RFC822.test45 oneAtSign
            RFC822.test46 oneAtSign
            RFC822.test47 oneAtSign
            RFC822.test48 oneAtSign
            RFC822.test49 oneAtSign
            RFC822.test50 oneAtSign
            RFC822.test51 oneAtSign
            RFC822.test52 oneAtSign
            RFC822.test53 oneAtSign
            RFC822.test54 oneAtSign
            RFC822.test55 oneAtSign
            RFC822.test56 oneAtSign
            RFC822.test57 oneAtSign
            RFC822.test58 oneAtSign
            RFC822.test59 oneAtSign
            RFC822.test60 oneAtSign
            RFC822.test61 oneAtSign
            RFC822.test62 oneAtSign
            RFC822.test63 oneAtSign
            RFC822.test64 oneAtSign
            RFC822.test65 oneAtSign
            RFC822.test66 oneAtSign
            RFC822.test67 oneAtSign
            RFC822.test68 oneAtSign
            RFC822.test69 oneAtSign
            RFC822.test70 oneAtSign
            RFC822.test71 oneAtSign
            RFC822.test72 oneAtSign
            RFC822.test73 oneAtSign
            RFC822.test74 oneAtSign
            RFC822.test75 oneAtSign
            RFC822.test76 oneAtSign
            RFC822.test77 oneAtSign
            RFC822.test78 oneAtSign
            RFC822.test79 oneAtSign
            RFC822.test80 oneAtSign
            RFC822.test81 oneAtSign
            RFC822.test82 oneAtSign
            RFC822.test83 oneAtSign
            RFC822.test84 oneAtSign
            RFC822.test85 oneAtSign
            RFC822.test86 oneAtSign
            RFC822.test87 oneAtSign
            RFC822.test88 oneAtSign
            RFC822.test89 oneAtSign
            RFC822.test90 oneAtSign
            RFC822.test91 oneAtSign
            RFC822.test92 oneAtSign
            RFC822.test93 oneAtSign
            RFC822.test94 oneAtSign
            RFC822.test95 oneAtSign
            RFC822.test96 oneAtSign
            RFC822.test97 oneAtSign
            RFC822.test98 oneAtSign
            RFC822.test99 oneAtSign
            RFC822.test100 oneAtSign
            RFC822.test101 oneAtSign
            RFC822.test102 oneAtSign
            RFC822.test103 oneAtSign
            RFC822.test104 oneAtSign
            RFC822.test105 oneAtSign
            RFC822.test106 oneAtSign
            RFC822.test107 oneAtSign
            RFC822.test108 oneAtSign
            RFC822.test109 oneAtSign
            RFC822.test110 oneAtSign
            RFC822.test111 oneAtSign
            RFC822.test112 oneAtSign
            RFC822.test113 oneAtSign
            RFC822.test114 oneAtSign
            RFC822.test115 oneAtSign
            RFC822.test116 oneAtSign
            RFC822.test117 oneAtSign
            RFC822.test118 oneAtSign
            RFC822.test119 oneAtSign
            RFC822.test120 oneAtSign
            RFC822.test121 oneAtSign
            RFC822.test122 oneAtSign
            RFC822.test123 oneAtSign
            RFC822.test124 oneAtSign
            RFC822.test125 oneAtSign
            RFC822.test126 oneAtSign
            RFC822.test127 oneAtSign
            RFC822.test128 oneAtSign
            RFC822.test129 oneAtSign
            RFC822.test130 oneAtSign
            RFC822.test131 oneAtSign
            RFC822.test132 oneAtSign
            RFC822.test133 oneAtSign
            RFC822.test134 oneAtSign
            RFC822.test135 oneAtSign
            RFC822.test136 oneAtSign
            RFC822.test137 oneAtSign
            RFC822.test138 oneAtSign
            RFC822.test139 oneAtSign
            RFC822.test140 oneAtSign
            RFC822.test141 oneAtSign
            RFC822.test142 oneAtSign
            RFC822.test143 oneAtSign
            RFC822.test144 oneAtSign
            RFC822.test145 oneAtSign
            RFC822.test146 oneAtSign
            RFC822.test147 oneAtSign
            RFC822.test148 oneAtSign
            RFC822.test149 oneAtSign
            RFC822.test150 oneAtSign
            RFC822.test151 oneAtSign
            RFC822.test152 oneAtSign
            RFC822.test153 oneAtSign
            RFC822.test154 oneAtSign
            RFC822.test155 oneAtSign
            RFC822.test156 oneAtSign
            RFC822.test157 oneAtSign
            RFC822.test158 oneAtSign
            RFC822.test159 oneAtSign
            RFC822.test160 oneAtSign
            RFC822.test161 oneAtSign
            RFC822.test162 oneAtSign
            RFC822.test163 oneAtSign
            RFC822.test164 oneAtSign
            RFC822.test165 oneAtSign
            RFC822.test166 oneAtSign
            RFC822.test167 oneAtSign
            RFC822.test168 oneAtSign
            RFC822.test169 oneAtSign
            RFC822.test170 oneAtSign
            RFC822.test171 oneAtSign
            RFC822.test172 oneAtSign
            RFC822.test173 oneAtSign
            RFC822.test174 oneAtSign
            RFC822.test175 oneAtSign
            RFC822.test176 oneAtSign
            RFC822.test177 oneAtSign
            RFC822.test178 oneAtSign
            RFC822.test179 oneAtSign
            RFC822.test180 oneAtSign
            RFC822.test181 oneAtSign
            RFC822.test182 oneAtSign
            RFC822.test183 oneAtSign
            RFC822.test184 oneAtSign
            RFC822.test185 oneAtSign
            RFC822.test186 oneAtSign
            RFC822.test187 oneAtSign
            RFC822.test188 oneAtSign
            RFC822.test189 oneAtSign
            RFC822.test190 oneAtSign
            RFC822.test191 oneAtSign
            RFC822.test192 oneAtSign
            RFC822.test193 oneAtSign
            RFC822.test194 oneAtSign
            RFC822.test195 oneAtSign
            RFC822.test196 oneAtSign
            RFC822.test197 oneAtSign
            RFC822.test198 oneAtSign
            RFC822.test199 oneAtSign
            RFC822.test200 oneAtSign
            RFC822.test201 oneAtSign
            RFC822.test202 oneAtSign
            RFC822.test203 oneAtSign
            RFC822.test204 oneAtSign
            RFC822.test205 oneAtSign
            RFC822.test206 oneAtSign
            RFC822.test207 oneAtSign
            RFC822.test208 oneAtSign
            RFC822.test209 oneAtSign
            RFC822.test210 oneAtSign
            RFC822.test211 oneAtSign
            RFC822.test212 oneAtSign
            RFC822.test213 oneAtSign
            RFC822.test214 oneAtSign
            RFC822.test215 oneAtSign
            RFC822.test216 oneAtSign
            RFC822.test217 oneAtSign
            RFC822.test218 oneAtSign
            RFC822.test219 oneAtSign
            RFC822.test220 oneAtSign
            RFC822.test221 oneAtSign
            RFC822.test222 oneAtSign
            RFC822.test223 oneAtSign
            RFC822.test224 oneAtSign
            RFC822.test225 oneAtSign
            RFC822.test226 oneAtSign
            RFC822.test227 oneAtSign
            RFC822.test228 oneAtSign
            RFC822.test229 oneAtSign
            RFC822.test230 oneAtSign
            RFC822.test231 oneAtSign
            RFC822.test232 oneAtSign
            RFC822.test233 oneAtSign
            RFC822.test234 oneAtSign
            RFC822.test235 oneAtSign
            RFC822.test236 oneAtSign
            RFC822.test237 oneAtSign
            RFC822.test238 oneAtSign
            RFC822.test239 oneAtSign
            RFC822.test240 oneAtSign
            RFC822.test241 oneAtSign
            RFC822.test242 oneAtSign
            RFC822.test243 oneAtSign
            RFC822.test244 oneAtSign
            RFC822.test245 oneAtSign
            RFC822.test246 oneAtSign
            RFC822.test247 oneAtSign
            RFC822.test248 oneAtSign
            RFC822.test249 oneAtSign
            RFC822.test250 oneAtSign
            RFC822.test251 oneAtSign
            RFC822.test252 oneAtSign
            RFC822.test253 oneAtSign
            RFC822.test254 oneAtSign
            RFC822.test255 oneAtSign
            RFC822.test256 oneAtSign
            RFC822.test257 oneAtSign
            RFC822.test258 oneAtSign
            RFC822.test259 oneAtSign
            RFC822.test260 oneAtSign
            RFC822.test261 oneAtSign
            RFC822.test262 oneAtSign
            RFC822.test263 oneAtSign
            RFC822.test264 oneAtSign
            RFC822.test265 oneAtSign
            RFC822.test266 oneAtSign
            RFC822.test267 oneAtSign
            RFC822.test268 oneAtSign
            RFC822.test269 oneAtSign
            RFC822.test270 oneAtSign
            RFC822.test271 oneAtSign
            RFC822.test272 oneAtSign
            RFC822.test273 oneAtSign
            RFC822.test274 oneAtSign
            RFC822.test275 oneAtSign
            RFC822.test276 oneAtSign
            RFC822.test277 oneAtSign
            RFC822.test278 oneAtSign
            RFC822.test279 oneAtSign
            RFC822.test280 oneAtSign
            ]


text:
namespace Email.Tests

open Expecto

module RunTests =

    [<EntryPoint>]
    let main args =

        Tests.runTestsWithArgs defaultConfig args SimpleEmail.testSimpleEmail |> ignore
        Tests.runTestsWithArgs defaultConfig args ModerateEmail.testModerateEmail |> ignore
        Tests.runTestsWithArgs defaultConfig args ComplexEmail.testComplexEmail |> ignore
        Tests.runTestsWithArgs defaultConfig args OneAtSign.testOneAtSign |> ignore
        Tests.runTestsWithArgs defaultConfig args Ultimate.testUltimate |> ignore

        0