{-# LANGUAGE BangPatterns, MagicHash, UnboxedTuples #-}
module Data.Attoparsec.Zepto
(
Parser
, parse
, atEnd
, string
, take
, takeWhile
) where
import Data.Word (Word8)
import Control.Applicative
import Control.Monad
import Data.Monoid
import qualified Data.ByteString as B
import qualified Data.ByteString.Unsafe as B
import Data.ByteString (ByteString)
import Prelude hiding (take, takeWhile)
newtype S = S {
input :: ByteString
}
data Result a = Fail String
| OK !a
newtype Parser a = Parser {
runParser :: S -> (# Result a, S #)
}
instance Functor Parser where
fmap f m = Parser $ \s -> case runParser m s of
(# OK a, s' #) -> (# OK (f a), s' #)
(# Fail err, s' #) -> (# Fail err, s' #)
{-# INLINE fmap #-}
instance Monad Parser where
return a = Parser $ \s -> (# OK a, s #)
{-# INLINE return #-}
m >>= k = Parser $ \s -> case runParser m s of
(# OK a, s' #) -> runParser (k a) s'
(# Fail err, s' #) -> (# Fail err, s' #)
{-# INLINE (>>=) #-}
fail msg = Parser $ \s -> (# Fail msg, s #)
instance MonadPlus Parser where
mzero = fail "mzero"
{-# INLINE mzero #-}
mplus a b = Parser $ \s ->
case runParser a s of
(# ok@(OK _), s' #) -> (# ok, s' #)
(# _, _ #) -> case runParser b s of
(# ok@(OK _), s'' #) -> (# ok, s'' #)
(# err, s'' #) -> (# err, s'' #)
{-# INLINE mplus #-}
instance Applicative Parser where
pure = return
{-# INLINE pure #-}
(<*>) = ap
{-# INLINE (<*>) #-}
gets :: (S -> a) -> Parser a
gets f = Parser $ \s -> (# OK (f s), s #)
{-# INLINE gets #-}
put :: S -> Parser ()
put s = Parser $ \_ -> (# OK (), s #)
{-# INLINE put #-}
parse :: Parser a -> ByteString -> Either String a
parse p bs = case runParser p (S bs) of
(# OK a, _ #) -> Right a
(# Fail err, _ #) -> Left err
instance Monoid (Parser a) where
mempty = fail "mempty"
{-# INLINE mempty #-}
mappend = mplus
{-# INLINE mappend #-}
instance Alternative Parser where
empty = fail "empty"
{-# INLINE empty #-}
(<|>) = mplus
{-# INLINE (<|>) #-}
takeWhile :: (Word8 -> Bool) -> Parser ByteString
takeWhile p = do
(h,t) <- gets (B.span p . input)
put (S t)
return h
{-# INLINE takeWhile #-}
take :: Int -> Parser ByteString
take !n = do
s <- gets input
if B.length s >= n
then put (S (B.unsafeDrop n s)) >> return (B.unsafeTake n s)
else fail "insufficient input"
{-# INLINE take #-}
string :: ByteString -> Parser ()
string s = do
i <- gets input
if s `B.isPrefixOf` i
then put (S (B.unsafeDrop (B.length s) i)) >> return ()
else fail "string"
{-# INLINE string #-}
atEnd :: Parser Bool
atEnd = do
i <- gets input
return $! B.null i
{-# INLINE atEnd #-}