age-0.0.1.0: src/Data/Attoparsec/ByteString/Extra.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE LambdaCase #-}
module Data.Attoparsec.ByteString.Extra
( takeWhileMN
, countMN
) where
import Control.Applicative ( optional )
import Control.Monad ( MonadPlus )
import Data.Attoparsec.ByteString ( Parser, scan )
import Data.ByteString ( ByteString )
import qualified Data.ByteString as BS
import Data.Word ( Word8 )
import Prelude
-- | Consume the longest (@m <= len <= n@) input slice where the predicate
-- returns 'True', and return the consumed input.
--
-- This parser fails in the event that the length of its consumed input does
-- not satisfy @m <= len <= n@.
takeWhileMN ::
-- | @m@.
Word ->
-- | @n@.
Word ->
-- | Predicate.
(Word8 -> Bool) ->
Parser ByteString
takeWhileMN m n f
| m > n = fail "takeWhileMN: m cannot be greater than n"
| otherwise = do
bs <- scan 0 transformState
let len = BS.length bs
if mI <= len && nI >= len
then pure bs
else
fail $
"takeWhileMN: consumed input length ("
<> show len
<> ") must be >= "
<> show mI
<> " and <= "
<> show nI
<> "."
where
mI :: Int
mI = fromIntegral m
nI :: Int
nI = fromIntegral n
-- Parse up to @n@ bytes where the predicate returns 'True'.
transformState :: Word -> Word8 -> Maybe Word
transformState s b
| s == n = Nothing
| s < n && f b = Just (s + 1)
| otherwise = Nothing
-- | Applies from @m@ to @n@ occurrences of @p@. Returns a list of the returned
-- values of @p@. The value returned by @p@ is forced to WHNF.
countMN :: MonadPlus m => Word -> Word -> m a -> m [a]
countMN m n p
| m > n = error "countMN: m cannot be greater than n"
| n == 0 = pure []
| otherwise = reverse <$> goUntilM []
where
mI :: Int
mI = fromIntegral m
nI :: Int
nI = fromIntegral n
goUntilM !acc
| length acc == mI = goUntilN acc
| otherwise = do
!x <- p
goUntilM (x : acc)
goUntilN !acc
| length acc == nI = pure acc
| otherwise = optional p >>= \case
Nothing -> pure acc
Just !x -> goUntilN (x : acc)