module FParsecCombinators open FParsec open System exception Error of string type Arg = | WithType of string * string | NoType of string type LocaleContents = | Argument of Arg | Text of string | Line of LocaleContents list | Comment of string | NewLine type Locale = | Entry of string * LocaleContents | IgnoreEntry of LocaleContents (* Utilities *) let brackets = isAnyOf ['{';'}'] (* non new line space *) let regSpace = manySatisfy (isAnyOf [' ';'\t']) (* any string literal that is charcaters *) let phrase = many1Chars (satisfy (isNoneOf ['{';'\n'])) let singleWord = many1Chars (satisfy isDigit <|> satisfy isLetter <|> satisfy (isAnyOf ['_';'-'])) (* utility method to set between parsers space agnostic *) let between x y p = pstring x >>. regSpace >>. p .>> regSpace .>> pstring y (* Arguments *) let argDelim = pstring ":" let argumentNoType = singleWord |>> NoType let argumentWithType = singleWord .>>.? (argDelim >>. singleWord) |>> WithType let arg = (argumentWithType <|> argumentNoType) |> between "{" "}" |>> Argument (* Text Elements *) let textElement = phrase |>> Text let newLine = (unicodeNewline >>? regSpace >>? pstring "=") >>% NewLine let line = many (regSpace >>? (arg <|> textElement <|> newLine)) |>> Line (* Entries *) let delim = regSpace >>. pstring "=" .>> regSpace let identifier = regSpace >>. singleWord .>> delim let localeElement = unicodeSpaces >>? (identifier .>>. line .>> skipRestOfLine true) |>> Entry (* Comments *) let comment = pstring "#" >>. restOfLine false |>> Comment let commentElement = unicodeSpaces >>? comment |>> IgnoreEntry (* Full Locale *) let locale = many (commentElement <|> localeElement) .>> eof let test input = match run locale input with | Success(r,_,_) -> r | Failure(r,_,_) -> Console.WriteLine r raise (Error(r))