content: {-# language MagicHash #-}
  {-# language BlockArguments #-}
  {-# language UnboxedTuples #-}
  {-# language BangPatterns #-}
  {-# language TypeApplications #-}
  {-# language NegativeLiterals #-}
  {-# language UnliftedFFITypes #-}
  {-# language ScopedTypeVariables #-}
  {-# language ForeignFunctionInterface #-}
  
  -- | Note: @parsnip@ will still be assuming that the input is null terminated
  -- even if you use these combinators.
  module Text.Parsnip.Word8
  ( satisfy
  , word8
  , notWord8
  , anyWord8
  , while, whileSome
  , till, tillSome, tillWord8
  , skipWhile, skipWhileSome
  , skipTill, skipTillSome, skipTillWord8
  , previousWord8, previousWord8'
  , nextWord8, nextWord8'
  ) where
  
  import Data.ByteString (ByteString)
  import Data.Word
  import GHC.Prim
  import GHC.Ptr
  import GHC.Types
  import GHC.Word
  import Text.Parsnip.Internal.Parser
  import Text.Parsnip.Internal.Private
  import Text.Parsnip.Parser
  
  --------------------------------------------------------------------------------
  -- * Word8 parsers
  --------------------------------------------------------------------------------
  
  satisfy :: (Word8 -> Bool) -> Parser s Word8
  satisfy f = Parser \p s -> case readWord8OffAddr# p 0# s of
    (# t, c #) -> if isTrue# (0## `neWord#` c) && f (W8# c)
      then OK (W8# c) (plusAddr# p 1#) t
      else Fail p t
  {-# inline satisfy #-}
  
  notWord8 :: Word8 -> Parser s Word8
  notWord8 0 = anyWord8
  notWord8 (W8# c) = Parser \p s -> case readWord8OffAddr# p 0# s of
    (# t, c' #) -> if isTrue# (0## `neWord#` c') && isTrue# (c `neWord#` c')
      then OK (W8# c') (plusAddr# p 1#) t
      else Fail p t
  {-# inline notWord8 #-}
  
  nextWord8 :: Parser s (Maybe Word8)
  nextWord8 = Parser \p s -> case readWord8OffAddr# p 0# s of
    (# t, c #) -> OK (if isTrue# (0## `neWord#` c) then Just (W8# c) else Nothing) p t
  {-# inline nextWord8 #-}
  
  nextWord8' :: Parser s Word8
  nextWord8' = Parser \p s -> case readWord8OffAddr# p 0# s of
    (# t, c #) -> if isTrue# (0## `neWord#` c)
      then OK (W8# c) p t
      else Fail p t
  {-# inline nextWord8' #-}
  
  anyWord8 :: Parser s Word8
  anyWord8 = Parser \p s -> case readWord8OffAddr# p 0# s of
    (# t, c #) -> if isTrue# (0## `neWord#` c)
      then OK (W8# c) (plusAddr# p 1#) t
      else Fail p t
  {-# inline anyWord8 #-}
  
  scan :: (Word8 -> Bool) -> Addr# -> State# s -> (# State# s, Addr# #)
  scan f = go where
    go p s = case readWord8OffAddr# p 0# s of
      (# t, c #) -> if isTrue# (0## `neWord#` c) && f (W8# c)
        then (# t, p #)
        else scan f (plusAddr# p 1#) t
  {-# inline scan #-}
  
  skipWhile :: (Word8 -> Bool) -> Parser s ()
  skipWhile f = Parser \p s -> case scan f p s of
    (# t, q #) -> OK () q t
  {-# inline [1] skipWhile #-}
  
  {-# RULES
  "skipWhile (x/=)" forall x.
    skipWhile (x `neWord8`) = skipTillWord8 x
  "skipWhile (/=x)" forall x.
    skipWhile (`neWord8` x) = skipTillWord8 x
    #-}
  
  skipTill :: (Word8 -> Bool) -> Parser s ()
  skipTill p = skipWhile (not . p)
  {-# inline [1] skipTill #-}
  
  {-# RULES
  "skipTill (x==)" forall x.
    skipTill (x `eqWord8`) = skipTillWord8 x
  "skipWhile (==x)" forall x.
    skipWhile (`eqWord8` x) = skipTillWord8 x
    #-}
  
  skipTillSome :: (Word8 -> Bool) -> Parser s ()
  skipTillSome p = skipWhileSome (not . p)
  {-# inline skipTillSome #-}
  
  foreign import ccall "parsnip.h" strchr0 :: Addr# -> Char# -> IO (Ptr Word8) -- lazy reimport is lazy
  
  skipTillWord8 :: Word8 -> Parser s ()
  skipTillWord8 (W8# c) = Parser $ \p s -> case io (strchr0 p (chr# (word2Int# c))) s of -- lazy cast is lazy
    (# t, Ptr q #) -> OK () q t
  {-# inline skipTillWord8 #-}
  
  skipWhileSome :: (Word8 -> Bool) -> Parser s ()
  skipWhileSome p = satisfy p *> skipWhile p
  {-# inline skipWhileSome #-}
  
  while :: KnownBase s => (Word8 -> Bool) -> Parser s ByteString
  while f = snipping (skipWhile f)
  {-# inline while #-}
  
  till :: KnownBase s => (Word8 -> Bool) -> Parser s ByteString
  till p = snipping (skipTill p)
  {-# inline till #-}
  
  tillWord8 :: KnownBase s => Word8 -> Parser s ByteString
  tillWord8 c = snipping (skipTillWord8 c)
  {-# inline tillWord8 #-}
  
  whileSome :: KnownBase s => (Word8 -> Bool) -> Parser s ByteString
  whileSome p = snipping (skipWhileSome p)
  {-# inline whileSome #-}
  
  tillSome :: KnownBase s => (Word8 -> Bool) -> Parser s ByteString
  tillSome p = snipping (skipTillSome p)
  {-# inline tillSome #-}
  
  -- | Peek at the previous character. Always succeeds.
  previousWord8 :: forall s. KnownBase s => Parser s (Maybe Word8)
  previousWord8 = case reflectBase @s of
    !(Base _ _ l _) -> Parser \p s ->
      if isTrue# (ltAddr# l p)
      then case readWord8OffAddr# p (-1#) s of
        (# t, c #) -> OK (Just (W8# c)) p t
      else OK Nothing p s
  {-# inline previousWord8 #-}
  
  -- | Peek at the previous character. Fails if we're at the start of input.
  previousWord8' :: forall s. KnownBase s => Parser s Word8
  previousWord8' = case reflectBase @s of
    !(Base _ _ l _) -> Parser \p s ->
      if isTrue# (ltAddr# l p)
      then case readWord8OffAddr# p (-1#) s of
        (# t, c #) -> OK (W8# c) p t
      else Fail p s
  {-# inline previousWord8' #-}
  