{-# LANGUAGE FlexibleContexts #-} -- | Builder-based parsing. This is useful for parsing Bash\'s complicated -- words. module Language.Bash.Parse.Builder ( -- * Builders Builder , fromChar , fromString , toString -- * Monoid parsing , (<+>) , many , many1 -- * Characters , oneOf , noneOf , char , anyChar , satisfy , string -- * Spans , span , matchedPair ) where import Prelude hiding (span) import Control.Applicative hiding (many) import qualified Control.Applicative as Applicative import Data.Monoid import qualified Text.Parsec.Char as P import Text.Parsec.Prim hiding ((<|>), many) infixr 4 <+> -- | An efficient 'String' builder. type Builder = Endo String -- | Construct a 'Builder' from a 'Char'. fromChar :: Char -> Builder fromChar = Endo . showChar -- | Construct a 'Builder' from a 'String'. fromString :: String -> Builder fromString = Endo . showString -- | Convert a 'Builder' to a 'String'. toString :: Builder -> String toString = flip appEndo "" -- | Append two monoidal results. (<+>) :: (Applicative f, Monoid a) => f a -> f a -> f a (<+>) = liftA2 mappend -- | Concat zero or more monoidal results. many :: (Alternative f, Monoid a) => f a -> f a many = fmap mconcat . Applicative.many -- | Concat one or more monoidal results. many1 :: (Alternative f, Monoid a) => f a -> f a many1 = fmap mconcat . Applicative.some -- | 'Builder' version of 'P.oneOf'. oneOf :: Stream s m Char => [Char] -> ParsecT s u m Builder oneOf cs = fromChar <$> P.oneOf cs -- | 'Builder' version of 'P.noneOf'. noneOf :: Stream s m Char => [Char] -> ParsecT s u m Builder noneOf cs = fromChar <$> P.noneOf cs -- | 'Builder' version of 'P.char'. char :: Stream s m Char => Char -> ParsecT s u m Builder char c = fromChar c <$ P.char c -- | 'Builder' version of 'P.anyChar'. anyChar :: Stream s m Char => ParsecT s u m Builder anyChar = fromChar <$> P.anyChar -- | 'Builder' version of 'P.satisfy'. satisfy :: Stream s m Char => (Char -> Bool) -> ParsecT s u m Builder satisfy p = fromChar <$> P.satisfy p -- | 'Builder' version of 'P.string'. string :: Stream s m Char => String -> ParsecT s u m Builder string s = fromString s <$ P.string s -- | @span start end escape@ parses a span of text starting with @start@ and -- ending with @end@, with possible @escape@ sequences inside. span :: Stream s m Char => Char -> Char -> ParsecT s u m Builder -> ParsecT s u m Builder span start end esc = char start *> many inner <* char end where inner = esc <|> satisfy (/= end) -- | @matchedPair start end escape@ parses @span start end escape@, including -- the surrounding @start@ and @end@ characters. matchedPair :: Stream s m Char => Char -> Char -> ParsecT s u m Builder -> ParsecT s u m Builder matchedPair start end esc = char start <+> many inner <+> char end where inner = esc <|> satisfy (/= end)