{-# LANGUAGE CPP #-}
{-|
Module      : Parsley.Selective
Description : The @Selective@ combinators
License     : BSD-3-Clause
Maintainer  : Jamie Willis
Stability   : stable

A version of the @Selective@ combinators as described in [/Selective Applicative Functors/
(Mokhov et al. 2019)](https://dl.acm.org/doi/10.1145/3341694).

Like the @Applicative@ and @Alternative@ combinators, these cannot be properly described by the
@Selective@ typeclass, since the API relies on Template Haskell code being used by @Applicative@.

@since 0.1.0.0
-}
module Parsley.Selective (
    branch, select,
    (>??>), filteredBy, (>?>),
    predicate, (<?:>),
    conditional, match, (||=),
    when, while,
    fromMaybeP
  ) where

import Prelude hiding             (pure, (<$>)
#if __GLASGOW_HASKELL__ >= 906
  , liftA2
#endif
  )
import Data.Function              (fix)
import Language.Haskell.TH.Syntax (Lift(..))
import Parsley.Alternative        (empty)
import Parsley.Applicative        (pure, (<$>), liftA2, unit, constp)
import Parsley.Defunctionalized   (Defunc(ID, EQ_H, IF_S, LAM_S, LET_S, APP_H))
import Parsley.Internal           (makeQ, Parser)
import Parsley.ParserOps          (ParserOps, conditional)

import qualified Parsley.Internal as Internal (branch)

{-|
One of the core @Selective@ operations. The behaviour of @branch p l r@ is to first to parse
@p@, if it fails then the combinator fails. If @p@ succeeded then if its result is a @Left@, then
the parser @l@ is executed and applied to the result of @p@, otherwise @r@ is executed and applied
to the right from a @Right@.

Crucially, only one of @l@ or @r@ will be executed on @p@'s success.

@since 0.1.0.0
-}
branch :: Parser (Either a b) -- ^ The first parser to execute
       -> Parser (a -> c)     -- ^ The parser to execute if the first returned a @Left@
       -> Parser (b -> c)     -- ^ The parser to execute if the first returned a @Right@
       -> Parser c
branch :: forall a b c.
Parser (Either a b)
-> Parser (a -> c) -> Parser (b -> c) -> Parser c
branch = forall a b c.
Parser (Either a b)
-> Parser (a -> c) -> Parser (b -> c) -> Parser c
Internal.branch

{-|
Similar to `branch`, except the given branch is only executed on a @Left@ returned.

> select p q = branch p q (pure id)

@since 0.1.0.0
-}
select :: Parser (Either a b) -> Parser (a -> b) -> Parser b
select :: forall a b. Parser (Either a b) -> Parser (a -> b) -> Parser b
select Parser (Either a b)
p Parser (a -> b)
q = forall a b c.
Parser (Either a b)
-> Parser (a -> c) -> Parser (b -> c) -> Parser c
branch Parser (Either a b)
p Parser (a -> b)
q (forall (rep :: Type -> Type) a. ParserOps rep => rep a -> Parser a
pure forall a1. Defunc (a1 -> a1)
ID)

-- Filter Combinators
{-|
This combinator is used for filtering. Given @px >??> pf@, if @px@ succeeds, then @pf@ will be
attempted too. Then the result of @px@ is given to @pf@'s. If the function returns true then the
parser succeeds and returns the result of @px@, otherwise it will fail.

@since 0.1.0.0
-}
infixl 4 >??>
(>??>) :: Parser a -> Parser (a -> Bool) -> Parser a
Parser a
px >??> :: forall a. Parser a -> Parser (a -> Bool) -> Parser a
>??> Parser (a -> Bool)
pf = forall a b. Parser (Either a b) -> Parser (a -> b) -> Parser b
select (forall (rep :: Type -> Type) a b c.
ParserOps rep =>
rep (a -> b -> c) -> Parser a -> Parser b -> Parser c
liftA2 forall {a1}. Defunc ((a1 -> Bool) -> a1 -> Either () a1)
g Parser (a -> Bool)
pf Parser a
px) forall a. Parser a
empty
  where
    -- Not sure if I need the LET_S?
    g :: Defunc ((a1 -> Bool) -> a1 -> Either () a1)
g =
      forall a1 b. (Defunc a1 -> Defunc b) -> Defunc (a1 -> b)
LAM_S forall a b. (a -> b) -> a -> b
$ \Defunc (a1 -> Bool)
f ->
        forall a1 b. (Defunc a1 -> Defunc b) -> Defunc (a1 -> b)
LAM_S forall a b. (a -> b) -> a -> b
$ \Defunc a1
x ->
          forall a1 a. Defunc a1 -> (Defunc a1 -> Defunc a) -> Defunc a
LET_S Defunc a1
x forall a b. (a -> b) -> a -> b
$ \Defunc a1
x ->
            forall a. Defunc Bool -> Defunc a -> Defunc a -> Defunc a
IF_S (forall a1 a. Defunc (a1 -> a) -> Defunc a1 -> Defunc a
APP_H Defunc (a1 -> Bool)
f Defunc a1
x)
                 (forall a1 a. Defunc (a1 -> a) -> Defunc a1 -> Defunc a
APP_H (forall (q :: Type -> Type) a. Quapplicative q => a -> Code a -> q a
makeQ forall a b. b -> Either a b
Right [||Right||]) Defunc a1
x)
                 (forall (q :: Type -> Type) a. Quapplicative q => a -> Code a -> q a
makeQ (forall a b. a -> Either a b
Left ()) [||Left ()||])

{-|
An alias for @(`>?>`)@.

@since 0.1.0.0
-}
filteredBy :: ParserOps rep => Parser a -> rep (a -> Bool) -> Parser a
filteredBy :: forall (rep :: Type -> Type) a.
ParserOps rep =>
Parser a -> rep (a -> Bool) -> Parser a
filteredBy Parser a
p rep (a -> Bool)
f = Parser a
p forall a. Parser a -> Parser (a -> Bool) -> Parser a
>??> forall (rep :: Type -> Type) a. ParserOps rep => rep a -> Parser a
pure rep (a -> Bool)
f

{-|
This combinator is used for filtering, similar to @(`>??>`)@ except the predicate is given without
parsing anything.

> px >?> f = px >??> pure f

@since 0.1.0.0
-}
infixl 4 >?>
(>?>) :: ParserOps rep => Parser a -> rep (a -> Bool) -> Parser a
>?> :: forall (rep :: Type -> Type) a.
ParserOps rep =>
Parser a -> rep (a -> Bool) -> Parser a
(>?>) = forall (rep :: Type -> Type) a.
ParserOps rep =>
Parser a -> rep (a -> Bool) -> Parser a
filteredBy

-- Conditional Combinators
{-|
Similar to an @if@ statement: @predicate f p t e@ first parses @p@ and collects its result @x@.
If @f x@ is @True@ then @t@ is parsed, else @e@ is parsed.

@since 0.1.0.0
-}
predicate :: ParserOps rep => rep (a -> Bool) -> Parser a -> Parser b -> Parser b -> Parser b
predicate :: forall (rep :: Type -> Type) a b.
ParserOps rep =>
rep (a -> Bool) -> Parser a -> Parser b -> Parser b -> Parser b
predicate rep (a -> Bool)
cond Parser a
p Parser b
t Parser b
e = forall (rep :: Type -> Type) a b.
ParserOps rep =>
[(rep (a -> Bool), Parser b)] -> Parser a -> Parser b -> Parser b
conditional [(rep (a -> Bool)
cond, Parser b
t)] Parser a
p Parser b
e

{-|
A \"ternary\" combinator, essentially `predicate` given the identity function.

@since 0.1.0.0
-}
infixl 4 <?:>
(<?:>) :: Parser Bool -> (Parser a, Parser a) -> Parser a
Parser Bool
cond <?:> :: forall a. Parser Bool -> (Parser a, Parser a) -> Parser a
<?:> (Parser a
p, Parser a
q) = forall (rep :: Type -> Type) a b.
ParserOps rep =>
rep (a -> Bool) -> Parser a -> Parser b -> Parser b -> Parser b
predicate forall a1. Defunc (a1 -> a1)
ID Parser Bool
cond Parser a
p Parser a
q

-- Match Combinators
{-|
The `match` combinator can be thought of as a restricted form of @(>>=)@, where there is a fixed
domain on the valid outputs of the second argument.

More concretely, @match dom p f def@ first parses @p@, and, if its result is an element of the list
@dom@, its result is applied to the function @f@ and the resulting parser is executed. If the result
was not in @dom@, then @def@ will be executed.

Note: To eliminate the dynamic nature of the operation, every possible outcome of the parser is
enumerated and tried in turn.

@since 0.1.0.0
-}
--TODO: This /could/ be improved by generating a neat switch for Characters, and forwarding the input.
match :: (Eq a, Lift a)
      => [a]             -- ^ The domain of the function given as the third argument
      -> Parser a        -- ^ The parser whose result will be given to the function
      -> (a -> Parser b) -- ^ A function uses to generate the parser to execute
      -> Parser b        -- ^ A parser to execute if the result is not in the domain of the function
      -> Parser b
match :: forall a b.
(Eq a, Lift a) =>
[a] -> Parser a -> (a -> Parser b) -> Parser b -> Parser b
match [a]
vs Parser a
p a -> Parser b
f = forall (rep :: Type -> Type) a b.
ParserOps rep =>
[(rep (a -> Bool), Parser b)] -> Parser a -> Parser b -> Parser b
conditional (forall a b. (a -> b) -> [a] -> [b]
map (\a
v -> (forall a1. Eq a1 => Defunc a1 -> Defunc (a1 -> Bool)
EQ_H (forall (q :: Type -> Type) a. Quapplicative q => a -> Code a -> q a
makeQ a
v [||v||]), a -> Parser b
f a
v)) [a]
vs) Parser a
p

{-|
This combinator, known as @sbind@ in the literature, is best avoided for efficiency sake. It is
built on `match`, but where the domain of the function is /all/ of the possible values of the
datatype. This means the type must be finite, or else this combinator would never terminate.

The problem with the combinator is not so much that it takes linear time to take the right branch
(as opposed to monadic @(>>=)@) but that it generates a /massive/ amount of code when the datatype
gets too big. For instance, using it for `Char` would generate a 66535-way case split!

The role this combinator fulfils is the branching behaviour that monadic operations can provide.
For the persistence or duplication of data that monads can provide, `Parsley.Register.bind` is a much better
alternative.

@since 0.1.0.0
-}
infixl 1 ||=
(||=) :: (Enum a, Bounded a, Eq a, Lift a) => Parser a -> (a -> Parser b) -> Parser b
Parser a
p ||= :: forall a b.
(Enum a, Bounded a, Eq a, Lift a) =>
Parser a -> (a -> Parser b) -> Parser b
||= a -> Parser b
f = forall a b.
(Eq a, Lift a) =>
[a] -> Parser a -> (a -> Parser b) -> Parser b -> Parser b
match [forall a. Bounded a => a
minBound..forall a. Bounded a => a
maxBound] Parser a
p a -> Parser b
f forall a. Parser a
empty

-- Composite Combinators
{-|
This combinator will only execute its second argument if the first one returned @True@.

@since 0.1.0.0
-}
when :: Parser Bool -> Parser () -> Parser ()
when :: Parser Bool -> Parser () -> Parser ()
when Parser Bool
p Parser ()
q = Parser Bool
p forall a. Parser Bool -> (Parser a, Parser a) -> Parser a
<?:> (Parser ()
q, Parser ()
unit)

{-|
The fixed-point of the `when` combinator: it will continuously parse its argument until either it
fails (in which case it fails), or until it returns @False@.

@since 0.1.0.0
-}
while :: Parser Bool -> Parser ()
while :: Parser Bool -> Parser ()
while Parser Bool
x = forall a. (a -> a) -> a
fix (Parser Bool -> Parser () -> Parser ()
when Parser Bool
x)

{-|
Given @fromMaybeP p def@, if @p@ returns a @Nothing@ then @def@ is executed, otherwise the result
of @p@ will be returned with the @Just@ removed.

@since 0.1.0.0
-}
fromMaybeP :: Parser (Maybe a) -> Parser a -> Parser a
fromMaybeP :: forall a. Parser (Maybe a) -> Parser a -> Parser a
fromMaybeP Parser (Maybe a)
pm Parser a
px = forall a b. Parser (Either a b) -> Parser (a -> b) -> Parser b
select (forall (q :: Type -> Type) a. Quapplicative q => a -> Code a -> q a
makeQ (forall b a. b -> (a -> b) -> Maybe a -> b
maybe (forall a b. a -> Either a b
Left ()) forall a b. b -> Either a b
Right) [||maybe (Left ()) Right||] forall (rep :: Type -> Type) a b.
ParserOps rep =>
rep (a -> b) -> Parser a -> Parser b
<$> Parser (Maybe a)
pm) (forall a b. Parser a -> Parser (b -> a)
constp Parser a
px)