content: {-# language MagicHash #-}
  {-# language BlockArguments #-}
  {-# language UnboxedTuples #-}
  {-# language BangPatterns #-}
  {-# language TypeApplications #-}
  {-# language NegativeLiterals #-}
  {-# language UnliftedFFITypes #-}
  {-# language ScopedTypeVariables #-}
  {-# language ForeignFunctionInterface #-}
  module Text.Parsnip.Char8
  ( satisfy
  , char
  , notChar
  , anyChar
  , digit
  , space
  , skipSpace
  , letter_ascii
  , letter_iso8859_15
  , while, whileSome
  , till, tillSome, tillChar
  , skipWhile, skipWhileSome
  , skipTill, skipTillSome, skipTillChar
  , previousChar, previousChar'
  , nextChar, nextChar'
  ) where
  
  import Control.Applicative
  import qualified Data.Attoparsec.ByteString.Char8 as A
  import Data.ByteString (ByteString)
  import Data.Word
  import GHC.Char
  import GHC.Prim
  import GHC.Ptr
  import GHC.Types
  import Text.Parsnip.Internal.Parser
  import Text.Parsnip.Internal.Private
  import Text.Parsnip.Parser
  
  --------------------------------------------------------------------------------
  -- * Char parsers
  --------------------------------------------------------------------------------
  
  satisfy :: (Char -> Bool) -> Parser s Char
  satisfy f = Parser \p s -> case readCharOffAddr# p 0# s of
    (# t, c #) -> if isTrue# (chr# 0# `neChar#` c) && f (C# c)
      then OK (C# c) (plusAddr# p 1#) t
      else Fail p t
  {-# inline satisfy #-}
  
  char :: Char -> Parser s Char
  char '\0' = empty
  char r@(C# c) = Parser \p s -> case readCharOffAddr# p 0# s of
    (# t, c' #) -> if isTrue# (eqChar# c c')
      then OK r (plusAddr# p 1#) t
      else Fail p t
  {-# inline char #-}
  
  notChar :: Char -> Parser s Char
  notChar '\0' = anyChar
  notChar (C# c) = Parser \p s -> case readCharOffAddr# p 0# s of
    (# t, c' #) -> if isTrue# (chr# 0# `neChar#` c') && isTrue# (neChar# c c')
      then OK (C# c') (plusAddr# p 1#) t
      else Fail p t
  {-# inline notChar #-}
  
  nextChar :: Parser s (Maybe Char)
  nextChar = Parser \p s -> case readCharOffAddr# p 0# s of
    (# t, c #) -> OK (if isTrue# (chr# 0# `neChar#` c) then Just (C# c) else Nothing) p t
  {-# inline nextChar #-}
  
  nextChar' :: Parser s Char
  nextChar' = Parser \p s -> case readCharOffAddr# p 0# s of
    (# t, c #) -> if isTrue# (chr# 0# `neChar#` c)
      then OK (C# c) p t
      else Fail p t
  {-# inline nextChar' #-}
  
  anyChar :: Parser s Char
  anyChar = Parser \p s -> case readCharOffAddr# p 0# s of
    (# t, c #) -> if isTrue# (chr# 0# `neChar#` c)
      then OK (C# c) (plusAddr# p 1#) t
      else Fail p t
  {-# inline anyChar #-}
  
  digit :: Parser s Char
  digit = satisfy A.isDigit 
  {-# inline digit #-}
  
  space :: Parser s Char
  space = satisfy A.isSpace
  {-# inline space #-}
  
  skipSpace :: Parser s ()
  skipSpace = skipWhile A.isSpace
  {-# inline skipSpace #-}
  
  letter_ascii :: Parser s Char
  letter_ascii = satisfy A.isAlpha_ascii
  {-# inline letter_ascii #-}
  
  letter_iso8859_15:: Parser s Char
  letter_iso8859_15 = satisfy A.isAlpha_iso8859_15
  {-# inline letter_iso8859_15 #-}
  
  scan :: (Char -> Bool) -> Addr# -> State# s -> (# State# s, Addr# #)
  scan f = go where
    go p s = case readCharOffAddr# p 0# s of
      (# t, c #) -> if isTrue# (chr# 0# `neChar#` c) && f (C# c)
        then (# t, p #)
        else scan f (plusAddr# p 1#) t
  {-# inline scan #-}
  
  skipWhile :: (Char -> 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 `neChar`) = skipTillChar x
  "skipWhile (/=x)" forall x.
    skipWhile (`neChar` x) = skipTillChar x
    #-}
  
  skipTill :: (Char -> Bool) -> Parser s ()
  skipTill p = skipWhile (not . p)
  {-# inline [1] skipTill #-}
  
  {-# RULES
  "skipTill (x==)" forall x.
    skipTill (x `eqChar`) = skipTillChar x
  "skipWhile (==x)" forall x.
    skipWhile (`eqChar` x) = skipTillChar x
    #-}
  
  skipTillSome :: (Char -> Bool) -> Parser s ()
  skipTillSome p = skipWhileSome (not . p)
  {-# inline skipTillSome #-}
  
  foreign import ccall "parsnip.h" strchr0 :: Addr# -> Char# -> IO (Ptr Word8)
  
  skipTillChar :: Char -> Parser s ()
  skipTillChar (C# c) = Parser $ \p s -> case io (strchr0 p c) s of
    (# t, Ptr q #) -> OK () q t
  {-# inline skipTillChar #-}
  
  skipWhileSome :: (Char -> Bool) -> Parser s ()
  skipWhileSome p = satisfy p *> skipWhile p
  {-# inline skipWhileSome #-}
  
  while :: KnownBase s => (Char -> Bool) -> Parser s ByteString
  while f = snipping (skipWhile f)
  {-# inline while #-}  
  
  till :: KnownBase s => (Char -> Bool) -> Parser s ByteString
  till p = snipping (skipTill p)
  {-# inline till #-}
  
  tillChar :: KnownBase s => Char -> Parser s ByteString
  tillChar c = snipping (skipTillChar c)
  {-# inline tillChar #-}
  
  whileSome :: KnownBase s => (Char -> Bool) -> Parser s ByteString
  whileSome p = snipping (skipWhileSome p)
  {-# inline whileSome #-}
  
  tillSome :: KnownBase s => (Char -> Bool) -> Parser s ByteString
  tillSome p = snipping (skipTillSome p)
  {-# inline tillSome #-}
  
  -- Peek at the previous character. Always succeeds. 
  previousChar :: forall s. KnownBase s => Parser s (Maybe Char)
  previousChar = case reflectBase @s of
    !(Base _ _ l _) -> Parser \p s ->
      if isTrue# (ltAddr# l p)
      then case readCharOffAddr# p (-1#) s of
        (# t, c #) -> OK (Just (C# c)) p t
      else OK Nothing p s
  
  -- Peek at the previous character. Fails if we're at the start of input.
  previousChar' :: forall s. KnownBase s => Parser s Char
  previousChar' = case reflectBase @s of
    !(Base _ _ l _) -> Parser \p s ->
      if isTrue# (ltAddr# l p)
      then case readCharOffAddr# p (-1#) s of
        (# t, c #) -> OK (C# c) p t
      else Fail p s
  