{-# LANGUAGE CPP #-}
-- |
-- Module      : Streamly.Data.ParserK
-- Copyright   : (c) 2023 Composewell Technologies
-- License     : BSD-3-Clause
-- Maintainer  : streamly@composewell.com
-- Stability   : pre-release
-- Portability : GHC
--
-- See the general notes about parsing in the "Streamly.Data.Parser" module.
-- This module implements a using Continuation Passing Style (CPS) wrapper over
-- the "Streamly.Data.Parser" module. It is as fast or faster than attoparsec.
--
-- == Parser vs ParserK
--
-- 'ParserK' is preferred over 'Streamly.Data.Parser.Parser' when extensive
-- applicative, alternative and monadic composition is required, or when
-- recursive or dynamic composition of parsers is required. The
-- 'Streamly.Data.Parser.Parser' type fuses statically and creates efficient
-- loops whereas 'ParserK' uses function call based composition and has
-- comparatively larger runtime overhead but it is better suited to the
-- specific use cases mentioned above. 'ParserK' also allows to efficient parse
-- a stream of arrays, it can also break the input stream into a parse result
-- and remaining stream so that the stream can be parsed independently in
-- segments.
--
-- == Using ParserK
--
-- All the parsers from the "Streamly.Data.Parser" module can be adapted to
-- ParserK using the 'Streamly.Data.ParserK.adaptC',
-- 'Streamly.Internal.Data.ParserK.adapt', and
-- 'Streamly.Internal.Data.ParserK.adaptCG' combinators.
--
-- 'Streamly.Data.StreamK.parseChunks' runs a parser on a stream of unboxed
-- arrays, this is the preferred and most efficient way to parse chunked input.
-- The more general 'Streamly.Data.StreamK.parseBreakChunks' function returns
-- the remaining stream as well along with the parse result. There are
-- 'Streamly.Internal.Data.StreamK.parseChunksGeneric',
-- 'Streamly.Internal.Data.StreamK.parseBreakChunksGeneric' as well to run
-- parsers on boxed arrays. 'Streamly.Internal.Data.StreamK.parse',
-- 'Streamly.Internal.Data.StreamK.parseBreak' run parsers on a stream of
-- individual elements instead of stream of arrays.
--
-- == Monadic Composition
--
-- Monad composition can be used for lookbehind parsers, we can dynamically
-- compose new parsers based on the results of the previously parsed values.
--
-- If we have to parse "a9" or "9a" but not "99" or "aa" we can use the
-- following non-monadic, backtracking parser:
--
-- >>> digits p1 p2 = ((:) <$> p1 <*> ((:) <$> p2 <*> pure []))
-- >>> :{
-- backtracking :: Monad m => ParserK Char m String
-- backtracking = ParserK.adapt $
--     digits (Parser.satisfy isDigit) (Parser.satisfy isAlpha)
--     <|>
--     digits (Parser.satisfy isAlpha) (Parser.satisfy isDigit)
-- :}
--
-- We know that if the first parse resulted in a digit at the first place then
-- the second parse is going to fail.  However, we waste that information and
-- parse the first character again in the second parse only to know that it is
-- not an alphabetic char.  By using lookbehind in a 'Monad' composition we can
-- avoid redundant work:
--
-- >>> data DigitOrAlpha = Digit Char | Alpha Char
--
-- >>> :{
-- lookbehind :: Monad m => ParserK Char m String
-- lookbehind = do
--     x1 <- ParserK.adapt $
--              Digit <$> Parser.satisfy isDigit
--          <|> Alpha <$> Parser.satisfy isAlpha
--     -- Note: the parse depends on what we parsed already
--     x2 <- ParserK.adapt $
--           case x1 of
--              Digit _ -> Parser.satisfy isAlpha
--              Alpha _ -> Parser.satisfy isDigit
--     return $ case x1 of
--         Digit x -> [x,x2]
--         Alpha x -> [x,x2]
-- :}
--
-- == Experimental APIs
--
-- Please refer to "Streamly.Internal.Data.ParserK" for functions that have
-- not yet been released.
--
module Streamly.Data.ParserK
    (
    -- * Setup
    -- | To execute the code examples provided in this module in ghci, please
    -- run the following commands first.
    --
    -- $setup

    -- * Parser Type
      ParserK

    -- * Parsers
    -- ** Conversions
    , adapt
    , adaptC
    , adaptCG
    -- , toParser

    -- ** Without Input
    , fromPure
    , fromEffect
    , die

    -- * Deprecated
    , fromFold
    , fromParser
    )

where

import Control.Monad.IO.Class (MonadIO)
import Streamly.Internal.Data.Fold (Fold)
import Streamly.Internal.Data.Unbox (Unbox)
import Streamly.Internal.Data.Array (Array)
import qualified Streamly.Internal.Data.Parser as ParserD

import Streamly.Internal.Data.ParserK.Type

#include "DocTestDataParserK.hs"

{-# DEPRECATED fromFold "Please use \"ParserK.adaptC . Parser.fromFold\" instead." #-}
{-# INLINE fromFold #-}
fromFold :: (MonadIO m, Unbox a) => Fold m a b -> ParserK (Array a) m b
fromFold :: forall (m :: * -> *) a b.
(MonadIO m, Unbox a) =>
Fold m a b -> ParserK (Array a) m b
fromFold = forall (m :: * -> *) a b.
(Monad m, Unbox a) =>
Parser a m b -> ParserK (Array a) m b
adaptC forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *) a b. Monad m => Fold m a b -> Parser a m b
ParserD.fromFold

{-# DEPRECATED fromParser "Please use \"adaptC\" instead." #-}
{-# INLINE fromParser #-}
fromParser ::
       (MonadIO m, Unbox a) => ParserD.Parser a m b -> ParserK (Array a) m b
fromParser :: forall (m :: * -> *) a b.
(MonadIO m, Unbox a) =>
Parser a m b -> ParserK (Array a) m b
fromParser = forall (m :: * -> *) a b.
(Monad m, Unbox a) =>
Parser a m b -> ParserK (Array a) m b
adaptC