regexdot 0.11.0.1 → 0.11.1.0
raw patch · 8 files changed
+103/−80 lines, 8 filesdep ~toolshedPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: toolshed
API changes (from Hackage documentation)
- RegExDot.RegEx: instance (ShortcutExpander m, ShortcutExpander m, Eq m, Read m) => Read (ExtendedRegEx m)
- RegExDot.RegEx: instance (ShortcutExpander m, ShortcutExpander m, Eq m, Read m) => Read (Pattern m)
+ RegExDot.Anchor: type AnchorSpecification = (Maybe Anchor, Maybe Anchor)
+ RegExDot.Anchor: unanchored :: AnchorSpecification
+ RegExDot.RegEx: instance (Eq m, ShortcutExpander m, Read m, ShortcutExpander m) => Read (ExtendedRegEx m)
+ RegExDot.RegEx: instance (Eq m, ShortcutExpander m, Read m, ShortcutExpander m) => Read (Pattern m)
- RegExDot.DSL: (<~>) :: (Maybe Anchor, Maybe Anchor) -> Concatenation a -> ExtendedRegEx a
+ RegExDot.DSL: (<~>) :: AnchorSpecification -> Concatenation a -> ExtendedRegEx a
- RegExDot.RegEx: (+~) :: (Eq m, NFData m, Show m) => InputData m -> RegExOpts (ExtendedRegEx m) -> Result m
+ RegExDot.RegEx: (+~) :: (Eq m, NFData m) => InputData m -> RegExOpts (ExtendedRegEx m) -> Result m
- RegExDot.RegEx: (/~) :: (Eq m, NFData m, Show m) => InputData m -> RegExOpts (ExtendedRegEx m) -> Bool
+ RegExDot.RegEx: (/~) :: (Eq m, NFData m) => InputData m -> RegExOpts (ExtendedRegEx m) -> Bool
- RegExDot.RegEx: (=~) :: (Eq m, NFData m, Show m) => InputData m -> RegExOpts (ExtendedRegEx m) -> Bool
+ RegExDot.RegEx: (=~) :: (Eq m, NFData m) => InputData m -> RegExOpts (ExtendedRegEx m) -> Bool
Files
- changelog +5/−0
- makefile +8/−8
- regexdot.cabal +3/−3
- src/RegExDot/Anchor.hs +11/−1
- src/RegExDot/ConsumptionProfile.hs +3/−5
- src/RegExDot/DSL.hs +3/−3
- src/RegExDot/RegEx.hs +63/−53
- src/RegExDot/Repeatable.hs +7/−7
changelog view
@@ -35,3 +35,8 @@ * Replaced "System" with "System.Environment" and "System.Exit". 0.11.0.1 * Added class 'Eq' to the context of 'RegExDot.RegEx.safeReciprocal', for migration to 'ghc-7.4'.+0.11.1.0+ * Removed comments referring to deleted module "ToolShed.Unsafe", from "RegExDot.RegEx".+ * Removed 'Show' from the context of functions in "RegExDot.RegEx"+ * Exported a new constant 'RegExDot.Anchor.unanchored'.+ * Refactored 'RegExDot.ConsumptionProfile.withinConsumptionBounds'.
makefile view
@@ -19,37 +19,37 @@ install: build haddock @[ -z "$$CABAL_INSTALL_OPTIONS" ] || echo "INFO: CABAL_INSTALL_OPTIONS='$$CABAL_INSTALL_OPTIONS'"- runhaskell Setup.hs $@ $$CABAL_INSTALL_OPTIONS+ runhaskell Setup $@ $$CABAL_INSTALL_OPTIONS prof: CABAL_CONFIGURE_OPTIONS="--enable-library-profiling $$CABAL_CONFIGURE_OPTIONS" make install copy: build @[ -z "$$CABAL_COPY_OPTIONS" ] || echo "INFO: CABAL_COPY_OPTIONS='$$CABAL_COPY_OPTIONS'"- runhaskell Setup.hs $@ $$CABAL_COPY_OPTIONS+ runhaskell Setup $@ $$CABAL_COPY_OPTIONS build: configure @[ -z "$$CABAL_BUILD_OPTIONS" ] || echo "INFO: CABAL_BUILD_OPTIONS='$$CABAL_BUILD_OPTIONS'"- runhaskell Setup.hs $@ $$CABAL_BUILD_OPTIONS+ runhaskell Setup $@ $$CABAL_BUILD_OPTIONS configure: regexdot.cabal Setup.hs @[ -z "$$CABAL_CONFIGURE_OPTIONS" ] || echo "INFO: CABAL_CONFIGURE_OPTIONS='$$CABAL_CONFIGURE_OPTIONS'"- runhaskell Setup.hs $@ $$CABAL_CONFIGURE_OPTIONS #--user+ runhaskell Setup $@ $$CABAL_CONFIGURE_OPTIONS #--user haddock: configure- PATH=~/.cabal/bin:$$PATH runhaskell Setup.hs $@ --hyperlink-source #Amend path to find 'HsColour', as required for 'hyperlink-source'.+ PATH=~/.cabal/bin:$$PATH runhaskell Setup $@ --hyperlink-source #Amend path to find 'HsColour', as required for 'hyperlink-source'. hlint: @$@ -i 'Use &&' src/ -i 'Use ||' -sdist: configure- runhaskell Setup.hs $@+sdist:+ runhaskell Setup $@ check: sdist cabal upload --check --verbose=3 dist/*.tar.gz; clean:- runhaskell Setup.hs $@+ runhaskell Setup $@ find src -type f \( -name '*.hc' -o -name '*.hcr' -o -name '*.hi' -o -name '*.o' \) -delete help:
regexdot.cabal view
@@ -1,6 +1,6 @@ --Package-properties Name: regexdot-Version: 0.11.0.1+Version: 0.11.1.0 Cabal-Version: >= 1.6 Copyright: (C) 2010 Dr. Alistair Ward License: GPL@@ -11,7 +11,7 @@ Build-Type: Simple Description: Provides a portable, POSIX, extended regex-engine, designed to process a list of /arbitrary/ objects. Category: Search, Regex-Tested-With: GHC == 6.10, GHC == 6.12, GHC == 7.0+Tested-With: GHC == 6.10, GHC == 6.12, GHC == 7.0, GHC == 7.4 Homepage: http://functionalley.eu Maintainer: regexdot <at> functionalley <dot> eu Bug-reports: regexdot <at> functionalley <dot> eu@@ -58,7 +58,7 @@ Build-depends: base == 4.*, parsec == 3.*,- toolshed == 0.13.*+ toolshed >= 0.13 GHC-options: -Wall -O2
src/RegExDot/Anchor.hs view
@@ -34,10 +34,13 @@ -- * Types -- ** Data-types Anchor(..),+-- ** Type-synonyms+ AnchorSpecification, -- * Constants bowToken, sternToken,- tokens+ tokens,+ unanchored ) where #ifdef HAVE_DEEPSEQ@@ -82,4 +85,11 @@ -- | The set of 'Char' to which a specific meaning is attributed, when reading from 'String'. tokens :: String tokens = [bowToken, sternToken]++-- | A specification for the anchors required around the expression+type AnchorSpecification = (Maybe Anchor, Maybe Anchor)++-- | The specification for an unanchored expression.+unanchored :: AnchorSpecification+unanchored = (Nothing, Nothing)
src/RegExDot/ConsumptionProfile.hs view
@@ -43,7 +43,9 @@ ) where import Control.Applicative((<$>), (<*>))+import Control.Arrow((***)) import qualified Data.List+import qualified Data.Maybe import qualified RegExDot.ConsumptionBounds as ConsumptionBounds import qualified ToolShed.SelfValidate @@ -90,11 +92,7 @@ :: ConsumptionBounds.DataLength -- ^ The actual quantity of data consumed. -> ConsumptionProfile -- ^ The bounds within which data-consumption is required to fall. -> Bool-withinConsumptionBounds dataLength MkConsumptionProfile {- consumptionBounds = (minConsumption, maxConsumption)-} = dataLength >= minConsumption && case maxConsumption of- Nothing -> True- Just m -> dataLength <= m+withinConsumptionBounds dataLength = uncurry (&&) . ((dataLength >=) *** Data.Maybe.maybe True (dataLength <=)) . consumptionBounds -- | Both minimum & maximum set to zero; which can be used as the initial value when accumulating the sum of a list. zero :: ConsumptionProfile
src/RegExDot/DSL.hs view
@@ -107,9 +107,9 @@ * Accounts for the unusual requirement to place an 'Anchor.Stern' at the start of the 'RegEx.Concatenation', or an 'Anchor.Bow' at the end. -} (<~>)- :: (Maybe Anchor.Anchor, Maybe Anchor.Anchor) -- ^ A specification for the anchors required around the expression.- -> RegEx.Concatenation a -- ^ The bare list of repeatable patterns to anchor.- -> RegEx.ExtendedRegEx a -- ^ The completed regex.+ :: Anchor.AnchorSpecification -- ^ A specification for the anchors required around the expression.+ -> RegEx.Concatenation a -- ^ The bare list of repeatable patterns to anchor.+ -> RegEx.ExtendedRegEx a -- ^ The completed regex. (bow, stern) <~> concatenation = RegEx.MkExtendedRegEx { RegEx.bowAnchor = bow, RegEx.concatenation = concatenation,
src/RegExDot/RegEx.hs view
@@ -21,7 +21,7 @@ [@DESCRIPTION@] * This implementation of extended /regex/es, generalises the familiar concept of pattern-matching of character-strings, to matching lists composed from an arbitrary data-type.- The polymorphic data, from which the input data-list is composed, need only support @Eq@ & @Show@.+ The polymorphic data, from which the input data-list is composed, need only support @Eq@. * Because of the unknown stringified form of the underlying polymorphic data, the /regex/ must be described by a comparatively verbose bracketed & comma-separated list, rather than the traditional /String/ containing Meta-characters.@@ -241,7 +241,12 @@ -> Alternatives m transformAlternatives f = MkAlternatives . f . deconstructAlternatives -instance (ShortcutExpander m, Meta.ShortcutExpander m, Eq m, Read m) => Read (Alternatives m) where+instance (+ ShortcutExpander m,+ Meta.ShortcutExpander m,+ Eq m,+ Read m+ ) => Read (Alternatives m) where readsPrec _ (' ' : s) = reads s --Consume white-space. readsPrec _ ('\t' : s) = reads s --Consume white-space. readsPrec _ s = case reads s of@@ -274,7 +279,7 @@ * 'True' if there's no possibility, that the specified input datum, can be consumed by any of the specified 'MetaDataList'. * Because data may not be consumed, perhaps because of unsuitable 'ConsumptionBounds.ConsumptionBounds', the converse doesn't hold,- ie @not isUnconsumableByAnyOf@ /doesn't/ imply that the input datum can be consumed, just that it hasn't been proven that it can't.+ i.e. @not isUnconsumableByAnyOf@ /doesn't/ imply that the input datum can be consumed, just that it hasn't been proven that it can't. -} isUnconsumableByAnyOf :: Eq m => m -- ^ The input datum.@@ -292,7 +297,12 @@ | CaptureGroup (Alternatives m) -- ^ A sub-expression containing a selection of recursively defined alternatives, thus forming a tree-structure. deriving Eq -instance (ShortcutExpander m, Meta.ShortcutExpander m, Eq m, Read m) => Read (Pattern m) where+instance (+ Eq m,+ Meta.ShortcutExpander m,+ Read m,+ ShortcutExpander m+ ) => Read (Pattern m) where readsPrec _ (' ' : s) = reads s --Consume white-space. readsPrec _ ('\t' : s) = reads s --Consume white-space. readsPrec _ ('(' : s) = case {-Alternatives.-} reads s of@@ -361,7 +371,7 @@ {- | * Represents a black hole, which will greedily consume all data. - * CAVEAT: nullary, ie a constant.+ * CAVEAT: nullary, i.e. a constant. -} (.*) :: RepeatablePattern m (.*) = Repeatable.zeroOrMore anyDatum@@ -408,7 +418,12 @@ -> Transformation m transformExtendedRegEx f extendedRegEx = extendedRegEx { concatenation = f $ concatenation extendedRegEx } -instance (ShortcutExpander m, Meta.ShortcutExpander m, Eq m, Read m) => Read (ExtendedRegEx m) where+instance (+ Eq m,+ Meta.ShortcutExpander m,+ Read m,+ ShortcutExpander m+ ) => Read (ExtendedRegEx m) where readsPrec _ [] = [] readsPrec _ (' ' : s) = reads s --Consume white-space. readsPrec _ ('\t' : s) = reads s --Consume white-space.@@ -419,7 +434,7 @@ | otherwise -> error $ ToolShed.SelfValidate.getFirstError extendedRegEx --Parsed OK, but invalid. _ -> [] --No parse. where--- singleton :: (ShortcutExpander m, Meta.ShortcutExpander m, Eq m, Read m) => [(ExtendedRegEx m, String)]+-- singleton :: (Eq m, Meta.ShortcutExpander m, Read m, ShortcutExpander m) => [(ExtendedRegEx m, String)] singleton = [ ( MkExtendedRegEx {@@ -432,7 +447,7 @@ (maybeBowAnchor, s1) <- readsMaybeAnchor s, (concatenation', s2) <- reads s1, (maybeSternAnchor, remainder) <- readsMaybeAnchor s2- ] where+ ] {-list-comprehension-} where readsMaybeAnchor :: ReadS (Maybe Anchor.Anchor) readsMaybeAnchor s' = return {-to List-monad-} $ case reads s' of [pair] -> Control.Arrow.first Just pair@@ -490,7 +505,7 @@ hasSternAnchor :: ExtendedRegEx m -> Bool hasSternAnchor = (== Just Anchor.Stern) . sternAnchor --- | True if there's at least one 'RepeatablePattern' in the 'Concatenation', ie that it's non-null.+-- | True if there's at least one 'RepeatablePattern' in the 'Concatenation', i.e. that it's non-null. isDefined :: ExtendedRegEx m -> Bool isDefined = not . null . concatenation @@ -498,7 +513,7 @@ mkNullMatchFromExtendedRegEx :: ConsumptionBounds.DataLength -> ExtendedRegEx m -> MatchList m mkNullMatchFromExtendedRegEx offset = mkNullMatchFromConcatenation offset . concatenation --- | The set of distinct (ie unique) 'Meta.Meta', in the specified 'ExtendedRegEx'.+-- | The set of distinct (i.e. unique) 'Meta.Meta', in the specified 'ExtendedRegEx'. getDistinctMetaDataFromExtendedRegEx :: Eq m => ExtendedRegEx m -> MetaDataList m getDistinctMetaDataFromExtendedRegEx = getDistinctMetaDataFromConcatenation . concatenation @@ -581,7 +596,7 @@ * One could waste a lot of time trying to consume 'InputData' from the head of a high-'Consumer.starHeight' 'ExtendedRegEx', only to find that there's a low-'Consumer.starHeight' tail that can't ever match the 'InputData'; checks this first before delegating. -}-findMatch :: (Eq m, NFData m, Show m)+findMatch :: (Eq m, NFData m) => RegExOpts.RegExOpts (ExtendedRegEx m) -- ^ The match-options parameterised by the regex against which to match the input data. -> InputData m -- ^ The input data within which to locate a match. -> Maybe (MatchList m)@@ -613,7 +628,7 @@ concatenation = lowStarHeightTail, sternAnchor = Nothing } --Check for mismatch with the corresponding tail of 'InputData'.- ] = {-Unsafe.printShow "Tail-mismatch:\t"-} Nothing+ ] = Nothing | otherwise = findMatchSlave originalConcatenation ( Consumer.accumulateConsumptionProfiles originalConcatenation ) (@@ -632,21 +647,18 @@ Whilst the only strictly necessary parameters are the lists 'Concatenation' & 'InputData', many ancillary lists derived from them, are also passed by parameter. These are proportionally reduced on recursion, to avoid the requirement to regenerate them each time they're required. -}- findMatchSlave :: (- Eq m,- NFData m,- Show m- ) => Concatenation m --The list of 'RepeatablePattern's from which the regex is constructed.+ findMatchSlave :: (Eq m, NFData m)+ => Concatenation m --The list of 'RepeatablePattern's from which the regex is constructed. -> ConsumptionProfile.AccumulatedConsumptionProfiles --The capacity-bounds for the consumption of 'InputData', that the 'Concatenation' extending right from any given 'RepeatablePattern', can consume. -> [MetaDataList m] --The set of distinct 'Meta.Meta'-data, in the 'Concatenation extending right from any given 'RepeatablePattern'. -> InputData m --The input data, which will be fed to the 'Concatenation'. -> ConsumptionBounds.DataLength --The length of the previously specified 'InputData'. -> [InputData m] --The set of distinct input data, extending right from any given point. -> Maybe (MatchList m)- findMatchSlave [] _ _ [] _ _ = {-Unsafe.printShow "Dead heat:\t" $-} Just [] --Simultaneous exhaustion of the 'Concatenation' of 'RepeatablePattern's & the 'InputData' => success.- findMatchSlave [] _ _ _ _ _ = {-Unsafe.printShow "Excess data:\t"-} Nothing+ findMatchSlave [] _ _ [] _ _ = Just [] --Simultaneous exhaustion of the 'Concatenation' of 'RepeatablePattern's & the 'InputData' => success.+ findMatchSlave [] _ _ _ _ _ = Nothing findMatchSlave concatenation'@(repeatablePatternHead : concatenationTail) (accumulatedConsumptionProfileHead : accumulatedConsumptionProfilesTail) (distinctMetaDataHead : distinctMetaDataTail) inputData inputDataLength distinctInputData@(distinctInputDataHead : _)- | not $ inputDataLength `ConsumptionProfile.withinConsumptionBounds` accumulatedConsumptionProfileHead = {-Unsafe.printShow "outside ConsumptionBounds:\t"-} Nothing+ | not $ inputDataLength `ConsumptionProfile.withinConsumptionBounds` accumulatedConsumptionProfileHead = Nothing | null inputData = Just $ mkNullMatchFromConcatenation inputDataOffset concatenation' --Build a 'Match', of null 'InputData'. | and [ ExecutionOptions.bypassInputDataForLiberalConsumer executionOptions, --We may be able to establish success, without evaluating 'inputData' any further than required to determine its length.@@ -657,34 +669,32 @@ ExecutionOptions.checkForUnconsumableData executionOptions, not $ ConsumptionProfile.canConsumeAnything accumulatedConsumptionProfileHead, --Otherwise the subsequent test will always fail. (`isUnconsumableByAnyOf` distinctMetaDataHead) `any` distinctInputDataHead --Occasionally failure is both inevitable & obvious.- ] = {-Unsafe.printShow ("Unconsumable data=" ++ show inputData ++ ":\t")-} Nothing+ ] = Nothing | otherwise = {-#SCC "findMatchSlave" #-} let tailConsumptionProfile :: ConsumptionProfile.ConsumptionProfile tailConsumptionProfile@( ConsumptionProfile.MkConsumptionProfile { ConsumptionProfile.consumptionBounds = (minConsumptionConcatenationTail, maybeMaxConsumptionConcatenationTail) }- ) = {-Unsafe.printShow "tailConsumptionProfile:\t" $-} head accumulatedConsumptionProfilesTail --Extract the aggregate consumption-profile, of the tail of the 'Concatenation'.+ ) = head accumulatedConsumptionProfilesTail --Extract the aggregate consumption-profile, of the tail of the 'Concatenation'. maxDataAvailable :: ConsumptionBounds.DataLength maxDataAvailable = inputDataLength - minConsumptionConcatenationTail --The maximum data available to match 'repeatablePatternHead'. in if maxDataAvailable < 0- then {-Unsafe.printShow ("Excess Concatenation=" ++ show concatenation' ++ ":\t")-} Nothing+ then Nothing else {-inputData is sufficient for concatenationTail-} let Repeatable.MkRepeatable { Repeatable.base = base, Repeatable.repetitionBounds = (fewest, most), Repeatable.isGreedy = isGreedy- } = {-Unsafe.printShow "repeatablePatternHead:\t"-} repeatablePatternHead --Completely deconstruct the 'Repeatable' at the head of the 'Concatenation'.+ } = repeatablePatternHead --Completely deconstruct the 'Repeatable' at the head of the 'Concatenation'. {- Find the maximum sequence of inputData, preceding the first of the mandatory requirements of 'concatenationTail'. This enables one to more tightly constrain the maximum number of repetitions of a 'RepeatablePattern', which consequently reduces the subsequent requirement to back-track. -} -- maximumDataBeforePegs :: Maybe (InputData m)- maximumDataBeforePegs = {-#SCC "maximumDataBeforePegs" #-} reverse (- {-Unsafe.printShow "minimumRequirement:\t" $-} extractMinimumRequirementFromConcatenation concatenationTail- ) `maximumDataAfterPegs` reverse inputData where+ maximumDataBeforePegs = {-#SCC "maximumDataBeforePegs" #-} reverse (extractMinimumRequirementFromConcatenation concatenationTail) `maximumDataAfterPegs` reverse inputData where {- Extract those 'RepeatablePattern's which must match at least once. Singleton 'Alternatives' are flattened by replication, & non-singleton 'Alternatives' removed, allowing a reduction of the list's base-type to just 'Repeatable.Repeatable' 'Meta.Meta'.@@ -730,12 +740,12 @@ Once the repetition-range has been narrowed, iterate from the appropriate end of the range (depending on 'isGreedy'), extract the corresponding length from inputData, & check for a match. -} -- maybeMatchList :: [Maybe (MatchList m)]- maybeMatchList = {-#SCC "maybeMatchList" #-} dropWhile Data.Maybe.isNothing . {-Unsafe.printShow "maybeMatchList:\t" .-} map (+ maybeMatchList = {-#SCC "maybeMatchList" #-} dropWhile Data.Maybe.isNothing . map ( \(consumedInputData, unconsumedInputData) -> let consumedInputDataLength :: ConsumptionBounds.DataLength- consumedInputDataLength = {-Unsafe.printShow "consumedInputDataLength:\t" $-} length consumedInputData+ consumedInputDataLength = length consumedInputData in (- Tree.Leaf (repeatablePatternHead, inputDataOffset, {-Unsafe.printShow "consumedInputData:\t"-} consumedInputData) :+ Tree.Leaf (repeatablePatternHead, inputDataOffset, consumedInputData) : ) <$> {-apply to Maybe Functor-} findMatchSlave concatenationTail accumulatedConsumptionProfilesTail distinctMetaDataTail unconsumedInputData ( inputDataLength - consumedInputDataLength ) (@@ -743,12 +753,12 @@ ) --Recurse, to check whether the 'unconsumedInputData' tail also matches. ) $ let fewestData, mostData, mostData' :: Repeatable.Repetitions- fewestData = {-#SCC "fewestData" #-} {-Unsafe.printShow "fewestData:\t" $-} case maybeMaxConsumptionConcatenationTail of+ fewestData = {-#SCC "fewestData" #-} case maybeMaxConsumptionConcatenationTail of Just m -> (inputDataLength - m) {- :: ConsumptionBounds.DataLength-} `max` fewest {- :: Repeatable.Repetitions-} --CAVEAT: conceptually different types. _ -> fewest -- mostData = length . takeWhile (~= meta) $ (`take` inputData) maxData --As slow as it is concise.- mostData = {-#SCC "mostData" #-} {-Unsafe.printShow "mostData:\t" $-} maxData - measureUnmatchableTail inputData maxData where+ mostData = {-#SCC "mostData" #-} maxData - measureUnmatchableTail inputData maxData where maxData :: ConsumptionBounds.DataLength maxData = case most of Just cap -> cap {- :: Repeatable.Repetitions-} `min` maxDataAvailable {- :: ConsumptionBounds.DataLength-} --CAVEAT: conceptually different types.@@ -772,8 +782,8 @@ isGreedy, --Otherwise, since the search proceeds from 'fewest' to 'most', the optimal solution is located before backtracking. mostData > fewestData --Otherwise, there's no unbridled greed to moderate. ] = case maximumDataBeforePegs of- Just maximumDataBeforePegs' -> {-Unsafe.printShow ("Reducing mostData=" ++ show mostData ++ " ->\t") $-} mostData `min` length maximumDataBeforePegs' --Cap upper bound.- _ -> {-Unsafe.printShow "Peg match-failure:\t" $-} negate 1 --Guaranteed to be < 'fewestData'.+ Just maximumDataBeforePegs' -> mostData `min` length maximumDataBeforePegs' --Cap upper bound.+ _ -> negate 1 --Guaranteed to be < 'fewestData'. | otherwise {-no requirement for this optimisation-} = mostData in ( succ {-fence-post-} mostData' - fewestData --CAVEAT: possibly <= 0.@@ -788,7 +798,7 @@ not isGreedy, fewest <= 0, --Zero repetitions permissible. Data.Maybe.isJust tailMatch- ] {-Unsafe.printShow "Trying zero alternatives:\t"-} -> zeroRepetitions --Zero repetitions is the optimal solution, rendering the choice of Alternative irrelevant.+ ] -> zeroRepetitions --Zero repetitions is the optimal solution, rendering the choice of Alternative irrelevant. | and [ ExecutionOptions.catchIncompatibleAnchors executionOptions, fewest > 1, --Multiple repetitions.@@ -802,7 +812,7 @@ Either way, if the user requires data-capture, then for POSIX-compliance we must perform an exhaustive search of the O(Alternatives ^ Repetitions) permutations for the optimal solution; though the search may be narrowed as we proceed. -} -- matchPairList :: [(Match m, MatchList m)]- matchPairList = {-#SCC "matchPairList" #-} Data.Maybe.catMaybes . {-Unsafe.printShow "matchPairList:\t" .-}+ matchPairList = {-#SCC "matchPairList" #-} Data.Maybe.catMaybes . #if MIN_VERSION_parallel(3,0,0) concat . Control.Parallel.Strategies.parMap Control.Parallel.Strategies.rseq --Particularly effective when 'not ExecutionOptions.requireMatchList'. #else@@ -850,7 +860,7 @@ sternAnchor = Just Anchor.Stern } then maybeMatchPairList- else {-Unsafe.printShow ("Bypassing " ++ show (length maybeMatchPairList) ++ " repetitions; ")-} [] --Abandon further repetitions of 'Alternatives'.+ else [] --Abandon further repetitions of 'Alternatives'. _ {-Alternative is OK, or there's no subsequent attempt to bypass-} -> maybeMatchPairList else {-optimisation not required-} maybeMatchPairList ) . map (@@ -921,15 +931,15 @@ concatenationFromAlternative {-expand 1st repetition-} ++ replicate remainingRepetitions {-potentially zero-} singletonRepeatable --Enumerate all remaining repetitions of ANY Alternative; a different Alternative can match for each repetition. ) in {-#SCC "collater" #-} collater <$> {-apply to Maybe Functor-} findMatchSlave (- {-Unsafe.printShow ("Expanded Alternatives for repetitions=" ++ show repetitions ++ ":\t") $-} expandedConcatenationPrefix ++ concatenationTail+ expandedConcatenationPrefix ++ concatenationTail ) ( ConsumptionProfile.accumulateFrom tailConsumptionProfile {-initial value-} consumptionProfileExpandedConcatenationPrefix ++ tail accumulatedConsumptionProfilesTail ) (- {-Unsafe.printShow "MetaDataList:\t" $-} accumulateDistinctMetaDataFrom (head distinctMetaDataTail) {-initial value-} expandedConcatenationPrefix ++ tail distinctMetaDataTail+ accumulateDistinctMetaDataFrom (head distinctMetaDataTail) {-initial value-} expandedConcatenationPrefix ++ tail distinctMetaDataTail ) inputData inputDataLength distinctInputData --Recurse to get 'Maybe (MatchList a)'.- ) {-. Unsafe.printShow ("Range for " ++ show concatenationFromAlternative ++ ":\t")-} $ if Repeatable.isPrecise repeatablePatternHead --These are frequently generated by the previous recursion, as a trial expansion of an Alternative.+ ) $ if Repeatable.isPrecise repeatablePatternHead --These are frequently generated by the previous recursion, as a trial expansion of an Alternative. then if ExecutionOptions.validateMinConsumptionOfAlternatives executionOptions && maxDataAvailable < fewest * minConsumptionAlternatives- then {-Unsafe.printShow "Insufficient data:\t"-} [] --Failure is inevitable.+ then [] --Failure is inevitable. else [fewest] else {-imprecise range-} let {-@@ -939,7 +949,7 @@ The range can be narrowed more aggressively, for the special case of a singleton 'Alternative', as used in a capture-group. -} fewestAlternatives, mostAlternatives, mostAlternatives' :: Repeatable.Repetitions- fewestAlternatives = {-#SCC "fewestAlternatives" #-} {-Unsafe.printShow "fewestAlternatives:\t" $-} 1 {-zero is handled later-} `max` if any Data.Maybe.isNothing [maybeMaxConsumptionConcatenationTail, maybeMaxConsumptionAlternatives]+ fewestAlternatives = {-#SCC "fewestAlternatives" #-} 1 {-zero is handled later-} `max` if any Data.Maybe.isNothing [maybeMaxConsumptionConcatenationTail, maybeMaxConsumptionAlternatives] then fewest else {-no unlimited capacities-} let minDataAvailable, maxConsumptionAlternatives :: ConsumptionBounds.DataLength@@ -956,11 +966,11 @@ minDataAvailable' = minDataAvailable - Data.Maybe.fromJust maybeMaxConsumptionConcatenationFromAlternative --CAVEAT: potentially negative. in if maxConsumptionAlternatives == 0 --Denominator. then {-no Alternative can consume anything-} if minDataAvailable' <= 0 --Numerator.- then {-zero unconsumable data-} {-Unsafe.printShow "0/0 => fewestAlternatives:\t"-} fewest --Repeat to meet the minimum requirement.+ then {-zero unconsumable data-} fewest --Repeat to meet the minimum requirement. else {-unconsumable data-} maxBound --Failure is inevitable. else {-non-zero => can divide-} max fewest . succ {-account for expanded instance-} $ minDataAvailable' /+ maxConsumptionAlternatives - mostAlternatives = {-#SCC "mostAlternatives" #-} {-Unsafe.printShow "mostAlternatives:\t" $-} case most of+ mostAlternatives = {-#SCC "mostAlternatives" #-} case most of Just cap -> cap `min` mostPermissibleRepetitions --Create a ceiling above which the calculated number of permissible repetitions can't rise. _ -> mostPermissibleRepetitions where@@ -998,13 +1008,13 @@ minConsumptionAlternatives > 0 --Otherwise, any number of repetitions can occur without necessarily triggering back-tracking. ] then case maximumDataBeforePegs of- Just maximumDataBeforePegs' -> {-Unsafe.printShow ("Reducing mostAlternatives=" ++ show mostAlternatives ++ "\t-> ") $-} mostAlternatives `min` (+ Just maximumDataBeforePegs' -> mostAlternatives `min` ( length maximumDataBeforePegs' `div` minConsumptionAlternatives ) --Cap upper bound.- _ -> {-Unsafe.printShow "Peg match-failure:\t" $-} negate 1 --Guaranteed to be < 'fewestAlternatives'.+ _ -> negate 1 --Guaranteed to be < 'fewestAlternatives'. else {-no requirement for this optimisation-} mostAlternatives in [fewestAlternatives .. mostAlternatives'] --The permissible repetition-range of any Alternative, constrained by the amount of data available, after subtracting fixed consumption-requirements.- ) $ {-Unsafe.printShow "extendedRegExList:\t" .-} (+ ) . ( if ExecutionOptions.permitReorderingOfAlternatives executionOptions {- The order in which 'Alternatives' are evaluated, should make no difference to the final result; unless 'ExecutionOptions.useFirstMatchAmongAlternatives'.@@ -1026,8 +1036,8 @@ ) extendedRegExAlternatives in if null matchPairList then if fewest <= 0- then {-Unsafe.printShow "Trying zero Alternatives:\t"-} zeroRepetitions --Which might still fail, depending on 'tailMatch'.- else {-zero repetitions isn't permissible-} {-Unsafe.printShow "Alternatives-mismatch:\t"-} Nothing+ then zeroRepetitions --Which might still fail, depending on 'tailMatch'.+ else {-zero repetitions isn't permissible-} Nothing else {-at least one Alternative matched-} {-#SCC "selectAlternative" #-} Just . uncurry (:) {-re-join head & tail-} $ ( {- At least one 'Match' has been found amongst the combinations of repetitions of various 'Alternatives',@@ -1039,7 +1049,7 @@ then head else snd {-remove prepended selection-criterion-} . Data.List.maximumBy {-select the best match-} ( Data.Ord.comparing fst --Compare using only the criterion, not the result from which it was derived.- ) . {-Unsafe.printShow "Ranked matchPairList:\t" .-} map (+ ) . map ( ( {- If the primary selection-criterion doesn't resolve the choice between candidate 'Match'es, I employ these ad-hoc criteria.@@ -1118,7 +1128,7 @@ * CAVEAT: much more expensive then '=~': in /ghci/, 'Just' can be observed to be printed /long/ before the 'MatchList' from which 'Result' is constructed, as the lazy algorithm finds the first solution, but not yet necessarily the optimal solution, amongst 'Alternatives'. -}-(+~) :: (Eq m, NFData m, Show m)+(+~) :: (Eq m, NFData m) => InputData m -- ^ The input data within which to locate a match. -> RegExOpts.RegExOpts (ExtendedRegEx m) -- ^ The match-options parameterised by the regex against which to match the input data. -> Result m@@ -1150,14 +1160,14 @@ the discovery of /any/ solution is sufficient to generate the return-value; lazy-evaluation avoids the requirement to identify the irrelevant optimal solution. -}-(=~) :: (Eq m, NFData m, Show m)+(=~) :: (Eq m, NFData m) => InputData m -- ^ The input data within which to locate a match. -> RegExOpts.RegExOpts (ExtendedRegEx m) -- ^ The match-options parameterised by the regex against which to match the input data. -> Bool inputData =~ regExOpts = Data.Maybe.isJust $ fmap drift (RegExOpts.setVerbose False regExOpts) `findMatch` inputData -- | Pattern-mismatch operator.-(/~) :: (Eq m, NFData m, Show m)+(/~) :: (Eq m, NFData m) => InputData m -- ^ The input data within which to locate a match. -> RegExOpts.RegExOpts (ExtendedRegEx m) -- ^ The match-options parameterised by the regex against which to match the input data. -> Bool
src/RegExDot/Repeatable.hs view
@@ -132,9 +132,9 @@ one b --The default; there's no concept of greediness here. ) $ do repeatable <- Parsec.choice [- (Parsec.char oneOrMoreToken <?> "Repeatable.oneOrMoreToken " ++ show oneOrMoreToken) >> return {-to GenParser-monad-} (oneOrMore b),- (Parsec.char zeroOrOneToken <?> "Repeatable.zeroOrOneToken " ++ show zeroOrOneToken) >> return {-to GenParser-monad-} (zeroOrOne b),- (Parsec.char zeroOrMoreToken <?> "Repeatable.zeroOrMoreToken " ++ show zeroOrMoreToken) >> return {-to GenParser-monad-} (zeroOrMore b),+ (Parsec.char oneOrMoreToken <?> "Repeatable.oneOrMoreToken " ++ show oneOrMoreToken) >> return {-to ParsecT-monad-} (oneOrMore b),+ (Parsec.char zeroOrOneToken <?> "Repeatable.zeroOrOneToken " ++ show zeroOrOneToken) >> return {-to ParsecT-monad-} (zeroOrOne b),+ (Parsec.char zeroOrMoreToken <?> "Repeatable.zeroOrMoreToken " ++ show zeroOrMoreToken) >> return {-to ParsecT-monad-} (zeroOrMore b), (b ^#->#) {-arbitrarily greedy for now-} <$> uncurry Parsec.between (ToolShed.Data.Pair.mirror Parsec.char rangeDelimiters) ( do fewest <- Parsec.spaces >> (read <$> Parsec.many1 Parsec.digit <?> "Repetition-range minimum")@@ -148,16 +148,16 @@ Just . read <$> Parsec.many1 Parsec.digit <?> "Repetition-range maximum" ) - Parsec.spaces >> return {-to GenParser-monad-} i+ Parsec.spaces >> return {-to ParsecT-monad-} i ) - return {-to GenParser-monad-} (fewest, most)+ return {-to ParsecT-monad-} (fewest, most) ) <?> "Repeatable.rangeDelimiters " ++ show rangeDelimiters ] - g <- Parsec.option True {-the default-} $ (Parsec.char nonGreedyToken <?> "Repeatable.nonGreedyToken " ++ show nonGreedyToken) >> return {-to GenParser-monad-} False+ g <- Parsec.option True {-the default-} $ (Parsec.char nonGreedyToken <?> "Repeatable.nonGreedyToken " ++ show nonGreedyToken) >> return {-to ParsecT-monad-} False - return {-to GenParser-monad-} repeatable { isGreedy = g } --Correct prior assumption.+ return {-to ParsecT-monad-} repeatable { isGreedy = g } --Correct prior assumption. instance Read a => Read (Repeatable a) where readsPrec _ s = case reads s {-first, read the base-type-} of