Simple example - how to validate hexadecimal input

Suppose we want to use Regex as a format validator of an input string. We will use the widely known definition.

Throughout history, there have been various ways to indicate a hex string. In the 1970's-1990's, a hex number was indicated with a leading '$' ie "$A0", but nowadays, a hex-number is notated with a leading '0x', ie "0xF5"

We will support both notations, and accept bytes (8-bit) and words (16-bit), and we don't care about upper/lower-case.

The patterns are composed from the ground up:.

  1. Define the alphabet;
  2. Define the value-format;
  3. Define the format of notation style in conjunction wih the value-format;
  4. Put it all together;
  5. Create a validation function which returns the hex-value when the input is valid.
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
38: 
39: 
40: 
41: 
open ReggerIt
open System.Text.RegularExpressions

//  Define the alphabet
let HexDigit = Between '0' '9' |||  Between 'A' 'F'

//  Define the value-format
let hexByte = RepeatExact 2 HexDigit
let hexWord = RepeatExact 4 HexDigit

//  With leading indicators - old fashioned
let formatHexByteAntique = Plain "$" + NamedGroup "value" hexByte
let formatHexWordAntique = Plain "$" + NamedGroup "value" hexWord

//  With leading indicators - modern
let formatHexByte = Plain "0x" + NamedGroup "value" hexByte
let formatHexWord = Plain "0x" + NamedGroup "value" hexWord

//  Put it all together
let pattern = formatHexByteAntique ||| formatHexWordAntique ||| formatHexByte ||| formatHexWord

let validate s =
    let parse = Regex.Match(s, pattern |> Convert.ToFullstringPattern, RegexOptions.IgnoreCase)
    if parse.Success then Some (parse.Groups.["value"].Value)
    else None

//  Some test values
[
    "$AF"
    "$C001"
    "0x9F"
    "0xAAF1"
    "$eF"
    "illegal"
]
|>  List.iter(fun input ->
    validate input
    |>  function
        |   Some v -> printfn "Hex value: %s" v
        |   None   -> printfn "Not a hex value: %s" input
)
module ReggerIt
namespace System
namespace System.Text
namespace System.Text.RegularExpressions
val HexDigit : RexPatt
val Between : char -> char -> RexPatt
val hexByte : RexPatt
val RepeatExact : int -> RexPatt -> RexPatt
val hexWord : RexPatt
val formatHexByteAntique : RexPatt
val Plain : string -> RexPatt
val NamedGroup : string -> RexPatt -> RexPatt
val formatHexWordAntique : RexPatt
val formatHexByte : RexPatt
val formatHexWord : RexPatt
val pattern : RexPatt
val validate : s:string -> string option
val s : string
val parse : Match
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
module Convert

from ReggerIt
val ToFullstringPattern : RexPatt -> string
type RegexOptions =
  | None = 0
  | IgnoreCase = 1
  | Multiline = 2
  | ExplicitCapture = 4
  | Compiled = 8
  | Singleline = 16
  | IgnorePatternWhitespace = 32
  | RightToLeft = 64
  | ECMAScript = 256
  | CultureInvariant = 512
field RegexOptions.IgnoreCase: RegexOptions = 1
property Group.Success: bool with get
union case Option.Some: Value: 'T -> Option<'T>
property Match.Groups: GroupCollection with get
union case Option.None: Option<'T>
Multiple items
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
    interface IReadOnlyList<'T>
    interface IReadOnlyCollection<'T>
    interface IEnumerable
    interface IEnumerable<'T>
    member GetReverseIndex : rank:int * offset:int -> int
    member GetSlice : startIndex:int option * endIndex:int option -> 'T list
    member Head : 'T
    member IsEmpty : bool
    member Item : index:int -> 'T with get
    member Length : int
    ...
val iter : action:('T -> unit) -> list:'T list -> unit
val input : string
val v : string
val printfn : format:Printf.TextWriterFormat<'T> -> 'T