3 people like it.

A Simple Parser

This is a basic parser that I wrote which takes heavily from the examples in Dom Syme's Expert F# book and http://fsharpforfunandprofit.com/posts/pattern-matching-command-line/

  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: 
 42: 
 43: 
 44: 
 45: 
 46: 
 47: 
 48: 
 49: 
 50: 
 51: 
 52: 
 53: 
 54: 
 55: 
 56: 
 57: 
 58: 
 59: 
 60: 
 61: 
 62: 
 63: 
 64: 
 65: 
 66: 
 67: 
 68: 
 69: 
 70: 
 71: 
 72: 
 73: 
 74: 
 75: 
 76: 
 77: 
 78: 
 79: 
 80: 
 81: 
 82: 
 83: 
 84: 
 85: 
 86: 
 87: 
 88: 
 89: 
 90: 
 91: 
 92: 
 93: 
 94: 
 95: 
 96: 
 97: 
 98: 
 99: 
100: 
101: 
102: 
103: 
104: 
105: 
106: 
107: 
108: 
109: 
110: 
111: 
112: 
113: 
114: 
115: 
116: 
117: 
118: 
119: 
120: 
121: 
122: 
123: 
124: 
let splitString (s : string) = 
    s.Split([|' '|])

type Token = 
    |SOURCE
    |REGISTRY
    |ZIP
    |SERVICE
    |DESTINATION
    |MAXDAYS
    |SMTPSERVER
    |FROMEMAIL
    |SUCCESSEMAIL
    |FAILUREEMAIL
    |VALUE of string

let tokenize (args : string[]) = 
    [for x in args do
        let token = 
            match x with
            | "-o" -> SOURCE
            | "-r" -> REGISTRY
            | "-z" -> ZIP
            | "-v" -> SERVICE
            | "-d" -> DESTINATION
            | "-m" -> MAXDAYS
            | "-t" -> SMTPSERVER
            | "-e" -> FROMEMAIL
            | "-s" -> SUCCESSEMAIL
            | "-f" -> FAILUREEMAIL
            | _ -> VALUE x
        yield token]

type Zip = ZipFiles | DoNotZipFiles

type Options = {
    Source : string;
    Registry : string;
    Zip : Zip;
    Service : string;
    Destination : string;
    MaxDays : int;
    SmtpServer : string;
    FromEmail : string;
    SuccessEmail : string;
    FailureEmail : string;
    }

let isWholeNumber s = String.forall (fun c -> System.Char.IsDigit(c)) s

// Strips VALUE tokens from top of list, returning the rest of the list
let returnNonValueTail tokenList =
    tokenList
    |>List.toSeq
    |>Seq.skipWhile (fun t -> match t with VALUE y -> true | _ -> false)
    |>Seq.toList

// Takes VALUE tokens from the top of the list, contatenates their associated strings and returns the contatenated string.
let returnConcatHeadValues tokenList =
    tokenList
    |> List.toSeq
    |> Seq.takeWhile (fun t -> match t with VALUE y -> true | _ -> false)
    |> Seq.fold (fun acc elem -> match elem with VALUE y -> acc + " " + y | _ -> acc) " "
    |> fun s -> s.Trim()

let rec parseTokenListRec tokenList optionsSoFar =
    match tokenList with
    | [] -> optionsSoFar
    | SOURCE::t -> 
        match t with
        | VALUE x::tt -> parseTokenListRec (returnNonValueTail t) {optionsSoFar with Source = (returnConcatHeadValues t)}
        | _ -> failwith "Expected a value after the source argument."
    | REGISTRY::t ->
        match t with
        | VALUE x::tt -> parseTokenListRec (returnNonValueTail t) {optionsSoFar with Registry = (returnConcatHeadValues t)}
        | _ -> failwith "Expected a value after the registry argument."
    | ZIP::t -> parseTokenListRec t {optionsSoFar with Zip = ZipFiles}
    | SERVICE::t ->
        match t with
        | VALUE x::tt -> parseTokenListRec (returnNonValueTail t) {optionsSoFar with Service = (returnConcatHeadValues t)}
        | _ -> failwith "Expected a value after the service argument."
    | DESTINATION::t ->
        match t with
        | VALUE x::tt -> parseTokenListRec (returnNonValueTail t) {optionsSoFar with Destination = (returnConcatHeadValues t)}
        | _ -> failwith "Expected a value after the destination argument."
    | MAXDAYS::t ->
        match t with
        |VALUE x::tt when (isWholeNumber x) -> parseTokenListRec tt {optionsSoFar with MaxDays = int x}
        | _ -> failwith "Expected a whole number to be supplied after the maxdays argument."
    | SMTPSERVER::t ->
        match t with
        | VALUE x::tt -> parseTokenListRec (returnNonValueTail t) {optionsSoFar with SmtpServer = (returnConcatHeadValues t)}
        | _ -> failwith "Expected a value after the smtp server argument."
    | FROMEMAIL::t ->
        match t with
        | VALUE x::tt -> parseTokenListRec (returnNonValueTail t) {optionsSoFar with FromEmail = (returnConcatHeadValues t)}
        | _ -> failwith "Expected a value after the from email argument."
    | SUCCESSEMAIL::t ->
        match t with
        | VALUE x::tt -> parseTokenListRec (returnNonValueTail t) {optionsSoFar with SuccessEmail = (returnConcatHeadValues t)}
        | _ -> failwith "Expected a value after the success email argument."
    | FAILUREEMAIL::t ->
        match t with
        | VALUE x::tt -> parseTokenListRec (returnNonValueTail t) {optionsSoFar with FailureEmail = (returnConcatHeadValues t)}
        | _ -> failwith "Expected a value after the failure email argument."
    | VALUE x::t -> failwith (sprintf "Encountered a value ('%s') without an associated argument." x)

let parseArgs args =
    let tokenList = tokenize(args)

    let defaultOptions = {
        Source = "";
        Registry = "";
        Zip = DoNotZipFiles;
        Service = "";
        Destination = "";
        MaxDays = 0;
        SmtpServer = "";
        FromEmail = "";
        SuccessEmail = "";
        FailureEmail = "";
        }

    parseTokenListRec tokenList defaultOptions
val splitString : s:string -> string []

Full name: Script.splitString
val s : string
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string
System.String.Split([<System.ParamArray>] separator: char []) : string []
System.String.Split(separator: string [], options: System.StringSplitOptions) : string []
System.String.Split(separator: char [], options: System.StringSplitOptions) : string []
System.String.Split(separator: char [], count: int) : string []
System.String.Split(separator: string [], count: int, options: System.StringSplitOptions) : string []
System.String.Split(separator: char [], count: int, options: System.StringSplitOptions) : string []
type Token =
  | SOURCE
  | REGISTRY
  | ZIP
  | SERVICE
  | DESTINATION
  | MAXDAYS
  | SMTPSERVER
  | FROMEMAIL
  | SUCCESSEMAIL
  | FAILUREEMAIL
  ...

Full name: Script.Token
union case Token.SOURCE: Token
union case Token.REGISTRY: Token
union case Token.ZIP: Token
union case Token.SERVICE: Token
union case Token.DESTINATION: Token
union case Token.MAXDAYS: Token
union case Token.SMTPSERVER: Token
union case Token.FROMEMAIL: Token
union case Token.SUCCESSEMAIL: Token
union case Token.FAILUREEMAIL: Token
union case Token.VALUE: string -> Token
val tokenize : args:string [] -> Token list

Full name: Script.tokenize
val args : string []
val x : string
val token : Token
type Zip =
  | ZipFiles
  | DoNotZipFiles

Full name: Script.Zip
union case Zip.ZipFiles: Zip
union case Zip.DoNotZipFiles: Zip
type Options =
  {Source: string;
   Registry: string;
   Zip: Zip;
   Service: string;
   Destination: string;
   MaxDays: int;
   SmtpServer: string;
   FromEmail: string;
   SuccessEmail: string;
   FailureEmail: string;}

Full name: Script.Options
Options.Source: string
Options.Registry: string
Multiple items
Options.Zip: Zip

--------------------
type Zip =
  | ZipFiles
  | DoNotZipFiles

Full name: Script.Zip
Options.Service: string
Options.Destination: string
Options.MaxDays: int
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
Options.SmtpServer: string
Options.FromEmail: string
Options.SuccessEmail: string
Options.FailureEmail: string
val isWholeNumber : s:string -> bool

Full name: Script.isWholeNumber
module String

from Microsoft.FSharp.Core
val forall : predicate:(char -> bool) -> str:string -> bool

Full name: Microsoft.FSharp.Core.String.forall
val c : char
namespace System
type Char =
  struct
    member CompareTo : value:obj -> int + 1 overload
    member Equals : obj:obj -> bool + 1 overload
    member GetHashCode : unit -> int
    member GetTypeCode : unit -> TypeCode
    member ToString : unit -> string + 1 overload
    static val MaxValue : char
    static val MinValue : char
    static member ConvertFromUtf32 : utf32:int -> string
    static member ConvertToUtf32 : highSurrogate:char * lowSurrogate:char -> int + 1 overload
    static member GetNumericValue : c:char -> float + 1 overload
    ...
  end

Full name: System.Char
System.Char.IsDigit(c: char) : bool
System.Char.IsDigit(s: string, index: int) : bool
val returnNonValueTail : tokenList:Token list -> Token list

Full name: Script.returnNonValueTail
val tokenList : Token list
Multiple items
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
  interface IEnumerable
  interface IEnumerable<'T>
  member Head : 'T
  member IsEmpty : bool
  member Item : index:int -> 'T with get
  member Length : int
  member Tail : 'T list
  static member Cons : head:'T * tail:'T list -> 'T list
  static member Empty : 'T list

Full name: Microsoft.FSharp.Collections.List<_>
val toSeq : list:'T list -> seq<'T>

Full name: Microsoft.FSharp.Collections.List.toSeq
module Seq

from Microsoft.FSharp.Collections
val skipWhile : predicate:('T -> bool) -> source:seq<'T> -> seq<'T>

Full name: Microsoft.FSharp.Collections.Seq.skipWhile
val t : Token
val y : string
val toList : source:seq<'T> -> 'T list

Full name: Microsoft.FSharp.Collections.Seq.toList
val returnConcatHeadValues : tokenList:Token list -> string

Full name: Script.returnConcatHeadValues
val takeWhile : predicate:('T -> bool) -> source:seq<'T> -> seq<'T>

Full name: Microsoft.FSharp.Collections.Seq.takeWhile
val fold : folder:('State -> 'T -> 'State) -> state:'State -> source:seq<'T> -> 'State

Full name: Microsoft.FSharp.Collections.Seq.fold
val acc : string
val elem : Token
System.String.Trim() : string
System.String.Trim([<System.ParamArray>] trimChars: char []) : string
val parseTokenListRec : tokenList:Token list -> optionsSoFar:Options -> Options

Full name: Script.parseTokenListRec
val optionsSoFar : Options
val t : Token list
val tt : Token list
val failwith : message:string -> 'T

Full name: Microsoft.FSharp.Core.Operators.failwith
val sprintf : format:Printf.StringFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
val parseArgs : args:string [] -> Options

Full name: Script.parseArgs
val defaultOptions : Options
Raw view Test code New version

More information

Link:http://fssnip.net/nU
Posted:9 years ago
Author:Joe C
Tags: parse , parsing , lexing