replace-megaparsec 1.4.3.0 → 1.4.4.0
raw patch · 7 files changed
+114/−33 lines, 7 files
Files
- CHANGELOG.md +4/−0
- README.md +3/−4
- replace-megaparsec.cabal +1/−1
- src/Replace/Megaparsec.hs +84/−23
- tests/TestByteString.hs +4/−1
- tests/TestString.hs +9/−2
- tests/TestText.hs +9/−2
CHANGELOG.md view
@@ -1,5 +1,9 @@ # Revision history for replace-megaparsec +## 1.4.4.0 -- 2020-12-04++Add `splitCapT` and `breakCapT`.+ ## 1.4.3.0 -- 2020-09-28 Bugfix sepCap backtracking when sep fails
README.md view
@@ -263,10 +263,9 @@ let editThree :: Char -> MTL.State Int String editThree x = do i <- get- let i' = i+1- if i'<=3+ if i<3 then do- put i'+ put $ i+1 pure [toUpper x] else pure [x] @@ -288,7 +287,7 @@ #!/usr/bin/env stack {- stack script- --resolver lts-15+ --resolver lts-16 --package megaparsec --package replace-megaparsec -}
replace-megaparsec.cabal view
@@ -1,5 +1,5 @@ name: replace-megaparsec-version: 1.4.3.0+version: 1.4.4.0 cabal-version: 1.18 synopsis: Find, replace, and split string patterns with Megaparsec parsers (instead of regex) homepage: https://github.com/jamesdbrock/replace-megaparsec
src/Replace/Megaparsec.hs view
@@ -36,13 +36,10 @@ -- -- See the __replace-megaparsec__ package README for usage examples. ----- == Special accelerated inputs------ There are specialization re-write rules to speed up all functions in this--- module when the input stream type @s@ is "Data.Text" or "Data.ByteString".--- -- == Type constraints --+-- === output stream type @Tokens s@ = input stream type @s@+-- -- All functions in the __Running Parser__ section require the type of the -- stream of text that is input to be -- @'Text.Megaparsec.Stream.Stream' s@@@ -58,12 +55,32 @@ -- * "Data.ByteString" -- * "Data.ByteString.Lazy" ----- Megaparsec parsers have an error type parameter @e@. When writing parsers--- to be used by this module, the error type parameter @e@ should usually+-- === Custom error type @e@ should be 'Data.Void'+--+-- Megaparsec parsers have a custom error data component @e@. When writing parsers+-- to be used by this module, the custom error type @e@ should usually -- be 'Data.Void', because every function in this module expects a parser -- failure to occur on every token in a non-matching section of the input--- stream, so parser failure error descriptions are not returned.+-- stream, so parser failure error descriptions are not returned, and you'll+-- never see the custom error information.+--+-- == Special fast input types+--+-- Functions in this module will be “fast” when the input stream+-- type @s@ is:+--+-- * "Data.Text"+-- * "Data.ByteString"+--+-- We mean “fast” in the same sense as 'Text.Megaparsec.MonadParsec':+-- when returning subsections of the input stream,+-- we return slices of the input stream data, rather than constructing a list+-- of tokens and then building a new stream subsection from that list.+-- This relies on implementation details of the stream representation,+-- so there are specialization re-write rules in this module to make+-- that possible without adding new typeclasses. + {-# LANGUAGE LambdaCase #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TypeFamilies #-}@@ -77,16 +94,19 @@ ( -- * Running parser --- -- | Functions in this section are ways to run parsers. They take+ -- | Functions in this section are /ways to run parsers/+ -- (like 'Text.Megaparsec.runParser'). They take -- as arguments a @sep@ parser and some input, run the parser on the input, -- and return a result. breakCap+ , breakCapT , splitCap+ , splitCapT , streamEdit , streamEditT -- * Parser combinator --- -- | Functions in this section are parser combinators. They take+ -- | Functions in this section are /parser combinators/. They take -- a @sep@ parser for an argument, combine @sep@ with another parser, -- and return a new parser. , anyTill@@ -157,17 +177,37 @@ -- ^ The input stream of text -> Maybe (s, a, s) -- ^ Maybe (prefix, parse_result, suffix)-breakCap sep input =- case runParser pser "" input of- (Left _) -> Nothing- (Right x) -> Just x+breakCap sep input = runIdentity $ breakCapT sep input+{-# INLINABLE breakCap #-}++-- |+-- === Break on and capture one pattern+--+-- Monad transformer version of 'breakCap'.+--+-- The parser @sep@ will run in the underlying monad context.+breakCapT+ :: forall m e s a. (Ord e, Stream s, Tokens s ~ s, Monad m)+ => ParsecT e s m a+ -- ^ The pattern matching parser @sep@+ -> s+ -- ^ The input stream of text+ -> m (Maybe (s, a, s))+ -- ^ Maybe (prefix, parse_result, suffix)+breakCapT sep input =+ runParserT pser "" input >>= \case+ (Left _) -> pure Nothing+ (Right x) -> pure $ Just x where pser = do (prefix, cap) <- anyTill sep suffix <- getInput pure (prefix, cap, suffix)-{-# INLINABLE breakCap #-}+{-# INLINABLE breakCapT #-} +++ -- | -- === Split on and capture all patterns --@@ -199,20 +239,39 @@ -- input == 'Data.Monoid.mconcat' ('Data.Bifunctor.second' 'Data.Tuple.fst' '<$>' output) -- @ splitCap- :: forall e s a. (Ord e, Show e, Show (Token s), Stream s, Tokens s ~ s)+ :: forall e s a. (Ord e, Stream s, Tokens s ~ s) => Parsec e s a -- ^ The pattern matching parser @sep@ -> s -- ^ The input stream of text -> [Either s a] -- ^ List of matching and non-matching input sections.-splitCap sep input = do- case runParser (sepCap sep) "" input of- (Left (ParseErrorBundle errs _)) -> error (show errs) -- undefined -- sepCap can never fail- (Right r) -> r+splitCap sep input = runIdentity $ splitCapT sep input {-# INLINABLE splitCap #-} + -- |+-- === Split on and capture all patterns+--+-- Monad transformer version of 'splitCap'.+--+-- The parser @sep@ will run in the underlying monad context.+splitCapT+ :: forall e s m a. (Ord e, Stream s, Tokens s ~ s, Monad m)+ => ParsecT e s m a+ -- ^ The pattern matching parser @sep@+ -> s+ -- ^ The input stream of text+ -> m [Either s a]+ -- ^ List of matching and non-matching input sections.+splitCapT sep input =+ runParserT (sepCap sep) "" input >>= \case+ (Left _) -> undefined -- sepCap can never fail+ (Right r) -> pure r+{-# INLINABLE splitCapT #-}+++-- | -- === Stream editor -- -- Also known as “find-and-replace”, or “match-and-substitute”. Finds all@@ -251,11 +310,11 @@ {-# INLINABLE streamEdit #-} -- |--- === Stream editor transformer+-- === Stream editor -- -- Monad transformer version of 'streamEdit'. ----- Both the parser @sep@ and the @editor@ function run in the underlying+-- Both the parser @sep@ and the @editor@ function will run in the underlying -- monad context. -- -- If you want to do 'IO' operations in the @editor@ function or the@@ -277,7 +336,7 @@ streamEditT sep editor input = do runParserT (sepCap sep) "" input >>= \case (Left _) -> undefined -- sepCap can never fail- (Right r) -> fmap mconcat $ traverse (either return editor) r+ (Right r) -> mconcat <$> traverse (either return editor) r {-# INLINABLE streamEditT #-} -- |@@ -430,6 +489,7 @@ -> m [Either (Tokens s) (Tokens s, a)] -- ^ parser findAllCap sep = sepCap (match sep) {-# INLINABLE findAllCap #-}+{-# DEPRECATED findAllCap "replace with `findAllCap sep = sepCap (match sep)`" #-} -- |@@ -452,3 +512,4 @@ -> m [Either (Tokens s) (Tokens s)] -- ^ parser findAll sep = (fmap.fmap) (second fst) $ sepCap (match sep) {-# INLINABLE findAll #-}+{-# DEPRECATED findAll "replace with `findAll sep = (fmap.fmap) (second fst) $ sepCap (match sep)`" #-}
tests/TestByteString.hs view
@@ -17,10 +17,13 @@ type Parser = Parsec Void B.ByteString +findAllCap' :: MonadParsec e s m => m a -> m [Either (Tokens s) (Tokens s, a)]+findAllCap' sep = sepCap (match sep)+ tests :: IO [Test] tests = return [ Test $ runParserTest "findAll upperChar"- (findAllCap (upperChar :: Parser Word8))+ (findAllCap' (upperChar :: Parser Word8)) ("aBcD" :: B.ByteString) [Left "a", Right ("B", c2w 'B'), Left "c", Right ("D", c2w 'D')] -- check that sepCap can progress even when parser consumes nothing
tests/TestString.hs view
@@ -10,13 +10,20 @@ import Text.Megaparsec import Text.Megaparsec.Char import Data.Void+import Data.Bifunctor (second) type Parser = Parsec Void String +findAllCap' :: MonadParsec e s m => m a -> m [Either (Tokens s) (Tokens s, a)]+findAllCap' sep = sepCap (match sep)++findAll' :: MonadParsec e s f => f b -> f [Either (Tokens s) (Tokens s)]+findAll' sep = (fmap.fmap) (second fst) $ sepCap (match sep)+ tests :: IO [Test] tests = return [ Test $ runParserTest "findAll upperChar"- (findAllCap (upperChar :: Parser Char))+ (findAllCap' (upperChar :: Parser Char)) ("aBcD" :: String) [Left "a", Right ("B", 'B'), Left "c", Right ("D", 'D')] -- check that sepCap can progress even when parser consumes nothing@@ -44,7 +51,7 @@ ([Left "a"]) #endif , Test $ runParserTest "findAll astral"- (findAll ((takeWhileP Nothing (=='𝅘𝅥𝅯') :: Parser String)))+ (findAll' ((takeWhileP Nothing (=='𝅘𝅥𝅯') :: Parser String))) ("𝄞𝅘𝅥𝅘𝅥𝅘𝅥𝅘𝅥𝅘𝅥𝅯𝅘𝅥𝅯𝅘𝅥𝅯𝅘𝅥𝅯𝅘𝅥𝅘𝅥𝅘𝅥𝅘𝅥") [Left "𝄞𝅘𝅥𝅘𝅥𝅘𝅥𝅘𝅥", Right "𝅘𝅥𝅯𝅘𝅥𝅯𝅘𝅥𝅯𝅘𝅥𝅯", Left "𝅘𝅥𝅘𝅥𝅘𝅥𝅘𝅥"] , Test $ runParserTest "empty input" (sepCap (fail "" :: Parser ())) "" []
tests/TestText.hs view
@@ -12,13 +12,20 @@ import Text.Megaparsec.Char import Data.Void import qualified Data.Text as T+import Data.Bifunctor (second) type Parser = Parsec Void T.Text +findAllCap' :: MonadParsec e s m => m a -> m [Either (Tokens s) (Tokens s, a)]+findAllCap' sep = sepCap (match sep)++findAll' :: MonadParsec e s f => f b -> f [Either (Tokens s) (Tokens s)]+findAll' sep = (fmap.fmap) (second fst) $ sepCap (match sep)+ tests :: IO [Test] tests = return [ Test $ runParserTest "findAll upperChar"- (findAllCap (upperChar :: Parser Char))+ (findAllCap' (upperChar :: Parser Char)) ("aBcD" :: T.Text) [Left "a", Right ("B", 'B'), Left "c", Right ("D", 'D')] -- check that sepCap can progress even when parser consumes nothing@@ -46,7 +53,7 @@ ([Left "a"]) #endif , Test $ runParserTest "findAll astral"- (findAll ((takeWhileP Nothing (=='𝅘𝅥𝅯') :: Parser T.Text)))+ (findAll' ((takeWhileP Nothing (=='𝅘𝅥𝅯') :: Parser T.Text))) ("𝄞𝅘𝅥𝅘𝅥𝅘𝅥𝅘𝅥𝅘𝅥𝅯𝅘𝅥𝅯𝅘𝅥𝅯𝅘𝅥𝅯𝅘𝅥𝅘𝅥𝅘𝅥𝅘𝅥" :: T.Text) [Left "𝄞𝅘𝅥𝅘𝅥𝅘𝅥𝅘𝅥", Right "𝅘𝅥𝅯𝅘𝅥𝅯𝅘𝅥𝅯𝅘𝅥𝅯", Left "𝅘𝅥𝅘𝅥𝅘𝅥𝅘𝅥"] , Test $ runParserTest "empty input" (sepCap (fail "" :: Parser ())) "" []