-- | This module defines our parsing monad. In the past there have been lazy -- and strict parsers in this module. Currently we have only the strict -- variant and it is used for parsing patch files. module Darcs.Patch.ReadMonads (ParserM, work, maybeWork, alterInput, parseStrictly, peekInput, lexChar, lexString, lexStrings, lexEof, myLex) where import ByteStringUtils ( dropSpace, breakSpace ) import qualified Data.ByteString as B (null, empty, ByteString) import qualified Data.ByteString.Char8 as BC (unpack, pack) -- | 'lexChar' checks if the next space delimited token from -- the input stream matches a specific 'Char'. -- Uses 'Maybe' inside 'ParserM' to handle failed matches, so -- that it always returns () on success. lexChar :: ParserM m => Char -> m () lexChar c = lexString [c] -- | 'lexString' fetches the next whitespace delimited token from -- from the input and checks if it matches the 'String' input. -- Uses 'Maybe' inside 'ParserM' to handle failed matches, so -- that it always returns () on success. lexString :: ParserM m => String -> m () lexString str = work $ \s -> case myLex s of Just (xs, ys) | xs == BC.pack str -> Just ((), ys) _ -> Nothing -- | 'lexEof' looks for optional spaces followed by the end of input. -- Uses 'Maybe' inside 'ParserM' to handle failed matches, so -- that it always returns () on success. lexEof :: ParserM m => m () lexEof = work $ \s -> if B.null (dropSpace s) then Just ((), B.empty) else Nothing -- | Checks if any of the input 'String's match the next -- space delimited token in the input stream. -- Uses 'Maybe' inside 'ParserM' to handle failed matches, -- on success it returns the matching 'String'. lexStrings :: ParserM m => [String] -> m String lexStrings str = work $ \s -> case myLex s of Just (xs, ys) | xs' `elem` str -> Just (xs', ys) where xs' = BC.unpack xs _ -> Nothing -- | 'myLex' drops leading spaces and then breaks the string at the -- next space. Returns 'Nothing' when the string is empty after -- dropping leading spaces, otherwise it returns the first sequence -- of non-spaces and the remainder of the input. myLex :: B.ByteString -> Maybe (B.ByteString, B.ByteString) myLex s = let s' = dropSpace s in if B.null s' then Nothing else Just $ breakSpace s' -- | Applies a function to the input stream and discards the -- result of the function. alterInput :: ParserM m => (B.ByteString -> B.ByteString) -> m () alterInput f = work (\s -> Just ((), f s)) class Monad m => ParserM m where -- | Applies a parsing function inside the 'ParserM' monad. work :: (B.ByteString -> Maybe (a, B.ByteString)) -> m a -- | Applies a parsing function, that can return 'Nothing', -- inside the 'ParserM' monad. maybeWork :: (B.ByteString -> Maybe (a, B.ByteString)) -> m (Maybe a) -- | Allows for the inspection of the input that is yet to be parsed. peekInput :: m B.ByteString ----- Strict Monad ----- -- | 'parseStrictly' applies the parser functions to a string -- and checks that each parser produced a result as it goes. -- The strictness is in the 'ParserM' instance for 'SM'. parseStrictly :: SM a -> B.ByteString -> Maybe (a, B.ByteString) parseStrictly (SM f) s = f s -- | 'SM' is the Strict Monad for parsing. newtype SM a = SM (B.ByteString -> Maybe (a, B.ByteString)) instance Monad SM where SM m >>= k = SM $ \s -> case m s of Nothing -> Nothing Just (x, s') -> case k x of SM y -> y s' return x = SM (\s -> Just (x,s)) fail _ = SM (\_ -> Nothing) instance ParserM SM where work f = SM f maybeWork f = SM $ \s -> case f s of Just (x, s') -> Just (Just x, s') Nothing -> Just (Nothing, s) peekInput = SM $ \s -> Just (s, s)