{-|
Module      : Parsley.Combinator
Description : The parsing combinators
License     : BSD-3-Clause
Maintainer  : Jamie Willis
Stability   : stable

This module contains the classic parser combinator operations specific to parsers themselves, excluding
those that consume input.

@since 0.1.0.0
-}
module Parsley.Combinator (
    eof, more,
    someTill,
    try,
    lookAhead, notFollowedBy,
    line, col, pos
  ) where

import Prelude hiding           ((*>))
import Parsley.Alternative      (manyTill)
import Parsley.Applicative      (void, (<:>), (*>), (<~>))
import Parsley.Internal         (Parser)
import Parsley.Char             (item)

import qualified Parsley.Internal as Internal (try, lookAhead, notFollowedBy, line, col)

{-|
This combinator will attempt to parse a given parser. If it succeeds, the result is returned without
having consumed any input. If it fails, however, any consumed input remains consumed.

@since 0.1.0.0
-}
lookAhead :: Parser a -> Parser a
lookAhead :: forall a. Parser a -> Parser a
lookAhead = forall a. Parser a -> Parser a
Internal.lookAhead

{-|
This combinator will ensure that a given parser fails. If the parser does fail, a @()@ is returned
and no input is consumed. If the parser succeeded, then this combinator will fail, however it will
not consume any input.

@since 0.1.0.0
-}
notFollowedBy :: Parser a -> Parser ()
notFollowedBy :: forall a. Parser a -> Parser ()
notFollowedBy = forall a. Parser a -> Parser ()
Internal.notFollowedBy

{-|
This combinator allows a parser to backtrack on failure, which is to say that it will
not have consumed any input if it were to fail. This is important since @parsec@ semantics demand
that the second branch of @(`Parsley.Alternative.<|>`)@ can only be taken if the first did not consume input on failure.

Excessive use of `try` will reduce the efficiency of the parser and effect the generated error
messages. It should only be used in one of two circumstances:

* When two branches of a parser share a common leading prefix (in which case, it is often better
  to try and factor this out).
* When a parser needs to be executed atomically (for example, tokens).

@since 0.1.0.0
-}
try :: Parser a -> Parser a
try :: forall a. Parser a -> Parser a
try = forall a. Parser a -> Parser a
Internal.try

{-|
This parser succeeds only if there is no input left to consume, and fails without consuming input
otherwise.

@since 0.1.0.0
-}
eof :: Parser ()
eof :: Parser ()
eof = forall a. Parser a -> Parser ()
notFollowedBy Parser Char
item

{-|
This parser succeeds if there is still input left to consume, and fails otherwise.

@since 0.1.0.0
-}
more :: Parser ()
more :: Parser ()
more = forall a. Parser a -> Parser a
lookAhead (forall a. Parser a -> Parser ()
void Parser Char
item)

-- Composite Combinators
{-|
The combinator @someTill p end@ will try and parse @p@ as many times as possible (but at least once)
so long as @end@ cannot be successfully parsed. It will return the results from the successful parses of @p@.

@since 0.1.0.0
-}
someTill :: Parser a -> Parser b -> Parser [a]
someTill :: forall a b. Parser a -> Parser b -> Parser [a]
someTill Parser a
p Parser b
end = forall a. Parser a -> Parser ()
notFollowedBy Parser b
end forall a b. Parser a -> Parser b -> Parser b
*> (Parser a
p forall a. Parser a -> Parser [a] -> Parser [a]
<:> forall a b. Parser a -> Parser b -> Parser [a]
manyTill Parser a
p Parser b
end)

{-|
The `line` combinator returns the current line number at this point in the parse. Line numbers
start from 1.

@since 1.0.1.0
-}
line :: Parser Int
line :: Parser Int
line = Parser Int
Internal.line

{-|
The `col` combinator returns the current column number at this point in the parse. Column numbers
start from 1.

@since 1.0.1.0
-}
col :: Parser Int
col :: Parser Int
col = Parser Int
Internal.col

{-|
The `pos` combinator returns the current line and column number at this point in the parse.

@since 1.0.1.0
-}
pos :: Parser (Int, Int)
pos :: Parser (Int, Int)
pos = Parser Int
line forall a b. Parser a -> Parser b -> Parser (a, b)
<~> Parser Int
col