-- |
-- Module    : Replace.Megaparsec.Internal.Text
-- Copyright : ©2019 James Brock
-- License   : BSD2
-- Maintainer: James Brock <jamesbrock@gmail.com>
--
-- This internal module is for 'Data.Text.Text' specializations.
--
-- The functions in this module are intended to be chosen automatically
-- by rewrite rules in the "Replace.Megaparsec" module, so you should never
-- need to import this module.
--
-- Names in this module may change without a major version increment.

{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}

module Replace.Megaparsec.Internal.Text
  (
    -- * Parser combinator
    sepCapText
  , anyTillText
  )
where

import Control.Monad
import qualified Data.Text as T
import Data.Text.Internal (Text(..))
import Text.Megaparsec

{-# INLINE [1] sepCapText #-}
sepCapText
    :: forall e s m a. (MonadParsec e s m, s ~ T.Text)
    => m a -- ^ The pattern matching parser @sep@
    -> m [Either (Tokens s) a]
sepCapText :: forall e s (m :: * -> *) a.
(MonadParsec e s m, s ~ Text) =>
m a -> m [Either (Tokens s) a]
sepCapText m a
sep = forall e s (m :: * -> *). MonadParsec e s m => m s
getInput forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Text -> m [Either Text a]
go
  where
    -- the go function will search for the first pattern match,
    -- and then capture the pattern match along with the preceding
    -- unmatched string, and then recurse.
    -- restBegin is the rest of the buffer after the last pattern
    -- match.
    go :: Text -> m [Either Text a]
go restBegin :: Text
restBegin@(Text Array
tarray Int
beginIndx Int
beginLen) = do
        forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
(<|>)
            ( do
                (Text Array
_ Int
_ Int
thisLen) <- forall e s (m :: * -> *). MonadParsec e s m => m s
getInput
                -- About 'thisiter':
                -- It looks stupid and introduces a completely unnecessary
                -- Maybe, but when I refactor to eliminate 'thisiter' and
                -- the Maybe then the benchmarks get dramatically worse.
                Maybe (a, Text)
thisiter <- forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
(<|>)
                    ( do
                        a
x <- forall e s (m :: * -> *) a. MonadParsec e s m => m a -> m a
try m a
sep
                        restAfter :: Text
restAfter@(Text Array
_ Int
_ Int
afterLen) <- forall e s (m :: * -> *). MonadParsec e s m => m s
getInput
                        -- Don't allow a match of a zero-width pattern
                        forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Int
afterLen forall a. Ord a => a -> a -> Bool
>= Int
thisLen) forall (f :: * -> *) a. Alternative f => f a
empty
                        forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall a. a -> Maybe a
Just (a
x, Text
restAfter)
                    )
                    (forall e s (m :: * -> *). MonadParsec e s m => m (Token s)
anySingle forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a. Maybe a
Nothing)
                case Maybe (a, Text)
thisiter of
                    (Just (a
x, Text
restAfter)) | Int
thisLen forall a. Ord a => a -> a -> Bool
< Int
beginLen -> do
                        -- we've got a match with some preceding unmatched string
                        let unmatched :: Text
unmatched = Array -> Int -> Int -> Text
Text Array
tarray Int
beginIndx (Int
beginLen forall a. Num a => a -> a -> a
- Int
thisLen)
                        (forall a b. a -> Either a b
Left Text
unmatchedforall a. a -> [a] -> [a]
:) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (forall a b. b -> Either a b
Right a
xforall a. a -> [a] -> [a]
:) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> m [Either Text a]
go Text
restAfter
                    (Just (a
x, Text
restAfter)) -> do
                        -- we're got a match with no preceding unmatched string
                        (forall a b. b -> Either a b
Right a
xforall a. a -> [a] -> [a]
:) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> m [Either Text a]
go Text
restAfter
                    Maybe (a, Text)
Nothing -> Text -> m [Either Text a]
go Text
restBegin -- no match, try again
            )
            ( do
                    -- We're at the end of the input, so return
                    -- whatever unmatched string we've got since offsetBegin
                if Int
beginLen forall a. Ord a => a -> a -> Bool
> Int
0 then
                    forall (f :: * -> *) a. Applicative f => a -> f a
pure [forall a b. a -> Either a b
Left Text
restBegin]
                else forall (f :: * -> *) a. Applicative f => a -> f a
pure []
            )

{-# INLINE [1] anyTillText #-}
anyTillText
    :: forall e s m a. (MonadParsec e s m, s ~ T.Text)
    => m a -- ^ The pattern matching parser @sep@
    -> m (Tokens s, a)
anyTillText :: forall e s (m :: * -> *) a.
(MonadParsec e s m, s ~ Text) =>
m a -> m (Tokens s, a)
anyTillText m a
sep = do
    (Text Array
tarray Int
beginIndx Int
beginLen) <- forall e s (m :: * -> *). MonadParsec e s m => m s
getInput
    (Int
thisLen, a
x) <- m (Int, a)
go
    forall (f :: * -> *) a. Applicative f => a -> f a
pure (Array -> Int -> Int -> Text
Text Array
tarray Int
beginIndx (Int
beginLen forall a. Num a => a -> a -> a
- Int
thisLen), a
x)
  where
    go :: m (Int, a)
go = do
      (Text Array
_ Int
_ Int
thisLen) <- forall e s (m :: * -> *). MonadParsec e s m => m s
getInput
      Maybe a
r <- forall (f :: * -> *) a. Alternative f => f a -> f (Maybe a)
optional forall a b. (a -> b) -> a -> b
$ forall e s (m :: * -> *) a. MonadParsec e s m => m a -> m a
try m a
sep
      case Maybe a
r of
        Maybe a
Nothing -> forall e s (m :: * -> *). MonadParsec e s m => m (Token s)
anySingle forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> m (Int, a)
go
        Just a
x -> forall (f :: * -> *) a. Applicative f => a -> f a
pure (Int
thisLen, a
x)