turtle-1.3.1: Shell programming, Haskell-style

Safe Haskell | None
Language | Haskell2010

Turtle.Pattern

Contents

- Pattern
- Primitive patterns
- Character patterns
- Numbers
- Combinators
- High-efficiency primitives

Description

Use this module to either:

- match `Text` with light-weight backtracking patterns, or:
- parse structured values from `Text`.

Example usage:

    >>> :set -XOverloadedStrings
    >>> match ("can" <|> "cat") "cat"
    ["cat"]
    >>> match ("can" <|> "cat") "dog"
    []
    >>> match (decimal `sepBy` ",") "1,2,3"
    [[1,2,3]]

This pattern has unlimited backtracking, and will return as many solutions as possible:

    >>> match (prefix (star anyChar)) "123"
    ["123","12","1",""]

Use `do` notation to structure more complex patterns:

    >>> :{
    let bit = ("0" *> pure False) <|> ("1" *> pure True) :: Pattern Bool;
        portableBitMap = do
            { "P1"
            ; width  <- spaces1 *> decimal
            ; height <- spaces1 *> decimal
            ; count width (count height (spaces1 *> bit))
            };
    in  match (prefix portableBitMap) "P1\n2 2\n0 0\n1 0\n"
    :}
    [[[False,False],[True,False]]]

Synopsis

- data Pattern a
- match :: Pattern a -> Text -> [a]
- anyChar :: Pattern Char
- eof :: Pattern ()
- dot :: Pattern Char
- satisfy :: (Char -> Bool) -> Pattern Char
- char :: Char -> Pattern Char
- notChar :: Char -> Pattern Char
- text :: Text -> Pattern Text
- asciiCI :: Text -> Pattern Text
- oneOf :: [Char] -> Pattern Char
- noneOf :: [Char] -> Pattern Char
- space :: Pattern Char
- spaces :: Pattern Text
- spaces1 :: Pattern Text
- tab :: Pattern Char
- newline :: Pattern Char
- crlf :: Pattern Text
- upper :: Pattern Char
- lower :: Pattern Char
- alphaNum :: Pattern Char
- letter :: Pattern Char
- digit :: Pattern Char
- hexDigit :: Pattern Char
- octDigit :: Pattern Char
- decimal :: Num n => Pattern n
- signed :: Num a => Pattern a -> Pattern a
- prefix :: Pattern a -> Pattern a
- suffix :: Pattern a -> Pattern a
- has :: Pattern a -> Pattern a
- begins :: Pattern Text -> Pattern Text
- ends :: Pattern Text -> Pattern Text
- contains :: Pattern Text -> Pattern Text
- invert :: Pattern a -> Pattern ()
- once :: Pattern Char -> Pattern Text
- star :: Pattern Char -> Pattern Text
- plus :: Pattern Char -> Pattern Text
- selfless :: Pattern a -> Pattern a
- choice :: [Pattern a] -> Pattern a
- count :: Int -> Pattern a -> Pattern [a]
- lowerBounded :: Int -> Pattern a -> Pattern [a]
- upperBounded :: Int -> Pattern a -> Pattern [a]
- bounded :: Int -> Int -> Pattern a -> Pattern [a]
- option :: Monoid a => Pattern a -> Pattern a
- between :: Pattern a -> Pattern b -> Pattern c -> Pattern c
- skip :: Pattern a -> Pattern ()
- within :: Int -> Pattern a -> Pattern a
- fixed :: Int -> Pattern a -> Pattern a
- sepBy :: Pattern a -> Pattern b -> Pattern [a]
- sepBy1 :: Pattern a -> Pattern b -> Pattern [a]
- chars :: Pattern Text
- chars1 :: Pattern Text

# Pattern

data Pattern a #

A fully backtracking pattern that parses an `'a'` from some `Text`

Instances

Monad Pattern # |
Methods (>>=) :: Pattern a -> (a -> Pattern b) -> Pattern b # (>>) :: Pattern a -> Pattern b -> Pattern b # return :: a -> Pattern a # fail :: String -> Pattern a #
Functor Pattern # |
Methods fmap :: (a -> b) -> Pattern a -> Pattern b # (<$) :: a -> Pattern b -> Pattern a #
Applicative Pattern # |
Methods pure :: a -> Pattern a # (<*>) :: Pattern (a -> b) -> Pattern a -> Pattern b # (*>) :: Pattern a -> Pattern b -> Pattern b # (<*) :: Pattern a -> Pattern b -> Pattern a #
Alternative Pattern # |
Methods empty :: Pattern a # (<|>) :: Pattern a -> Pattern a -> Pattern a # some :: Pattern a -> Pattern [a] # many :: Pattern a -> Pattern [a] #
MonadPlus Pattern # |
Methods mzero :: Pattern a # mplus :: Pattern a -> Pattern a -> Pattern a #
Monoid a => Num (Pattern a) # | Pattern forms a semiring, this is the closest approximation
Methods (+) :: Pattern a -> Pattern a -> Pattern a # (-) :: Pattern a -> Pattern a -> Pattern a # (*) :: Pattern a -> Pattern a -> Pattern a # negate :: Pattern a -> Pattern a # abs :: Pattern a -> Pattern a # signum :: Pattern a -> Pattern a # fromInteger :: Integer -> Pattern a #
(~) * a Text => IsString (Pattern a) # |
Methods fromString :: String -> Pattern a #
Monoid a => Monoid (Pattern a) # |
Methods mempty :: Pattern a # mappend :: Pattern a -> Pattern a -> Pattern a # mconcat :: [Pattern a] -> Pattern a #

match :: Pattern a -> Text -> [a] #

Match a `Pattern` against a `Text` input, returning all possible solutions

The `Pattern` must match the entire `Text`

# Primitive patterns

anyChar :: Pattern Char #

Match any character

    >>> match anyChar "1"
    "1"
    >>> match anyChar ""
    ""

eof :: Pattern () #

Matches the end of input

    >>> match eof "1"
    []
    >>> match eof ""
    [()]

# Character patterns

dot :: Pattern Char #

Synonym for `anyChar`

satisfy :: (Char -> Bool) -> Pattern Char #

Match any character that satisfies the given predicate

    >>> match (satisfy (== '1')) "1"
    "1"
    >>> match (satisfy (== '2')) "1"
    ""

char :: Char -> Pattern Char #

Match a specific character

    >>> match (char '1') "1"
    "1"
    >>> match (char '2') "1"
    ""

notChar :: Char -> Pattern Char #

Match any character except the given one

    >>> match (notChar '2') "1"
    "1"
    >>> match (notChar '1') "1"
    ""

text :: Text -> Pattern Text #

Match a specific string

    >>> match (text "123") "123"
    ["123"]

You can also omit the `text` function if you enable the `OverloadedStrings` extension:

    >>> match "123" "123"
    ["123"]

asciiCI :: Text -> Pattern Text #

Match a specific string in a case-insensitive way

This only handles ASCII strings

    >>> match (asciiCI "abc") "ABC"
    ["ABC"]

oneOf :: [Char] -> Pattern Char #

Match any one of the given characters

    >>> match (oneOf "1a") "1"
    "1"
    >>> match (oneOf "2a") "1"
    ""

noneOf :: [Char] -> Pattern Char #

Match anything other than the given characters

    >>> match (noneOf "2a") "1"
    "1"
    >>> match (noneOf "1a") "1"
    ""

space :: Pattern Char #

Match a whitespace character

    >>> match space " "
    " "
    >>> match space "1"
    ""

spaces :: Pattern Text #

Match zero or more whitespace characters

    >>> match spaces "  "
    ["  "]
    >>> match spaces ""
    [""]

spaces1 :: Pattern Text #

Match one or more whitespace characters

    >>> match spaces1 "  "
    ["  "]
    >>> match spaces1 ""
    []

tab :: Pattern Char #

Match the tab character (`'t'`)

    >>> match tab "\t"
    "\t"
    >>> match tab " "
    ""

newline :: Pattern Char #

Match the newline character (`'n'`)

    >>> match newline "\n"
    "\n"
    >>> match newline " "
    ""

crlf :: Pattern Text #

Matches a carriage return (`'r'`) followed by a newline (`'n'`)

    >>> match crlf "\r\n"
    ["\r\n"]
    >>> match crlf "\n\r"
    []

upper :: Pattern Char #

Match an uppercase letter

    >>> match upper "A"
    "A"
    >>> match upper "a"
    ""

lower :: Pattern Char #

Match a lowercase letter

    >>> match lower "a"
    "a"
    >>> match lower "A"
    ""

alphaNum :: Pattern Char #

Match a letter or digit

    >>> match alphaNum "1"
    "1"
    >>> match alphaNum "a"
    "a"
    >>> match alphaNum "A"
    "A"
    >>> match alphaNum "."
    ""

letter :: Pattern Char #

Match a letter

    >>> match letter "A"
    "A"
    >>> match letter "a"
    "a"
    >>> match letter "1"
    ""

digit :: Pattern Char #

Match a digit

    >>> match digit "1"
    "1"
    >>> match digit "a"
    ""

hexDigit :: Pattern Char #

Match a hexadecimal digit

    >>> match hexDigit "1"
    "1"
    >>> match hexDigit "A"
    "A"
    >>> match hexDigit "a"
    "a"
    >>> match hexDigit "g"
    ""

octDigit :: Pattern Char #

Match an octal digit

    >>> match octDigit "1"
    "1"
    >>> match octDigit "9"
    ""

# Numbers

decimal :: Num n => Pattern n #

Match an unsigned decimal number

    >>> match decimal  "123"
    [123]
    >>> match decimal "-123"
    []

signed :: Num a => Pattern a -> Pattern a #

Transform a numeric parser to accept an optional leading `'+'` or `'-'` sign

    >>> match (signed decimal) "+123"
    [123]
    >>> match (signed decimal) "-123"
    [-123]
    >>> match (signed decimal)  "123"
    [123]

# Combinators

prefix :: Pattern a -> Pattern a #

Use this to match the prefix of a string

    >>> match         "A"  "ABC"
    []
    >>> match (prefix "A") "ABC"
    ["A"]

suffix :: Pattern a -> Pattern a #

Use this to match the suffix of a string

    >>> match         "C"  "ABC"
    []
    >>> match (suffix "C") "ABC"
    ["C"]

has :: Pattern a -> Pattern a #

Use this to match the interior of a string

    >>> match      "B"  "ABC"
    []
    >>> match (has "B") "ABC"
    ["B"]

begins :: Pattern Text -> Pattern Text #

Match the entire string if it begins with the given pattern

This returns the entire string, not just the matched prefix

    >>> match (begins  "A"             ) "ABC"
    ["ABC"]
    >>> match (begins ("A" *> pure "1")) "ABC"
    ["1BC"]

ends :: Pattern Text -> Pattern Text #

Match the entire string if it ends with the given pattern

This returns the entire string, not just the matched prefix

    >>> match (ends  "C"             ) "ABC"
    ["ABC"]
    >>> match (ends ("C" *> pure "1")) "ABC"
    ["AB1"]

contains :: Pattern Text -> Pattern Text #

Match the entire string if it contains the given pattern

This returns the entire string, not just the interior pattern

    >>> match (contains  "B"             ) "ABC"
    ["ABC"]
    >>> match (contains ("B" *> pure "1")) "ABC"
    ["A1C"]

invert :: Pattern a -> Pattern () #

`(`invert` p)` succeeds if `p` fails and fails if `p` succeeds

    >>> match (invert "A") "A"
    []
    >>> match (invert "A") "B"
    [()]

once :: Pattern Char -> Pattern Text #

Match a `Char`, but return `Text`

    >>> match (once (char '1')) "1"
    ["1"]
    >>> match (once (char '1')) ""
    []

star :: Pattern Char -> Pattern Text #

Parse 0 or more occurrences of the given character

    >>> match (star anyChar) "123"
    ["123"]
    >>> match (star anyChar) ""
    [""]

See also: `chars`

plus :: Pattern Char -> Pattern Text #

Parse 1 or more occurrences of the given character

    >>> match (plus digit) "123"
    ["123"]
    >>> match (plus digit) ""
    []

See also: `chars1`

selfless :: Pattern a -> Pattern a #

Patterns that match multiple times are greedy by default, meaning that they try to match as many times as possible. The `selfless` combinator makes a pattern match as few times as possible

This only changes the order in which solutions are returned, by prioritizing less greedy solutions

    >>> match (prefix (selfless (some anyChar))) "123"
    ["1","12","123"]
    >>> match (prefix           (some anyChar) ) "123"
    ["123","12","1"]

choice :: [Pattern a] -> Pattern a #

Apply the patterns in the list in order, until one of them succeeds

    >>> match (choice ["cat", "dog", "egg"]) "egg"
    ["egg"]
    >>> match (choice ["cat", "dog", "egg"]) "cat"
    ["cat"]
    >>> match (choice ["cat", "dog", "egg"]) "fan"
    []

count :: Int -> Pattern a -> Pattern [a] #

Apply the given pattern a fixed number of times, collecting the results

    >>> match (count 3 anyChar) "123"
    ["123"]
    >>> match (count 4 anyChar) "123"
    []

lowerBounded :: Int -> Pattern a -> Pattern [a] #

Apply the given pattern at least the given number of times, collecting the results

    >>> match (lowerBounded 5 dot) "123"
    []
    >>> match (lowerBounded 2 dot) "123"
    ["123"]

upperBounded :: Int -> Pattern a -> Pattern [a] #

Apply the given pattern 0 or more times, up to a given bound, collecting the results

    >>> match (upperBounded 5 dot) "123"
    ["123"]
    >>> match (upperBounded 2 dot) "123"
    []
    >>> match ((,) <$> upperBounded 2 dot <*> chars) "123"
    [("12","3"),("1","23")]

bounded :: Int -> Int -> Pattern a -> Pattern [a] #

Apply the given pattern a number of times restricted by given lower and upper bounds, collecting the results

    >>> match (bounded 2 5 "cat") "catcatcat"
    [["cat","cat","cat"]]
    >>> match (bounded 2 5 "cat") "cat"
    []
    >>> match (bounded 2 5 "cat") "catcatcatcatcatcat"
    []

`bounded` could be implemented naively as follows:

    bounded m n p = do
      x <- choice (map pure [m..n])
      count x p

option :: Monoid a => Pattern a -> Pattern a #

Transform a parser to a succeed with an empty value instead of failing

See also: `optional`

    >>> match (option "1" <> "2") "12"
    ["12"]
    >>> match (option "1" <> "2") "2"
    ["2"]

between :: Pattern a -> Pattern b -> Pattern c -> Pattern c #

`(between open close p)` matches `'p'` in between `'open'` and `'close'`

    >>> match (between (char '(') (char ')') (star anyChar)) "(123)"
    ["123"]
    >>> match (between (char '(') (char ')') (star anyChar)) "(123"
    []

skip :: Pattern a -> Pattern () #

Discard the pattern's result

    >>> match (skip anyChar) "1"
    [()]
    >>> match (skip anyChar) ""
    []

within :: Int -> Pattern a -> Pattern a #

Restrict the pattern to consume no more than the given number of characters

    >>> match (within 2 decimal) "12"
    [12]
    >>> match (within 2 decimal) "1"
    [1]
    >>> match (within 2 decimal) "123"
    []

fixed :: Int -> Pattern a -> Pattern a #

Require the pattern to consume exactly the given number of characters

    >>> match (fixed 2 decimal) "12"
    [12]
    >>> match (fixed 2 decimal) "1"
    []

sepBy :: Pattern a -> Pattern b -> Pattern [a] #

`p `sepBy` sep` matches zero or more occurrences of `p` separated by `sep`

    >>> match (decimal `sepBy` char ',') "1,2,3"
    [[1,2,3]]
    >>> match (decimal `sepBy` char ',') ""
    [[]]

sepBy1 :: Pattern a -> Pattern b -> Pattern [a] #

`p `sepBy1` sep` matches one or more occurrences of `p` separated by `sep`

    >>> match (decimal `sepBy1` ",") "1,2,3"
    [[1,2,3]]
    >>> match (decimal `sepBy1` ",") ""
    []

# High-efficiency primitives

chars :: Pattern Text #

Like `star dot` or `star anyChar`, except more efficient

chars1 :: Pattern Text #

Like `plus dot` or `plus anyChar`, except more efficient