-- | Fast not-backtracking incremental scanner for bytestrings
--
-- Unlike attoparsec or most of other parser combinator libraries,
-- scanner doesn't support backtracking. But you probably don't need it
-- anyway, at least if you need fast parser.
--
-- Scanner processes input incrementally. When more input is needed, scanner
-- returns `More` continuation. All the already processed input is discarded.

module Scanner
( Scanner
, Result (..)
, scan
, scanOnly
, scanLazy
, scanWith
, anyWord8
, anyChar8
, word8
, char8
, take
, takeWhile
, takeWhileChar8
, string
, skipWhile
, skipSpace
, lookAhead
, lookAheadChar8
, foldlWhile
, foldlWhile1
, satisfy
, satisfyMaybe
, decimal
)
where

import Scanner.Internal

import Prelude hiding (take, takeWhile)
import Data.Word
import qualified Data.Char as Char
import Data.ByteString (ByteString)
import qualified Data.ByteString as ByteString
import qualified Data.ByteString.Lazy as Lazy (ByteString)
import qualified Data.ByteString.Lazy as Lazy.ByteString
import Control.Monad
import GHC.Base (unsafeChr)

-- | Scan the complete input, without resupplying
scanOnly :: Scanner a -> ByteString -> Either String a
scanOnly :: forall a. Scanner a -> ByteString -> Either String a
scanOnly Scanner a
s ByteString
bs = Result a -> Either String a
forall {b}. Result b -> Either String b
go (Scanner a -> ByteString -> Result a
forall r. Scanner r -> ByteString -> Result r
scan Scanner a
s ByteString
bs)
  where
  go :: Result b -> Either String b
go Result b
res = case Result b
res of
    Done ByteString
_ b
r -> b -> Either String b
forall a b. b -> Either a b
Right b
r
    Fail ByteString
_ String
err -> String -> Either String b
forall a b. a -> Either a b
Left String
err
    More ByteString -> Result b
more -> Result b -> Either String b
go (ByteString -> Result b
more ByteString
ByteString.empty)

-- | Scan lazy bytestring by resupplying scanner with chunks
scanLazy :: Scanner a -> Lazy.ByteString -> Either String a
scanLazy :: forall a. Scanner a -> ByteString -> Either String a
scanLazy Scanner a
s ByteString
lbs = (ByteString -> Result a) -> [ByteString] -> Either String a
forall {b}.
(ByteString -> Result b) -> [ByteString] -> Either String b
go (Scanner a -> ByteString -> Result a
forall r. Scanner r -> ByteString -> Result r
scan Scanner a
s) (ByteString -> [ByteString]
Lazy.ByteString.toChunks ByteString
lbs)
  where
  go :: (ByteString -> Result b) -> [ByteString] -> Either String b
go ByteString -> Result b
more [ByteString]
chunks =
    let (ByteString
chunk, [ByteString]
chunks') = case [ByteString]
chunks of
          [] -> (ByteString
ByteString.empty, [])
          (ByteString
c:[ByteString]
cs) -> (ByteString
c, [ByteString]
cs)
    in case ByteString -> Result b
more ByteString
chunk of
      Done ByteString
_ b
r -> b -> Either String b
forall a b. b -> Either a b
Right b
r
      Fail ByteString
_ String
err -> String -> Either String b
forall a b. a -> Either a b
Left String
err
      More ByteString -> Result b
more' -> (ByteString -> Result b) -> [ByteString] -> Either String b
go ByteString -> Result b
more' [ByteString]
chunks'

-- | Scan with the provided resupply action
scanWith :: Monad m => m ByteString -> Scanner a -> ByteString -> m (Result a)
scanWith :: forall (m :: * -> *) a.
Monad m =>
m ByteString -> Scanner a -> ByteString -> m (Result a)
scanWith m ByteString
more Scanner a
s ByteString
input = ByteString -> (ByteString -> Result a) -> m (Result a)
forall {r}. ByteString -> (ByteString -> Result r) -> m (Result r)
go ByteString
input (Scanner a -> ByteString -> Result a
forall r. Scanner r -> ByteString -> Result r
scan Scanner a
s)
  where
  go :: ByteString -> (ByteString -> Result r) -> m (Result r)
go ByteString
bs ByteString -> Result r
next = case ByteString -> Result r
next ByteString
bs of
    More ByteString -> Result r
next' -> do
      ByteString
bs' <- m ByteString
more
      ByteString -> (ByteString -> Result r) -> m (Result r)
go ByteString
bs' ByteString -> Result r
next'
    Result r
res -> Result r -> m (Result r)
forall (m :: * -> *) a. Monad m => a -> m a
return Result r
res

-- | Consume the next 8-bit char
--
-- It fails if end of input
{-# INLINE anyChar8 #-}
anyChar8 :: Scanner Char
anyChar8 :: Scanner Char
anyChar8 = Word8 -> Char
w2c (Word8 -> Char) -> Scanner Word8 -> Scanner Char
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Scanner Word8
anyWord8

-- | Consume the specified word or fail
{-# INLINE word8 #-}
word8 :: Word8 -> Scanner ()
word8 :: Word8 -> Scanner ()
word8 Word8
w = do
  Word8
w' <- Scanner Word8
anyWord8
  Bool -> Scanner () -> Scanner ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Word8
w' Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
w) (Scanner () -> Scanner ()) -> Scanner () -> Scanner ()
forall a b. (a -> b) -> a -> b
$
    String -> Scanner ()
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"unexpected word"

-- | Consume the specified 8-bit char or fail
{-# INLINE char8 #-}
char8 :: Char -> Scanner ()
char8 :: Char -> Scanner ()
char8 = Word8 -> Scanner ()
word8 (Word8 -> Scanner ()) -> (Char -> Word8) -> Char -> Scanner ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Word8
c2w

-- | Take input while the predicate is `True`
{-# INLINE takeWhileChar8 #-}
takeWhileChar8 :: (Char -> Bool) -> Scanner ByteString
takeWhileChar8 :: (Char -> Bool) -> Scanner ByteString
takeWhileChar8 Char -> Bool
p = (Word8 -> Bool) -> Scanner ByteString
takeWhile (Char -> Bool
p (Char -> Bool) -> (Word8 -> Char) -> Word8 -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> Char
w2c)

-- | Return the next byte, if any, without consuming it
{-# INLINE lookAheadChar8 #-}
lookAheadChar8 :: Scanner (Maybe Char)
lookAheadChar8 :: Scanner (Maybe Char)
lookAheadChar8 = (Word8 -> Char) -> Maybe Word8 -> Maybe Char
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Word8 -> Char
w2c (Maybe Word8 -> Maybe Char)
-> Scanner (Maybe Word8) -> Scanner (Maybe Char)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Scanner (Maybe Word8)
lookAhead

-- | Skip any input while the preducate is `True`
{-# INLINE skipWhile #-}
skipWhile :: (Word8 -> Bool) -> Scanner ()
skipWhile :: (Word8 -> Bool) -> Scanner ()
skipWhile = Scanner ByteString -> Scanner ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (Scanner ByteString -> Scanner ())
-> ((Word8 -> Bool) -> Scanner ByteString)
-> (Word8 -> Bool)
-> Scanner ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Word8 -> Bool) -> Scanner ByteString
takeWhile

-- | Skip space
{-# INLINE skipSpace #-}
skipSpace :: Scanner ()
skipSpace :: Scanner ()
skipSpace = (Word8 -> Bool) -> Scanner ()
skipWhile Word8 -> Bool
isSpaceWord8

{-# INLINE isSpaceWord8 #-}
isSpaceWord8 :: Word8 -> Bool
isSpaceWord8 :: Word8 -> Bool
isSpaceWord8 Word8
w = Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
32 Bool -> Bool -> Bool
|| Word8
w Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word8
13

{-# INLINE w2c #-}
w2c :: Word8 -> Char
w2c :: Word8 -> Char
w2c = Int -> Char
unsafeChr (Int -> Char) -> (Word8 -> Int) -> Word8 -> Char
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral

{-# INLINE c2w #-}
c2w :: Char -> Word8
c2w :: Char -> Word8
c2w = Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Word8) -> (Char -> Int) -> Char -> Word8
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Int
Char.ord