Control.Applicative.Combinators

parser-combinators-1.3.0: Lightweight package providing commonly useful parser combinators

Copyright | © 2017–present Mark Karpov
License | BSD 3 clause
Maintainer | Mark Karpov <markkarpov92@gmail.com>
Stability | experimental
Portability | portable
Safe Haskell | Safe-Inferred
Language | Haskell2010

Control.Applicative.Combinators

Contents

- Re-exports from Control.Applicative
- Original combinators

Description

The module provides parser combinators defined for instances of `Applicative` and `Alternative`. It also re-exports functions that are commonly used in parsing from Control.Applicative with additional parsing-related comments added.

Due to the nature of the `Applicative` and `Alternative` abstractions, they are prone to memory leaks and not as efficient as their monadic counterparts. Although all the combinators we provide in this module are perfectly expressible in terms of `Applicative` and `Alternative`, please prefer Control.Monad.Combinators instead when possible.

If you wish that the combinators that cannot return empty lists return values of the `NonEmpty` data type, use the Control.Applicative.Combinators.NonEmpty module.

### A note on backtracking

Certain parsing libraries, such as Megaparsec, do not backtrack every branch of parsing automatically for the sake of performance and better error messages. They typically backtrack only “atomic” parsers, e.g. those that match a token or several tokens in a row. To backtrack an arbitrary complex parser/branch, a special combinator should be used, typically called `try`. Combinators in this module are defined in terms `Applicative` and `Alternative` operations. Being quite abstract, they cannot know anything about inner workings of any concrete parsing library, and so they cannot use `try`.

The essential feature of the `Alternative` type class is the `(`<|>`)` operator that allows to express choice. In libraries that do not backtrack everything automatically, the choice operator and everything that is build on top of it require the parser on the left hand side to backtrack in order for the alternative branch of parsing to be tried. Thus it is the responsibility of the programmer to wrap more complex, composite parsers in `try` to achieve correct behavior.

Synopsis

- (<|>) :: Alternative f => f a -> f a -> f a
- many :: Alternative f => f a -> f [a]
- some :: Alternative f => f a -> f [a]
- optional :: Alternative f => f a -> f (Maybe a)
- empty :: Alternative f => f a
- between :: Applicative m => m open -> m close -> m a -> m a
- choice :: (Foldable f, Alternative m) => f (m a) -> m a
- count :: Applicative m => Int -> m a -> m [a]
- count' :: Alternative m => Int -> Int -> m a -> m [a]
- eitherP :: Alternative m => m a -> m b -> m (Either a b)
- endBy :: Alternative m => m a -> m sep -> m [a]
- endBy1 :: Alternative m => m a -> m sep -> m [a]
- manyTill :: Alternative m => m a -> m end -> m [a]
- manyTill_ :: Alternative m => m a -> m end -> m ([a], end)
- someTill :: Alternative m => m a -> m end -> m [a]
- someTill_ :: Alternative m => m a -> m end -> m ([a], end)
- option :: Alternative m => a -> m a -> m a
- sepBy :: Alternative m => m a -> m sep -> m [a]
- sepBy1 :: Alternative m => m a -> m sep -> m [a]
- sepEndBy :: Alternative m => m a -> m sep -> m [a]
- sepEndBy1 :: Alternative m => m a -> m sep -> m [a]
- skipMany :: Alternative m => m a -> m ()
- skipSome :: Alternative m => m a -> m ()
- skipCount :: Applicative m => Int -> m a -> m ()
- skipManyTill :: Alternative m => m a -> m end -> m end
- skipSomeTill :: Alternative m => m a -> m end -> m end

# Re-exports from Control.Applicative

(<|>) :: Alternative f => f a -> f a -> f a infixl 3 #

An associative binary operation

This combinator implements choice. The parser `p `<|>` q` first applies `p`. If it succeeds, the value of `p` is returned. If `p` fails, parser `q` is tried.

many :: Alternative f => f a -> f [a] #

Zero or more.

#### Examples

Expand

    >>> many (putStr "la")
    lalalalalalalalala... * goes on forever *

    >>> many Nothing
    Just []

    >>> take 5 <$> many (Just 1)
    * hangs forever *

Note that this function can be used with Parsers based on Applicatives. In that case `many parser` will attempt to parse `parser` zero or more times until it fails.

``many` p` applies the parser `p` zero or more times and returns a list of the values returned by `p`.

    identifier = (:) <$> letter <*> many (alphaNumChar <|> char '_')

some :: Alternative f => f a -> f [a] #

One or more.

#### Examples

Expand

    >>> some (putStr "la")
    lalalalalalalalala... * goes on forever *

    >>> some Nothing
    nothing

    >>> take 5 <$> some (Just 1)
    * hangs forever *

Note that this function can be used with Parsers based on Applicatives. In that case `some parser` will attempt to parse `parser` one or more times until it fails.

``some` p` applies the parser `p` one or more times and returns a list of the values returned by `p`.

    word = some letter

optional :: Alternative f => f a -> f (Maybe a) #

One or none.

It is useful for modelling any computation that is allowed to fail.

#### Examples

Expand

Using the `Alternative` instance of Control.Monad.Except, the following functions:

    >>> import Control.Monad.Except

    >>> canFail = throwError "it failed" :: Except String Int
    >>> final = return 42                :: Except String Int

Can be combined by allowing the first function to fail:

    >>> runExcept $ canFail *> final
    Left "it failed"

    >>> runExcept $ optional canFail *> final
    Right 42

``optional` p` tries to apply the parser `p`. It will parse `p` or `Nothing`. It only fails if `p` fails after consuming input. On success result of `p` is returned inside of `Just`, on failure `Nothing` is returned.

See also: `option`.

empty :: Alternative f => f a #

The identity of `<|>`

    empty <|> a     == a
    a     <|> empty == a

This parser fails unconditionally without providing any information about the cause of the failure.

Since: 0.4.0

# Original combinators

between :: Applicative m => m open -> m close -> m a -> m a #

``between` open close p` parses `open`, followed by `p` and `close`. Returns the value returned by `p`.

    braces = between (symbol "{") (symbol "}")

choice :: (Foldable f, Alternative m) => f (m a) -> m a #

``choice` ps` tries to apply the parsers in the list `ps` in order, until one of them succeeds. Returns the value of the succeeding parser.

    choice = asum

count :: Applicative m => Int -> m a -> m [a] #

``count` n p` parses `n` occurrences of `p`. If `n` is smaller or equal to zero, the parser equals to ``pure` []`. Returns a list of `n` parsed values.

    count = replicateM

See also: `skipCount`, `count'`.

count' :: Alternative m => Int -> Int -> m a -> m [a] #

``count'` m n p` parses from `m` to `n` occurrences of `p`. If `n` is not positive or `m > n`, the parser equals to ``pure` []`. Returns a list of parsed values.

Please note that `m` may be negative, in this case effect is the same as if it were equal to zero.

See also: `skipCount`, `count`.

eitherP :: Alternative m => m a -> m b -> m (Either a b) #

Combine two alternatives.

    eitherP a b = (Left <$> a) <|> (Right <$> b)

endBy :: Alternative m => m a -> m sep -> m [a] #

``endBy` p sep` parses zero or more occurrences of `p`, separated and ended by `sep`. Returns a list of values returned by `p`.

    cStatements = cStatement `endBy` semicolon

endBy1 :: Alternative m => m a -> m sep -> m [a] #

``endBy1` p sep` parses one or more occurrences of `p`, separated and ended by `sep`. Returns a list of values returned by `p`.

manyTill :: Alternative m => m a -> m end -> m [a] #

``manyTill` p end` applies parser `p` zero or more times until parser `end` succeeds. Returns the list of values returned by `p`. `end` result is consumed and lost. Use `manyTill_` if you wish to keep it.

See also: `skipMany`, `skipManyTill`.

manyTill_ :: Alternative m => m a -> m end -> m ([a], end) #

``manyTill_` p end` applies parser `p` zero or more times until parser `end` succeeds. Returns the list of values returned by `p` and the `end` result. Use `manyTill` if you have no need in the result of the `end`.

See also: `skipMany`, `skipManyTill`.

Since: 1.2.0

someTill :: Alternative m => m a -> m end -> m [a] #

``someTill` p end` works similarly to ``manyTill` p end`, but `p` should succeed at least once. `end` result is consumed and lost. Use `someTill_` if you wish to keep it.

    someTill p end = liftA2 (:) p (manyTill p end)

See also: `skipSome`, `skipSomeTill`.

someTill_ :: Alternative m => m a -> m end -> m ([a], end) #

``someTill_` p end` works similarly to ``manyTill_` p end`, but `p` should succeed at least once. Use `someTill` if you have no need in the result of the `end`.

See also: `skipSome`, `skipSomeTill`.

Since: 1.2.0

option :: Alternative m => a -> m a -> m a #

``option` x p` tries to apply the parser `p`. If `p` fails without consuming input, it returns the value `x`, otherwise the value returned by `p`.

    option x p = p <|> pure x

See also: `optional`.

sepBy :: Alternative m => m a -> m sep -> m [a] #

``sepBy` p sep` parses zero or more occurrences of `p`, separated by `sep`. Returns a list of values returned by `p`.

    commaSep p = p `sepBy` comma

sepBy1 :: Alternative m => m a -> m sep -> m [a] #

``sepBy1` p sep` parses one or more occurrences of `p`, separated by `sep`. Returns a list of values returned by `p`.

sepEndBy :: Alternative m => m a -> m sep -> m [a] #

``sepEndBy` p sep` parses zero or more occurrences of `p`, separated and optionally ended by `sep`. Returns a list of values returned by `p`.

sepEndBy1 :: Alternative m => m a -> m sep -> m [a] #

``sepEndBy1` p sep` parses one or more occurrences of `p`, separated and optionally ended by `sep`. Returns a list of values returned by `p`.

skipMany :: Alternative m => m a -> m () #

``skipMany` p` applies the parser `p` zero or more times, skipping its result.

See also: `manyTill`, `skipManyTill`.

skipSome :: Alternative m => m a -> m () #

``skipSome` p` applies the parser `p` one or more times, skipping its result.

See also: `someTill`, `skipSomeTill`.

skipCount :: Applicative m => Int -> m a -> m () #

``skipCount` n p` parses `n` occurrences of `p`, skipping its result. If `n` is not positive, the parser equals to ``pure` ()`.

    skipCount = replicateM_

See also: `count`, `count'`.

Since: 0.3.0

skipManyTill :: Alternative m => m a -> m end -> m end #

``skipManyTill` p end` applies the parser `p` zero or more times skipping results until parser `end` succeeds. Result parsed by `end` is then returned.

See also: `manyTill`, `skipMany`.

skipSomeTill :: Alternative m => m a -> m end -> m end #

``skipSomeTill` p end` applies the parser `p` one or more times skipping results until parser `end` succeeds. Result parsed by `end` is then returned.

See also: `someTill`, `skipSome`.