csharpfftfsharpintegrationinterpolationlinear-algebramathdifferentiationmatrixnumericsrandomregressionstatisticsmathnet
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
160 lines
6.7 KiB
160 lines
6.7 KiB
// --------------------------------------------------------------------------------------
|
|
// F# Markdown (StringParsing.fs)
|
|
// (c) Tomas Petricek, 2012, Available under Apache 2.0 license.
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
module FSharp.Patterns
|
|
|
|
open System
|
|
open FSharp.Collections
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// Active patterns that simplify parsing of strings and lists of strings (lines)
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
module String =
|
|
/// Matches when a string is a whitespace or null
|
|
let (|WhiteSpace|_|) s =
|
|
if String.IsNullOrWhiteSpace(s) then Some() else None
|
|
|
|
/// Matches when a string does starts with non-whitespace
|
|
let (|Unindented|_|) (s:string) =
|
|
if not (String.IsNullOrWhiteSpace(s)) && s.TrimStart() = s then Some() else None
|
|
|
|
/// Returns a string trimmed from both start and end
|
|
let (|TrimBoth|) (text:string) = text.Trim()
|
|
/// Returns a string trimmed from the end
|
|
let (|TrimEnd|) (text:string) = text.TrimEnd()
|
|
/// Returns a string trimmed from the start
|
|
let (|TrimStart|) (text:string) = text.TrimStart()
|
|
|
|
/// Retrusn a string trimmed from the end using characters given as a parameter
|
|
let (|TrimEndUsing|) chars (text:string) = text.TrimEnd(Array.ofSeq chars)
|
|
|
|
/// Returns a string trimmed from the start together with
|
|
/// the number of skipped whitespace characters
|
|
let (|TrimStartAndCount|) (text:string) =
|
|
let trimmed = text.TrimStart()
|
|
text.Length - trimmed.Length, trimmed
|
|
|
|
/// Matches when a string starts with any of the specified sub-strings
|
|
let (|StartsWithAny|_|) (starts:seq<string>) (text:string) =
|
|
if starts |> Seq.exists (text.StartsWith) then Some() else None
|
|
/// Matches when a string starts with the specified sub-string
|
|
let (|StartsWith|_|) (start:string) (text:string) =
|
|
if text.StartsWith(start) then Some(text.Substring(start.Length)) else None
|
|
/// Matches when a string starts with the specified sub-string
|
|
/// The matched string is trimmed from all whitespace.
|
|
let (|StartsWithTrim|_|) (start:string) (text:string) =
|
|
if text.StartsWith(start) then Some(text.Substring(start.Length).Trim()) else None
|
|
|
|
/// Matches when a string starts with the given value and ends
|
|
/// with a given value (and returns the rest of it)
|
|
let (|StartsAndEndsWith|_|) (starts, ends) (s:string) =
|
|
if s.StartsWith(starts) && s.EndsWith(ends) &&
|
|
s.Length >= starts.Length + ends.Length then
|
|
Some(s.Substring(starts.Length, s.Length - starts.Length - ends.Length))
|
|
else None
|
|
|
|
/// Matches when a string starts with the given value and ends
|
|
/// with a given value (and returns trimmed body)
|
|
let (|StartsAndEndsWithTrim|_|) args = function
|
|
| StartsAndEndsWith args (TrimBoth res) -> Some res
|
|
| _ -> None
|
|
|
|
/// Matches when a string starts with a non-zero number of complete
|
|
/// repetitions of the specified parameter (and returns the number
|
|
/// of repetitions, together with the rest of the string)
|
|
///
|
|
/// let (StartsWithRepeated "/\" (2, " abc")) = "/\/\ abc"
|
|
///
|
|
let (|StartsWithRepeated|_|) (repeated:string) (text:string) =
|
|
let rec loop i =
|
|
if i = text.Length then i
|
|
elif text.[i] <> repeated.[i % repeated.Length] then i
|
|
else loop (i + 1)
|
|
|
|
let n = loop 0
|
|
if n = 0 || n % repeated.Length <> 0 then None
|
|
else Some(n/repeated.Length, text.Substring(n, text.Length - n))
|
|
|
|
/// Matches when a string starts with a sub-string wrapped using the
|
|
/// opening and closing sub-string specified in the parameter.
|
|
/// For example "[aa]bc" is wrapped in [ and ] pair. Returns the wrapped
|
|
/// text together with the rest.
|
|
let (|StartsWithWrapped|_|) (starts:string, ends:string) (text:string) =
|
|
if text.StartsWith(starts) then
|
|
let id = text.IndexOf(ends, starts.Length)
|
|
if id >= 0 then
|
|
let wrapped = text.Substring(starts.Length, id - starts.Length)
|
|
let rest = text.Substring(id + ends.Length, text.Length - id - ends.Length)
|
|
Some(wrapped, rest)
|
|
else None
|
|
else None
|
|
|
|
/// Matches when a string consists of some number of
|
|
/// complete repetitions of a specified sub-string.
|
|
let (|EqualsRepeated|_|) repeated = function
|
|
| StartsWithRepeated repeated (n, "") -> Some()
|
|
| _ -> None
|
|
|
|
module List =
|
|
/// Matches a list if it starts with a sub-list that is delimited
|
|
/// using the specified delimiters. Returns a wrapped list and the rest.
|
|
let inline (|DelimitedWith|_|) startl endl input =
|
|
if List.startsWith startl input then
|
|
match List.partitionUntilEquals endl (List.skip startl.Length input) with
|
|
| Some(pre, post) -> Some(pre, List.skip endl.Length post)
|
|
| None -> None
|
|
else None
|
|
|
|
/// Matches a list if it starts with a sub-list that is delimited
|
|
/// using the specified delimiter. Returns a wrapped list and the rest.
|
|
let inline (|Delimited|_|) str = (|DelimitedWith|_|) str str
|
|
|
|
/// Matches a list if it starts with a bracketed list. Nested brackets
|
|
/// are skipped (by counting opening and closing brackets) and can be
|
|
/// escaped using the '\' symbol.
|
|
let (|BracketDelimited|_|) startc endc input =
|
|
let rec loop acc count = function
|
|
| '\\'::x::xs when x = endc -> loop (x::acc) count xs
|
|
| x::xs when x = endc && count = 0 -> Some(List.rev acc, xs)
|
|
| x::xs when x = endc -> loop (x::acc) (count - 1) xs
|
|
| x::xs when x = startc -> loop (x::acc) (count + 1) xs
|
|
| x::xs -> loop (x::acc) count xs
|
|
| [] -> None
|
|
match input with
|
|
| x::xs when x = startc -> loop [] 0 xs
|
|
| _ -> None
|
|
|
|
/// Retruns a list of characters as a string.
|
|
let (|AsString|) chars = String(Array.ofList chars)
|
|
|
|
module Lines =
|
|
/// Removes blank lines from the start and the end of a list
|
|
let (|TrimBlank|) lines =
|
|
lines
|
|
|> List.skipWhile String.IsNullOrWhiteSpace |> List.rev
|
|
|> List.skipWhile String.IsNullOrWhiteSpace |> List.rev
|
|
|
|
/// Matches when there are some lines at the beginning that are
|
|
/// either empty (or whitespace) or start with the specified string.
|
|
/// Returns all such lines from the beginning until a different line.
|
|
let (|TakeStartingWithOrBlank|_|) start input =
|
|
match List.partitionWhile (fun s ->
|
|
String.IsNullOrWhiteSpace s || s.StartsWith(start)) input with
|
|
| matching, rest when matching <> [] -> Some(matching, rest)
|
|
| _ -> None
|
|
|
|
/// Removes whitespace lines from the beginning of the list
|
|
let (|TrimBlankStart|) = List.skipWhile (String.IsNullOrWhiteSpace)
|
|
|
|
|
|
/// Parameterized pattern that assigns the specified value to the
|
|
/// first component of a tuple. Usage:
|
|
///
|
|
/// match str with
|
|
/// | Let 1 (n, "one") | Let 2 (n, "two") -> n
|
|
///
|
|
let (|Let|) a b = (a, b)
|
|
|
|
|