    {-# LANGUAGE CPP, BangPatterns #-}
    {-# OPTIONS -Wall #-}
    -----------------------------------------------------------------------------
    -- |
    -- Module      :  Language.C.Data.InputStream
    -- Copyright   :  (c) 2008,2011 Benedikt Huber
    -- License     :  BSD-style
    -- Maintainer  :  benedikt.huber@gmail.com
    -- Stability   :  experimental
    -- Portability :  ghc
    --
    -- Compile time input abstraction for the parser, relying on ByteString.
    -- The String interface only supports Latin-1 since alex-3, as alex now requires
    -- byte based access to the input stream.
    -------------------------------------------------------------------------------
    module Language.C.Data.InputStream (
        InputStream, readInputStream,inputStreamToString,inputStreamFromString,
        takeByte, takeChar, inputStreamEmpty, takeChars,
        countLines,
    )
    where

    import Data.Word

    #ifndef NO_BYTESTRING
    import Data.ByteString (ByteString)
    import qualified Data.ByteString as BSW
    import qualified Data.ByteString.Char8 as BSC
    #else
    import qualified Data.Char as Char
    #endif

    -- Generic InputStream stuff

    -- | read a file into an 'InputStream'
    readInputStream :: FilePath -> IO InputStream

    -- | convert 'InputStream' to 'String'
    inputStreamToString :: InputStream -> String
    {-# INLINE inputStreamToString #-}

    -- | convert a 'String' to an 'InputStream'
    inputStreamFromString :: String -> InputStream

    -- | @(b,is') = takeByte is@ reads and removes
    -- the first byte @b@ from the 'InputStream' @is@
    takeByte :: InputStream -> (Word8, InputStream)
    {-# INLINE takeByte #-}

    -- | @(c,is') = takeChar is@ reads and removes
    -- the first character @c@ from the 'InputStream' @is@
    takeChar :: InputStream -> (Char, InputStream)
    {-# INLINE takeChar #-}

    -- | return @True@ if the given input stream is empty
    inputStreamEmpty :: InputStream -> Bool
    {-# INLINE inputStreamEmpty #-}

    -- | @str = takeChars n is@ returns the first @n@ characters
    -- of the given input stream, without removing them
    takeChars :: Int -> InputStream -> [Char]
    {-# INLINE takeChars #-}

    -- | @countLines@ returns the number of text lines  in the
    -- given 'InputStream'
    countLines :: InputStream -> Int

    #ifndef NO_BYTESTRING

    type InputStream = ByteString
    takeByte :: InputStream -> (Word8, InputStream)
    takeByte InputStream
    bs = HasCallStack => InputStream -> Word8
    InputStream -> Word8
    BSW.head  InputStream
    bs Word8 -> (Word8, InputStream) -> (Word8, InputStream)
    forall a b. a -> b -> b
    `seq`  (HasCallStack => InputStream -> Word8
    InputStream -> Word8
    BSW.head InputStream
    bs, HasCallStack => InputStream -> InputStream
    InputStream -> InputStream
    BSW.tail InputStream
    bs)
    takeChar :: InputStream -> (Char, InputStream)
    takeChar InputStream
    bs = InputStream -> Char
    BSC.head InputStream
    bs Char -> (Char, InputStream) -> (Char, InputStream)
    forall a b. a -> b -> b
    `seq`  (InputStream -> Char
    BSC.head InputStream
    bs, HasCallStack => InputStream -> InputStream
    InputStream -> InputStream
    BSC.tail InputStream
    bs)
    inputStreamEmpty :: InputStream -> Bool
    inputStreamEmpty = InputStream -> Bool
    BSW.null
    takeChars :: Int -> InputStream -> [Char]
    takeChars !Int
    n InputStream
    bstr = InputStream -> [Char]
    BSC.unpack (InputStream -> [Char]) -> InputStream -> [Char]
    forall a b. (a -> b) -> a -> b
    $ Int -> InputStream -> InputStream
    BSC.take Int
    n InputStream
    bstr
    readInputStream :: [Char] -> IO InputStream
    readInputStream       = [Char] -> IO InputStream
    BSW.readFile

    inputStreamToString :: InputStream -> [Char]
    inputStreamToString   = InputStream -> [Char]
    BSC.unpack
    inputStreamFromString :: [Char] -> InputStream
    inputStreamFromString = [Char] -> InputStream
    BSC.pack
    countLines :: InputStream -> Int
    countLines             = [InputStream] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length ([InputStream] -> Int)
    -> (InputStream -> [InputStream]) -> InputStream -> Int
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . InputStream -> [InputStream]
    BSC.lines

    #else

    type InputStream = String
    takeByte bs
      | Char.isLatin1 c = let b = fromIntegral (Char.ord c) in b `seq` (b, tail bs)
      | otherwise       = error "takeByte: not a latin-1 character"
      where c = head bs
    takeChar bs = (head bs, tail bs)
    inputStreamEmpty = null
    takeChars n str = take n str
    readInputStream = readFile
    inputStreamToString = id
    inputStreamFromString = id
    countLines = length . lines
    #endif