argo-0.2021.10.12: source/library/Argo/Decoder.hs
module Argo.Decoder where
import qualified Argo.Literal as Literal
import qualified Control.Applicative as Applicative
import qualified Control.Monad as Monad
import qualified Data.Array as Array
import qualified Data.ByteString as ByteString
import qualified Data.Word as Word
newtype Decoder a = Decoder
{ run :: ByteString.ByteString -> Maybe (ByteString.ByteString, a)
}
instance Functor Decoder where
fmap f d = Decoder $ \ b1 -> case run d b1 of
Nothing -> Nothing
Just (b2, x) -> Just (b2, f x)
instance Applicative Decoder where
pure x = Decoder $ \ b -> Just (b, x)
df <*> dx = Decoder $ \ b1 -> case run df b1 of
Nothing -> Nothing
Just (b2, f) -> case run dx b2 of
Nothing -> Nothing
Just (b3, x) -> Just (b3, f x)
instance Monad Decoder where
d >>= f = Decoder $ \ b1 -> case run d b1 of
Nothing -> Nothing
Just (b2, x) -> run (f x) b2
instance MonadFail Decoder where
fail _ = Decoder $ const Nothing
instance Applicative.Alternative Decoder where
empty = fail "empty"
dx <|> dy = Decoder $ \ b1 -> case run dx b1 of
Nothing -> run dy b1
Just (b2, x) -> Just (b2, x)
array :: Decoder a -> Decoder (Array.Array Int a)
array f = arrayWith f 0 []
arrayWith :: Decoder a -> Int -> [(Int, a)] -> Decoder (Array.Array Int a)
arrayWith f n xs = do
m <- Applicative.optional $ do
Monad.when (n /= 0) $ do
word8 Literal.comma
spaces
f
case m of
Nothing -> pure $ Array.array (0, n - 1) xs
Just x -> arrayWith f (n + 1) $ (n, x) : xs
byteString :: ByteString.ByteString -> Decoder ()
byteString x = do
b1 <- get
case ByteString.stripPrefix x b1 of
Nothing -> fail $ "byteString: " <> show x
Just b2 -> put b2
dropWhile :: (Word.Word8 -> Bool) -> Decoder ()
dropWhile f = do
b <- get
put $ ByteString.dropWhile f b
eof :: Decoder ()
eof = do
b <- get
Monad.unless (ByteString.null b) $ fail "eof"
get :: Decoder ByteString.ByteString
get = Decoder $ \ b -> Just (b, b)
isDigit :: Word.Word8 -> Bool
isDigit x = Literal.digitZero <= x && x <= Literal.digitNine
isSpace :: Word.Word8 -> Bool
isSpace x =
x == Literal.space
|| x == Literal.horizontalTabulation
|| x == Literal.newLine
|| x == Literal.carriageReturn
put :: ByteString.ByteString -> Decoder ()
put b = Decoder $ \ _ -> Just (b, ())
satisfy :: (Word.Word8 -> Bool) -> Decoder Word.Word8
satisfy f = do
b1 <- get
case ByteString.uncons b1 of
Just (x, b2) | f x -> do
put b2
pure x
_ -> fail "satisfy"
spaces :: Decoder ()
spaces = Argo.Decoder.dropWhile isSpace
takeWhile :: (Word.Word8 -> Bool) -> Decoder ByteString.ByteString
takeWhile f = do
b1 <- get
let (x, b2) = ByteString.span f b1
put b2
pure x
takeWhile1 :: (Word.Word8 -> Bool) -> Decoder ByteString.ByteString
takeWhile1 f = do
x <- Argo.Decoder.takeWhile f
Monad.when (ByteString.null x) $ fail "takeWhile1"
pure x
word8 :: Word.Word8 -> Decoder ()
word8 = Monad.void . satisfy . (==)