{-#LANGUAGE FlexibleContexts, ExistentialQuantification, ScopedTypeVariables, OverloadedStrings, TypeSynonymInstances, FlexibleInstances #-}
module Transient.Parse where
import Transient.Internals
import Transient.Indeterminism
import Data.String
import Data.Typeable
import Control.Applicative
import Data.Char
import Data.Monoid
import System.IO.Unsafe
import Control.Monad
import Control.Monad.State
import Control.Concurrent.MVar
import qualified Data.ByteString.Lazy.Char8 as BS
setParseStream :: IO (StreamData BS.ByteString) -> TransIO ()
setParseStream iox= do delData NoRemote; setState $ ParseContext iox ""
setParseString :: BS.ByteString -> TransIO ()
setParseString x = do delData NoRemote; setState $ ParseContext (return SDone) x
withParseString :: BS.ByteString -> TransIO a -> TransIO a
withParseString x parse= do
p@(ParseContext c str) <- getState <|> return(ParseContext (return SDone) mempty)
setParseString x
r <- parse
setState (ParseContext c (str :: BS.ByteString))
return r
data ParseContext str = IsString str => ParseContext (IO (StreamData str)) str deriving Typeable
string :: BS.ByteString -> TransIO BS.ByteString
string s= withData $ \str -> do
let len= BS.length s
ret@(s',_) = BS.splitAt len str
if s == s'
then return ret
else empty
tDropUntilToken token= withData $ \str ->
if BS.null str then empty else drop2 str
where
drop2 str=
if token `BS.isPrefixOf` str !> (BS.take 2 str)
then return ((),BS.drop (BS.length token) str)
else if not $ BS.null str then drop2 $ BS.tail str else empty
tTakeUntilToken :: BS.ByteString -> TransIO BS.ByteString
tTakeUntilToken token= withData $ \str -> takeit mempty str
where
takeit :: BS.ByteString -> BS.ByteString -> TransIO ( BS.ByteString, BS.ByteString)
takeit res str=
if BS.null str then return (res,str) else
if token `BS.isPrefixOf` str
then return (res !> ("tTakeUntilString",res),BS.drop (BS.length token) str)
else if not $ BS.null str then takeit ( BS.snoc res (BS.head str)) $ BS.tail str else empty
integer :: TransIO Integer
integer= do
s <- tTakeWhile isNumber
if BS.null s then empty else return $ stoi 0 s
:: TransIO Integer
where
stoi :: Integer -> BS.ByteString -> Integer
stoi x s| BS.null s = x
| otherwise= stoi (x *10 + fromIntegral(ord (BS.head s) - ord '0')) (BS.tail s)
int :: TransIO Int
int= do
s <- tTakeWhile' isNumber
if BS.null s then empty else return $ stoi 0 s
where
stoi :: Int -> BS.ByteString -> Int
stoi x s| BS.null s = x
| otherwise= stoi (x *10 + (ord (BS.head s) - ord '0')) (BS.tail s)
manyTill :: TransIO a -> TransIO b -> TransIO [a]
manyTill= chainManyTill (:)
chainManyTill op p end= op <$> p <*> scan
where
scan = do{ end; return mempty }
<|>
do{ x <- p; xs <- scan; return (x `op` xs) }
between open close p
= do{ open; x <- p; close; return x }
symbol = string
parens p = between (symbol "(") (symbol ")") p !> "parens "
braces p = between (symbol "{") (symbol "}") p !> "braces "
angles p = between (symbol "<") (symbol ">") p !> "angles "
brackets p = between (symbol "[") (symbol "]") p !> "brackets "
semi = symbol ";" !> "semi"
comma = symbol "," !> "comma"
dot = symbol "." !> "dot"
colon = symbol ":" !> "colon"
sepBy p sep = sepBy1 p sep <|> return []
sepBy1 = chainSepBy1 (:)
chainSepBy chain p sep= chainSepBy1 chain p sep <|> return mempty
chainSepBy1
:: (Monad m, Monoid b, Alternative m) =>
(a -> b -> b) -> m a -> m x -> m b
chainSepBy1 chain p sep= do{ x <- p
; xs <- chainMany chain (sep >> p)
; return (x `chain` xs)
}
!> "chainSepBy "
chainMany chain v= (chain <$> v <*> chainMany chain v) <|> return mempty
commaSep p = sepBy p comma
semiSep p = sepBy p semi
commaSep1 p = sepBy1 p comma
semiSep1 p = sepBy1 p semi
dropSpaces= withData $ \str -> return( (),BS.dropWhile isSpace str)
dropTillEndOfLine= withData $ \str -> return ((),BS.dropWhile ( /= '\n') str) !> "dropTillEndOfLine"
parseString= do
dropSpaces
tTakeWhile (not . isSpace)
tTakeWhile :: (Char -> Bool) -> TransIO BS.ByteString
tTakeWhile cond=
withData $ \s -> let (h,t)= BS.span cond s in if BS.null h then empty else return (h,t) !> ("tTakeWhile",h)
tTakeWhile' :: (Char -> Bool) -> TransIO BS.ByteString
tTakeWhile' cond= withData $ \s -> do
let (h,t)= BS.span cond s
return () !> ("takewhile'",h,t)
if BS.null h then empty else return (h, if BS.null t then t else BS.tail t)
just1 f x= let (h,t)= f x in (Just h,t)
tTake n= withData $ \s -> return $ BS.splitAt n s
tDrop n= withData $ \s -> return $ ((),BS.drop n s)
anyChar= withData $ \s -> if BS.null s then empty else return (BS.head s,BS.tail s)
tChar c= withData $ \s -> if BS.null s || BS.head s /= c then empty else return (BS.head s,BS.tail s) !> ("tChar", BS.head s)
withData :: (BS.ByteString -> TransIO (a,BS.ByteString)) -> TransIO a
withData parser= Transient $ do
ParseContext readMore s <- getData `onNothing` error "parser: no context"
let loop = unsafeInterleaveIO $ do
mr <- readMore
return () !> ("readMore",mr)
case mr of
SMore r -> return r <> loop
SLast r -> return r
SDone -> return mempty
str <- liftIO $ return s <> loop
mr <- runTrans $ parser str
case mr of
Nothing -> return Nothing
Just (v,str') -> do
setData $ ParseContext readMore str'
return $ Just v
giveData= (noTrans $ do
ParseContext readMore s <- getData `onNothing` error "parser: no context"
:: StateIO (ParseContext BS.ByteString)
let loop = unsafeInterleaveIO $ do
mr <- readMore
case mr of
SMore r -> (r <>) `liftM` loop
SLast r -> (r <>) `liftM` loop
SDone -> return mempty
liftIO $ (s <> ) `liftM` loop)
isDone :: TransIO Bool
isDone= noTrans $ do
return () !> "isDone"
ParseContext readMore s <- getData `onNothing` error "parser: no context"
:: StateIO (ParseContext BS.ByteString)
if not $ BS.null s then return False else do
mr <- liftIO readMore
case mr of
SMore r -> do setData $ ParseContext readMore r ; return False
SLast r -> do setData $ ParseContext readMore r ; return False
SDone -> return True
(|-) :: TransIO (StreamData BS.ByteString) -> TransIO b -> TransIO b
p |- q = do
v <- liftIO $ newEmptyMVar
initp v <|> initq v
where
initq v= do
setParseStream (takeMVar v >>= \v -> (return v !> ("!- operator return",v)))
q
initp v= abduce >> repeatIt
where
repeatIt= (do r <- p; liftIO (putMVar v r !> "putMVar") ; empty) <|> repeatIt