module Data.Git.Parser
(
Parser
, P.Result(..)
, eitherParse
, eitherParseChunks
, maybeParse
, maybeParseChunks
, word32
, ref
, referenceBin
, referenceHex
, vlf
, tillEOL
, skipEOL
, skipASCII
, takeUntilASCII
, decimal
, takeWhile1
, string
, P.anyByte
, P.byte
, P.bytes
, P.take
, P.takeWhile
, P.parse
, P.parseFeed
, P.takeAll
, P.hasMore
) where
import qualified Data.ByteArray.Parse as P
import Data.ByteArray (ByteArray)
import Data.Bits
import Data.Word (Word8, Word32)
import Data.Char (isDigit)
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as BC
import Data.Git.Ref
import Data.Git.Internal
import Data.Git.Imports
type Parser = P.Parser B.ByteString
vlf :: Parser [Word8]
vlf = do
bs <- P.takeWhile (\w -> w `testBit` 7)
l <- P.anyByte
return $ (map (\w -> w `clearBit` 7) $ B.unpack bs) ++ [l]
word32 :: Parser Word32
word32 = be32 <$> P.take 4
ref, referenceBin, referenceHex :: HashAlgorithm hash => Parser (Ref hash)
ref = referenceBin
referenceBin = takeDigestSize hashAlg 1 fromBinary
referenceHex = takeDigestSize hashAlg 2 fromHex
takeDigestSize :: HashAlgorithm hash => hash -> Int -> (B.ByteString -> Ref hash) -> Parser (Ref hash)
takeDigestSize alg modifier constr = constr <$> P.take (modifier * hashDigestSize alg)
decimal :: (Read n, Num n) => Parser n
decimal = toNum <$> P.takeWhile (\x -> isDigit $ toEnum (fromIntegral x))
where toNum = read . BC.unpack
string :: B.ByteString -> Parser ()
string = P.bytes
maybeParse :: Parser a -> B.ByteString -> Maybe a
maybeParse f = toMaybe . P.parse f
maybeParseChunks :: Parser a -> [B.ByteString] -> Maybe a
maybeParseChunks p [] = toMaybe $ P.parse p B.empty
maybeParseChunks p (i:is) = loop (P.parse p i) is
where
loop (P.ParseOK _ a) [] = Just a
loop (P.ParseMore c) [] = toMaybe $ c Nothing
loop (P.ParseMore c) (x:xs) = loop (c $ Just x) xs
loop _ _ = Nothing
toMaybe :: P.Result t a -> Maybe a
toMaybe (P.ParseOK _ a) = Just a
toMaybe (P.ParseMore c) = toMaybe (c Nothing)
toMaybe _ = Nothing
eitherParse :: Parser a -> B.ByteString -> Either String a
eitherParse f = toEither . P.parse f
eitherParseChunks :: Show a => Parser a -> [B.ByteString] -> Either String a
eitherParseChunks p [] = toEither $ P.parse p B.empty
eitherParseChunks p (i:is) = loop (P.parse p i) is
where
loop (P.ParseOK _ a) [] = Right a
loop (P.ParseMore c) [] = toEither $ c Nothing
loop (P.ParseMore c) (x:xs) = loop (c $ Just x) xs
loop ps l = Left ("eitherParseChunk: error: " <> show ps <> " : " <> show l)
toEither :: P.Result t b -> Either String b
toEither (P.ParseOK _ a) = Right a
toEither (P.ParseFail e) = Left e
toEither (P.ParseMore c) = toEither (c Nothing)
takeUntilASCII :: ByteArray byteArray => Char -> P.Parser byteArray byteArray
takeUntilASCII char = P.takeWhile (\c -> if fromEnum c < 0x80 then fromEnum c /= fromEnum char else True)
tillEOL :: Parser B.ByteString
tillEOL = P.takeWhile (/= asciiEOL)
skipEOL :: Parser ()
skipEOL = P.byte asciiEOL >> return ()
skipASCII :: Char -> Parser ()
skipASCII c
| cp < 0x80 = P.byte (fromIntegral cp) >> return ()
| otherwise = error ("skipASCII: " ++ show c ++ " not a valid ASCII character")
where
cp = fromEnum c
asciiEOL :: Word8
asciiEOL = fromIntegral $ fromEnum '\n'
isByte :: ByteArray byteArray => (Word8 -> Bool) -> P.Parser byteArray Word8
isByte predicate = do
b <- P.anyByte
if predicate b then return b else fail "isByte"
takeWhile1 :: (Word8 -> Bool) -> Parser B.ByteString
takeWhile1 predicate =
B.cons <$> isByte predicate <*> P.takeWhile predicate