regex-base 0.93.2 → 0.94.0.0
raw patch · 10 files changed
+882/−807 lines, 10 filesdep +faildep +textdep ~arraydep ~basedep ~bytestringnew-uploaderPVP ok
version bump matches the API change (PVP)
Dependencies added: fail, text
Dependency ranges changed: array, base, bytestring, containers, mtl
API changes (from Hackage documentation)
+ Text.Regex.Base.RegexLike: instance Text.Regex.Base.RegexLike.Extract Data.Text.Internal.Lazy.Text
+ Text.Regex.Base.RegexLike: instance Text.Regex.Base.RegexLike.Extract Data.Text.Internal.Text
- Text.Regex.Base.Impl: polymatchM :: (RegexLike a b, Monad m) => a -> b -> m b
+ Text.Regex.Base.Impl: polymatchM :: (RegexLike a b, MonadFail m) => a -> b -> m b
- Text.Regex.Base.RegexLike: makeRegexM :: (RegexMaker regex compOpt execOpt source, Monad m) => source -> m regex
+ Text.Regex.Base.RegexLike: makeRegexM :: (RegexMaker regex compOpt execOpt source, MonadFail m) => source -> m regex
- Text.Regex.Base.RegexLike: makeRegexOptsM :: (RegexMaker regex compOpt execOpt source, Monad m) => compOpt -> execOpt -> source -> m regex
+ Text.Regex.Base.RegexLike: makeRegexOptsM :: (RegexMaker regex compOpt execOpt source, MonadFail m) => compOpt -> execOpt -> source -> m regex
- Text.Regex.Base.RegexLike: matchM :: (RegexContext regex source target, Monad m) => regex -> source -> m target
+ Text.Regex.Base.RegexLike: matchM :: (RegexContext regex source target, MonadFail m) => regex -> source -> m target
Files
- ChangeLog.md +13/−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 +70/−56
- src/Text/Regex/Base.hs +80/−0
- src/Text/Regex/Base/Context.hs +412/−0
- src/Text/Regex/Base/Impl.hs +67/−0
- src/Text/Regex/Base/RegexLike.hs +240/−0
+ ChangeLog.md view
@@ -0,0 +1,13 @@+See also http://pvp.haskell.org/faq++## 0.94.0.0++- **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++----
− 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,72 @@-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.12+name: regex-base+version: 0.94.0.0+ 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: hvr@gnu.org+homepage: https://wiki.haskell.org/Regular_expressions+bug-reports: https://github.com/hvr/regex-base/issues+synopsis: Common "Text.Regex.*" API for Regex matching+category: Text+description:+ This package doesn't provide the ability to do regex matching, but instead provides the type-classes that constitute the abstract API that is implemented by @regex-*@ backends such as+ .+ * <//hackage.haskell.org/package/regex-posix regex-posix>+ .+ * <//hackage.haskell.org/package/regex-parsec regex-parsec>+ .+ * <//hackage.haskell.org/package/regex-dfa regex-dfa>+ .+ * <//hackage.haskell.org/package/regex-tdfa regex-tdfa>+ .+ * <//hackage.haskell.org/package/regex-pcre regex-pcre>+ .+ See also <https://wiki.haskell.org/Regular_expressions> for more information.++extra-source-files:+ ChangeLog.md++source-repository head+ type: git+ location: https://github.com/hvr/regex-base.git+ 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+ other-extensions:+ MultiParamTypeClasses+ FunctionalDependencies+ TypeSynonymInstances+ FlexibleInstances+ FlexibleContexts++ if impl(ghc >= 7.4)+ default-extensions: Safe+ build-depends: containers >= 0.4.2.1+ , bytestring >= 0.9.2.1++ build-depends: base >= 4.3 && < 4.14+ , mtl >= 1.1 && < 2.3+ , containers >= 0.4 && < 0.7+ , bytestring >= 0.9 && < 0.11+ , array >= 0.3 && < 0.6+ , text >= 1.2.3 && < 1.3++ if !impl(ghc >= 8)+ build-depends: fail == 4.9.*++ ghc-options: -Wall -O2
+ src/Text/Regex/Base.hs view
@@ -0,0 +1,80 @@+{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}+-----------------------------------------------------------------------------+-- |+--+-- Module : Text.Regex.Base+-- Copyright : (c) Chris Kuklewicz 2006+-- SPDX-License-Identifier: BSD-3-Clause+--+-- Maintainer : hvr@gnu.org+-- 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.+--+-- == Example+--+-- The code below+--+-- @+-- import Text.Regex.Base+-- import Text.Regex.Posix((=~),(=~~)) -- or DFA or PCRE or PosixRE+--+-- main = let b :: Bool+-- b = ("abaca" =~ "(.)a")+-- c :: [MatchArray]+-- c = ("abaca" =~ "(.)a")+-- d :: Maybe (String,String,String,[String])+-- d = ("abaca" =~~ "(.)a")+-- in do print b+-- print c+-- print d+-- @+--+-- 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,412 @@+{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts, TypeSynonymInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-|++Module : Text.Regex.Base.Context+Copyright : (c) Chris Kuklewicz 2006+SPDX-License-Identifier: BSD-3-Clause++Maintainer : hvr@gnu.org+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 Prelude hiding (fail)+import Control.Monad.Fail (MonadFail(fail)) -- see 'regexFailed'++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),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,67 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.Regex.Impl+-- Copyright : (c) Chris Kuklewicz 2006+-- SPDX-License-Identifier: BSD-3-Clause+-- +-- Maintainer : hvr@gnu.org+-- 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+-- @+-- +-- @+-- instance RegexContext Regex Text Text where+-- match = polymatch+-- matchM = polymatchM+-- @+-------------------------------------------------------------------------------++module Text.Regex.Base.Impl(polymatch,polymatchM) where++import Prelude hiding (fail)+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,240 @@+{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, TypeSynonymInstances #-}+-----------------------------------------------------------------------------+-- |+-- Module : Text.Regex.Base.RegexLike+-- Copyright : (c) Chris Kuklewicz 2006+-- SPDX-License-Identifier: BSD-3-Clause+--+-- Maintainer : hvr@gnu.org+-- 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', '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 hiding (fail)+import Control.Monad.Fail as Fail (MonadFail)++import Data.Array(Array,(!))+import Data.Maybe(isJust)+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 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 :: (Fail.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. 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 :: (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+ -- | 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 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) }