replace-megaparsec 1.2.1.0 → 1.3.0.0
raw patch · 4 files changed
+88/−49 lines, 4 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Replace.Megaparsec: streamEdit :: forall e s a. (Show e, ShowErrorComponent e, Typeable e, Stream s, Monoid s, Tokens s ~ s, Show s, Show (Token s), Typeable s) => Parsec e s a -> (a -> s) -> s -> s
+ Replace.Megaparsec: streamEdit :: forall e s a. (Ord e, Stream s, Monoid s, Tokens s ~ s, Show s, Show (Token s), Typeable s) => Parsec e s a -> (a -> s) -> s -> s
- Replace.Megaparsec: streamEditT :: forall e s m a. (Show e, ShowErrorComponent e, Typeable e, Stream s, Monad m, Monoid s, Tokens s ~ s, Show s, Show (Token s), Typeable s) => ParsecT e s m a -> (a -> m s) -> s -> m s
+ Replace.Megaparsec: streamEditT :: forall e s m a. (Ord e, Stream s, Monad m, Monoid s, Tokens s ~ s, Show s, Show (Token s), Typeable s) => ParsecT e s m a -> (a -> m s) -> s -> m s
Files
- CHANGELOG.md +11/−0
- README.md +36/−25
- replace-megaparsec.cabal +1/−1
- src/Replace/Megaparsec.hs +40/−23
CHANGELOG.md view
@@ -1,5 +1,16 @@ # Revision history for replace-megaparsec +## 1.3.0.0 -- 2020-03-06++`sepCap` won't throw.++Don't throw an exception on an unreachable error case, just bottom.+Remove type constraints for `Exception`.++## 1.2.1.0 -- 2020-01-01++Allow any error parameter, not just `Void`.+ ## 1.2.0.0 -- 2019-10-31 Benchmark improvements
README.md view
@@ -64,6 +64,8 @@ string-to-number conversion function fails. A typed parser will perform both the pattern match and the conversion, so it will never be in that situation. + [Parse, don't validate.](https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/)+ * Regular expressions are only able to pattern-match [regular](https://en.wikipedia.org/wiki/Chomsky_hierarchy#The_hierarchy) grammers.@@ -87,6 +89,7 @@ The examples depend on these imports. ```haskell+import Data.Void import Replace.Megaparsec import Text.Megaparsec import Text.Megaparsec.Char@@ -185,7 +188,8 @@ Replace all carriage-return-newline instances with newline. ```haskell-streamEdit (chunk "\r\n") (const "\n") "1\r\n2\r\n"+let crnl = chunk "\r\n" :: Parsec Void String String+streamEdit crnl (const "\n") "1\r\n2\r\n" ``` ```haskell "1\n2\n"@@ -196,7 +200,8 @@ Replace alphabetic characters with the next character in the alphabet. ```haskell-streamEdit (some letterChar) (fmap succ) "HAL 9000"+let somelet = some letterChar :: Parsec Void String String+streamEdit somelet (fmap succ) "HAL 9000" ``` ```haskell "IBM 9000"@@ -225,7 +230,8 @@ ```haskell import System.Environment-streamEditT (char '{' *> manyTill anySingle (char '}')) getEnv "- {HOME} -"+let bracevar = char '{' *> manyTill anySingle (char '}') :: ParsecT Void String IO String+streamEditT bracevar getEnv "- {HOME} -" ``` ```haskell "- /home/jbrock -"@@ -270,18 +276,19 @@ #!/usr/bin/env stack {- stack script- --resolver nightly-2019-09-13+ --resolver lts-15 --package megaparsec --package replace-megaparsec -} -- https://docs.haskellstack.org/en/stable/GUIDE/#script-interpreter +import Data.Void import Text.Megaparsec import Text.Megaparsec.Char import Text.Megaparsec.Char.Lexer import Replace.Megaparsec -main = interact $ streamEdit decimal (show . (*2))+main = interact $ streamEdit (decimal :: Parsec Void String Int) (show . (*2)) ``` If you have@@ -356,27 +363,28 @@ | Program | dense | sparse | | :--- | ---: | ---: |-| Python `re.sub`¹ | 89.23ms | 23.98ms |-| Perl `s///ge`² | 180.65ms | 5.60ms |-| [`Replace.Megaparsec.streamEdit`][m] `String` | 454.95ms | 375.04ms |+| [Python 3.7.4 `re.sub`][sub] *repl* function | 89.23ms | 23.98ms |+| [Perl 5 `s///ge`][s] | 180.65ms | 5.02ms |+| [`Replace.Megaparsec.streamEdit`][m] `String` | 441.94ms | 375.04ms | | [`Replace.Megaparsec.streamEdit`][m] `ByteString` | 529.99ms | 73.76ms | | [`Replace.Megaparsec.streamEdit`][m] `Text` | 547.47ms | 139.21ms | | [`Replace.Attoparsec.ByteString.streamEdit`][ab] | 394.12ms | 41.13ms | | [`Replace.Attoparsec.Text.streamEdit`][at] | 515.26ms | 46.10ms | | [`Text.Regex.Applicative.replace`][ra] `String` | 1083.98ms | 646.40ms |-| [`Text.Regex.PCRE.Heavy.gsub`][ph] `Text` | ⊥³ | 14.76ms |--¹ Python 3.7.4--² This is perl 5, version 28, subversion 2 (v5.28.2) built for x86_64-linux-thread-multi--³ Does not finish.+| [`Text.Regex.PCRE.Heavy.gsub`][ph] `Text` | > 10min | 14.29ms |+| [`Control.Lens.Regex.ByteString.match`][lb] | > 10min | 4.27ms |+| [`Control.Lens.Regex.Text.match`][lt] | > 10min | 14.74ms | +[sub]: https://docs.python.org/3/library/re.html#re.sub+[s]: https://perldoc.perl.org/functions/s.html [m]: https://hackage.haskell.org/package/replace-megaparsec/docs/Replace-Megaparsec.html#v:streamEdit [ab]: https://hackage.haskell.org/package/replace-attoparsec/docs/Replace-Attoparsec-ByteString.html#v:streamEdit [at]: https://hackage.haskell.org/package/replace-attoparsec/docs/Replace-Attoparsec-Text.html#v:streamEdit [ra]: http://hackage.haskell.org/package/regex-applicative/docs/Text-Regex-Applicative.html#v:replace-[ph]: http://hackage.haskell.org/package/pcre-heavy/docs/Text-Regex-PCRE-Heavy.html+[ss]: http://hackage.haskell.org/package/stringsearch/docs/Data-ByteString-Search.html#v:replace+[ph]: http://hackage.haskell.org/package/pcre-heavy/docs/Text-Regex-PCRE-Heavy.html#v:gsub+[lb]: https://hackage.haskell.org/package/lens-regex-pcre/docs/Control-Lens-Regex-ByteString.html#v:match+[lt]: https://hackage.haskell.org/package/lens-regex-pcre/docs/Control-Lens-Regex-Text.html#v:match # Hypothetically Asked Questions@@ -390,18 +398,21 @@ 2. *Is this a good idea?* - You may have heard it suggested that monadic parsers are better when+ You may have+ [heard it suggested](https://stackoverflow.com/questions/57667534/how-can-i-use-a-parser-in-haskell-to-find-the-locations-of-some-substrings-in-a/57712672#comment101804063_57667534)+ that monadic parsers are better for pattern-matching when the input stream is mostly signal, and regular expressions are better when the input stream is mostly noise. - The premise of this library is:- that sentiment is outdated; monadic parsers are great for finding- small patterns in a stream of otherwise uninteresting text; and the- reluctance to forego the speedup opportunities afforded by restricting- ourselves to regular grammars is an old superstition about+ The premise of this library is that monadic parsers are great for finding+ small signal patterns in a stream of otherwise noisy text.++ There is a reluctance to forego the speedup opportunities afforded by restricting+ ourselves to regular grammars, but this is an old superstition about opportunities which [remain mostly unexploited anyway](https://swtch.com/~rsc/regexp/regexp1.html).- The performance compromise of allowing stack memory allocation (a.k.a pushdown- automata, a.k.a context-free grammar) was once considered- [controversial for *general-purpose* programming languages](https://vanemden.wordpress.com/2014/06/18/how-recursion-got-into-programming-a-comedy-of-errors-3/). I think we+ The performance compromise of allowing stack memory allocation (a.k.a. pushdown+ automata, a.k.a. context-free grammar) was once considered+ [controversial for *general-purpose* programming languages](https://vanemden.wordpress.com/2014/06/18/how-recursion-got-into-programming-a-comedy-of-errors-3/).+ I think we can now resolve that controversy the same way for pattern matching languages.
replace-megaparsec.cabal view
@@ -1,5 +1,5 @@ name: replace-megaparsec-version: 1.2.1.0+version: 1.3.0.0 cabal-version: 1.18 synopsis: Find, replace, and edit text patterns with Megaparsec parsers homepage: https://github.com/jamesdbrock/replace-megaparsec
src/Replace/Megaparsec.hs view
@@ -55,7 +55,6 @@ import Data.Bifunctor import Data.Functor.Identity import Data.Proxy-import Control.Exception (throw) import Data.Typeable import Control.Monad import qualified Data.ByteString as B@@ -68,34 +67,57 @@ -- == Separate and capture -- -- Parser combinator to find all of the non-overlapping ocurrences--- of the pattern @sep@ in a text stream. Separate the stream into sections:+-- of the pattern parser @sep@ in a text stream.+-- The 'sepCap' parser will always consume its entire input and can never fail. --+-- === Output+--+-- The input stream is separated into a list of sections:+-- -- * sections which can parsed by the pattern @sep@ will be captured as -- matching sections in 'Right' -- * non-matching sections of the stream will be captured in 'Left'. ----- This parser will always consume its entire input and can never fail.--- If there are no pattern matches, then the entire input stream will be--- returned as a non-matching 'Left' section.+-- There are two constraints on the output: ----- The pattern matching parser @sep@ will not be allowed to succeed without--- consuming any input. If we allow the parser to match a zero-width pattern,+-- * The output list will non-empty. If there are no pattern matches, then+-- the entire input stream will be returned as one non-matching 'Left' section.+-- If the input is @""@ then the output list will be @[Left ""]@.+-- * The output list will not contain two consecutive 'Left's.+--+-- === Zero-width matches forbidden+--+-- If the pattern matching parser @sep@ would succeed without consuming any+-- input then 'sepCap' will force it to fail.+-- If we allow @sep@ to match a zero-width pattern, -- then it can match the same zero-width pattern again at the same position -- on the next iteration, which would result in an infinite number of--- overlapping pattern matches. So, for example, the--- pattern @many digitChar@, which can match zero occurences of a digit,--- will be treated by @sepCap@ as @some digitChar@, and required to match--- at least one digit.+-- overlapping pattern matches. --+-- === Special accelerated inputs+--+-- There are specialization re-write rules to speed up this function when+-- the input type is "Data.Text" or "Data.Bytestring".+--+-- === Error parameter+--+-- The error type parameter @e@ for @sep@ should usually be 'Data.Void',+-- because @sep@ fails on every token in a non-matching 'Left' section,+-- so parser failures will not be reported.+--+-- === Notes+-- -- This @sepCap@ parser combinator is the basis for all of the other--- features of this module. It is similar to the @sep*@ family of functions+-- features of this module.+--+-- It is similar to the @sep*@ family of functions -- found in -- <http://hackage.haskell.org/package/parser-combinators/docs/Control-Monad-Combinators.html parser-combinators> -- and -- <http://hackage.haskell.org/package/parsers/docs/Text-Parser-Combinators.html parsers> -- but, importantly, it returns the parsed result of the @sep@ parser instead--- of throwing it away.---+-- of throwing it away, like+-- <http://hackage.haskell.org/package/parser-combinators/docs/Control-Monad-Combinators.html#v:manyTill_ manyTill_>. sepCap :: forall e s m a. (MonadParsec e s m) => m a -- ^ The pattern matching parser @sep@@@ -233,12 +255,9 @@ -- We need the @Monoid s@ instance so that we can 'Data.Monoid.mconcat' the output -- stream. ----- We need @Typeable s@ and @Show s@ for 'Control.Exception.throw'. In theory--- this function should never throw an exception, because it only throws--- when the 'sepCap' parser fails, and the 'sepCap' parser--- can never fail. The error type parameter @e@ should usually be 'Data.Void'.+-- The error type parameter @e@ should usually be 'Data.Void'. streamEdit- :: forall e s a. (Show e, ShowErrorComponent e, Typeable e, Stream s, Monoid s, Tokens s ~ s, Show s, Show (Token s), Typeable s)+ :: forall e s a. (Ord e, Stream s, Monoid s, Tokens s ~ s, Show s, Show (Token s), Typeable s) => Parsec e s a -- ^ The parser @sep@ for the pattern of interest. -> (a -> s)@@ -264,7 +283,7 @@ -- If you want the @editor@ function or the parser @sep@ to remember some state, -- then run this in a stateful monad. streamEditT- :: forall e s m a. (Show e, ShowErrorComponent e, Typeable e, Stream s, Monad m, Monoid s, Tokens s ~ s, Show s, Show (Token s), Typeable s)+ :: forall e s m a. (Ord e, Stream s, Monad m, Monoid s, Tokens s ~ s, Show s, Show (Token s), Typeable s) => ParsecT e s m a -- ^ The parser @sep@ for the pattern of interest. -> (a -> m s)@@ -275,9 +294,7 @@ -> m s streamEditT sep editor input = do runParserT (sepCap sep) "" input >>= \case- (Left err) -> throw err- -- sepCap can never fail, but if it does, throw.- -- Don't use MonadFail because Identity is not a MonadFail.+ (Left _) -> undefined -- sepCap can never fail (Right r) -> fmap mconcat $ traverse (either return editor) r {-# INLINABLE streamEditT #-}