regex-base 0.93.2 → 0.94.0.3
raw patch · 12 files changed
Files
- ChangeLog.md +49/−0
- LICENSE +1/−2
- README.md +11/−0
- Text/Regex/Base.hs +0/−56
- Text/Regex/Base/Context.hs +0/−409
- Text/Regex/Base/Impl.hs +0/−58
- Text/Regex/Base/RegexLike.hs +0/−228
- regex-base.cabal +91/−56
- src/Text/Regex/Base.hs +81/−0
- src/Text/Regex/Base/Context.hs +418/−0
- src/Text/Regex/Base/Impl.hs +71/−0
- src/Text/Regex/Base/RegexLike.hs +286/−0
+ ChangeLog.md view
@@ -0,0 +1,49 @@+See also http://pvp.haskell.org/faq++## 0.94.0.3++_2025-03-02 Andreas Abel_++- Drop support for GHC 7+- Make `Prelude` imports explicit, add `LANGUAGE NoImplicitPrelude`+- Make upper bounds of dependencies major-major (all are shipped with GHC)+- Tested with GHC 8.0 - 9.12.1++## 0.94.0.2 Revision 1++_2022-05-25 Andreas Abel_++- Allow `base-4.17` and higher, for GHC 9.4++## 0.94.0.2++_2021-11-16 Andreas Abel_++- Allow `text-2.0`+- Remove unused dependency `mtl`+- Warning free up to GHC 9.2+- Tested with GHC 7.0 - 9.2++## 0.94.0.1 Revision 1++_2021-08-12 Andreas Abel_++- Allow `base-4.16`, for GHC 9.2++## 0.94.0.1++_2021-02-20 Andreas Abel_++- Workaround for `{-# LANGUAGE Haskell2010 #-}` parser regression introduced in GHC 9.0+- Optimization flag `-O2` has been removed++## 0.94.0.0++_2019-09-25 Herbert Valerio Riedel_++- **Breaking change**: Switch RegExp API from the previously used `Monad(fail)` to `MonadFail(fail)` to denote matching failures+- Define `Extract Text` instances for strict and lazy `Text` types+- Compatibility with `base-4.13.0`+- Explicitly declare all modules `Safe` under SafeHaskell for GHC 7.4 and higher++----
LICENSE view
@@ -1,5 +1,3 @@-This modile is under this "3 clause" BSD license:- Copyright (c) 2007, Christopher Kuklewicz All rights reserved. @@ -7,6 +5,7 @@ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.+ * The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,11 @@+[](http://hackage.haskell.org/package/regex-base)+[](http://stackage.org/nightly/package/regex-base)+[](http://stackage.org/lts/package/regex-base)+[](https://github.com/haskell-hvr/regex-base/actions/workflows/haskell-ci.yml)+[](https://opensource.org/licenses/BSD-3-Clause)+regex-base+==========++Common interface to several Haskell implementations of regular expressions.++[Documentation](https://hackage.haskell.org/package/regex-base/docs/Text-Regex-Base.html) on hackage.
− Text/Regex/Base.hs
@@ -1,56 +0,0 @@-{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}--------------------------------------------------------------------------------- |------ Module : Text.Regex.Base--- Copyright : (c) Chris Kuklewicz 2006--- License : BSD-style (see the file LICENSE)--- --- Maintainer : libraries@haskell.org, textregexlazy@personal.mightyreason.com--- Stability : experimental--- Portability : non-portable (MPTC+FD)------ Classes and instances for Regex matching.------ --- This module merely imports and re-exports the common part of the new--- api: "Text.Regex.Base.RegexLike" and "Text.Regex.Base.Context".--- --- To see what result types the instances of RegexContext can produce,--- please read the "Text.Regex.Base.Context" haddock documentation.--- --- This does not provide any of the backends, just the common interface--- they all use. The modules which provide the backends and their cabal--- packages are:--- --- * @Text.Regex.Posix@ from regex-posix--- --- * @Text.Regex@ from regex-compat (uses regex-posix)--- --- * @Text.Regex.Parsec@ from regex-parsec--- --- * @Text.Regex.DFA@ from regex-dfa--- --- * @Text.Regex.PCRE@ from regex-pcre--- --- * @Test.Regex.TRE@ from regex-tre--- --- In fact, just importing one of the backends is adequate, you do not--- also need to import this module.--- --- TODO: Copy Example*hs files into this haddock comment--------------------------------------------------------------------------------module Text.Regex.Base (getVersion_Text_Regex_Base- -- | RegexLike defines classes and type, and 'Extract' instances- ,module Text.Regex.Base.RegexLike) where--import Data.Version(Version(..))-import Text.Regex.Base.RegexLike-import Text.Regex.Base.Context()--getVersion_Text_Regex_Base :: Version-getVersion_Text_Regex_Base =- Version { versionBranch = [0,93,2]- , versionTags = ["unstable"]- }
− Text/Regex/Base/Context.hs
@@ -1,409 +0,0 @@-{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}-{-|--Module : Text.Regex.Base.Context-Copyright : (c) Chris Kuklewicz 2006-License : BSD-style (see the file LICENSE)--Maintainer : libraries@haskell.org, textregexlazy@personal.mightyreason.com-Stability : experimental-Portability : non-portable (MPTC+FD)--This is a module of instances of 'RegexContext' (defined in-Text.Regex.Base.RegexLike). Nothing else is exported. This is-usually imported via the Text.Regex.Base convenience package which-itself is re-exported from newer Text.Regex.XXX modules provided by-the different regex-xxx backends.--These instances work for all the supported types and backends-interchangably. These instances provide the different results that-can be gotten from a match or matchM operation (often via the @=~@ and-@=~~@ operators with combine @makeRegex@ with @match@ and @matchM@-respectively). This module name is Context because they operators are-context dependent: use them in a context that expects an Int and you-get a count of matches, use them in a Bool context and get True if-there is a match, etc.--@RegexContext a b c@ takes a regular expression suppied in a type 'a'-generated by 'RegexMaker' and a target text supplied in type 'b' to a-result type 'c' using the 'match' class function. The 'matchM' class-function works like 'match' unless there is no match found, in which-case it calls 'fail' in the (arbitrary) monad context.--There are a few type synonyms from RegexLike that are used here:--@ --- | 0 based index from start of source, or (-1) for unused-type MatchOffset = Int--- | non-negative length of a match-type MatchLength = Int-type MatchArray = Array Int (MatchOffset, MatchLength)-type MatchText source = Array Int (source, (MatchOffset, MatchLength))-@--There are also a few newtypes that used to prevent any possible-overlap of types, which were not needed for GHC's late overlap-detection but are needed for use in Hugs.--@-newtype AllSubmatches f b = AllSubmatches {getAllSubmatches :: (f b)}-newtype AllTextSubmatches f b = AllTextSubmatches {getAllTextSubmatches :: (f b)}-newtype AllMatches f b = AllMatches {getAllMatches :: (f b)}-newtype AllTextMatches f b = AllTextMatches {getAllTextMatches :: (f b) }-@--The newtypes' @f@ parameters are the containers, usually @[]@ or-@Array Int@, (where the arrays all have lower bound 0).--The two *Submatches newtypes return only information on the first-match. The other two newtypes return information on all the-non-overlapping matches. The two *Text* newtypes are used to mark-result types that contain the same type as the target text.--Where provided, noncaptured submatches will have a @MatchOffset@ of-(-1) and non-negative otherwise. The semantics of submatches depend-on the backend and its compile and execution options. Where provided,-@MatchLength@ will always be non-negative. Arrays with no elements-are returned with bounds of (1,0). Arrays with elements will have a-lower bound of 0.--XXX THIS HADDOCK DOCUMENTATION IS OUT OF DATE XXX--These are for finding the first match in the target text:---@ RegexContext a b Bool @ :- Whether there is any match or not.---@ RegexContext a b () @ :- Useful as a guard with @matchM@ or @=~~@ in a monad, since failure to match calls 'fail'.---@ RegexContext a b b @ :- This returns the text of the whole match.- It will return 'empty' from the 'Extract' type class if there is no match.- These are defined in each backend module, but documented here for convenience.---@ RegexContext a b (MatchOffset,MatchLength) @ :- This returns the initial index and length of the whole match.- MatchLength will always be non-negative, and 0 for a failed match.---@ RegexContext a b (MatchResult b) @ : The- 'MatchResult' structure with details for the match. This is the- structure copied from the old @JRegex@ pacakge.---@ RegexContext a b (b, b, b) @ :- The text before the match, the text of the match, the text after the match---@ RegexContext a b (b, MatchText b, b) @ :- The text before the match, the details of the match, and the text after the match---@ RegexContext a b (b, b, b, [b]) @ : - The text before the match, the text of the match, the text after the- match, and a list of the text of the 1st and higher sub-parts of the- match. This is the same return value as used in the old- @Text.Regex@ API.--Two containers of the submatch offset information:---@ RegexContext a b MatchArray @ :- Array of @(MatchOffset,MatchLength)@ for all the sub matches.- The whole match is at the intial 0th index.- Noncaptured submatches will have a @MatchOffset@ of (-1)- The array will have no elements and bounds (1,0) if there is no match.---@ RegexContext a b (AllSubmatches [] (MatchOffset,MatchLength) @ :- List of @(MatchOffset,MatchLength)@- The whole match is the first element, the rest are the submatches (if any) in order.- The list is empty if there is no match.--Two containers of the submatch text and offset information:--@ RegexContext a b (AllTextSubmatches (Array Int) (b, (MatchOffset, MatchLength))) @--@ RegexContext a b (AllTextSubmatches [] (b, (MatchOffset, MatchLength))) @--Two containers of the submatch text information:--@ RegexContext a b (AllTextSubmatches [] b) @--@ RegexContext a b (AllTextSubmatches (Array Int) b) @--These instances are for all the matches (non-overlapping). Note that-backends are supposed to supply 'RegexLike' instances for which the-default 'matchAll' and 'matchAllText' stop searching after returning-any successful but empty match.---@ RegexContext a b Int @ :- The number of matches, non-negative.--Two containers for locations of all matches:--@ RegexContext a b (AllMatches [] (MatchOffset, MatchLength)) @--@ RegexContext a b (AllMatches (Array Int) (MatchOffset,MatchLength)) @--Two containers for the locations of all matches and their submatches:--@ RegexContext a b [MatchArray] @ :--@ RegexContext a b (AllMatches (Array Int) MatchArray) @--Two containers for the text and locations of all matches and their submatches:--@ RegexContext a b [MatchText b] @--@ RegexContext a b (AllTextMatches (Array Int) (MatchText b)) @--Two containers for text of all matches:-@ RegexContext a b (AllTextMatches [] b) @--@ RegexContext a b (AllTextMatches (Array Int) b) @--Four containers for text of all matches and their submatches:--@ RegexContext a b [[b]] @--@ RegexContext a b (AllTextMatches (Array Int) [b]) @--@ RegexContext a b (AllTextMatches [] (Array Int b)) @--@ RegexContext a b (AllTextMatches (Array Int) (Array Int b)) @--Unused matches are 'empty' (defined via 'Extract')---}--module Text.Regex.Base.Context() where--import Control.Monad(liftM)-import Data.Array(Array,(!),elems,listArray)--- import Data.Maybe(maybe)-import Text.Regex.Base.RegexLike(RegexLike(..),RegexContext(..)- ,AllSubmatches(..),AllTextSubmatches(..),AllMatches(..),AllTextMatches(..)- ,MatchResult(..),Extract(empty),MatchOffset,MatchLength,MatchArray,MatchText)---{---- Get the ByteString type for mood/doom-import Data.ByteString(ByteString)--- Get the Regex types for the mood/doom workaround-import qualified Text.Regex.Lib.WrapPosix as R1(Regex)-import qualified Text.Regex.Lib.WrapPCRE as R2(Regex)-import qualified Text.Regex.Lib.WrapLazy as R3(Regex)-import qualified Text.Regex.Lib.WrapDFAEngine as R4(Regex)--- Get the RegexLike instances-import Text.Regex.Lib.StringPosix()-import Text.Regex.Lib.StringPCRE()-import Text.Regex.Lib.StringLazy()-import Text.Regex.Lib.StringDFAEngine()-import Text.Regex.Lib.ByteStringPosix()-import Text.Regex.Lib.ByteStringPCRE()-import Text.Regex.Lib.ByteStringLazy()-import Text.Regex.Lib.ByteStringDFAEngine()--}-{---mood :: (RegexLike a b) => a -> b -> b-{-# INLINE mood #-}-mood r s = case matchOnceText r s of- Nothing -> empty- Just (_,ma,_) -> fst (ma!0)--doom :: (RegexLike a b,Monad m) => a -> b -> m b-{-# INLINE doom #-}-doom = actOn (\(_,ma,_)->fst (ma!0))--{- These run afoul of various restrictions if I say- "instance RegexContext a b b where"- so I am listing these cases explicitly--}--instance RegexContext R1.Regex String String where match = mood; matchM = doom-instance RegexContext R2.Regex String String where match = mood; matchM = doom-instance RegexContext R3.Regex String String where match = mood; matchM = doom-instance RegexContext R4.Regex String String where match = mood; matchM = doom-instance RegexContext R1.Regex ByteString ByteString where match = mood; matchM = doom-instance RegexContext R2.Regex ByteString ByteString where match = mood; matchM = doom-instance RegexContext R3.Regex ByteString ByteString where match = mood; matchM = doom-instance RegexContext R4.Regex ByteString ByteString where match = mood; matchM = doom--}---nullArray :: Array Int a-{-# INLINE nullArray #-}-nullArray = listArray (1,0) []--nullFail :: (RegexContext regex source (AllMatches [] target),Monad m) => regex -> source -> m (AllMatches [] target)-{-# INLINE nullFail #-}-nullFail r s = case match r s of- (AllMatches []) -> regexFailed- xs -> return xs--nullFailText :: (RegexContext regex source (AllTextMatches [] target),Monad m) => regex -> source -> m (AllTextMatches [] target)-{-# INLINE nullFailText #-}-nullFailText r s = case match r s of- (AllTextMatches []) -> regexFailed- xs -> return xs--nullFail' :: (RegexContext regex source ([] target),Monad m) => regex -> source -> m ([] target)-{-# INLINE nullFail' #-}-nullFail' r s = case match r s of- ([]) -> regexFailed- xs -> return xs--regexFailed :: (Monad m) => m b-{-# INLINE regexFailed #-}-regexFailed = fail $ "regex failed to match"--actOn :: (RegexLike r s,Monad m) => ((s,MatchText s,s)->t) -> r -> s -> m t-{-# INLINE actOn #-}-actOn f r s = case matchOnceText r s of- Nothing -> regexFailed- Just preMApost -> return (f preMApost)---- ** Instances based on matchTest ()--instance (RegexLike a b) => RegexContext a b Bool where - match = matchTest- matchM r s = case match r s of- False -> regexFailed- True -> return True--instance (RegexLike a b) => RegexContext a b () where- match _ _ = ()- matchM r s = case matchTest r s of- False -> regexFailed- True -> return ()---- ** Instance based on matchCount--instance (RegexLike a b) => RegexContext a b Int where- match = matchCount- matchM r s = case match r s of- 0 -> regexFailed- x -> return x---- ** Instances based on matchOnce,matchOnceText--instance (RegexLike a b) => RegexContext a b (MatchOffset,MatchLength) where - match r s = maybe (-1,0) (!0) (matchOnce r s)- matchM r s = maybe regexFailed (return.(!0)) (matchOnce r s)--instance (RegexLike a b) => RegexContext a b (MatchResult b) where - match r s = maybe (MR {mrBefore = s,mrMatch = empty,mrAfter = empty- ,mrSubs = nullArray,mrSubList = []}) id (matchM r s)- matchM = actOn (\(pre,ma,post) -> - let ((whole,_):subs) = elems ma- in MR { mrBefore = pre- , mrMatch = whole- , mrAfter = post- , mrSubs = fmap fst ma- , mrSubList = map fst subs })--instance (RegexLike a b) => RegexContext a b (b,MatchText b,b) where - match r s = maybe (s,nullArray,empty) id (matchOnceText r s)- matchM r s = maybe regexFailed return (matchOnceText r s)--instance (RegexLike a b) => RegexContext a b (b,b,b) where - match r s = maybe (s,empty,empty) id (matchM r s)- matchM = actOn (\(pre,ma,post) -> let ((whole,_):_) = elems ma- in (pre,whole,post))--instance (RegexLike a b) => RegexContext a b (b,b,b,[b]) where - match r s = maybe (s,empty,empty,[]) id (matchM r s)- matchM = actOn (\(pre,ma,post) -> let ((whole,_):subs) = elems ma- in (pre,whole,post,map fst subs))---- now AllSubmatches wrapper-instance (RegexLike a b) => RegexContext a b MatchArray where - match r s = maybe nullArray id (matchOnce r s)- matchM r s = maybe regexFailed return (matchOnce r s)-instance (RegexLike a b) => RegexContext a b (AllSubmatches [] (MatchOffset,MatchLength)) where - match r s = maybe (AllSubmatches []) id (matchM r s)- matchM r s = case matchOnce r s of- Nothing -> regexFailed- Just ma -> return (AllSubmatches (elems ma))---- essentially AllSubmatches applied to (MatchText b)-instance (RegexLike a b) => RegexContext a b (AllTextSubmatches (Array Int) (b, (MatchOffset, MatchLength))) where - match r s = maybe (AllTextSubmatches nullArray) id (matchM r s)- matchM r s = actOn (\(_,ma,_) -> AllTextSubmatches ma) r s-instance (RegexLike a b) => RegexContext a b (AllTextSubmatches [] (b, (MatchOffset, MatchLength))) where - match r s = maybe (AllTextSubmatches []) id (matchM r s)- matchM r s = actOn (\(_,ma,_) -> AllTextSubmatches (elems ma)) r s--instance (RegexLike a b) => RegexContext a b (AllTextSubmatches [] b) where - match r s = maybe (AllTextSubmatches []) id (matchM r s)- matchM r s = liftM AllTextSubmatches $ actOn (\(_,ma,_) -> map fst . elems $ ma) r s-instance (RegexLike a b) => RegexContext a b (AllTextSubmatches (Array Int) b) where - match r s = maybe (AllTextSubmatches nullArray) id (matchM r s)- matchM r s = liftM AllTextSubmatches $ actOn (\(_,ma,_) -> fmap fst ma) r s---- ** Instances based on matchAll,matchAllText--instance (RegexLike a b) => RegexContext a b (AllMatches [] (MatchOffset,MatchLength)) where- match r s = AllMatches [ ma!0 | ma <- matchAll r s ]- matchM r s = nullFail r s-instance (RegexLike a b) => RegexContext a b (AllMatches (Array Int) (MatchOffset,MatchLength)) where- match r s = maybe (AllMatches nullArray) id (matchM r s)- matchM r s = case match r s of- (AllMatches []) -> regexFailed- (AllMatches pairs) -> return . AllMatches . listArray (0,pred $ length pairs) $ pairs---- No AllMatches wrapper-instance (RegexLike a b) => RegexContext a b [MatchArray] where- match = matchAll- matchM = nullFail'-instance (RegexLike a b) => RegexContext a b (AllMatches (Array Int) MatchArray) where- match r s = maybe (AllMatches nullArray) id (matchM r s)- matchM r s = case match r s of- [] -> regexFailed- mas -> return . AllMatches . listArray (0,pred $ length mas) $ mas---- No AllTextMatches wrapper-instance (RegexLike a b) => RegexContext a b [MatchText b] where- match = matchAllText- matchM = nullFail'-instance (RegexLike a b) => RegexContext a b (AllTextMatches (Array Int) (MatchText b)) where- match r s = maybe (AllTextMatches nullArray) id (matchM r s)- matchM r s = case match r s of- ([]) -> regexFailed- (mts) -> return . AllTextMatches . listArray (0,pred $ length mts) $ mts--instance (RegexLike a b) => RegexContext a b (AllTextMatches [] b) where- match r s = AllTextMatches [ fst (ma!0) | ma <- matchAllText r s ]- matchM r s = nullFailText r s-instance (RegexLike a b) => RegexContext a b (AllTextMatches (Array Int) b) where- match r s = maybe (AllTextMatches nullArray) id (matchM r s)- matchM r s = case match r s of- (AllTextMatches []) -> regexFailed- (AllTextMatches bs) -> return . AllTextMatches . listArray (0,pred $ length bs) $ bs---- No AllTextMatches wrapper-instance (RegexLike a b) => RegexContext a b [[b]] where- match r s = [ map fst (elems ma) | ma <- matchAllText r s ]- matchM r s = nullFail' r s-instance (RegexLike a b) => RegexContext a b (AllTextMatches (Array Int) [b]) where- match r s = maybe (AllTextMatches nullArray) id (matchM r s)- matchM r s = case match r s of- ([]) -> regexFailed- (ls) -> return . AllTextMatches . listArray (0,pred $ length ls) $ ls-instance (RegexLike a b) => RegexContext a b (AllTextMatches [] (Array Int b)) where- match r s = AllTextMatches [ fmap fst ma | ma <- matchAllText r s ]- matchM r s = nullFailText r s-instance (RegexLike a b) => RegexContext a b (AllTextMatches (Array Int) (Array Int b)) where- match r s = maybe (AllTextMatches nullArray) id (matchM r s)- matchM r s = case match r s of- (AllTextMatches []) -> regexFailed- (AllTextMatches as) -> return . AllTextMatches . listArray (0,pred $ length as) $ as
− Text/Regex/Base/Impl.hs
@@ -1,58 +0,0 @@--------------------------------------------------------------------------------- |--- Module : Text.Regex.Impl--- Copyright : (c) Chris Kuklewicz 2006--- License : BSD-style (see the file LICENSE)--- --- Maintainer : libraries@haskell.org, textregexlazy@personal.mightyreason.com--- Stability : experimental--- Portability : non-portable (Text.Regex.Base needs MPTC+FD)--- --- Helper functions for defining certain instances of--- RegexContext. These help when defining instances of RegexContext--- with repeated types:--- --- @--- instance (RegexLike regex source) => RegexContext regex source source where--- @--- --- runs into overlapping restrictions. To avoid this I have each backend--- define, for its own Regex type:--- --- @--- instance RegexContext Regex String String where--- match = polymatch--- matchM = polymatchM--- @--- --- @--- instance RegexContext Regex ByteString ByteString where--- match = polymatch--- matchM = polymatchM--- @----------------------------------------------------------------------------------module Text.Regex.Base.Impl(polymatch,polymatchM) where--import Text.Regex.Base-import Data.Array((!))--regexFailed :: (Monad m) => m b-{-# INLINE regexFailed #-}-regexFailed = fail $ "regex failed to match"--actOn :: (RegexLike r s,Monad m) => ((s,MatchText s,s)->t) -> r -> s -> m t-{-# INLINE actOn #-}-actOn f r s = case matchOnceText r s of- Nothing -> regexFailed- Just preMApost -> return (f preMApost)--polymatch :: (RegexLike a b) => a -> b -> b-{-# INLINE polymatch #-}-polymatch r s = case matchOnceText r s of- Nothing -> empty- Just (_,ma,_) -> fst (ma!0)--polymatchM :: (RegexLike a b,Monad m) => a -> b -> m b-{-# INLINE polymatchM #-}-polymatchM = actOn (\(_,ma,_)->fst (ma!0))
− Text/Regex/Base/RegexLike.hs
@@ -1,228 +0,0 @@-{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}--------------------------------------------------------------------------------- |--- Module : Text.Regex.Base.RegexLike--- Copyright : (c) Chris Kuklewicz 2006--- License : BSD-style (see the file LICENSE)--- --- Maintainer : libraries@haskell.org, textregexlazy@personal.mightyreason.com--- Stability : experimental--- Portability : non-portable (MPTC+FD)------ Classes and instances for Regex matching.------ --- All the classes are declared here, and some common type aliases, and--- the MatchResult data type.--- --- The only instances here are for Extract String and Extract ByteString.--- There are no data values. The 'RegexContext' instances are in--- "Text.Regex.Base.Context", except for ones which run afoul of a--- repeated variable (RegexContext regex a a), which are defined in each--- modules' String and ByteString modules.--------------------------------------------------------------------------------module Text.Regex.Base.RegexLike (- -- ** Type aliases- MatchOffset,- MatchLength,- MatchArray,- MatchText,- -- ** Data types- MatchResult(..),- -- ** Classes- RegexOptions(..),- RegexMaker(..),- RegexLike(..),- RegexContext(..),- Extract(..),- AllSubmatches(..),AllTextSubmatches(..),AllMatches(..),AllTextMatches(..)- ) where--import Data.Array(Array,(!))-import Data.Maybe(isJust)-import qualified Data.ByteString as B (take,drop,empty,ByteString)-import qualified Data.ByteString.Lazy as L (take,drop,empty,ByteString)-import qualified Data.Sequence as S(take,drop,empty,Seq)---- | 0 based index from start of source, or (-1) for unused-type MatchOffset = Int--- | non-negative length of a match-type MatchLength = Int--- | 0 based array, with 0th index indicating the full match. If the--- full match location is not available, represent as (0,0).-type MatchArray = Array Int (MatchOffset,MatchLength)-type MatchText source = Array Int (source,(MatchOffset,MatchLength))---- | This is the same as the type from JRegex.-data MatchResult a = MR {- mrBefore :: a,- mrMatch :: a,- mrAfter :: a,- mrSubList :: [a],- mrSubs :: Array Int a-}--------------------- | Rather than carry them around spearately, the options for how to--- execute a regex are kept as part of the regex. There are two types--- of options. Those that can only be specified at compilation time--- and never changed are CompOpt. Those that can be changed later and--- affect how matching is performed are ExecOpt. The actually types--- for these depend on the backend.-class RegexOptions regex compOpt execOpt - | regex->compOpt execOpt, compOpt->regex execOpt, execOpt->regex compOpt where- blankCompOpt :: compOpt -- ^ no options set at all in the backend- blankExecOpt :: execOpt -- ^ no options set at all in the backend- defaultCompOpt :: compOpt -- ^ reasonable options (extended,caseSensitive,multiline regex)- defaultExecOpt :: execOpt -- ^ reasonable options (extended,caseSensitive,multiline regex)- setExecOpts :: execOpt -> regex -> regex- -- ^ forget old flags and use new ones- getExecOpts :: regex -> execOpt- -- ^ retrieve the current flags--------------------- | RegexMaker captures the creation of the compiled regular--- expression from a source type and an option type. 'makeRegexM' and--- 'makeRegexM' report parse error using 'MonadError', usually (Either--- String regex).--- --- The 'makeRegex' function has a default implementation that depends--- on makeRegexOpts and used 'defaultCompOpt' and 'defaultExecOpt'.--- Similarly for 'makeRegexM' and 'makeRegexOptsM'.------ There are also default implementaions for 'makeRegexOpts' and--- 'makeRegexOptsM' in terms of each other. So a minimal instance--- definition needs to only define one of these, hopefully--- 'makeRegexOptsM'.-class (RegexOptions regex compOpt execOpt) => RegexMaker regex compOpt execOpt source - | regex -> compOpt execOpt, compOpt -> regex execOpt, execOpt -> regex compOpt where- -- | make using the defaultCompOpt and defaultExecOpt- makeRegex :: source -> regex- -- | Specify your own options- makeRegexOpts :: compOpt -> execOpt -> source -> regex- -- | make using the defaultCompOpt and defaultExecOpt, reporting errors with fail- makeRegexM :: (Monad m) => source -> m regex- -- | Specify your own options, reporting errors with fail- makeRegexOptsM :: (Monad m) => compOpt -> execOpt -> source -> m regex-- makeRegex = makeRegexOpts defaultCompOpt defaultExecOpt- makeRegexM = makeRegexOptsM defaultCompOpt defaultExecOpt- makeRegexOpts c e s = maybe (error "makeRegexOpts failed") id (makeRegexOptsM c e s)- makeRegexOptsM c e s = return (makeRegexOpts c e s)--------------------- | RegexLike is parametrized on a regular expression type and a--- source type to run the matching on.------ There are default implementations: matchTest and matchOnceText use--- matchOnce; matchCount and matchAllText use matchAll. matchOnce uses--- matchOnceText and matchAll uses matchAllText. So a minimal complete--- instance need to provide at least (matchOnce or matchOnceText) and--- (matchAll or matchAllText). Additional definitions are often--- provided where they will increase efficiency.------ > [ c | let notVowel = makeRegex "[^aeiou]" :: Regex, c <- ['a'..'z'], matchTest notVowel [c] ]--- >--- > "bcdfghjklmnpqrstvwxyz"------ The strictness of these functions is instance dependent.-class (Extract source)=> RegexLike regex source where- -- | This returns the first match in the source (it checks the whole- -- source, not just at the start). This returns an array of- -- (offset,length) index pairs for the match and captured- -- substrings. The offset is 0-based. A (-1) for an offset means a- -- failure to match. The lower bound of the array is 0, and the 0th- -- element is the (offset,length) for the whole match.- matchOnce :: regex -> source-> Maybe MatchArray- -- | matchAll returns a list of matches. The matches are in order- -- and do not overlap. If any match succeeds but has 0 length then- -- this will be the last match in the list.- matchAll :: regex -> source-> [MatchArray]- -- | matchCount returns the number of non-overlapping matches- -- returned by matchAll.- matchCount :: regex -> source-> Int- -- | matchTest return True if there is a match somewhere in the- -- source (it checks the whole source not just at the start).- matchTest :: regex -> source-> Bool- -- | This is matchAll with the actual subsections of the source- -- instead of just the (offset,length) information.- matchAllText :: regex -> source-> [MatchText source]- -- | This can return a tuple of three items: the source before the- -- match, an array of the match and captured substrings (with their- -- indices), and the source after the match.- matchOnceText :: regex -> source-> Maybe (source,MatchText source,source)-- matchAll regex source = map (fmap snd) (matchAllText regex source)- matchOnce regex source = fmap (\(_,mt,_) -> fmap snd mt) (matchOnceText regex source)- matchTest regex source = isJust (matchOnce regex source)- matchCount regex source = length (matchAll regex source)- matchOnceText regex source = - fmap (\ma -> let (o,l) = ma!0- in (before o source- ,fmap (\ol -> (extract ol source,ol)) ma- ,after (o+l) source))- (matchOnce regex source)- matchAllText regex source =- map (fmap (\ol -> (extract ol source,ol)))- (matchAll regex source)--------------------- | RegexContext is the polymorphic interface to do matching. Since--- 'target' is polymorphic you may need to suply the type explicitly--- in contexts where it cannot be inferred.------ The monadic 'matchM' version uses 'fail' to report when the 'regex'--- has no match in 'source'. Two examples:------ Here the contest 'Bool' is inferred:------ > [ c | let notVowel = makeRegex "[^aeiou]" :: Regex, c <- ['a'..'z'], match notVowel [c] ]--- >--- > "bcdfghjklmnpqrstvwxyz"------ Here the context '[String]' must be supplied:------ > let notVowel = (makeRegex "[^aeiou]" :: Regex )--- > in do { c <- ['a'..'z'] ; matchM notVowel [c] } :: [String]--- >--- > ["b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z"]-class (RegexLike regex source) => RegexContext regex source target where- match :: regex -> source -> target- matchM :: (Monad m) => regex -> source -> m target--------------------- | Extract allows for indexing operations on String or ByteString.-class Extract source where- -- | before is a renamed "take"- before :: Int -> source -> source- -- | after is a renamed "drop"- after :: Int -> source -> source- -- | For when there is no match, this can construct an empty data value- empty :: source- -- | extract takes an offset and length and has a default- -- implementation of @extract (off,len) source = before len (after- -- off source)@- extract :: (Int,Int) -> source -> source- extract (off,len) source = before len (after off source)--instance Extract String where- before = take; after = drop; empty = []--instance Extract B.ByteString where- before = B.take; after = B.drop; empty = B.empty--instance Extract L.ByteString where- before = L.take . toEnum; after = L.drop . toEnum; empty = L.empty--instance Extract (S.Seq a) where- before = S.take; after = S.drop; empty = S.empty---- | Used in results of RegexContext instances-newtype AllSubmatches f b = AllSubmatches {getAllSubmatches :: (f b)}--- | Used in results of RegexContext instances-newtype AllTextSubmatches f b = AllTextSubmatches {getAllTextSubmatches :: (f b)}--- | Used in results of RegexContext instances-newtype AllMatches f b = AllMatches {getAllMatches :: (f b)}--- | Used in results of RegexContext instances-newtype AllTextMatches f b = AllTextMatches {getAllTextMatches :: (f b) }
regex-base.cabal view
@@ -1,58 +1,93 @@-Name: regex-base--- Keep the version below in sync with ./Text/Regex/Base.hs value getVersion_Text_Regex_Base :: Version-Version: 0.93.2-Cabal-Version: >=1.2-License: BSD3-License-File: LICENSE-Copyright: Copyright (c) 2006, Christopher Kuklewicz-Author: Christopher Kuklewicz-Maintainer: TextRegexLazy@personal.mightyreason.com-Stability: Seems to work, passes a few tests-Homepage: http://sourceforge.net/projects/lazy-regex-Package-URL: http://darcs.haskell.org/packages/regex-unstable/regex-base/-Synopsis: Replaces/Enhances Text.Regex-Description: Interface API for regex-posix,pcre,parsec,tdfa,dfa-Category: Text-Tested-With: GHC+cabal-version: 1.24+name: regex-base+version: 0.94.0.3+ build-type: Simple-flag newBase- description: Choose base >= 4- default: True-flag splitBase- description: Choose the new smaller, split-up base package.- default: True+license: BSD3+license-file: LICENSE+copyright: Copyright (c) 2006, Christopher Kuklewicz+author: Christopher Kuklewicz+maintainer: Andreas Abel+homepage: https://wiki.haskell.org/Regular_expressions+bug-reports: https://github.com/haskell-hvr/regex-base/issues+synopsis: Common "Text.Regex.*" API for Regex matching+category: Text+description:+ This package does not provide the ability to do regular expression matching.+ Instead, it provides the type classes that constitute the abstract API+ that is implemented by @regex-*@ backends such as:+ .+ * <https://hackage.haskell.org/package/regex-posix regex-posix>+ .+ * <https://hackage.haskell.org/package/regex-parsec regex-parsec>+ .+ * <https://hackage.haskell.org/package/regex-dfa regex-dfa>+ .+ * <https://hackage.haskell.org/package/regex-tdfa regex-tdfa>+ .+ * <https://hackage.haskell.org/package/regex-pcre regex-pcre>+ .+ See also <https://wiki.haskell.org/Regular_expressions> for more information.++extra-doc-files:+ ChangeLog.md+ README.md++tested-with:+ GHC == 9.12.1+ GHC == 9.10.1+ GHC == 9.8.4+ GHC == 9.6.6+ GHC == 9.4.8+ GHC == 9.2.8+ GHC == 9.0.2+ GHC == 8.10.7+ GHC == 8.8.4+ GHC == 8.6.5+ GHC == 8.4.4+ GHC == 8.2.2+ GHC == 8.0.2++source-repository head+ type: git+ location: https://github.com/haskell-hvr/regex-base.git++source-repository this+ type: git+ location: https://github.com/haskell-hvr/regex-base.git+ tag: v0.94.0.3+ library- if flag(newBase)- Build-Depends: base >=4 && < 5, mtl, containers, bytestring, array- Extensions: MultiParamTypeClasses, FunctionalDependencies, TypeSynonymInstances, FlexibleInstances, FlexibleContexts- else- if flag(splitBase)- Build-Depends: base >= 3.0, mtl, containers, bytestring, array- Extensions: MultiParamTypeClasses, FunctionalDependencies, TypeSynonymInstances, FlexibleInstances, FlexibleContexts- else- Build-Depends: base < 3.0, mtl- Extensions: MultiParamTypeClasses, FunctionalDependencies- -- Data-Files:- -- Extra-Source-Files:- -- Extra-Tmp-Files:- Exposed-Modules: Text.Regex.Base- Text.Regex.Base.RegexLike- Text.Regex.Base.Context- Text.Regex.Base.Impl- Buildable: True- -- Other-Modules:- -- HS-Source-Dirs: "."- GHC-Options: -Wall -O2- -- GHC-Options: -Wall -Werror -O2- -- GHC-Options: -Wall -ddump-minimal-GHC- -- imports-Prof-Options: -auto-all- -- Hugs-Options:- -- NHC-Options:- -- Includes:- -- Include-Dirs:- -- C-Sources:- -- Extra-Libraries:- -- Extra-Lib-Dirs:- -- CC-Options:- -- LD-Options:- -- Frameworks:+ hs-source-dirs: src++ exposed-modules:+ Text.Regex.Base+ Text.Regex.Base.RegexLike+ Text.Regex.Base.Context+ Text.Regex.Base.Impl++ other-modules:+ Paths_regex_base++ default-language:+ Haskell2010++ default-extensions:+ NoImplicitPrelude+ Safe+ MultiParamTypeClasses+ FunctionalDependencies+ TypeSynonymInstances+ FlexibleInstances+ FlexibleContexts++ build-depends:+ base >= 4.9 && < 5+ , containers >= 0.5 && < 1+ , bytestring >= 0.10 && < 1+ , array >= 0.5 && < 1+ , text >= 1.2.3 && < 1.3 || >= 2.0 && < 3++ ghc-options:+ -Wall+ -Wcompat
+ src/Text/Regex/Base.hs view
@@ -0,0 +1,81 @@+-----------------------------------------------------------------------------+-- |+--+-- Module : Text.Regex.Base+-- Copyright : (c) Chris Kuklewicz 2006+-- SPDX-License-Identifier: BSD-3-Clause+--+-- Maintainer : Andreas Abel+-- Stability : stable+-- Portability : non-portable (MPTC+FD)+--+-- Classes and instances for Regex matching.+--+--+-- This module merely imports and re-exports the common part of the new+-- api: "Text.Regex.Base.RegexLike" and "Text.Regex.Base.Context".+--+-- To see what result types the instances of RegexContext can produce,+-- please read the "Text.Regex.Base.Context" haddock documentation.+--+-- This does not provide any of the backends, just the common interface+-- they all use. The modules which provide the backends and their cabal+-- packages are:+--+-- * @<https://hackage.haskell.org/package/regex-posix/docs/Text-Regex-Posix.html Text.Regex.Posix>@ from <https://hackage.haskell.org/package/regex-posix regex-posix>+--+-- * @<https://hackage.haskell.org/package/regex-compat/docs/Text-Regex.html Text.Regex>@ from <https://hackage.haskell.org/package/regex-compat regex-compat> (uses <https://hackage.haskell.org/package/regex-posix regex-posix>)+--+-- * @<https://hackage.haskell.org/package/regex-parsec/docs/Text-Regex-Parsec.html Text.Regex.Parsec>@ from <https://hackage.haskell.org/package/regex-parsec regex-parsec>+--+-- * @<https://hackage.haskell.org/package/regex-dfa/docs/Text-Regex-DFA.html Text.Regex.DFA>@ from <https://hackage.haskell.org/package/regex-dfa regex-dfa>+--+-- * @<https://hackage.haskell.org/package/regex-pcre/docs/Text-Regex-PCRE.html Text.Regex.PCRE>@ from <https://hackage.haskell.org/package/regex-pcre regex-pcre>+--+-- * @<https://hackage.haskell.org/package/regex-tre/docs/Test-Regex-TRE.html Test.Regex.TRE>@ from <https://hackage.haskell.org/package/regex-tre regex-tre>+--+-- In fact, just importing one of the backends is adequate, you do not+-- also need to import this module.+--+-- == Example+--+-- The code+--+-- @+-- import Text.Regex.Base+-- import Text.Regex.Posix ((=~),(=~~)) -- or TDFA or PCRE or ...+--+-- main = do+-- print b+-- print c+-- print d+-- where+-- b :: Bool+-- b = ("abaca" =~ "(.)a")+-- c :: [MatchArray]+-- c = ("abaca" =~ "(.)a")+-- d :: Maybe (String,String,String,[String])+-- d = ("abaca" =~~ "(.)a")+-- @+--+-- will output+--+-- > True+-- > [array (0,1) [(0,(1,2)),(1,(1,1))],array (0,1) [(0,(3,2)),(1,(3,1))]]+-- > Just ("a","ba","ca",["b"])+--++-----------------------------------------------------------------------------++module Text.Regex.Base (getVersion_Text_Regex_Base+ -- | RegexLike defines classes and type, and 'Extract' instances+ ,module Text.Regex.Base.RegexLike) where++import Data.Version(Version)+import Text.Regex.Base.RegexLike+import Text.Regex.Base.Context()++import qualified Paths_regex_base++getVersion_Text_Regex_Base :: Version+getVersion_Text_Regex_Base = Paths_regex_base.version
+ src/Text/Regex/Base/Context.hs view
@@ -0,0 +1,418 @@+{-# OPTIONS_GHC -Wno-orphans #-}++{-|++Module : Text.Regex.Base.Context+Copyright : (c) Chris Kuklewicz 2006+SPDX-License-Identifier: BSD-3-Clause++Maintainer : Andreas Abel+Stability : stable+Portability : non-portable (MPTC+FD)++This is a module of instances of 'RegexContext' (defined in+"Text.Regex.Base.RegexLike"). Nothing else is exported. This is+usually imported via the "Text.Regex.Base" convenience package which+itself is re-exported from newer @Text.Regex.XXX@ modules provided by+the different @regex-xxx@ backends.++These instances work for all the supported types and backends+interchangeably. These instances provide the different results that+can be gotten from a 'match' or 'matchM' operation (often via the @=~@ and+@=~~@ operators with combine 'makeRegex' with 'match' and 'matchM'+respectively). This module name is @Context@ because they operators are+context dependent: use them in a context that expects an 'Int' and you+get a count of matches, use them in a 'Bool' context and get 'True' if+there is a match, etc.++@'RegexContext' a b c@ takes a regular expression suppied in a type @a@+generated by 'RegexMaker' and a target text supplied in type @b@ to a+result type @c@ using the 'match' class function. The 'matchM' class+function works like 'match' unless there is no match found, in which+case it calls 'fail' in the (arbitrary) monad context.++There are a few type synonyms from "Text.Regex.Base.RegexLike" that are used here:++@+-- | 0 based index from start of source, or (-1) for unused+type MatchOffset = Int+-- | non-negative length of a match+type MatchLength = Int+type MatchArray = Array Int (MatchOffset, MatchLength)+type MatchText source = Array Int (source, (MatchOffset, MatchLength))+@++There are also a few newtypes that used to prevent any possible+overlap of types, which were not needed for GHC's late overlap+detection but are needed for use in Hugs.++@+newtype AllSubmatches f b = AllSubmatches { getAllSubmatches :: f b }+newtype AllTextSubmatches f b = AllTextSubmatches { getAllTextSubmatches :: f b }+newtype AllMatches f b = AllMatches { getAllMatches :: f b }+newtype AllTextMatches f b = AllTextMatches { getAllTextMatches :: f b }+@++The newtypes' @f@ parameters are the containers, usually @[]@ or+@Array Int@, (where the arrays all have lower bound 0).++The two @Submatches@ newtypes return only information on the first+match. The other two newtypes return information on all the+non-overlapping matches. The two @Text@ newtypes are used to mark+result types that contain the same type as the target text.++Where provided, noncaptured submatches will have a 'MatchOffset' of+(-1) and non-negative otherwise. The semantics of submatches depend+on the backend and its compile and execution options. Where provided,+'MatchLength' will always be non-negative. Arrays with no elements+are returned with bounds of (1,0). Arrays with elements will have a+lower bound of 0.++XXX THIS HADDOCK DOCUMENTATION IS OUT OF DATE XXX++These are for finding the first match in the target text:+++@ 'RegexContext' a b Bool @:+ Whether there is any match or not.+++@ 'RegexContext' a b () @:+ Useful as a guard with @matchM@ or @=~~@ in a monad, since failure to match calls 'fail'.+++@ 'RegexContext' a b b @:+ This returns the text of the whole match.+ It will return 'empty' from the 'Extract' type class if there is no match.+ These are defined in each backend module, but documented here for convenience.+++@ 'RegexContext' a b ('MatchOffset', 'MatchLength') @:+ This returns the initial index and length of the whole match.+ MatchLength will always be non-negative, and 0 for a failed match.+++@ 'RegexContext' a b ('MatchResult' b) @: The+ 'MatchResult' structure with details for the match. This is the+ structure copied from the old @JRegex@ pacakge.+++@ 'RegexContext' a b (b, b, b) @:+ The text before the match, the text of the match, the text after the match+++@ 'RegexContext' a b (b, 'MatchText' b, b) @:+ The text before the match, the details of the match, and the text after the match+++@ 'RegexContext' a b (b, b, b, [b]) @:+ The text before the match, the text of the match, the text after the+ match, and a list of the text of the 1st and higher sub-parts of the+ match. This is the same return value as used in the old+ @Text.Regex@ API.++Two containers of the submatch offset information:+++@ 'RegexContext' a b 'MatchArray' @:+ Array of @('MatchOffset', 'MatchLength')@ for all the sub matches.+ The whole match is at the intial 0th index.+ Noncaptured submatches will have a @'MatchOffset'@ of (-1)+ The array will have no elements and bounds (1,0) if there is no match.+++@ 'RegexContext' a b ('AllSubmatches' [] ('MatchOffset', 'MatchLength') @:+ List of @('MatchOffset', 'MatchLength')@+ The whole match is the first element, the rest are the submatches (if any) in order.+ The list is empty if there is no match.++Two containers of the submatch text and offset information:++@ 'RegexContext' a b ('AllTextSubmatches' (Array Int) (b, ('MatchOffset', 'MatchLength'))) @++@ 'RegexContext' a b ('AllTextSubmatches' [] (b, ('MatchOffset', 'MatchLength'))) @++Two containers of the submatch text information:++@ 'RegexContext' a b ('AllTextSubmatches' [] b) @++@ 'RegexContext' a b ('AllTextSubmatches' (Array Int) b) @++These instances are for all the matches (non-overlapping). Note that+backends are supposed to supply 'RegexLike' instances for which the+default 'matchAll' and 'matchAllText' stop searching after returning+any successful but empty match.+++@ 'RegexContext' a b Int @:+ The number of matches, non-negative.++Two containers for locations of all matches:++@ 'RegexContext' a b ('AllMatches' [] ('MatchOffset', 'MatchLength')) @++@ 'RegexContext' a b ('AllMatches' (Array Int) ('MatchOffset', 'MatchLength')) @++Two containers for the locations of all matches and their submatches:++@ 'RegexContext' a b ['MatchArray'] @++@ 'RegexContext' a b ('AllMatches' (Array Int) 'MatchArray') @++Two containers for the text and locations of all matches and their submatches:++@ 'RegexContext' a b ['MatchText' b] @++@ 'RegexContext' a b ('AllTextMatches' (Array Int) ('MatchText' b)) @++Two containers for text of all matches:+@ 'RegexContext' a b ('AllTextMatches' [] b) @++@ 'RegexContext' a b ('AllTextMatches' (Array Int) b) @++Four containers for text of all matches and their submatches:++@ 'RegexContext' a b [[b]] @++@ 'RegexContext' a b ('AllTextMatches' (Array Int) [b]) @++@ 'RegexContext' a b ('AllTextMatches' [] (Array Int b)) @++@ 'RegexContext' a b ('AllTextMatches' (Array Int) (Array Int b)) @++Unused matches are 'empty' (defined via 'Extract')++-}++module Text.Regex.Base.Context() where++import Prelude+ ( Int+ , Bool(True,False)+ , (.), ($), fmap, fst, id, return+ , length, map+ , pred+ )++import Control.Monad.Fail (MonadFail(fail)) -- see 'regexFailed'+import Control.Monad(liftM)+import Data.Array(Array,(!),elems,listArray)+import Data.Maybe(Maybe(Nothing,Just), maybe)+import Text.Regex.Base.RegexLike(RegexLike(..),RegexContext(..)+ ,AllSubmatches(..),AllTextSubmatches(..),AllMatches(..),AllTextMatches(..)+ ,MatchResult(..),Extract(empty),MatchOffset,MatchLength,MatchArray,MatchText)+++{-+-- Get the ByteString type for mood/doom+import Data.ByteString(ByteString)+-- Get the Regex types for the mood/doom workaround+import qualified Text.Regex.Lib.WrapPosix as R1(Regex)+import qualified Text.Regex.Lib.WrapPCRE as R2(Regex)+import qualified Text.Regex.Lib.WrapLazy as R3(Regex)+import qualified Text.Regex.Lib.WrapDFAEngine as R4(Regex)+-- Get the RegexLike instances+import Text.Regex.Lib.StringPosix()+import Text.Regex.Lib.StringPCRE()+import Text.Regex.Lib.StringLazy()+import Text.Regex.Lib.StringDFAEngine()+import Text.Regex.Lib.ByteStringPosix()+import Text.Regex.Lib.ByteStringPCRE()+import Text.Regex.Lib.ByteStringLazy()+import Text.Regex.Lib.ByteStringDFAEngine()+-}+{-++mood :: (RegexLike a b) => a -> b -> b+{-# INLINE mood #-}+mood r s = case matchOnceText r s of+ Nothing -> empty+ Just (_, ma, _) -> fst (ma ! 0)++doom :: (RegexLike a b,Monad m) => a -> b -> m b+{-# INLINE doom #-}+doom = actOn (\ (_, ma, _) -> fst (ma ! 0))++{- These run afoul of various restrictions if I say+ "instance RegexContext a b b where"+ so I am listing these cases explicitly+-}++instance RegexContext R1.Regex String String where match = mood; matchM = doom+instance RegexContext R2.Regex String String where match = mood; matchM = doom+instance RegexContext R3.Regex String String where match = mood; matchM = doom+instance RegexContext R4.Regex String String where match = mood; matchM = doom+instance RegexContext R1.Regex ByteString ByteString where match = mood; matchM = doom+instance RegexContext R2.Regex ByteString ByteString where match = mood; matchM = doom+instance RegexContext R3.Regex ByteString ByteString where match = mood; matchM = doom+instance RegexContext R4.Regex ByteString ByteString where match = mood; matchM = doom+-}+++nullArray :: Array Int a+{-# INLINE nullArray #-}+nullArray = listArray (1,0) []++nullFail :: (RegexContext regex source (AllMatches [] target),MonadFail m) => regex -> source -> m (AllMatches [] target)+{-# INLINE nullFail #-}+nullFail r s = case match r s of+ (AllMatches []) -> regexFailed+ xs -> return xs++nullFailText :: (RegexContext regex source (AllTextMatches [] target),MonadFail m) => regex -> source -> m (AllTextMatches [] target)+{-# INLINE nullFailText #-}+nullFailText r s = case match r s of+ (AllTextMatches []) -> regexFailed+ xs -> return xs++nullFail' :: (RegexContext regex source ([] target),MonadFail m) => regex -> source -> m ([] target)+{-# INLINE nullFail' #-}+nullFail' r s = case match r s of+ ([]) -> regexFailed+ xs -> return xs++regexFailed :: (MonadFail m) => m b+{-# INLINE regexFailed #-}+regexFailed = fail $ "regex failed to match"++actOn :: (RegexLike r s,MonadFail m) => ((s,MatchText s,s)->t) -> r -> s -> m t+{-# INLINE actOn #-}+actOn f r s = case matchOnceText r s of+ Nothing -> regexFailed+ Just preMApost -> return (f preMApost)++-- ** Instances based on matchTest ()++instance (RegexLike a b) => RegexContext a b Bool where+ match = matchTest+ matchM r s = case match r s of+ False -> regexFailed+ True -> return True++instance (RegexLike a b) => RegexContext a b () where+ match _ _ = ()+ matchM r s = case matchTest r s of+ False -> regexFailed+ True -> return ()++-- ** Instance based on matchCount++instance (RegexLike a b) => RegexContext a b Int where+ match = matchCount+ matchM r s = case match r s of+ 0 -> regexFailed+ x -> return x++-- ** Instances based on matchOnce,matchOnceText++instance (RegexLike a b) => RegexContext a b (MatchOffset,MatchLength) where+ match r s = maybe (-1,0) (! 0) (matchOnce r s)+ matchM r s = maybe regexFailed (return . (! 0)) (matchOnce r s)++instance (RegexLike a b) => RegexContext a b (MatchResult b) where+ match r s = maybe (MR {mrBefore = s,mrMatch = empty,mrAfter = empty+ ,mrSubs = nullArray,mrSubList = []}) id (matchM r s)+ matchM = actOn (\(pre,ma,post) ->+ let ((whole,_):subs) = elems ma+ in MR { mrBefore = pre+ , mrMatch = whole+ , mrAfter = post+ , mrSubs = fmap fst ma+ , mrSubList = map fst subs })++instance (RegexLike a b) => RegexContext a b (b,MatchText b,b) where+ match r s = maybe (s,nullArray,empty) id (matchOnceText r s)+ matchM r s = maybe regexFailed return (matchOnceText r s)++instance (RegexLike a b) => RegexContext a b (b,b,b) where+ match r s = maybe (s,empty,empty) id (matchM r s)+ matchM = actOn (\(pre,ma,post) -> let ((whole,_):_) = elems ma+ in (pre,whole,post))++instance (RegexLike a b) => RegexContext a b (b,b,b,[b]) where+ match r s = maybe (s,empty,empty,[]) id (matchM r s)+ matchM = actOn (\(pre,ma,post) -> let ((whole,_):subs) = elems ma+ in (pre,whole,post,map fst subs))++-- now AllSubmatches wrapper+instance (RegexLike a b) => RegexContext a b MatchArray where+ match r s = maybe nullArray id (matchOnce r s)+ matchM r s = maybe regexFailed return (matchOnce r s)+instance (RegexLike a b) => RegexContext a b (AllSubmatches [] (MatchOffset,MatchLength)) where+ match r s = maybe (AllSubmatches []) id (matchM r s)+ matchM r s = case matchOnce r s of+ Nothing -> regexFailed+ Just ma -> return (AllSubmatches (elems ma))++-- essentially AllSubmatches applied to (MatchText b)+instance (RegexLike a b) => RegexContext a b (AllTextSubmatches (Array Int) (b, (MatchOffset, MatchLength))) where+ match r s = maybe (AllTextSubmatches nullArray) id (matchM r s)+ matchM r s = actOn (\(_,ma,_) -> AllTextSubmatches ma) r s+instance (RegexLike a b) => RegexContext a b (AllTextSubmatches [] (b, (MatchOffset, MatchLength))) where+ match r s = maybe (AllTextSubmatches []) id (matchM r s)+ matchM r s = actOn (\(_,ma,_) -> AllTextSubmatches (elems ma)) r s++instance (RegexLike a b) => RegexContext a b (AllTextSubmatches [] b) where+ match r s = maybe (AllTextSubmatches []) id (matchM r s)+ matchM r s = liftM AllTextSubmatches $ actOn (\(_,ma,_) -> map fst . elems $ ma) r s+instance (RegexLike a b) => RegexContext a b (AllTextSubmatches (Array Int) b) where+ match r s = maybe (AllTextSubmatches nullArray) id (matchM r s)+ matchM r s = liftM AllTextSubmatches $ actOn (\(_,ma,_) -> fmap fst ma) r s++-- ** Instances based on matchAll,matchAllText++instance (RegexLike a b) => RegexContext a b (AllMatches [] (MatchOffset,MatchLength)) where+ match r s = AllMatches [ ma ! 0 | ma <- matchAll r s ]+ matchM r s = nullFail r s+instance (RegexLike a b) => RegexContext a b (AllMatches (Array Int) (MatchOffset,MatchLength)) where+ match r s = maybe (AllMatches nullArray) id (matchM r s)+ matchM r s = case match r s of+ (AllMatches []) -> regexFailed+ (AllMatches pairs) -> return . AllMatches . listArray (0,pred $ length pairs) $ pairs++-- No AllMatches wrapper+instance (RegexLike a b) => RegexContext a b [MatchArray] where+ match = matchAll+ matchM = nullFail'+instance (RegexLike a b) => RegexContext a b (AllMatches (Array Int) MatchArray) where+ match r s = maybe (AllMatches nullArray) id (matchM r s)+ matchM r s = case match r s of+ [] -> regexFailed+ mas -> return . AllMatches . listArray (0,pred $ length mas) $ mas++-- No AllTextMatches wrapper+instance (RegexLike a b) => RegexContext a b [MatchText b] where+ match = matchAllText+ matchM = nullFail'+instance (RegexLike a b) => RegexContext a b (AllTextMatches (Array Int) (MatchText b)) where+ match r s = maybe (AllTextMatches nullArray) id (matchM r s)+ matchM r s = case match r s of+ ([]) -> regexFailed+ (mts) -> return . AllTextMatches . listArray (0,pred $ length mts) $ mts++instance (RegexLike a b) => RegexContext a b (AllTextMatches [] b) where+ match r s = AllTextMatches [ fst (ma ! 0) | ma <- matchAllText r s ]+ matchM r s = nullFailText r s+instance (RegexLike a b) => RegexContext a b (AllTextMatches (Array Int) b) where+ match r s = maybe (AllTextMatches nullArray) id (matchM r s)+ matchM r s = case match r s of+ (AllTextMatches []) -> regexFailed+ (AllTextMatches bs) -> return . AllTextMatches . listArray (0,pred $ length bs) $ bs++-- No AllTextMatches wrapper+instance (RegexLike a b) => RegexContext a b [[b]] where+ match r s = [ map fst (elems ma) | ma <- matchAllText r s ]+ matchM r s = nullFail' r s+instance (RegexLike a b) => RegexContext a b (AllTextMatches (Array Int) [b]) where+ match r s = maybe (AllTextMatches nullArray) id (matchM r s)+ matchM r s = case match r s of+ ([]) -> regexFailed+ (ls) -> return . AllTextMatches . listArray (0,pred $ length ls) $ ls+instance (RegexLike a b) => RegexContext a b (AllTextMatches [] (Array Int b)) where+ match r s = AllTextMatches [ fmap fst ma | ma <- matchAllText r s ]+ matchM r s = nullFailText r s+instance (RegexLike a b) => RegexContext a b (AllTextMatches (Array Int) (Array Int b)) where+ match r s = maybe (AllTextMatches nullArray) id (matchM r s)+ matchM r s = case match r s of+ (AllTextMatches []) -> regexFailed+ (AllTextMatches as) -> return . AllTextMatches . listArray (0,pred $ length as) $ as
+ src/Text/Regex/Base/Impl.hs view
@@ -0,0 +1,71 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.Regex.Impl+-- Copyright : (c) Chris Kuklewicz 2006+-- SPDX-License-Identifier: BSD-3-Clause+--+-- Maintainer : Andreas Abel+-- Stability : stable+-- Portability : non-portable (Text.Regex.Base needs MPTC+FD)+--+-- Helper functions for defining certain instances of+-- 'RegexContext'. These help when defining instances of 'RegexContext'+-- with repeated types:+--+-- @+-- instance (RegexLike regex source) => RegexContext regex source source where+-- @+--+-- runs into overlapping restrictions. To avoid this I have each backend+-- define, for its own @Regex@ type:+--+-- @+-- instance RegexContext Regex String String where+-- match = polymatch+-- matchM = polymatchM+-- @+--+-- @+-- instance RegexContext Regex ByteString ByteString where+-- match = polymatch+-- matchM = polymatchM+-- @+--+-- @+-- instance RegexContext Regex Text Text where+-- match = polymatch+-- matchM = polymatchM+-- @+-------------------------------------------------------------------------------++module Text.Regex.Base.Impl(polymatch,polymatchM) where++import Prelude+ ( Maybe(Nothing,Just)+ , ($)+ , fst, return+ )+import Control.Monad.Fail (MonadFail(fail))++import Text.Regex.Base+import Data.Array((!))++regexFailed :: (MonadFail m) => m b+{-# INLINE regexFailed #-}+regexFailed = fail $ "regex failed to match"++actOn :: (RegexLike r s,MonadFail m) => ((s,MatchText s,s)->t) -> r -> s -> m t+{-# INLINE actOn #-}+actOn f r s = case matchOnceText r s of+ Nothing -> regexFailed+ Just preMApost -> return (f preMApost)++polymatch :: (RegexLike a b) => a -> b -> b+{-# INLINE polymatch #-}+polymatch r s = case matchOnceText r s of+ Nothing -> empty+ Just (_, ma, _) -> fst (ma ! 0)++polymatchM :: (RegexLike a b,MonadFail m) => a -> b -> m b+{-# INLINE polymatchM #-}+polymatchM = actOn (\ (_, ma, _) -> fst (ma ! 0))
+ src/Text/Regex/Base/RegexLike.hs view
@@ -0,0 +1,286 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.Regex.Base.RegexLike+-- Copyright : (c) Chris Kuklewicz 2006+-- SPDX-License-Identifier: BSD-3-Clause+--+-- Maintainer : Andreas Abel+-- Stability : stable+-- Portability : non-portable (MPTC+FD)+--+-- Classes and instances for Regex matching.+--+-- All the /classes/ are declared here, and some common type aliases, and+-- the 'MatchResult' data type.+--+-- The only /instances/ here are for 'Extract' 'String', 'Extract' 'SB.ByteString',+-- and 'Extract' 'ST.Text'. There are no data values. The 'RegexContext'+-- instances are in "Text.Regex.Base.Context", except for ones which+-- run afoul of a repeated variable (@'RegexContext' regex a a@), which+-- are defined in each modules' String and ByteString modules.+-----------------------------------------------------------------------------++module Text.Regex.Base.RegexLike (+ -- ** Type aliases+ MatchOffset,+ MatchLength,+ MatchArray,+ MatchText,+ -- ** Data types+ MatchResult(..),+ -- ** Classes+ RegexOptions(..),+ RegexMaker(..),+ RegexLike(..),+ RegexContext(..),+ Extract(..),+ AllSubmatches(..),AllTextSubmatches(..),AllMatches(..),AllTextMatches(..)+ ) where++import Prelude+ (Int, Bool, String+ , (.), (+)+ , error, id, return, snd+ , drop, fmap, length, map, take+ , toEnum+ )+++import Control.Monad.Fail (MonadFail)+import Data.Array(Array,(!))+import Data.Maybe(Maybe,isJust,maybe)+import qualified Data.ByteString as SB (take,drop,empty,ByteString)+import qualified Data.ByteString.Lazy as LB (take,drop,empty,ByteString)+import qualified Data.Sequence as S(take,drop,empty,Seq)+import qualified Data.Text as ST (take,drop,empty,Text)+import qualified Data.Text.Lazy as LT (take,drop,empty,Text)++-- | 0 based index from start of source, or (-1) for unused+type MatchOffset = Int++-- | non-negative length of a match+type MatchLength = Int++-- | 0 based array, with 0th index indicating the full match. If the+-- full match location is not available, represent as (0,0).+type MatchArray = Array Int (MatchOffset,MatchLength)+type MatchText source = Array Int (source,(MatchOffset,MatchLength))++-- | This is the same as the type from JRegex.+data MatchResult a = MR {+ mrBefore :: a,+ mrMatch :: a,+ mrAfter :: a,+ mrSubList :: [a],+ mrSubs :: Array Int a+}+++-- | Rather than carry them around separately, the options for how to+-- execute a regex are kept as part of the regex. There are two types+-- of options. Those that can only be specified at compilation time+-- and never changed are @compOpt@. Those that can be changed later and+-- affect how matching is performed are @execOpt@. The actually types+-- for these depend on the backend.+--+class RegexOptions regex compOpt execOpt+ | regex -> compOpt execOpt+ , compOpt -> regex execOpt+ , execOpt -> regex compOpt+ where++ -- | No options set at all in the backend.+ blankCompOpt :: compOpt++ -- | No options set at all in the backend.+ blankExecOpt :: execOpt++ -- | Reasonable options (extended, caseSensitive, multiline regex).+ defaultCompOpt :: compOpt++ -- | Reasonable options (extended, caseSensitive, multiline regex).+ defaultExecOpt :: execOpt++ -- | Forget old flags and use new ones.+ setExecOpts :: execOpt -> regex -> regex++ -- | Retrieve the current flags.+ getExecOpts :: regex -> execOpt+++-- | @RegexMaker@ captures the creation of the compiled regular+-- expression from a source type and an option type. Methods 'makeRegexM' and+-- 'makeRegexM' report parse errors using 'MonadError', usually (@Either+-- String regex@).+--+-- The 'makeRegex' function has a default implementation that depends+-- on 'makeRegexOpts' and uses 'defaultCompOpt' and 'defaultExecOpt'.+-- Similarly for 'makeRegexM' and 'makeRegexOptsM'.+--+-- There are also default implementations for 'makeRegexOpts' and+-- 'makeRegexOptsM' in terms of each other. So a minimal instance+-- definition needs to only define one of these, hopefully+-- 'makeRegexOptsM'.+--+class (RegexOptions regex compOpt execOpt) => RegexMaker regex compOpt execOpt source+ | regex -> compOpt execOpt, compOpt -> regex execOpt, execOpt -> regex compOpt where++ -- | Use the 'defaultCompOpt' and 'defaultExecOpt'.+ makeRegex :: source -> regex++ -- | Specify your own options.+ makeRegexOpts :: compOpt -> execOpt -> source -> regex++ -- | Use the 'defaultCompOpt' and 'defaultExecOpt', reporting errors with 'fail'.+ makeRegexM :: (MonadFail m) => source -> m regex++ -- | Specify your own options, reporting errors with fail+ makeRegexOptsM :: (MonadFail m) => compOpt -> execOpt -> source -> m regex++ makeRegex = makeRegexOpts defaultCompOpt defaultExecOpt+ makeRegexM = makeRegexOptsM defaultCompOpt defaultExecOpt+ makeRegexOpts c e s = maybe (error "makeRegexOpts failed") id (makeRegexOptsM c e s)+ makeRegexOptsM c e s = return (makeRegexOpts c e s)+++-- | RegexLike is parametrized on a regular expression type and a+-- source type to run the matching on.+--+-- There are default implementations: 'matchTest' and 'matchOnceText' use+-- 'matchOnce'; 'matchCount' and 'matchAllText' use 'matchAll'.+-- Conversely, 'matchOnce' uses+-- 'matchOnceText' and 'matchAll' uses 'matchAllText'. So a minimal complete+-- instance need to provide at least ('matchOnce' or 'matchOnceText') and+-- ('matchAll' or 'matchAllText'). Additional definitions are often+-- provided where they will increase efficiency.+--+-- > [ c | let notVowel = makeRegex "[^aeiou]" :: Regex, c <- ['a'..'z'], matchTest notVowel [c] ]+-- >+-- > "bcdfghjklmnpqrstvwxyz"+--+-- The strictness of these functions is instance dependent.+--+class (Extract source) => RegexLike regex source where++ -- | This returns the first match in the source (it checks the whole+ -- source, not just at the start). This returns an array of+ -- (offset,length) index pairs for the match and captured+ -- substrings. The offset is 0-based. A (-1) for an offset means a+ -- failure to match. The lower bound of the array is 0, and the 0th+ -- element is the (offset,length) for the whole match.+ matchOnce :: regex -> source -> Maybe MatchArray++ -- | @matchAll@ returns a list of matches. The matches are in order+ -- and do not overlap. If any match succeeds but has 0 length then+ -- this will be the last match in the list.+ matchAll :: regex -> source -> [MatchArray]++ -- | @matchCount@ returns the number of non-overlapping matches+ -- returned by @matchAll@.+ matchCount :: regex -> source -> Int++ -- | @matchTest@ returns @True@ if there is a match somewhere in the+ -- source (it checks the whole source not just at the start).+ matchTest :: regex -> source -> Bool++ -- | This is @matchAll@ with the actual subsections of the source+ -- instead of just the (offset,length) information.+ matchAllText :: regex -> source -> [MatchText source]++ -- | This can return a tuple of three items: the source before the+ -- match, an array of the match and captured substrings (with their+ -- indices), and the source after the match.+ matchOnceText :: regex -> source -> Maybe (source, MatchText source, source)++ matchAll regex source = map (fmap snd) (matchAllText regex source)+ matchOnce regex source = fmap (\(_,mt,_) -> fmap snd mt) (matchOnceText regex source)+ matchTest regex source = isJust (matchOnce regex source)+ matchCount regex source = length (matchAll regex source)+ matchOnceText regex source =+ fmap (\ma -> let (o,l) = ma ! 0+ in (before o source+ ,fmap (\ol -> (extract ol source,ol)) ma+ ,after (o+l) source))+ (matchOnce regex source)+ matchAllText regex source =+ map (fmap (\ol -> (extract ol source,ol)))+ (matchAll regex source)+++-- | @RegexContext@ is the polymorphic interface to do matching. Since+-- 'target' is polymorphic you may need to supply the type explicitly+-- in contexts where it cannot be inferred.+--+-- The monadic 'matchM' version uses 'fail' to report when the 'regex'+-- has no match in 'source'. Two examples:+--+-- Here the contest 'Bool' is inferred:+--+-- > [ c | let notVowel = makeRegex "[^aeiou]" :: Regex, c <- ['a'..'z'], match notVowel [c] ]+-- >+-- > "bcdfghjklmnpqrstvwxyz"+--+-- Here the context @[String]@ must be supplied:+--+-- > let notVowel = (makeRegex "[^aeiou]" :: Regex )+-- > in do { c <- ['a'..'z'] ; matchM notVowel [c] } :: [String]+-- >+-- > ["b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z"]+--+class (RegexLike regex source) => RegexContext regex source target where+ match :: regex -> source -> target+ matchM :: (MonadFail m) => regex -> source -> m target+++-- | Extract allows for indexing operations on 'String' or 'ByteString'.+--+class Extract source where++ -- | @before@ is a renamed 'take'.+ before :: Int -> source -> source++ -- | @after@ is a renamed 'drop'.+ after :: Int -> source -> source++ -- | When there is no match, this can construct an empty data value.+ empty :: source++ -- | @extract@ takes an offset and length, and has this default implementation:+ --+ -- @+ -- extract (off, len) source = before len (after off source)+ -- @+ extract :: (Int,Int) -> source -> source+ extract (off,len) source = before len (after off source)++instance Extract String where+ before = take; after = drop; empty = []++instance Extract SB.ByteString where+ before = SB.take; after = SB.drop; empty = SB.empty++instance Extract LB.ByteString where+ before = LB.take . toEnum; after = LB.drop . toEnum; empty = LB.empty++instance Extract (S.Seq a) where+ before = S.take; after = S.drop; empty = S.empty++-- | @since 0.94.0.0+instance Extract ST.Text where+ before = ST.take; after = ST.drop; empty = ST.empty++-- | @since 0.94.0.0+instance Extract LT.Text where+ before = LT.take . toEnum; after = LT.drop . toEnum; empty = LT.empty++-- | Used in results of 'RegexContext' instances.+newtype AllSubmatches f b = AllSubmatches {getAllSubmatches :: (f b)}++-- | Used in results of 'RegexContext' instances.+newtype AllTextSubmatches f b = AllTextSubmatches {getAllTextSubmatches :: (f b)}++-- | Used in results of 'RegexContext' instances.+newtype AllMatches f b = AllMatches {getAllMatches :: (f b)}++-- | Used in results of 'RegexContext' instances.+newtype AllTextMatches f b = AllTextMatches {getAllTextMatches :: (f b) }