packages feed

parser-regex 0.1.0.0 → 0.2.0.0

raw patch · 13 files changed

+482/−115 lines, 13 filesdep −bytestringdep ~QuickCheckdep ~containersdep ~tasty-quickcheckPVP ok

version bump matches the API change (PVP)

Dependencies removed: bytestring

Dependency ranges changed: QuickCheck, containers, tasty-quickcheck

API changes (from Hackage documentation)

+ Regex.Base: parseNext :: Monad m => Parser c a -> m (Maybe c) -> m (Maybe a)
+ Regex.Internal.CharSet: CharSet :: IntMap Char -> CharSet
+ Regex.Internal.CharSet: [unCharSet] :: CharSet -> IntMap Char
+ Regex.Internal.CharSet: delete :: Char -> CharSet -> CharSet
+ Regex.Internal.CharSet: deleteRange :: (Char, Char) -> CharSet -> CharSet
+ Regex.Internal.CharSet: difference :: CharSet -> CharSet -> CharSet
+ Regex.Internal.CharSet: elems :: CharSet -> [Char]
+ Regex.Internal.CharSet: empty :: CharSet
+ Regex.Internal.CharSet: fromList :: [Char] -> CharSet
+ Regex.Internal.CharSet: fromRange :: (Char, Char) -> CharSet
+ Regex.Internal.CharSet: fromRanges :: [(Char, Char)] -> CharSet
+ Regex.Internal.CharSet: insert :: Char -> CharSet -> CharSet
+ Regex.Internal.CharSet: insertRange :: (Char, Char) -> CharSet -> CharSet
+ Regex.Internal.CharSet: instance Data.String.IsString Regex.Internal.CharSet.CharSet
+ Regex.Internal.CharSet: instance GHC.Base.Monoid Regex.Internal.CharSet.CharSet
+ Regex.Internal.CharSet: instance GHC.Base.Semigroup Regex.Internal.CharSet.CharSet
+ Regex.Internal.CharSet: instance GHC.Classes.Eq Regex.Internal.CharSet.CharSet
+ Regex.Internal.CharSet: instance GHC.Show.Show Regex.Internal.CharSet.CharSet
+ Regex.Internal.CharSet: intersection :: CharSet -> CharSet -> CharSet
+ Regex.Internal.CharSet: map :: (Char -> Char) -> CharSet -> CharSet
+ Regex.Internal.CharSet: member :: Char -> CharSet -> Bool
+ Regex.Internal.CharSet: newtype CharSet
+ Regex.Internal.CharSet: not :: CharSet -> CharSet
+ Regex.Internal.CharSet: notMember :: Char -> CharSet -> Bool
+ Regex.Internal.CharSet: ranges :: CharSet -> [(Char, Char)]
+ Regex.Internal.CharSet: singleton :: Char -> CharSet
+ Regex.Internal.CharSet: union :: CharSet -> CharSet -> CharSet
+ Regex.Internal.CharSet: valid :: CharSet -> Bool
+ Regex.Internal.Debug: dispCharRanges :: [Char] -> String
+ Regex.Internal.Debug: instance Data.String.IsString Regex.Internal.Debug.Str
+ Regex.Internal.Debug: instance GHC.Base.Monoid Regex.Internal.Debug.Str
+ Regex.Internal.Debug: instance GHC.Base.Semigroup Regex.Internal.Debug.Str
+ Regex.Internal.Debug: parserToDot :: forall c a. Maybe ([c], [c] -> String) -> Parser c a -> String
+ Regex.Internal.Debug: reToDot :: forall c a. Maybe ([c], [c] -> String) -> RE c a -> String
+ Regex.Internal.Parser: [NAccept] :: a -> Node c a
+ Regex.Internal.Parser: [NAlt] :: !Node c a -> !Node c a -> {-# UNPACK #-} !SmallArray (Node c a) -> Node c a
+ Regex.Internal.Parser: [NEmpty] :: Node c a
+ Regex.Internal.Parser: [NGuard] :: {-# UNPACK #-} !Unique -> Node c a -> Node c a
+ Regex.Internal.Parser: [NToken] :: !c -> Maybe a1 -> !Node c a -> Node c a
+ Regex.Internal.Parser: [PAlt] :: {-# UNPACK #-} !Unique -> !Parser c a -> !Parser c a -> {-# UNPACK #-} !SmallArray (Parser c a) -> Parser c a
+ Regex.Internal.Parser: [PEmpty] :: Parser c a
+ Regex.Internal.Parser: [PFmap] :: !Strictness -> !a1 -> a -> !Parser c a1 -> Parser c a
+ Regex.Internal.Parser: [PFmap_] :: !Node c a -> Parser c a
+ Regex.Internal.Parser: [PFoldGr] :: {-# UNPACK #-} !Unique -> !Strictness -> !a -> a1 -> a -> a -> !Parser c a1 -> Parser c a
+ Regex.Internal.Parser: [PFoldMn] :: {-# UNPACK #-} !Unique -> !Strictness -> !a -> a1 -> a -> a -> !Parser c a1 -> Parser c a
+ Regex.Internal.Parser: [PLiftA2] :: !Strictness -> !a1 -> a2 -> a -> !Parser c a1 -> !Parser c a2 -> Parser c a
+ Regex.Internal.Parser: [PMany] :: {-# UNPACK #-} !Unique -> !a1 -> a -> !a2 -> a -> !a2 -> a1 -> a2 -> !a2 -> !Parser c a1 -> Parser c a
+ Regex.Internal.Parser: [PPure] :: a -> Parser c a
+ Regex.Internal.Parser: [PToken] :: !c -> Maybe a -> Parser c a
+ Regex.Internal.Parser: compile :: RE c a -> Parser c a
+ Regex.Internal.Parser: compileBounded :: Int -> RE c a -> Maybe (Parser c a)
+ Regex.Internal.Parser: data Node c a
+ Regex.Internal.Parser: data Parser c a
+ Regex.Internal.Parser: data ParserState c a
+ Regex.Internal.Parser: finishParser :: ParserState c a -> Maybe a
+ Regex.Internal.Parser: parseFoldr :: Foldr f c -> Parser c a -> f -> Maybe a
+ Regex.Internal.Parser: parseNext :: Monad m => Parser c a -> m (Maybe c) -> m (Maybe a)
+ Regex.Internal.Parser: prepareParser :: Parser c a -> Maybe (ParserState c a)
+ Regex.Internal.Parser: stepParser :: ParserState c a -> c -> Maybe (ParserState c a)
+ Regex.Internal.Parser: type Foldr f a = forall b. (a -> b -> b) -> b -> f -> b
+ Regex.Internal.Regex: Finite :: [a] -> Many a
+ Regex.Internal.Regex: Greedy :: Greediness
+ Regex.Internal.Regex: Minimal :: Greediness
+ Regex.Internal.Regex: NonStrict :: Strictness
+ Regex.Internal.Regex: Repeat :: a -> Many a
+ Regex.Internal.Regex: Strict :: Strictness
+ Regex.Internal.Regex: [RAlt] :: !RE c a -> !RE c a -> RE c a
+ Regex.Internal.Regex: [REmpty] :: RE c a
+ Regex.Internal.Regex: [RFmap] :: !Strictness -> !a1 -> a -> !RE c a1 -> RE c a
+ Regex.Internal.Regex: [RFmap_] :: a -> !RE c a1 -> RE c a
+ Regex.Internal.Regex: [RFold] :: !Strictness -> !Greediness -> !a -> a1 -> a -> a -> !RE c a1 -> RE c a
+ Regex.Internal.Regex: [RLiftA2] :: !Strictness -> !a1 -> a2 -> a -> !RE c a1 -> !RE c a2 -> RE c a
+ Regex.Internal.Regex: [RMany] :: !a1 -> a -> !a2 -> a -> !a2 -> a1 -> a2 -> !a2 -> !RE c a1 -> RE c a
+ Regex.Internal.Regex: [RPure] :: a -> RE c a
+ Regex.Internal.Regex: [RToken] :: !c -> Maybe a -> RE c a
+ Regex.Internal.Regex: anySingle :: RE c c
+ Regex.Internal.Regex: atLeast :: Int -> RE c a -> RE c [a]
+ Regex.Internal.Regex: atLeastMin :: Int -> RE c a -> RE c [a]
+ Regex.Internal.Regex: atMost :: Int -> RE c a -> RE c [a]
+ Regex.Internal.Regex: atMostMin :: Int -> RE c a -> RE c [a]
+ Regex.Internal.Regex: betweenCount :: (Int, Int) -> RE c a -> RE c [a]
+ Regex.Internal.Regex: betweenCountMin :: (Int, Int) -> RE c a -> RE c [a]
+ Regex.Internal.Regex: chainl1 :: RE c a -> RE c (a -> a -> a) -> RE c a
+ Regex.Internal.Regex: chainr1 :: RE c a -> RE c (a -> a -> a) -> RE c a
+ Regex.Internal.Regex: data Greediness
+ Regex.Internal.Regex: data Many a
+ Regex.Internal.Regex: data RE c a
+ Regex.Internal.Regex: data Strictness
+ Regex.Internal.Regex: endBy :: RE c a -> RE c sep -> RE c [a]
+ Regex.Internal.Regex: endBy1 :: RE c a -> RE c sep -> RE c [a]
+ Regex.Internal.Regex: fmap' :: (a -> b) -> RE c a -> RE c b
+ Regex.Internal.Regex: foldlMany :: (b -> a -> b) -> b -> RE c a -> RE c b
+ Regex.Internal.Regex: foldlMany' :: (b -> a -> b) -> b -> RE c a -> RE c b
+ Regex.Internal.Regex: foldlManyMin :: (b -> a -> b) -> b -> RE c a -> RE c b
+ Regex.Internal.Regex: foldlManyMin' :: (b -> a -> b) -> b -> RE c a -> RE c b
+ Regex.Internal.Regex: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Regex.Internal.Regex.Many a)
+ Regex.Internal.Regex: instance Control.DeepSeq.NFData1 Regex.Internal.Regex.Many
+ Regex.Internal.Regex: instance Data.Foldable.Foldable Regex.Internal.Regex.Many
+ Regex.Internal.Regex: instance Data.Functor.Classes.Eq1 Regex.Internal.Regex.Many
+ Regex.Internal.Regex: instance Data.Functor.Classes.Ord1 Regex.Internal.Regex.Many
+ Regex.Internal.Regex: instance Data.Functor.Classes.Show1 Regex.Internal.Regex.Many
+ Regex.Internal.Regex: instance GHC.Base.Alternative (Regex.Internal.Regex.RE c)
+ Regex.Internal.Regex: instance GHC.Base.Applicative (Regex.Internal.Regex.RE c)
+ Regex.Internal.Regex: instance GHC.Base.Functor (Regex.Internal.Regex.RE c)
+ Regex.Internal.Regex: instance GHC.Base.Functor Regex.Internal.Regex.Many
+ Regex.Internal.Regex: instance GHC.Base.Monoid a => GHC.Base.Monoid (Regex.Internal.Regex.RE c a)
+ Regex.Internal.Regex: instance GHC.Base.Semigroup a => GHC.Base.Semigroup (Regex.Internal.Regex.RE c a)
+ Regex.Internal.Regex: instance GHC.Classes.Eq a => GHC.Classes.Eq (Regex.Internal.Regex.Many a)
+ Regex.Internal.Regex: instance GHC.Classes.Ord a => GHC.Classes.Ord (Regex.Internal.Regex.Many a)
+ Regex.Internal.Regex: instance GHC.Show.Show a => GHC.Show.Show (Regex.Internal.Regex.Many a)
+ Regex.Internal.Regex: liftA2' :: (a1 -> a2 -> b) -> RE c a1 -> RE c a2 -> RE c b
+ Regex.Internal.Regex: manyMin :: RE c a -> RE c [a]
+ Regex.Internal.Regex: manyr :: RE c a -> RE c (Many a)
+ Regex.Internal.Regex: optionalMin :: RE c a -> RE c (Maybe a)
+ Regex.Internal.Regex: satisfy :: (c -> Bool) -> RE c c
+ Regex.Internal.Regex: sepBy :: RE c a -> RE c sep -> RE c [a]
+ Regex.Internal.Regex: sepBy1 :: RE c a -> RE c sep -> RE c [a]
+ Regex.Internal.Regex: sepEndBy :: RE c a -> RE c sep -> RE c [a]
+ Regex.Internal.Regex: sepEndBy1 :: RE c a -> RE c sep -> RE c [a]
+ Regex.Internal.Regex: single :: Eq c => c -> RE c c
+ Regex.Internal.Regex: someMin :: RE c a -> RE c [a]
+ Regex.Internal.Regex: toFind :: RE c a -> RE c a
+ Regex.Internal.Regex: toFindMany :: RE c a -> RE c [a]
+ Regex.Internal.Regex: token :: (c -> Maybe a) -> RE c a
+ Regex.Internal.Text: TextToken :: {-# UNPACK #-} !Array -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Char -> TextToken
+ Regex.Internal.Text: [tArr] :: TextToken -> {-# UNPACK #-} !Array
+ Regex.Internal.Text: [tChar] :: TextToken -> {-# UNPACK #-} !Char
+ Regex.Internal.Text: [tOffset] :: TextToken -> {-# UNPACK #-} !Int
+ Regex.Internal.Text: anyChar :: REText Char
+ Regex.Internal.Text: char :: Char -> REText Char
+ Regex.Internal.Text: charIgnoreCase :: Char -> REText Char
+ Regex.Internal.Text: data TextToken
+ Regex.Internal.Text: find :: REText a -> Text -> Maybe a
+ Regex.Internal.Text: findAll :: REText a -> Text -> [a]
+ Regex.Internal.Text: instance GHC.Base.Applicative Regex.Internal.Text.WithMatch
+ Regex.Internal.Text: instance GHC.Base.Functor Regex.Internal.Text.WithMatch
+ Regex.Internal.Text: intRangeDec :: REText a -> (Int, Int) -> REText Int
+ Regex.Internal.Text: intRangeHex :: REText a -> (Int, Int) -> REText Int
+ Regex.Internal.Text: integerDec :: REText a -> REText Integer
+ Regex.Internal.Text: integerHex :: REText a -> REText Integer
+ Regex.Internal.Text: manyText :: REText Text
+ Regex.Internal.Text: manyTextMin :: REText Text
+ Regex.Internal.Text: manyTextOf :: CharSet -> REText Text
+ Regex.Internal.Text: manyTextOfMin :: CharSet -> REText Text
+ Regex.Internal.Text: naturalDec :: REText Natural
+ Regex.Internal.Text: naturalHex :: REText Natural
+ Regex.Internal.Text: oneOf :: CharSet -> REText Char
+ Regex.Internal.Text: parse :: ParserText a -> Text -> Maybe a
+ Regex.Internal.Text: parseSure :: ParserText a -> Text -> a
+ Regex.Internal.Text: reParse :: REText a -> Text -> Maybe a
+ Regex.Internal.Text: replace :: REText Text -> Text -> Maybe Text
+ Regex.Internal.Text: replaceAll :: REText Text -> Text -> Text
+ Regex.Internal.Text: satisfy :: (Char -> Bool) -> REText Char
+ Regex.Internal.Text: someText :: REText Text
+ Regex.Internal.Text: someTextMin :: REText Text
+ Regex.Internal.Text: someTextOf :: CharSet -> REText Text
+ Regex.Internal.Text: someTextOfMin :: CharSet -> REText Text
+ Regex.Internal.Text: splitOn :: REText a -> Text -> [Text]
+ Regex.Internal.Text: text :: Text -> REText Text
+ Regex.Internal.Text: textIgnoreCase :: Text -> REText Text
+ Regex.Internal.Text: textTokenFoldr :: (TextToken -> b -> b) -> b -> Text -> b
+ Regex.Internal.Text: toMatch :: REText a -> REText Text
+ Regex.Internal.Text: token :: (Char -> Maybe a) -> REText a
+ Regex.Internal.Text: type ParserText = Parser TextToken
+ Regex.Internal.Text: type REText = RE TextToken
+ Regex.Internal.Text: withMatch :: REText a -> REText (Text, a)
+ Regex.Internal.Text: wordDecN :: Int -> REText Word
+ Regex.Internal.Text: wordHexN :: Int -> REText Word
+ Regex.Internal.Text: wordRangeDec :: (Word, Word) -> REText Word
+ Regex.Internal.Text: wordRangeHex :: (Word, Word) -> REText Word
+ Regex.Internal.Unique: Unique :: Int -> Unique
+ Regex.Internal.Unique: [unUnique] :: Unique -> Int
+ Regex.Internal.Unique: data UniqueSet
+ Regex.Internal.Unique: empty :: UniqueSet
+ Regex.Internal.Unique: insert :: Unique -> UniqueSet -> UniqueSet
+ Regex.Internal.Unique: member :: Unique -> UniqueSet -> Bool
+ Regex.Internal.Unique: newtype Unique
- Regex.Base: prepareParser :: Parser c a -> ParserState c a
+ Regex.Base: prepareParser :: Parser c a -> Maybe (ParserState c a)

Files

CHANGELOG.md view
@@ -1,3 +1,13 @@+### 0.2.0.0 -- 2024-11-24++* Breaking changes+  * Parsing fails more eagerly. This affects lazy list parsing and parsing via+    the `Regex.Base` functions `prepareParser` and `stepParser`.+* Additions+  * Added `Regex.Base.parseNext`.+* Other+  * Some internal modules are now exported.+ ### 0.1.0.0 -- 2024-03-04  * First version.
README.md view
@@ -9,7 +9,7 @@  * Parsers based on [regular expressions](https://en.wikipedia.org/wiki/Regular_expression),   capable of parsing [regular languages](https://en.wikipedia.org/wiki/Regular_language).-  There are no extra features that would make parsing non-regular languages+  Note that there are no extra features to make parsing non-regular languages   possible. * Regexes are composed using combinators. * Resumable parsing of sequences of any type containing values of any type.@@ -18,8 +18,20 @@ * Parsing runtime is linear in the length of the sequence being parsed. No   exponential backtracking. -## Example+## Examples +### Versus regex patterns++```+^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?+```++Can you guess what this matches?++This is a non-validating regex to extract parts of a URI, from+[RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986#appendix-B). It can+be translated as follows.+ ```hs {-# LANGUAGE OverloadedStrings #-} import Control.Applicative (optional)@@ -37,9 +49,6 @@   , fragment  :: Maybe Text   } deriving Show --- ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?--- A non-validating regex to extract parts of a URI, from RFC 3986--- Translated: uriRE :: REText URI uriRE = URI   <$> optional (R.someTextOf (CS.not ":/?#") <* R.char ':')@@ -57,9 +66,87 @@           , fragment = Just "parser-regex" }) ``` +### More parsing++Parsing is straightforward, even for tasks which may be impractical with+submatch extraction typically offered by regex libraries.++```hs+import Control.Applicative ((<|>))+import Data.Text (Text)++import Regex.Text (REText)+import qualified Regex.Text as R+import qualified Data.CharSet as CS++data Expr+  = Var Text+  | Expr :+ Expr+  | Expr :- Expr+  | Expr :* Expr+  deriving Show++exprRE :: REText Expr+exprRE = var `R.chainl1` mul `R.chainl1` (add <|> sub)+  where+    var = Var <$> R.someTextOf CS.asciiLower+    add = (:+) <$ R.char '+'+    sub = (:-) <$ R.char '-'+    mul = (:*) <$ R.char '*'+```+```hs+>>> import qualified Regex.Text as R+>>> R.reParse exprRE "a+b-c*d*e+f"+Just (((Var "a" :+ Var "b") :- ((Var "c" :* Var "d") :* Var "e")) :+ Var "f")+```++### Find and replace++Find and replace using regexes are supported for `Text` and lists.++```hs+>>> import Control.Applicative ((<|>))+>>> import qualified Data.Text as T+>>> import qualified Regex.Text as R+>>>+>>> data Color = Blue | Orange deriving Show+>>> let re = Blue <$ R.text "blue" <|> Orange <$ R.text "orange"+>>> R.find re "color: orange"+Just Orange+>>>+>>> let re = T.toUpper <$> (R.text "cat" <|> R.text "dog" <|> R.text "fish")+>>> R.replaceAll re "locate selfish hotdog"+"loCATe selFISH hotDOG"+```++### Parse any sequence++Regexes are not restricted to parsing text. For example, one may parse vectors+from the [vector](https://hackage.haskell.org/package/vector) library, because+why not.++```hs+import Regex.Base (Parser)+import qualified Regex.Base as R+import qualified Data.Vector.Generic as VG++parseVector :: VG.Vector v c => Parser c a -> v c -> Maybe a+parseVector = R.parseFoldr VG.foldr+```+```hs+>>> import Control.Applicative (many)+>>> import qualified Data.Vector as V+>>> import qualified Regex.Base as R+>>>+>>> let p = R.compile $ many ((,) <$> R.satisfy even <*> R.satisfy odd)+>>> let v = V.fromList [0..5] :: V.Vector Int+>>> parseVector p v+Just [(0,1),(2,3),(4,5)]+```+ ## Documentation -Please find the documentation on Hackage:+Documentation is available on Hackage: [parser-regex](https://hackage.haskell.org/package/parser-regex)  Already familiar with regex patterns? See the@@ -70,33 +157,44 @@ ### `regex-applicative`  [`regex-applicative`](https://hackage.haskell.org/package/regex-applicative) is-the primary inspiration for this library, and provides a similar set of-features.-`parser-regex` attempts to be a more fully-featured library built on the-ideas of `regex-applicative`.+the primary inspiration for this library, and is similar in many ways. +`parser-regex` attempts to be a more efficient and featureful library built on+the ideas of `regex-applicative`, though it does not aim to provide a superset+of `regex-applicative`'s API.+ ### Traditional regex libraries -Other alternatives are more traditional regex libraries that use regex patterns,-like [`regex-tdfa`](https://hackage.haskell.org/package/regex-tdfa) and-[`regex-pcre`](https://hackage.haskell.org/package/regex-pcre)/-[`regex-pcre-builtin`](https://hackage.haskell.org/package/regex-pcre-builtin).+These libraries use regex patterns. -Reasons to use `parser-regex` over traditional regex libraries:+* [`regex-pcre`](https://hackage.haskell.org/package/regex-pcre)/[`regex-pcre-builtin`](https://hackage.haskell.org/package/regex-pcre-builtin)+* [`regex-tdfa`](https://hackage.haskell.org/package/regex-tdfa)+* [`pcre-light`](https://hackage.haskell.org/package/pcre-light)/[`pcre-heavy`](https://hackage.haskell.org/package/pcre-heavy)+* [`pcre2`](https://hackage.haskell.org/package/pcre2) +Consider using these if++* The terseness of regex patterns is well-suited for your use case.+* You need something very fast for typical use cases. `regex-pcre`,+  `regex-pcre-builtin`, `pcre-light`, `pcre-heavy` are faster than+  `parser-regex` for typical use cases, but there are trade-offs—such as losing+  Unicode support and a risk of [ReDoS](https://en.wikipedia.org/wiki/ReDoS).++Use `parser-regex` instead if+ * You prefer parser combinators over regex patterns * You need more powerful parsing capabilities than just submatch extraction-* You need to parse a sequence type that is not supported by these regex-  libraries+* You need to parse a sequence that is not supported by the above libraries -Reasons to use traditional regex libraries over `parser-regex`:+For a detailed comparison of regex libraries,+[see here](https://github.com/meooow25/parser-regex/tree/master/bench). -* The terseness of regex patterns is better suited for your use case-* You need something very fast, and adversarial input is not a concern.-  Use `regex-pcre`/`regex-pcre-builtin`.+### Other options -For a more detailed comparison of regex libraries, see-[here](https://github.com/meooow25/parser-regex/tree/master/bench).+If you are not restricted to regexes, there are many other parsing libraries you+may use, too many to list here. See the+["Parsing" category on Hackage](https://hackage.haskell.org/packages/#cat:Parsing)+for a start.  ## Contributing 
parser-regex.cabal view
@@ -1,6 +1,6 @@ cabal-version:      2.4 name:               parser-regex-version:            0.1.0.0+version:            0.2.0.0 synopsis:           Regex based parsers description:        Regex based parsers. homepage:           https://github.com/meooow25/parser-regex@@ -19,15 +19,17 @@     GHC == 9.0.2   , GHC == 9.2.8   , GHC == 9.4.8-  , GHC == 9.6.4-  , GHC == 9.8.1+  , GHC == 9.6.6+  , GHC == 9.8.2+  , GHC == 9.10.1  source-repository head     type:     git     location: https://github.com/meooow25/parser-regex.git  common warnings-    ghc-options: -Wall+    ghc-options:+        -Wall -Wcompat -Widentities -Wredundant-constraints -Wunused-packages  library     import:           warnings@@ -37,22 +39,21 @@         Regex.Base         Regex.List         Regex.Text--    other-modules:         Regex.Internal.CharSet-        Regex.Internal.CharSets         Regex.Internal.Debug-        Regex.Internal.Generated.CaseFold-        Regex.Internal.List-        Regex.Internal.Num         Regex.Internal.Parser         Regex.Internal.Regex         Regex.Internal.Text         Regex.Internal.Unique +    other-modules:+        Regex.Internal.CharSets+        Regex.Internal.Generated.CaseFold+        Regex.Internal.List+        Regex.Internal.Num+     build-depends:         base >= 4.15 && < 5.0-      , bytestring >= 0.10.12 && < 0.13       , containers >= 0.6.4 && < 0.8       , deepseq >= 1.4.5 && < 1.6       , ghc-bignum >= 1.1 && < 1.4@@ -68,14 +69,12 @@      build-depends:         base-      , bytestring-      , containers       , parser-regex-      , QuickCheck >= 2.14.3 && < 2.15+      , QuickCheck >= 2.14.3 && < 2.16       , quickcheck-classes-base >= 0.6.2 && < 0.7       , tasty >= 1.5 && < 1.6       , tasty-hunit >= 0.10.1 && < 0.11-      , tasty-quickcheck >= 0.10.3 && < 0.11+      , tasty-quickcheck >= 0.10.3 && < 0.12       , text      hs-source-dirs:   test
src/Regex/Base.hs view
@@ -19,6 +19,7 @@   , P.finishParser   , P.Foldr   , P.parseFoldr+  , P.parseNext      -- * @RE@s and combinators   , R.token@@ -67,8 +68,8 @@ -- a large amount of control over the parsing process, making it possible to -- parse in a resumable or even branching manner. ----- As a simpler alternative to the trio of functions above, @parseFoldr@ can be--- used on any sequence type that can be folded over.+-- @parseFoldr@ and @parseNext@ may be more convenient to use, depending on the+-- sequence to parse. --  -- $strict
src/Regex/Internal/CharSet.hs view
@@ -1,7 +1,17 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE MagicHash #-}+{-# OPTIONS_HADDOCK not-home #-}++-- | This is an internal module. You probably don't need to import this. Import+-- "Data.CharSet" instead.+--+-- = WARNING+--+-- Definitions in this module allow violating invariants that would otherwise be+-- guaranteed by non-internal modules. Use at your own risk!+-- module Regex.Internal.CharSet-  ( CharSet+  ( CharSet(..)   , empty   , singleton   , fromRange@@ -27,7 +37,7 @@ import qualified Prelude import Data.Char import Data.String-import Data.Foldable (foldl')+import qualified Data.Foldable as F import qualified Data.IntMap.Strict as IM import Data.Semigroup (Semigroup(..), stimesIdempotentMonoid) import GHC.Exts (Int(..), Char(..), chr#)@@ -46,21 +56,21 @@   showsPrec p cs = showParen (p > 10) $     showString "fromRanges " . shows (ranges cs) --- | @fromString = 'fromList'@+-- | @fromString@ = 'fromList' instance IsString CharSet where   fromString = fromList --- | @(<>) = 'union'@+-- | @(<>)@ = 'union' instance Semigroup CharSet where   (<>) = union-  sconcat = foldl' union empty+  sconcat = F.foldl' union empty   {-# INLINE sconcat #-}   stimes = stimesIdempotentMonoid --- | @mempty = 'empty'@+-- | @mempty@ = 'empty' instance Monoid CharSet where   mempty = empty-  mconcat = foldl' union empty+  mconcat = F.foldl' union empty   {-# INLINE mconcat #-}  -- | The empty set.@@ -78,12 +88,12 @@  -- | \(O(s \min(s,C))\). Create a set from @Char@s in a list. fromList :: [Char] -> CharSet-fromList = foldl' (flip insert) empty+fromList = F.foldl' (flip insert) empty {-# INLINE fromList #-}  -- | \(O(n \min(n,C))\). Create a set from the given @Char@ ranges (inclusive). fromRanges :: [(Char, Char)] -> CharSet-fromRanges = foldl' (flip insertRange) empty+fromRanges = F.foldl' (flip insertRange) empty {-# INLINE fromRanges #-}  -- | \(O(\min(n,C))\). Insert a @Char@ into a set.@@ -220,7 +230,10 @@  -- | Is the internal structure of the set valid? valid :: CharSet -> Bool-valid cs = and (zipWith (<=) ls hs)-        && all (>1) (zipWith (flip (-)) hs (tail ls))+valid cs = noneEmpty && noneAdjacent   where     (ls,hs) = unzip (fmap (fmap ord) (IM.assocs (unCharSet cs)))+    noneEmpty = and (zipWith (<=) ls hs)+    noneAdjacent = case ls of+      [] -> True+      _:ls' -> all (>1) (zipWith (-) ls' hs)
src/Regex/Internal/Debug.hs view
@@ -1,6 +1,11 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-}++-- | This module provides functions for visualizing @RE@s and @Parser@s.+-- [See here](https://github.com/meooow25/parser-regex/wiki/Visualizations)+-- for some examples.+-- module Regex.Internal.Debug   ( reToDot   , parserToDot@@ -29,7 +34,7 @@  -- | Generate a [Graphviz DOT](https://graphviz.org/doc/info/lang.html) -- visualization of a 'RE'. Optionally takes an alphabet @[c]@, which will be--- tested against the 'token' functions in the 'RE' and accepted characters+-- tested against the @token@ functions in the 'RE' and accepted characters -- displayed. reToDot :: forall c a. Maybe ([c], [c] -> String) -> RE c a -> String reToDot ma re0 = execM $ do@@ -69,7 +74,7 @@  -- | Generate a [Graphviz DOT](https://graphviz.org/doc/info/lang.html) -- visualization of a 'Parser'. Optionally takes an alphabet @[c]@, which will--- be tested against the 'token' functions in the 'Parser' and the accepted+-- be tested against the @token@ functions in the 'Parser' and the accepted -- characters displayed. parserToDot :: forall c a. Maybe ([c], [c] -> String) -> Parser c a -> String parserToDot ma p0 = execM $ do@@ -133,6 +138,9 @@ -- Display Chars ------------------ +-- |+-- >>> dispCharRanges "abc012def"+-- "[('0','2'),('a','f')]" dispCharRanges :: [Char] -> String dispCharRanges = show . CS.ranges . CS.fromList @@ -169,7 +177,10 @@     (fromString . escape . disp) (filter (isJust . t) cs))  escape :: String -> String-escape = init . tail . show+escape = init . tail' . show+  where+    tail' (_:xs) = xs+    tail' [] = error "tail'"  (<+>) :: Str -> Str -> Str s1 <+> s2 = s1 <> " " <> s2
src/Regex/Internal/Generated/CaseFold.hs view
@@ -1,9 +1,9 @@ -- DO NOT EDIT -- This file was generated by GenCaseFold.hs from a CaseFolding.txt with header ----- CaseFolding-15.1.0.txt--- Date: 2023-05-12, 21:53:10 GMT--- © 2023 Unicode®, Inc.+-- CaseFolding-16.0.0.txt+-- Date: 2024-04-30, 21:48:11 GMT+-- © 2024 Unicode®, Inc. -- module Regex.Internal.Generated.CaseFold   ( caseFoldSimple@@ -545,6 +545,7 @@   '\x1c86' -> '\x44a'   '\x1c87' -> '\x463'   '\x1c88' -> '\xa64b'+  '\x1c89' -> '\x1c8a'   '\x1c90' -> '\x10d0'   '\x1c91' -> '\x10d1'   '\x1c92' -> '\x10d2'@@ -1097,9 +1098,13 @@   '\xa7c6' -> '\x1d8e'   '\xa7c7' -> '\xa7c8'   '\xa7c9' -> '\xa7ca'+  '\xa7cb' -> '\x264'+  '\xa7cc' -> '\xa7cd'   '\xa7d0' -> '\xa7d1'   '\xa7d6' -> '\xa7d7'   '\xa7d8' -> '\xa7d9'+  '\xa7da' -> '\xa7db'+  '\xa7dc' -> '\x19b'   '\xa7f5' -> '\xa7f6'   '\xab70' -> '\x13a0'   '\xab71' -> '\x13a1'@@ -1370,6 +1375,28 @@   '\x10cb0' -> '\x10cf0'   '\x10cb1' -> '\x10cf1'   '\x10cb2' -> '\x10cf2'+  '\x10d50' -> '\x10d70'+  '\x10d51' -> '\x10d71'+  '\x10d52' -> '\x10d72'+  '\x10d53' -> '\x10d73'+  '\x10d54' -> '\x10d74'+  '\x10d55' -> '\x10d75'+  '\x10d56' -> '\x10d76'+  '\x10d57' -> '\x10d77'+  '\x10d58' -> '\x10d78'+  '\x10d59' -> '\x10d79'+  '\x10d5a' -> '\x10d7a'+  '\x10d5b' -> '\x10d7b'+  '\x10d5c' -> '\x10d7c'+  '\x10d5d' -> '\x10d7d'+  '\x10d5e' -> '\x10d7e'+  '\x10d5f' -> '\x10d7f'+  '\x10d60' -> '\x10d80'+  '\x10d61' -> '\x10d81'+  '\x10d62' -> '\x10d82'+  '\x10d63' -> '\x10d83'+  '\x10d64' -> '\x10d84'+  '\x10d65' -> '\x10d85'   '\x118a0' -> '\x118c0'   '\x118a1' -> '\x118c1'   '\x118a2' -> '\x118c2'
src/Regex/Internal/List.hs view
@@ -291,6 +291,9 @@  -- | \(O(mn \log m)\). Parse a list with a @RE@. --+-- Parses the entire list, not just a prefix or a substring.+-- Returns early without demanding the entire list on parse failure.+-- -- Uses 'Regex.List.compile', see the note there. -- -- If parsing multiple lists using the same @RE@, it is wasteful to compile@@ -305,6 +308,9 @@ {-# INLINE reParse #-}  -- | \(O(mn \log m)\). Parse a list with a @Parser@.+--+-- Parses the entire list, not just a prefix or a substring.+-- Returns early without demanding the entire list on parse failure. parse :: Parser c a -> [c] -> Maybe a parse = P.parseFoldr foldr {-# INLINE parse #-}@@ -313,6 +319,9 @@ -- parse failure. -- -- For use with parsers that are known to never fail.+--+-- Parses the entire list, not just a prefix or a substring.+-- Returns early without demanding the entire list on parse failure. parseSure :: Parser c a -> [c] -> a parseSure p = fromMaybe parseSureError . parse p {-# INLINE parseSure #-}@@ -415,20 +424,20 @@   where     f a b c = concat [a,b,c] --- | \(O(mn \log m)\). Replace non-overlapping matches of the given @RE@ with--- their results.+-- | \(O(mn \log m)\). Replace all non-overlapping matches of the given @RE@+-- with their results. -- -- ==== __Examples__ -- -- >>> replaceAll (" and " <$ list ", ") "red, blue, green" -- "red and blue and green" ----- >>> replaceAll ("Fruit" <$ list "Time" <|> "banana" <$ list "arrow") "Time flies like an arrow"+-- >>> replaceAll ("Fruit" <$ list "Time" <|> "a banana" <$ list "an arrow") "Time flies like an arrow" -- "Fruit flies like a banana" -- -- @ -- sep = 'oneOfChar' "-./"--- digits n = 'replicateM' n (oneOfChar 'Data.CharSet.digit')+-- digits n = 'Control.Monad.replicateM' n (oneOfChar 'Data.CharSet.digit') -- toYmd d m y = concat [y, \"-\", m, \"-\", d] -- date = toYmd \<$> digits 2 \<* sep --              \<*> digits 2 \<* sep
src/Regex/Internal/Parser.hs view
@@ -2,6 +2,15 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# OPTIONS_HADDOCK not-home #-}++-- | This is an internal module. You probably don't need to import this.+--+-- = WARNING+--+-- Definitions in this module allow violating invariants that would otherwise be+-- guaranteed by non-internal modules. Use at your own risk!+-- module Regex.Internal.Parser   ( Parser(..)   , Node(..)@@ -14,6 +23,7 @@   , finishParser   , Foldr   , parseFoldr+  , parseNext   ) where  import Control.Applicative@@ -22,6 +32,7 @@ import Data.Maybe (isJust) import Data.Primitive.SmallArray import qualified Data.Foldable as F+import qualified GHC.Exts as X  import Regex.Internal.Regex (RE(..), Strictness(..), Greediness(..)) import Regex.Internal.Unique (Unique(..), UniqueSet)@@ -39,18 +50,18 @@   PPure   :: a -> Parser c a   PLiftA2 :: !Strictness -> !(a1 -> a2 -> a) -> !(Parser c a1) -> !(Parser c a2) -> Parser c a   PEmpty  :: Parser c a-  PAlt    :: !Unique -> !(Parser c a) -> !(Parser c a) -> !(SmallArray (Parser c a)) -> Parser c a-  PFoldGr :: !Unique -> !Strictness -> !(a -> a1 -> a) -> a -> !(Parser c a1) -> Parser c a-  PFoldMn :: !Unique -> !Strictness -> !(a -> a1 -> a) -> a -> !(Parser c a1) -> Parser c a-  PMany   :: !Unique -> !(a1 -> a) -> !(a2 -> a) -> !(a2 -> a1 -> a2) -> !a2 -> !(Parser c a1) -> Parser c a+  PAlt    :: {-# UNPACK #-} !Unique -> !(Parser c a) -> !(Parser c a) -> {-# UNPACK #-} !(SmallArray (Parser c a)) -> Parser c a+  PFoldGr :: {-# UNPACK #-} !Unique -> !Strictness -> !(a -> a1 -> a) -> a -> !(Parser c a1) -> Parser c a+  PFoldMn :: {-# UNPACK #-} !Unique -> !Strictness -> !(a -> a1 -> a) -> a -> !(Parser c a1) -> Parser c a+  PMany   :: {-# UNPACK #-} !Unique -> !(a1 -> a) -> !(a2 -> a) -> !(a2 -> a1 -> a2) -> !a2 -> !(Parser c a1) -> Parser c a  -- | A node in the NFA. Used for recognition. data Node c a where   NAccept :: a -> Node c a-  NGuard  :: !Unique -> Node c a -> Node c a+  NGuard  :: {-# UNPACK #-} !Unique -> Node c a -> Node c a   NToken  :: !(c -> Maybe a1) -> !(Node c a) -> Node c a   NEmpty  :: Node c a-  NAlt    :: !(Node c a) -> !(Node c a) -> !(SmallArray (Node c a)) -> Node c a+  NAlt    :: !(Node c a) -> !(Node c a) -> {-# UNPACK #-} !(SmallArray (Node c a)) -> Node c a -- Note that NGuard is lazy in the node. We have to introduce laziness in -- at least one place, to make a graph with loops possible. @@ -186,10 +197,10 @@   CFmap_   :: !(Node c a1) -> !(Cont c a1 a) -> Cont c b a   CLiftA2A :: !Strictness -> !(b -> a2 -> a3) -> !(Parser c a2) -> !(Cont c a3 a) -> Cont c b a   CLiftA2B :: !Strictness -> !(a1 -> b -> a3) -> a1 -> !(Cont c a3 a) -> Cont c b a-  CAlt     :: !Unique -> !(Cont c b a) -> Cont c b a-  CFoldGr  :: !Unique -> !Strictness -> !(Parser c b) -> !(a1 -> b -> a1) -> a1 -> !(Cont c a1 a) -> Cont c b a-  CFoldMn  :: !Unique -> !Strictness -> !(Parser c b) -> !(a1 -> b -> a1) -> a1 -> !(Cont c a1 a) -> Cont c b a-  CMany    :: !Unique -> !(Parser c b) -> !(b -> a2) -> !(a1 -> a2) -> !(a1 -> b -> a1) -> !a1 -> !(Cont c a2 a) -> Cont c b a+  CAlt     :: {-# UNPACK #-} !Unique -> !(Cont c b a) -> Cont c b a+  CFoldGr  :: {-# UNPACK #-} !Unique -> !Strictness -> !(Parser c b) -> !(a1 -> b -> a1) -> a1 -> !(Cont c a1 a) -> Cont c b a+  CFoldMn  :: {-# UNPACK #-} !Unique -> !Strictness -> !(Parser c b) -> !(a1 -> b -> a1) -> a1 -> !(Cont c a1 a) -> Cont c b a+  CMany    :: {-# UNPACK #-} !Unique -> !(Parser c b) -> !(b -> a2) -> !(a1 -> a2) -> !(a1 -> b -> a1) -> !a1 -> !(Cont c a2 a) -> Cont c b a  data NeedCList c a where   NeedCCons :: !(c -> Maybe b) -> !(Cont c b a) -> !(NeedCList c a) -> NeedCList c a@@ -334,16 +345,20 @@   }  -- | \(O(m \log m)\). Prepare a parser for input.-prepareParser :: Parser c a -> ParserState c a+--+-- Returns @Nothing@ if parsing has failed regardless of further input.+-- Otherwise, returns the initial @ParserState@.+prepareParser :: Parser c a -> Maybe (ParserState c a) prepareParser p = toParserState (down p CTop stepStateZero) --- | \(O(m \log m)\). Step a parser by feeding a single element @c@. Returns--- @Nothing@ if the parse has failed regardless of further input. Otherwise,--- returns an updated @ParserState@.+-- | \(O(m \log m)\). Step a parser by feeding a single element @c@.+--+-- Returns @Nothing@ if parsing has failed regardless of further input.+-- Otherwise, returns an updated @ParserState@. stepParser :: ParserState c a -> c -> Maybe (ParserState c a) stepParser ps c = case psNeed ps of   NeedCNil -> Nothing-  needs -> Just $! toParserState (go needs)+  needs -> toParserState (go needs)   where     go (NeedCCons t ct rest) =       let !pt = go rest@@ -355,21 +370,102 @@ finishParser :: ParserState c a -> Maybe a finishParser = psResult -toParserState :: StepState c a -> ParserState c a-toParserState pt = ParserState-  { psNeed = sNeed pt-  , psResult = sResult pt-  }+toParserState :: StepState c a -> Maybe (ParserState c a)+toParserState ss = case (sNeed ss, sResult ss) of+  (NeedCNil, Nothing) -> Nothing+  (need, result) -> Just $! ParserState { psNeed = need, psResult = result }  -- | A fold function. type Foldr f a = forall b. (a -> b -> b) -> b -> f -> b --- | \(O(mn \log m)\). Run a parser given a sequence @f@ and a fold of @f@.+-- | \(O(mn \log m)\). Run a parser given a sequence @f@ and a fold function.+--+-- Parses the entire sequence, not just a prefix or an substring.+-- Returns early on parse failure, if the fold can short circuit.+--+-- ==== __Examples__+--+-- @+-- import qualified Data.Vector.Generic as VG -- from vector+--+-- import Regex.Base (Parser)+-- import qualified Regex.Base as R+--+-- parseVector :: VG.Vector v c => Parser c a -> v c -> Maybe a+-- parseVector p v = R.'parseFoldr' VG.foldr p v+-- @+--+-- >>> import Control.Applicative (many)+-- >>> import qualified Data.Vector as V+-- >>> import Regex.Base (Parser)+-- >>> import qualified Regex.Base as R+-- >>>+-- >>> let p = R.compile $ many ((,) <$> R.satisfy even <*> R.satisfy odd) :: Parser Int [(Int, Int)]+-- >>> parseVector p (V.fromList [0..5])+-- Just [(0,1),(2,3),(4,5)]+-- >>> parseVector p (V.fromList [0,2..6])+-- Nothing+-- parseFoldr :: Foldr f c -> Parser c a -> f -> Maybe a-parseFoldr fr = \p xs -> fr f finishParser xs (prepareParser p)+parseFoldr fr = \p xs -> prepareParser p >>= fr f finishParser xs   where-    f c k = \ps -> stepParser ps c >>= k+    f c k = X.oneShot (\ps -> stepParser ps c >>= k) {-# INLINE parseFoldr #-}++-- | \(O(mn \log m)\). Run a parser given a \"@next@\" action.+--+-- Calls @next@ repeatedly to yield elements. A @Nothing@ is interpreted as+-- end-of-sequence.+--+-- Parses the entire sequence, not just a prefix or an substring.+-- Returns without exhausting the input on parse failure.+--+-- ==== __Examples__+--+-- @+-- import Conduit (ConduitT, await, sinkNull) -- from conduit+--+-- import Regex.Base (Parser)+-- import qualified Regex.Base as R+--+-- parseConduit :: Monad m => Parser c a -> ConduitT c x m (Maybe a)+-- parseConduit p = R.'parseNext' p await <* sinkNull+-- @+--+-- >>> import Control.Applicative (many)+-- >>> import Conduit ((.|), iterMC, runConduit, yieldMany)+-- >>> import Regex.Base (Parser)+-- >>> import qualified Regex.Base as R+-- >>>+-- >>> let p = R.compile $ many ((,) <$> R.satisfy even <*> R.satisfy odd) :: Parser Int [(Int, Int)]+-- >>> let printYieldMany xs = yieldMany xs .| iterMC print+-- >>> runConduit $ printYieldMany [0..5] .| parseConduit p+-- 0+-- 1+-- 2+-- 3+-- 4+-- 5+-- Just [(0,1),(2,3),(4,5)]+-- >>> runConduit $ printYieldMany [0,2..6] .| parseConduit p+-- 0+-- 2+-- 4+-- 6+-- Nothing+--+-- @since 0.2.0.0+parseNext :: Monad m => Parser c a -> m (Maybe c) -> m (Maybe a)+parseNext p next = case prepareParser p of+  Nothing -> pure Nothing+  Just ps -> loop ps+  where+    loop ps = next >>= \m -> case m of+      Nothing -> pure (finishParser ps)+      Just c -> case stepParser ps c of+        Nothing -> pure Nothing+        Just ps' -> loop ps'+{-# INLINE parseNext #-}  --------- -- Util
src/Regex/Internal/Regex.hs view
@@ -1,5 +1,9 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE GADTs #-}+{-# OPTIONS_HADDOCK not-home #-}++-- | This is an internal module. You probably don't need to import this.+-- module Regex.Internal.Regex   ( RE(..)   , Strictness(..)@@ -120,13 +124,13 @@   some re = liftA2' (:) re (many re)   many = fmap reverse . foldlMany' (flip (:)) [] --- | @(<>) = liftA2 (<>)@+-- | @(<>)@ = @liftA2 (<>)@ instance Semigroup a => Semigroup (RE c a) where   (<>) = liftA2 (<>)   sconcat = fmap sconcat . sequenceA   {-# INLINE sconcat #-} --- | @mempty = pure mempty@+-- | @mempty@ = @pure mempty@ instance Monoid a => Monoid (RE c a) where   mempty = pure mempty   mconcat = fmap mconcat . sequenceA@@ -180,6 +184,7 @@ -- Many --------- +-- | A repeating value or a finite list. data Many a   = Repeat a   -- ^ A single value repeating indefinitely   | Finite [a] -- ^ A finite list
src/Regex/Internal/Text.hs view
@@ -1,8 +1,19 @@ {-# LANGUAGE BangPatterns #-}+{-# OPTIONS_HADDOCK not-home #-}++-- | This is an internal module. You probably don't need to import this. Import+-- "Regex.Text" instead.+--+-- = WARNING+--+-- Definitions in this module allow violating invariants that would otherwise be+-- guaranteed by non-internal modules. Use at your own risk!+-- module Regex.Internal.Text   (-    TextToken+    TextToken(..)   , REText+  , textTokenFoldr    , token   , satisfy@@ -49,7 +60,7 @@  import Control.Applicative import Data.Char-import Data.Foldable (foldl')+import qualified Data.Foldable as F import Data.Maybe (fromMaybe) import Numeric.Natural import Data.Text (Text)@@ -77,6 +88,10 @@ -- This module uses RE TextToken for Text regexes instead of simply RE Char to -- support Text slicing. It does mean that use cases not using slicing pay a -- small cost, but it is not worth having two separate Text regex APIs.+--+-- Slicing is made possible by the unsafeAdjacentAppend function. Of course,+-- this means that REs using it MUST NOT be used with multiple Texts, such as+-- trying to parse chunks of a lazy Text. data TextToken = TextToken   { tArr     :: {-# UNPACK #-} !TArray.Array   , tOffset  :: {-# UNPACK #-} !Int@@ -137,47 +152,47 @@ -- as described by the Unicode standard. textIgnoreCase :: Text -> REText Text textIgnoreCase t =-  T.foldr' (\c cs -> R.liftA2' adjacentAppend (ignoreCaseTokenMatch c) cs)+  T.foldr' (\c cs -> R.liftA2' unsafeAdjacentAppend (ignoreCaseTokenMatch c) cs)            (pure T.empty)            t -- See Note [Why simple case fold]  -- | Parse any @Text@. Biased towards matching more. manyText :: REText Text-manyText = R.foldlMany' adjacentAppend T.empty anyTokenMatch+manyText = R.foldlMany' unsafeAdjacentAppend T.empty anyTokenMatch  -- | Parse any non-empty @Text@. Biased towards matching more. someText :: REText Text-someText = R.liftA2' adjacentAppend anyTokenMatch manyText+someText = R.liftA2' unsafeAdjacentAppend anyTokenMatch manyText  -- | Parse any @Text@. Minimal, i.e. biased towards matching less. manyTextMin :: REText Text-manyTextMin = R.foldlManyMin' adjacentAppend T.empty anyTokenMatch+manyTextMin = R.foldlManyMin' unsafeAdjacentAppend T.empty anyTokenMatch  -- | Parse any non-empty @Text@. Minimal, i.e. biased towards matching less. someTextMin :: REText Text-someTextMin = R.liftA2' adjacentAppend anyTokenMatch manyTextMin+someTextMin = R.liftA2' unsafeAdjacentAppend anyTokenMatch manyTextMin  -- | Parse any @Text@ containing members of the @CharSet@. -- Biased towards matching more. manyTextOf :: CharSet -> REText Text-manyTextOf !cs = R.foldlMany' adjacentAppend T.empty (oneOfTokenMatch cs)+manyTextOf !cs = R.foldlMany' unsafeAdjacentAppend T.empty (oneOfTokenMatch cs)  -- | Parse any non-empty @Text@ containing members of the @CharSet@. -- Biased towards matching more. someTextOf :: CharSet -> REText Text-someTextOf !cs = R.liftA2' adjacentAppend (oneOfTokenMatch cs) (manyTextOf cs)+someTextOf !cs = R.liftA2' unsafeAdjacentAppend (oneOfTokenMatch cs) (manyTextOf cs)  -- | Parse any @Text@ containing members of the @CharSet@. -- Minimal, i.e. biased towards matching less. manyTextOfMin :: CharSet -> REText Text-manyTextOfMin !cs = R.foldlManyMin' adjacentAppend T.empty (oneOfTokenMatch cs)+manyTextOfMin !cs = R.foldlManyMin' unsafeAdjacentAppend T.empty (oneOfTokenMatch cs)  -- | Parse any non-empty @Text@ containing members of the @CharSet@. -- Minimal, i.e. biased towards matching less. someTextOfMin :: CharSet -> REText Text someTextOfMin !cs =-  R.liftA2' adjacentAppend (oneOfTokenMatch cs) (manyTextOfMin cs)+  R.liftA2' unsafeAdjacentAppend (oneOfTokenMatch cs) (manyTextOfMin cs)  ----------------- -- Numeric REs@@ -314,11 +329,14 @@       RFmap _ _ re1 -> go re1       RFmap_ _ re1 -> go re1       RPure _ -> RPure T.empty-      RLiftA2 _ _ re1 re2 -> RLiftA2 Strict adjacentAppend (go re1) (go re2)+      RLiftA2 _ _ re1 re2 ->+        RLiftA2 Strict unsafeAdjacentAppend (go re1) (go re2)       REmpty -> REmpty       RAlt re1 re2 -> RAlt (go re1) (go re2)-      RMany _ _ _ _ re1 -> RFold Strict Greedy adjacentAppend T.empty (go re1)-      RFold _ gr _ _ re1 -> RFold Strict gr adjacentAppend T.empty (go re1)+      RMany _ _ _ _ re1 ->+        RFold Strict Greedy unsafeAdjacentAppend T.empty (go re1)+      RFold _ gr _ _ re1 ->+        RFold Strict gr unsafeAdjacentAppend T.empty (go re1)  data WithMatch a = WM {-# UNPACK #-} !Text a @@ -330,10 +348,10 @@  instance Applicative WithMatch where   pure = WM T.empty-  liftA2 f (WM t1 x) (WM t2 y) = WM (adjacentAppend t1 t2) (f x y)+  liftA2 f (WM t1 x) (WM t2 y) = WM (unsafeAdjacentAppend t1 t2) (f x y)  liftA2WM' :: (a1 -> a2 -> b) -> WithMatch a1 -> WithMatch a2 -> WithMatch b-liftA2WM' f (WM t1 x) (WM t2 y) = WM (adjacentAppend t1 t2) $! f x y+liftA2WM' f (WM t1 x) (WM t2 y) = WM (unsafeAdjacentAppend t1 t2) $! f x y  -- | Rebuild the @RE@ to include the matched @Text@ alongside the result. withMatch :: REText a -> REText (Text, a)@@ -368,16 +386,18 @@ -- Parse ---------- -tokenFoldr :: (TextToken -> b -> b) -> b -> Text -> b-tokenFoldr f z (TInternal.Text a o0 l) = loop o0+textTokenFoldr :: (TextToken -> b -> b) -> b -> Text -> b+textTokenFoldr f z (TInternal.Text a o0 l) = loop o0   where     loop o | o - o0 >= l = z     loop o = case TUnsafe.iterArray a o of       TUnsafe.Iter c clen -> f (TextToken a o c) (loop (o + clen))-{-# INLINE tokenFoldr #-}+{-# INLINE textTokenFoldr #-}  -- | \(O(mn \log m)\). Parse a @Text@ with a @REText@. --+-- Parses the entire @Text@, not just a prefix or a substring.+-- -- Uses 'Regex.Text.compile', see the note there. -- -- If parsing multiple @Text@s using the same @RE@, it is wasteful to compile@@ -392,13 +412,17 @@ {-# INLINE reParse #-}  -- | \(O(mn \log m)\). Parse a @Text@ with a @ParserText@.+--+-- Parses the entire @Text@, not just a prefix or a substring. parse :: ParserText a -> Text -> Maybe a-parse = P.parseFoldr tokenFoldr+parse = P.parseFoldr textTokenFoldr  -- | \(O(mn \log m)\). Parse a @Text@ with a @ParserText@. Calls 'error' on -- parse failure. -- -- For use with parsers that are known to never fail.+--+-- Parses the entire @Text@, not just a prefix or a substring. parseSure :: ParserText a -> Text -> a parseSure p = fromMaybe parseSureError . parse p @@ -501,8 +525,8 @@   where     f a b c = reverseConcat [c,b,a] --- | \(O(mn \log m)\). Replace non-overlapping matches of the given @RE@ with--- their results.+-- | \(O(mn \log m)\). Replace all non-overlapping matches of the given @RE@+-- with their results. -- -- ==== __Examples__ --@@ -511,12 +535,12 @@ -- -- For simple replacements like above, prefer @Data.Text.'Data.Text.replace'@. ----- >>> replaceAll ("Fruit" <$ text "Time" <|> "banana" <$ text "arrow") "Time flies like an arrow"+-- >>> replaceAll ("Fruit" <$ text "Time" <|> "a banana" <$ text "an arrow") "Time flies like an arrow" -- "Fruit flies like a banana" -- -- @ -- sep = 'oneOf' "-./"--- digits n = 'toMatch' ('replicateM_' n (oneOf 'Data.CharSet.digit'))+-- digits n = 'toMatch' ('Control.Monad.replicateM_' n (oneOf 'Data.CharSet.digit')) -- toYmd d m y = mconcat [y, \"-\", m, \"-\", d] -- date = toYmd \<$> digits 2 \<* sep --              \<*> digits 2 \<* sep@@ -539,8 +563,8 @@  -- WARNING: If t1 and t2 are not empty, they must be adjacent slices of the -- same Text. In other words, sameByteArray# a1 _a2 && o1 + l1 == _o2.-adjacentAppend :: Text -> Text -> Text-adjacentAppend t1@(TInternal.Text a1 o1 l1) t2@(TInternal.Text _a2 _o2 l2)+unsafeAdjacentAppend :: Text -> Text -> Text+unsafeAdjacentAppend t1@(TInternal.Text a1 o1 l1) t2@(TInternal.Text _a2 _o2 l2)   | T.null t1 = t2   | T.null t2 = t1   | otherwise = TInternal.Text a1 o1 (l1+l2)@@ -558,7 +582,7 @@       | otherwise = reverseConcatOverflowError       where         acc' = acc + l-    len = foldl' flen 0 ts+    len = F.foldl' flen 0 ts     arr = TArray.run $ do       marr <- TArray.new len       let loop !_ [] = pure marr
src/Regex/Internal/Unique.hs view
@@ -1,3 +1,12 @@+{-# OPTIONS_HADDOCK not-home #-}++-- | This is an internal module. You probably don't need to import this.+--+-- = WARNING+--+-- Definitions in this module allow violating invariants that would otherwise be+-- guaranteed by non-internal modules. Use at your own risk!+-- module Regex.Internal.Unique   ( Unique(..)   , UniqueSet@@ -12,7 +21,9 @@ -- | A unique ID. Must be >= 0. newtype Unique = Unique { unUnique :: Int } --- | A set of 'Unique's. The bitmask is a set for IDs 0..63 (0..31 if 32-bit).+-- | A set of 'Unique's.++-- The bitmask is a set for IDs 0..63 on 64-bit and 0..31 on 32-bit. -- Set operations on this are very fast and speed up the common case of small -- regexes a little bit, at the cost of a little more memory. data UniqueSet = UniqueSet {-# UNPACK #-} !Int !IS.IntSet
test/Test.hs view
@@ -7,7 +7,7 @@ import Control.Monad import Data.Char import qualified Data.List as L-import Data.Maybe+import Data.Maybe (isJust, isNothing) import Data.List.NonEmpty (NonEmpty(..)) import Data.Proxy import Data.Semigroup@@ -41,6 +41,9 @@     , listCombinatorTests     , stringOpTests     ]+  , testGroup "Regex.Base"+    [ earlyFailureTests+    ]   , manyTests   , charSetTests   ]@@ -1545,6 +1548,66 @@     , testCase "abc xyz abcabc" $ RL.replaceAll ("xyz" <$ RL.list "abc") "abcabc" @?= "xyzxyz"     , testCase "aba xyz ababababa" $ RL.replaceAll ("xyz" <$ RL.list "aba") "ababababa" @?= "xyzbxyzba"     , testCase "abc xyz ab" $ RL.replaceAll ("xyz" <$ RL.list "abc") "ab" @?= "ab"+    ]+  ]++---------+-- Base+---------++earlyFailureTests :: TestTree+earlyFailureTests = testGroup "Early failure"+  [ testCase "prepareParser cases" $ do+      void (R.prepareParser (R.compile empty)) @?= Nothing+      void (R.prepareParser (R.compile (pure () *> empty))) @?= Nothing+      void (R.prepareParser (R.compile (empty *> pure ()))) @?= Nothing+      void (R.prepareParser (R.compile (empty <|> empty))) @?= Nothing++      void (R.prepareParser (R.compile R.anySingle)) @?= Just ()+      void (R.prepareParser (R.compile (pure ()))) @?= Just ()++  , testGroup "stepParser"+    [ testCase ". a,aa" $+        case R.prepareParser (R.compile R.anySingle) of+          Nothing -> assertFailure "prepare"+          Just ps0 -> do+            R.finishParser ps0 @?= Nothing+            case R.stepParser ps0 'a' of+              Just ps1 -> do+                R.finishParser ps1 @?= Just 'a'+                void (R.stepParser ps1 'b') @?= Nothing+              _ -> assertFailure "step ps0"++    , testCase "* aa,aaa" $+        case R.prepareParser (R.compile (many R.anySingle)) of+          Nothing -> assertFailure "prepare"+          Just ps0 -> do+            R.finishParser ps0 @?= Just ""+            case R.stepParser ps0 'a' of+              Just ps1 -> do+                R.finishParser ps1 @?= Just "a"+                case R.stepParser ps1 'a' of+                  Just ps2 -> do+                    R.finishParser ps2 @?= Just "aa"+                    void (R.stepParser ps2 'a') @?= Just ()+                  _ -> assertFailure "step ps1"+              _ -> assertFailure "step ps0"++    , testCase "ab ax,abx" $+        case R.prepareParser (R.compile (RL.list "ab")) of+          Nothing -> assertFailure "prepare"+          Just ps0 -> do+            R.finishParser ps0 @?= Nothing+            case R.stepParser ps0 'a' of+              Just ps1 -> do+                R.finishParser ps1 @?= Nothing+                void (R.stepParser ps1 'x') @?= Nothing+                case R.stepParser ps1 'b' of+                  Just ps2 -> do+                    R.finishParser ps2 @?= Just "ab"+                    void (R.stepParser ps2 'x') @?= Nothing+                  _ -> assertFailure "step ps1"+              _ -> assertFailure "step ps0"     ]   ]