{-# LANGUAGE CPP #-} module Database.MSSQLServer.Query.TokenStreamParser ( Parser(..) , parse , item , satisfy , many , many1 , oneOf , noneOf , noResult , listOfRow , rowCount ) where import Control.Applicative((<$>)) import Control.Applicative(Applicative((<*>),pure),Alternative((<|>),empty)) import Control.Monad(Monad(..),MonadPlus(..),ap) #if MIN_VERSION_base(4,9,0) import Control.Monad.Fail(MonadFail(..)) #endif import Data.Monoid (mconcat,(<>),All(..),Any(..)) import Database.Tds.Message import Database.MSSQLServer.Query.Row data Parser a = Parser ([TokenStream] -> [(a,[TokenStream])]) parse :: Parser a -> [TokenStream] -> [(a,[TokenStream])] parse (Parser p) = p instance Functor Parser where fmap f p = Parser $ \xs -> [(f x,xs') | (x,xs') <- parse p xs] instance Applicative Parser where pure = return (<*>) = ap instance Alternative Parser where empty = mzero (<|>) = mplus instance Monad Parser where return x = Parser $ \xs -> [(x,xs)] p >>= f = Parser $ \ts -> mconcat [parse (f t) ts' | (t,ts') <- parse p ts] instance MonadPlus Parser where mzero = Parser $ \_ -> [] mplus p q = Parser $ \xs -> parse p xs <> parse q xs #if MIN_VERSION_base(4,9,0) instance MonadFail Parser where fail _ = mzero #endif item :: Parser TokenStream item = Parser $ \xs -> case xs of [] -> [] (x:xs') -> [(x,xs')] satisfy :: (TokenStream -> Bool) -> Parser TokenStream satisfy f = do x <- item if f x then return x else empty many :: Parser a -> Parser [a] many p = many1 p <|> return [] many1 :: Parser a -> Parser [a] many1 p = do a <- p as <- many p return $ a:as oneOf :: [TokenStream -> Bool] -> Parser TokenStream oneOf xs = satisfy $ \x -> getAny $ mconcat $ (\f -> Any $ f x) <$> xs noneOf :: [TokenStream -> Bool] -> Parser TokenStream noneOf xs = satisfy $ \x -> getAll $ mconcat $ (\f -> All $ not $ f x) <$> xs noResult :: Parser () noResult = do _ <- many $ satisfy $ not . isTSDone _ <- satisfy isTSDone -- [MEMO] just parse here return () where -- [TODO] check Status contains 0x10 isTSDone :: TokenStream -> Bool isTSDone (TSDone{}) = True isTSDone (TSDoneInProc{}) = True isTSDone _ = False listOfRow :: Row a => Parser ([a]) listOfRow = do _ <- many $ satisfy $ not . isTSColMetaData tsCmd <- satisfy isTSColMetaData _ <- many $ satisfy $ not . isTSRow tsRows <- many $ satisfy isTSRow _ <- many $ satisfy $ not . isTSDone _ <- satisfy isTSDone -- [MEMO] just parse here return $ let (TSColMetaData (maybeCmd)) = tsCmd mcds = case (\(ColMetaData x) -> x) <$> maybeCmd of Nothing -> error "listOfRow: ColMetaData is necessary" Just mcds' -> mcds' rows = (\(TSRow row) -> getRawBytes <$> row) <$> tsRows in fromListOfRawBytes mcds <$> rows where isTSColMetaData :: TokenStream -> Bool isTSColMetaData (TSColMetaData{}) = True isTSColMetaData _ = False isTSRow :: TokenStream -> Bool isTSRow (TSRow{}) = True isTSRow _ = False -- [TODO] check Status contains 0x10 isTSDone :: TokenStream -> Bool isTSDone (TSDone{}) = True isTSDone (TSDoneInProc{}) = True isTSDone _ = False getRawBytes :: RowColumnData -> RawBytes getRawBytes (RCDOrdinal dt) = dt getRawBytes (RCDLarge _ _ dt) = dt rowCount :: Parser Int rowCount = do _ <- many $ satisfy $ not . isTSDone tsDone <- satisfy isTSDone return $ let Done _ _ rc = getDone tsDone in fromIntegral rc where -- [TODO] check Status contains 0x10 isTSDone :: TokenStream -> Bool isTSDone (TSDone{}) = True isTSDone (TSDoneInProc{}) = True isTSDone _ = False getDone :: TokenStream -> Done getDone (TSDone x) = x getDone (TSDoneInProc x) = x getDone _ = error "rowCount: TSDone and TSDoneInProc are only possible here"