module Data.Attoparsec.Text.Internal
(
Parser
, Result
, parse
, parseOnly
, (<?>)
, try
, module Data.Attoparsec.Combinator
, satisfy
, satisfyWith
, anyChar
, skip
, char
, notChar
, peekChar
, inClass
, notInClass
, skipWhile
, string
, stringCI
, asciiCI
, take
, scan
, takeWhile
, takeWhile1
, takeTill
, takeText
, takeLazyText
, endOfInput
, atEnd
, endOfLine
) where
import Control.Applicative ((<|>), (<$>))
import Control.Monad (when)
import Data.Attoparsec.Combinator
import Data.Attoparsec.Internal.Types hiding (Parser, Input, Added, Failure, Success)
import Data.Monoid (Monoid(..))
import Data.String (IsString(..))
import Data.Text (Text)
import Prelude hiding (getChar, take, takeWhile)
import Data.Char (chr, ord)
import qualified Data.Attoparsec.Internal.Types as T
import qualified Data.Attoparsec.Text.FastSet as Set
import qualified Data.Text as T
import qualified Data.Text.Internal as T
import qualified Data.Text.Lazy as L
type Parser = T.Parser Text
type Result = IResult Text
type Input = T.Input Text
type Added = T.Added Text
type Failure r = T.Failure Text r
type Success a r = T.Success Text a r
instance (a ~ Text) => IsString (Parser a) where
fromString = string . T.pack
lengthAtLeast :: T.Text -> Int -> Bool
lengthAtLeast t@(T.Text _ _ len) n = (len `div` 2) >= n || T.length t >= n
ensure :: Int -> Parser Text
ensure !n = T.Parser $ \i0 a0 m0 kf ks ->
if lengthAtLeast (unI i0) n
then ks i0 a0 m0 (unI i0)
else runParser (demandInput >> go n) i0 a0 m0 kf ks
where
go n' = T.Parser $ \i0 a0 m0 kf ks ->
if lengthAtLeast (unI i0) n'
then ks i0 a0 m0 (unI i0)
else runParser (demandInput >> go n') i0 a0 m0 kf ks
prompt :: Input -> Added -> More
-> (Input -> Added -> More -> Result r)
-> (Input -> Added -> More -> Result r)
-> Result r
prompt i0 a0 _m0 kf ks = Partial $ \s ->
if T.null s
then kf i0 a0 Complete
else ks (i0 <> I s) (a0 <> A s) Incomplete
demandInput :: Parser ()
demandInput = T.Parser $ \i0 a0 m0 kf ks ->
if m0 == Complete
then kf i0 a0 m0 ["demandInput"] "not enough input"
else let kf' i a m = kf i a m ["demandInput"] "not enough input"
ks' i a m = ks i a m ()
in prompt i0 a0 m0 kf' ks'
wantInput :: Parser Bool
wantInput = T.Parser $ \i0 a0 m0 _kf ks ->
case () of
_ | not (T.null (unI i0)) -> ks i0 a0 m0 True
| m0 == Complete -> ks i0 a0 m0 False
| otherwise -> let kf' i a m = ks i a m False
ks' i a m = ks i a m True
in prompt i0 a0 m0 kf' ks'
get :: Parser Text
get = T.Parser $ \i0 a0 m0 _kf ks -> ks i0 a0 m0 (unI i0)
put :: Text -> Parser ()
put s = T.Parser $ \_i0 a0 m0 _kf ks -> ks (I s) a0 m0 ()
try :: Parser a -> Parser a
try p = p
unsafeHead :: Text -> Char
unsafeHead = T.head
unsafeTail :: Text -> Text
unsafeTail = T.tail
unsafeTake :: Int -> Text -> Text
unsafeTake = T.take
unsafeDrop :: Int -> Text -> Text
unsafeDrop = T.drop
satisfy :: (Char -> Bool) -> Parser Char
satisfy p = do
s <- ensure 1
let !w = unsafeHead s
if p w
then put (unsafeTail s) >> return w
else fail "satisfy"
skip :: (Char -> Bool) -> Parser ()
skip p = do
s <- ensure 1
if p (unsafeHead s)
then put (unsafeTail s)
else fail "skip"
satisfyWith :: (Char -> a) -> (a -> Bool) -> Parser a
satisfyWith f p = do
s <- ensure 1
let c = f $! unsafeHead s
if p c
then let !t = unsafeTail s
in put t >> return c
else fail "satisfyWith"
takeWith :: Int -> (Text -> Bool) -> Parser Text
takeWith n p = do
s <- ensure n
let (h,t) = T.splitAt n s
if p h
then put t >> return h
else fail "takeWith"
take :: Int -> Parser Text
take n = takeWith n (const True)
string :: Text -> Parser Text
string s = takeWith (T.length s) (==s)
stringCI :: Text -> Parser Text
stringCI s = go 0
where
go !n
| n > T.length fs = fail "stringCI"
| otherwise = do
t <- ensure n
let h = unsafeTake n t
if T.toCaseFold h == fs
then put (unsafeDrop n t) >> return h
else go (n+1)
fs = T.toCaseFold s
asciiCI :: Text -> Parser Text
asciiCI input = do
t <- ensure n
let h = unsafeTake n t
if asciiToLower h == s
then put (unsafeDrop n t) >> return h
else fail "asciiCI"
where
n = T.length input
s = asciiToLower input
asciiToLower = T.map f
where
offset = ord 'a' ord 'A'
f c | 'A' <= c && c <= 'Z' = chr (ord c + offset)
| otherwise = c
skipWhile :: (Char -> Bool) -> Parser ()
skipWhile p = go
where
go = do
t <- T.dropWhile p <$> get
put t
when (T.null t) $ do
input <- wantInput
when input go
takeTill :: (Char -> Bool) -> Parser Text
takeTill p = takeWhile (not . p)
takeWhile :: (Char -> Bool) -> Parser Text
takeWhile p = (T.concat . reverse) `fmap` go []
where
go acc = do
(h,t) <- T.span p <$> get
put t
if T.null t
then do
input <- wantInput
if input
then go (h:acc)
else return (h:acc)
else return (h:acc)
takeRest :: Parser [Text]
takeRest = go []
where
go acc = do
input <- wantInput
if input
then do
s <- get
put T.empty
go (s:acc)
else return (reverse acc)
takeText :: Parser Text
takeText = T.concat `fmap` takeRest
takeLazyText :: Parser L.Text
takeLazyText = L.fromChunks `fmap` takeRest
data Scan s = Continue s
| Finished !Int T.Text
scan :: s -> (s -> Char -> Maybe s) -> Parser Text
scan s0 p = do
chunks <- go [] s0
case chunks of
[x] -> return x
xs -> return . T.concat . reverse $ xs
where
scanner s !n t =
case T.uncons t of
Just (c,t') -> case p s c of
Just s' -> scanner s' (n+1) t'
Nothing -> Finished n t
Nothing -> Continue s
go acc s = do
input <- get
case scanner s 0 input of
Continue s' -> do put T.empty
more <- wantInput
if more
then go (input : acc) s'
else return (input : acc)
Finished n t -> put t >> return (T.take n input : acc)
takeWhile1 :: (Char -> Bool) -> Parser Text
takeWhile1 p = do
(`when` demandInput) =<< T.null <$> get
(h,t) <- T.span p <$> get
when (T.null h) $ fail "takeWhile1"
put t
if T.null t
then (h<>) `fmap` takeWhile p
else return h
inClass :: String -> Char -> Bool
inClass s = (`Set.member` mySet)
where mySet = Set.charClass s
notInClass :: String -> Char -> Bool
notInClass s = not . inClass s
anyChar :: Parser Char
anyChar = satisfy $ const True
char :: Char -> Parser Char
char c = satisfy (== c) <?> show c
notChar :: Char -> Parser Char
notChar c = satisfy (/= c) <?> "not " ++ show c
peekChar :: Parser (Maybe Char)
peekChar = T.Parser $ \i0 a0 m0 _kf ks ->
if T.null (unI i0)
then if m0 == Complete
then ks i0 a0 m0 Nothing
else let ks' i a m = let !c = unsafeHead (unI i)
in ks i a m (Just c)
kf' i a m = ks i a m Nothing
in prompt i0 a0 m0 kf' ks'
else let !c = unsafeHead (unI i0)
in ks i0 a0 m0 (Just c)
endOfInput :: Parser ()
endOfInput = T.Parser $ \i0 a0 m0 kf ks ->
if T.null (unI i0)
then if m0 == Complete
then ks i0 a0 m0 ()
else let kf' i1 a1 m1 _ _ = addS i0 a0 m0 i1 a1 m1 $
\ i2 a2 m2 -> ks i2 a2 m2 ()
ks' i1 a1 m1 _ = addS i0 a0 m0 i1 a1 m1 $
\ i2 a2 m2 -> kf i2 a2 m2 []
"endOfInput"
in runParser demandInput i0 a0 m0 kf' ks'
else kf i0 a0 m0 [] "endOfInput"
atEnd :: Parser Bool
atEnd = not <$> wantInput
endOfLine :: Parser ()
endOfLine = (char '\n' >> return ()) <|> (string "\r\n" >> return ())
(<?>) :: Parser a
-> String
-> Parser a
p <?> msg0 = T.Parser $ \i0 a0 m0 kf ks ->
let kf' i a m strs msg = kf i a m (msg0:strs) msg
in runParser p i0 a0 m0 kf' ks
infix 0 <?>
failK :: Failure a
failK i0 _a0 _m0 stack msg = Fail (unI i0) stack msg
successK :: Success a a
successK i0 _a0 _m0 a = Done (unI i0) a
parse :: Parser a -> Text -> Result a
parse m s = runParser m (I s) mempty Incomplete failK successK
parseOnly :: Parser a -> Text -> Either String a
parseOnly m s = case runParser m (I s) mempty Complete failK successK of
Fail _ _ err -> Left err
Done _ a -> Right a
_ -> error "parseOnly: impossible error!"