packages feed

regexdot 0.11.1.1 → 0.11.1.2

raw patch · 46 files changed

+3213/−3218 lines, 46 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- RegExDot.Anchor: instance Eq Anchor
- RegExDot.Anchor: instance NFData Anchor
- RegExDot.Anchor: instance Read Anchor
- RegExDot.Anchor: instance Show Anchor
- RegExDot.BracketExpressionMember: instance (ShortcutExpander m, Read m) => Read (Member m)
- RegExDot.BracketExpressionMember: instance Eq m => Eq (Member m)
- RegExDot.BracketExpressionMember: instance NFData m => NFData (Member m)
- RegExDot.BracketExpressionMember: instance Show m => Show (Member m)
- RegExDot.CompilationOptions: complyStrictlyWithPosix :: CompilationOptions -> Bool
- RegExDot.CompilationOptions: instance Defaultable CompilationOptions
- RegExDot.CompilationOptions: instance Eq CompilationOptions
- RegExDot.CompilationOptions: instance Options CompilationOptions
- RegExDot.CompilationOptions: instance Show CompilationOptions
- RegExDot.Consumer: instance Consumer c => Consumer [c]
- RegExDot.ConsumptionProfile: canConsumeAnything :: ConsumptionProfile -> Bool
- RegExDot.ConsumptionProfile: consumptionBounds :: ConsumptionProfile -> ConsumptionBounds
- RegExDot.ConsumptionProfile: hasSpecificRequirement :: ConsumptionProfile -> Bool
- RegExDot.ConsumptionProfile: instance Eq ConsumptionProfile
- RegExDot.ConsumptionProfile: instance Read ConsumptionProfile
- RegExDot.ConsumptionProfile: instance SelfValidator ConsumptionProfile
- RegExDot.ConsumptionProfile: instance Show ConsumptionProfile
- RegExDot.ExecutionOptions: abortTrialRepetitionsOnInherentFailure :: ExecutionOptions -> Bool
- RegExDot.ExecutionOptions: abortTrialRepetitionsOnZeroConsumption :: ExecutionOptions -> Bool
- RegExDot.ExecutionOptions: bypassInputDataForLiberalConsumer :: ExecutionOptions -> Bool
- RegExDot.ExecutionOptions: catchIncompatibleAnchors :: ExecutionOptions -> Bool
- RegExDot.ExecutionOptions: checkExistenceOfInelasticTail :: ExecutionOptions -> Bool
- RegExDot.ExecutionOptions: checkForUnconsumableData :: ExecutionOptions -> Bool
- RegExDot.ExecutionOptions: instance Defaultable ExecutionOptions
- RegExDot.ExecutionOptions: instance Eq ExecutionOptions
- RegExDot.ExecutionOptions: instance Options ExecutionOptions
- RegExDot.ExecutionOptions: instance Show ExecutionOptions
- RegExDot.ExecutionOptions: moderateGreed :: ExecutionOptions -> Bool
- RegExDot.ExecutionOptions: permitReorderingOfAlternatives :: ExecutionOptions -> Bool
- RegExDot.ExecutionOptions: preferAlternativesWhichFeedTheGreedy :: ExecutionOptions -> Bool
- RegExDot.ExecutionOptions: preferAlternativesWhichMimickUnrolling :: ExecutionOptions -> Bool
- RegExDot.ExecutionOptions: preferFewerRepeatedAlternatives :: ExecutionOptions -> Bool
- RegExDot.ExecutionOptions: requireMatchList :: ExecutionOptions -> Bool
- RegExDot.ExecutionOptions: unrollRepeatedSingletonAlternative :: ExecutionOptions -> Bool
- RegExDot.ExecutionOptions: useFirstMatchAmongAlternatives :: ExecutionOptions -> Bool
- RegExDot.ExecutionOptions: validateMinConsumptionOfAlternatives :: ExecutionOptions -> Bool
- RegExDot.InstanceInt: instance ShortcutExpander Int
- RegExDot.Meta: instance (ShortcutExpander m, Read m) => Read (Meta m)
- RegExDot.Meta: instance Consumer (Meta m)
- RegExDot.Meta: instance Eq m => Eq (Meta m)
- RegExDot.Meta: instance NFData m => NFData (Meta m)
- RegExDot.Meta: instance SelfValidator (Meta m)
- RegExDot.Meta: instance Show m => Show (Meta m)
- RegExDot.RegEx: bowAnchor :: ExtendedRegEx m -> Maybe Anchor
- RegExDot.RegEx: concatenation :: ExtendedRegEx m -> Concatenation m
- 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.RegEx: instance (ShortcutExpander m, ShortcutExpander m, Eq m, Read m) => Read (Alternatives m)
- RegExDot.RegEx: instance Consumer (Alternatives m)
- RegExDot.RegEx: instance Consumer (ExtendedRegEx m)
- RegExDot.RegEx: instance Consumer (Pattern m)
- RegExDot.RegEx: instance Eq m => Eq (Alternatives m)
- RegExDot.RegEx: instance Eq m => Eq (ExtendedRegEx m)
- RegExDot.RegEx: instance Eq m => Eq (Pattern m)
- RegExDot.RegEx: instance NFData m => NFData (Alternatives m)
- RegExDot.RegEx: instance NFData m => NFData (ExtendedRegEx m)
- RegExDot.RegEx: instance NFData m => NFData (Pattern m)
- RegExDot.RegEx: instance SelfValidator (Alternatives m)
- RegExDot.RegEx: instance SelfValidator (ExtendedRegEx m)
- RegExDot.RegEx: instance SelfValidator (Pattern m)
- RegExDot.RegEx: instance Show m => Show (Alternatives m)
- RegExDot.RegEx: instance Show m => Show (ExtendedRegEx m)
- RegExDot.RegEx: instance Show m => Show (Pattern m)
- RegExDot.RegEx: sternAnchor :: ExtendedRegEx m -> Maybe Anchor
- RegExDot.RegExOpts: compilationOptions :: RegExOpts a -> CompilationOptions
- RegExDot.RegExOpts: executionOptions :: RegExOpts a -> ExecutionOptions
- RegExDot.RegExOpts: instance Functor RegExOpts
- RegExDot.RegExOpts: instance Show a => Show (RegExOpts a)
- RegExDot.RegExOpts: regEx :: RegExOpts a -> a
- RegExDot.Repeatable: base :: Repeatable a -> a
- RegExDot.Repeatable: instance Consumer a => Consumer (Repeatable a)
- RegExDot.Repeatable: instance Eq a => Eq (Repeatable a)
- RegExDot.Repeatable: instance Functor Repeatable
- RegExDot.Repeatable: instance NFData a => NFData (Repeatable a)
- RegExDot.Repeatable: instance Read a => Read (Repeatable a)
- RegExDot.Repeatable: instance SelfValidator a => SelfValidator (Repeatable a)
- RegExDot.Repeatable: instance Show a => Show (Repeatable a)
- RegExDot.Repeatable: isGreedy :: Repeatable a -> Bool
- RegExDot.Repeatable: repetitionBounds :: Repeatable a -> RepetitionBounds
- RegExDot.ShowablePredicate: instance Eq (ShowablePredicate a)
- RegExDot.ShowablePredicate: instance NFData (ShowablePredicate a)
- RegExDot.ShowablePredicate: instance Show (ShowablePredicate a)
- RegExDot.ShowablePredicate: name :: ShowablePredicate a -> String
- RegExDot.ShowablePredicate: predicate :: ShowablePredicate a -> Predicate a
- RegExDot.Tree: instance Eq a => Eq (Tree a)
- RegExDot.Tree: instance Foldable Tree
- RegExDot.Tree: instance Functor Tree
- RegExDot.Tree: instance NFData a => NFData (Tree a)
- RegExDot.Tree: instance Read a => Read (Tree a)
- RegExDot.Tree: instance Show a => Show (Tree a)
+ RegExDot.Anchor: instance Control.DeepSeq.NFData RegExDot.Anchor.Anchor
+ RegExDot.Anchor: instance GHC.Classes.Eq RegExDot.Anchor.Anchor
+ RegExDot.Anchor: instance GHC.Read.Read RegExDot.Anchor.Anchor
+ RegExDot.Anchor: instance GHC.Show.Show RegExDot.Anchor.Anchor
+ RegExDot.BracketExpressionMember: instance (RegExDot.BracketExpressionMember.ShortcutExpander m, GHC.Read.Read m) => GHC.Read.Read (RegExDot.BracketExpressionMember.Member m)
+ RegExDot.BracketExpressionMember: instance Control.DeepSeq.NFData m => Control.DeepSeq.NFData (RegExDot.BracketExpressionMember.Member m)
+ RegExDot.BracketExpressionMember: instance GHC.Classes.Eq m => GHC.Classes.Eq (RegExDot.BracketExpressionMember.Member m)
+ RegExDot.BracketExpressionMember: instance GHC.Show.Show m => GHC.Show.Show (RegExDot.BracketExpressionMember.Member m)
+ RegExDot.CompilationOptions: [complyStrictlyWithPosix] :: CompilationOptions -> Bool
+ RegExDot.CompilationOptions: instance GHC.Classes.Eq RegExDot.CompilationOptions.CompilationOptions
+ RegExDot.CompilationOptions: instance GHC.Show.Show RegExDot.CompilationOptions.CompilationOptions
+ RegExDot.CompilationOptions: instance ToolShed.Defaultable.Defaultable RegExDot.CompilationOptions.CompilationOptions
+ RegExDot.CompilationOptions: instance ToolShed.Options.Options RegExDot.CompilationOptions.CompilationOptions
+ RegExDot.Consumer: instance RegExDot.Consumer.Consumer c => RegExDot.Consumer.Consumer [c]
+ RegExDot.ConsumptionProfile: [canConsumeAnything] :: ConsumptionProfile -> Bool
+ RegExDot.ConsumptionProfile: [consumptionBounds] :: ConsumptionProfile -> ConsumptionBounds
+ RegExDot.ConsumptionProfile: [hasSpecificRequirement] :: ConsumptionProfile -> Bool
+ RegExDot.ConsumptionProfile: instance GHC.Classes.Eq RegExDot.ConsumptionProfile.ConsumptionProfile
+ RegExDot.ConsumptionProfile: instance GHC.Read.Read RegExDot.ConsumptionProfile.ConsumptionProfile
+ RegExDot.ConsumptionProfile: instance GHC.Show.Show RegExDot.ConsumptionProfile.ConsumptionProfile
+ RegExDot.ConsumptionProfile: instance ToolShed.SelfValidate.SelfValidator RegExDot.ConsumptionProfile.ConsumptionProfile
+ RegExDot.ExecutionOptions: [abortTrialRepetitionsOnInherentFailure] :: ExecutionOptions -> Bool
+ RegExDot.ExecutionOptions: [abortTrialRepetitionsOnZeroConsumption] :: ExecutionOptions -> Bool
+ RegExDot.ExecutionOptions: [bypassInputDataForLiberalConsumer] :: ExecutionOptions -> Bool
+ RegExDot.ExecutionOptions: [catchIncompatibleAnchors] :: ExecutionOptions -> Bool
+ RegExDot.ExecutionOptions: [checkExistenceOfInelasticTail] :: ExecutionOptions -> Bool
+ RegExDot.ExecutionOptions: [checkForUnconsumableData] :: ExecutionOptions -> Bool
+ RegExDot.ExecutionOptions: [moderateGreed] :: ExecutionOptions -> Bool
+ RegExDot.ExecutionOptions: [permitReorderingOfAlternatives] :: ExecutionOptions -> Bool
+ RegExDot.ExecutionOptions: [preferAlternativesWhichFeedTheGreedy] :: ExecutionOptions -> Bool
+ RegExDot.ExecutionOptions: [preferAlternativesWhichMimickUnrolling] :: ExecutionOptions -> Bool
+ RegExDot.ExecutionOptions: [preferFewerRepeatedAlternatives] :: ExecutionOptions -> Bool
+ RegExDot.ExecutionOptions: [requireMatchList] :: ExecutionOptions -> Bool
+ RegExDot.ExecutionOptions: [unrollRepeatedSingletonAlternative] :: ExecutionOptions -> Bool
+ RegExDot.ExecutionOptions: [useFirstMatchAmongAlternatives] :: ExecutionOptions -> Bool
+ RegExDot.ExecutionOptions: [validateMinConsumptionOfAlternatives] :: ExecutionOptions -> Bool
+ RegExDot.ExecutionOptions: instance GHC.Classes.Eq RegExDot.ExecutionOptions.ExecutionOptions
+ RegExDot.ExecutionOptions: instance GHC.Show.Show RegExDot.ExecutionOptions.ExecutionOptions
+ RegExDot.ExecutionOptions: instance ToolShed.Defaultable.Defaultable RegExDot.ExecutionOptions.ExecutionOptions
+ RegExDot.ExecutionOptions: instance ToolShed.Options.Options RegExDot.ExecutionOptions.ExecutionOptions
+ RegExDot.InstanceInt: instance RegExDot.BracketExpressionMember.ShortcutExpander GHC.Types.Int
+ RegExDot.InstanceInt: instance RegExDot.Meta.ShortcutExpander GHC.Types.Int
+ RegExDot.InstanceInt: instance RegExDot.RegEx.ShortcutExpander GHC.Types.Int
+ RegExDot.Meta: instance (RegExDot.Meta.ShortcutExpander m, GHC.Read.Read m) => GHC.Read.Read (RegExDot.Meta.Meta m)
+ RegExDot.Meta: instance Control.DeepSeq.NFData m => Control.DeepSeq.NFData (RegExDot.Meta.Meta m)
+ RegExDot.Meta: instance GHC.Classes.Eq m => GHC.Classes.Eq (RegExDot.Meta.Meta m)
+ RegExDot.Meta: instance GHC.Show.Show m => GHC.Show.Show (RegExDot.Meta.Meta m)
+ RegExDot.Meta: instance RegExDot.Consumer.Consumer (RegExDot.Meta.Meta m)
+ RegExDot.Meta: instance ToolShed.SelfValidate.SelfValidator (RegExDot.Meta.Meta m)
+ RegExDot.RegEx: [bowAnchor] :: ExtendedRegEx m -> Maybe Anchor
+ RegExDot.RegEx: [concatenation] :: ExtendedRegEx m -> Concatenation m
+ RegExDot.RegEx: [sternAnchor] :: ExtendedRegEx m -> Maybe Anchor
+ RegExDot.RegEx: instance (GHC.Classes.Eq m, RegExDot.Meta.ShortcutExpander m, GHC.Read.Read m, RegExDot.RegEx.ShortcutExpander m) => GHC.Read.Read (RegExDot.RegEx.ExtendedRegEx m)
+ RegExDot.RegEx: instance (GHC.Classes.Eq m, RegExDot.Meta.ShortcutExpander m, GHC.Read.Read m, RegExDot.RegEx.ShortcutExpander m) => GHC.Read.Read (RegExDot.RegEx.Pattern m)
+ RegExDot.RegEx: instance (RegExDot.RegEx.ShortcutExpander m, RegExDot.Meta.ShortcutExpander m, GHC.Classes.Eq m, GHC.Read.Read m) => GHC.Read.Read (RegExDot.RegEx.Alternatives m)
+ RegExDot.RegEx: instance Control.DeepSeq.NFData m => Control.DeepSeq.NFData (RegExDot.RegEx.Alternatives m)
+ RegExDot.RegEx: instance Control.DeepSeq.NFData m => Control.DeepSeq.NFData (RegExDot.RegEx.ExtendedRegEx m)
+ RegExDot.RegEx: instance Control.DeepSeq.NFData m => Control.DeepSeq.NFData (RegExDot.RegEx.Pattern m)
+ RegExDot.RegEx: instance GHC.Classes.Eq m => GHC.Classes.Eq (RegExDot.RegEx.Alternatives m)
+ RegExDot.RegEx: instance GHC.Classes.Eq m => GHC.Classes.Eq (RegExDot.RegEx.ExtendedRegEx m)
+ RegExDot.RegEx: instance GHC.Classes.Eq m => GHC.Classes.Eq (RegExDot.RegEx.Pattern m)
+ RegExDot.RegEx: instance GHC.Show.Show m => GHC.Show.Show (RegExDot.RegEx.Alternatives m)
+ RegExDot.RegEx: instance GHC.Show.Show m => GHC.Show.Show (RegExDot.RegEx.ExtendedRegEx m)
+ RegExDot.RegEx: instance GHC.Show.Show m => GHC.Show.Show (RegExDot.RegEx.Pattern m)
+ RegExDot.RegEx: instance RegExDot.Consumer.Consumer (RegExDot.RegEx.Alternatives m)
+ RegExDot.RegEx: instance RegExDot.Consumer.Consumer (RegExDot.RegEx.ExtendedRegEx m)
+ RegExDot.RegEx: instance RegExDot.Consumer.Consumer (RegExDot.RegEx.Pattern m)
+ RegExDot.RegEx: instance ToolShed.SelfValidate.SelfValidator (RegExDot.RegEx.Alternatives m)
+ RegExDot.RegEx: instance ToolShed.SelfValidate.SelfValidator (RegExDot.RegEx.ExtendedRegEx m)
+ RegExDot.RegEx: instance ToolShed.SelfValidate.SelfValidator (RegExDot.RegEx.Pattern m)
+ RegExDot.RegExOpts: [compilationOptions] :: RegExOpts a -> CompilationOptions
+ RegExDot.RegExOpts: [executionOptions] :: RegExOpts a -> ExecutionOptions
+ RegExDot.RegExOpts: [regEx] :: RegExOpts a -> a
+ RegExDot.RegExOpts: instance GHC.Base.Functor RegExDot.RegExOpts.RegExOpts
+ RegExDot.RegExOpts: instance GHC.Show.Show a => GHC.Show.Show (RegExDot.RegExOpts.RegExOpts a)
+ RegExDot.Repeatable: [base] :: Repeatable a -> a
+ RegExDot.Repeatable: [isGreedy] :: Repeatable a -> Bool
+ RegExDot.Repeatable: [repetitionBounds] :: Repeatable a -> RepetitionBounds
+ RegExDot.Repeatable: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (RegExDot.Repeatable.Repeatable a)
+ RegExDot.Repeatable: instance GHC.Base.Functor RegExDot.Repeatable.Repeatable
+ RegExDot.Repeatable: instance GHC.Classes.Eq a => GHC.Classes.Eq (RegExDot.Repeatable.Repeatable a)
+ RegExDot.Repeatable: instance GHC.Read.Read a => GHC.Read.Read (RegExDot.Repeatable.Repeatable a)
+ RegExDot.Repeatable: instance GHC.Show.Show a => GHC.Show.Show (RegExDot.Repeatable.Repeatable a)
+ RegExDot.Repeatable: instance RegExDot.Consumer.Consumer a => RegExDot.Consumer.Consumer (RegExDot.Repeatable.Repeatable a)
+ RegExDot.Repeatable: instance ToolShed.SelfValidate.SelfValidator a => ToolShed.SelfValidate.SelfValidator (RegExDot.Repeatable.Repeatable a)
+ RegExDot.ShowablePredicate: [name] :: ShowablePredicate a -> String
+ RegExDot.ShowablePredicate: [predicate] :: ShowablePredicate a -> Predicate a
+ RegExDot.ShowablePredicate: instance Control.DeepSeq.NFData (RegExDot.ShowablePredicate.ShowablePredicate a)
+ RegExDot.ShowablePredicate: instance GHC.Classes.Eq (RegExDot.ShowablePredicate.ShowablePredicate a)
+ RegExDot.ShowablePredicate: instance GHC.Show.Show (RegExDot.ShowablePredicate.ShowablePredicate a)
+ RegExDot.Tree: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (RegExDot.Tree.Tree a)
+ RegExDot.Tree: instance Data.Foldable.Foldable RegExDot.Tree.Tree
+ RegExDot.Tree: instance GHC.Base.Functor RegExDot.Tree.Tree
+ RegExDot.Tree: instance GHC.Classes.Eq a => GHC.Classes.Eq (RegExDot.Tree.Tree a)
+ RegExDot.Tree: instance GHC.Read.Read a => GHC.Read.Read (RegExDot.Tree.Tree a)
+ RegExDot.Tree: instance GHC.Show.Show a => GHC.Show.Show (RegExDot.Tree.Tree a)

Files

+ README.markdown view
@@ -0,0 +1,23 @@+# **RegExDot**.++This is **RegExDot**, a polymorphic regex-engine.++## Installation++It can be built and installed using [Cabal](https://www.haskell.org/cabal/users-guide/installing-packages.html).++## Documentation++More information about this library can be found at [RegExDot](http://functionalley.eu/RegExDot/regExDot.html).++## License++For information on copying and distributing this package, see the file **LICENSE** in this directory.++## Bug-reporting++Bug-reports should be emailed to <regexdot *at* functionalley *dot* eu>.++## Author++This library is written and maintained by Dr. Alistair Ward.
− changelog
@@ -1,47 +0,0 @@-2010-11-18 Dr. Alistair Ward <regexdot at functionalley dot eu>--0.9.0.0-	* First version of the package.-0.10.0.0-	* Reacted to major-number changes in the package 'ToolShed-0.10.0.0'.-	* Created "src/RegExDot/" sub-directory & then modified module-names accordingly.-	* Pacified 'hlint'.-	* Added a makefile.-	* Removed awkward unary operators from 'Repeatable.hs'.-	* Qualified identifiers used in error-messages.-0.10.0.1-	* Ported to ghc-7.0.1:-		Used definitions of 'NFData' & 'rnf' from new package 'deepseq', rather than 'parallel'.-		Replaced reference to the deprecated function 'Control.Parallel.Strategies.parFlatMap' with @concat . Control.Parallel.Strategies.parMap@.-0.10.1.0-	* Set @threaded@ flag to @True@ by default.-	* Replaced use of @threaded@-flag in source-code, with CABAL CPP-macro.-	* Relocated "RegExDot.Options" to "ToolShed.Options".-0.10.2.0-	* Renamed package from 'RegExDot' to 'regexdot', for compatibility with Debian's ".deb"-format.-0.10.2.1-	* Added manually controlled "llvm" flag to "regexdot.cabal".-	* Changed identifier for type-parameters, to better reflect its role.-	* Reacted to the creation of module "ToolShed.Defaultable".-	* Uploaded to <http://hackage.haskell.org/packages/hackage.html>.-0.10.2.2-	* Amended 'regexdot.cabal' to more correctly specify dependency on package 'toolshed'.-	* Used new module "ToolShed.Pair" from package toolshed-0.12.0.0.-	* Guarded 'eager-blackholing' flag in cabal file.-0.11.0.0-	* Replaced '(+ 1)' and '(- 1)' with the faster calls 'succ' and 'pred', in "RegExDot.RegEx".-	* Reacted to new module-hierarchy, creation of new module "ToolShed.Data.List.Splits" and addition of method 'ToolShed.SelfValidate.getErrors', in toolshed-0.13.0.0, and used it to improved error-reporting in @instance Read RegExDot.RegEx.ExtendedRegEx@.-	* Minor reworking of 'RegExDot.Repeatable.repeatableParser'.-	* 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'.-0.11.1.1-	* Tested with 'haskell-platform-2013.2.0.0'.-	* Replaced preprocessor-directives with 'build-depends' constraints in 'regexdot.cabal'.-	* In function 'RegExDot.RegEx.findMatch.findMatchSlave.matchPairList', changed 'fromIntegral' (which required a type-signature) to 'toRational'.-	* Either replaced instances of '(<$>)' with 'fmap' to avoid ambiguity between "Control.Applicative" & "Prelude" which (from 'base-4.8') also exports this symbol, or hid the symbol when importing the "Prelude"..
+ changelog.markdown view
@@ -0,0 +1,53 @@+# 2010-11-18 Dr. Alistair Ward <regexdot at functionalley dot eu>++## 0.9.0.0+	* First version of the package.+## 0.10.0.0+	* Reacted to major-number changes in the package **ToolShed-0.10.0.0**.+	* Created **src/RegExDot/** sub-directory & then modified module-names accordingly.+	* Pacified **hlint**.+	* Added a makefile.+	* Removed awkward unary operators from **Repeatable.hs**.+	* Qualified identifiers used in error-messages.+## 0.10.0.1+	* Ported to ghc-7.0.1:+		Used definitions of `NFData` & `rnf` from new package **deepseq**, rather than **parallel**.+		Replaced reference to the deprecated function `Control.Parallel.Strategies.parFlatMap` with `concat . Control.Parallel.Strategies.parMap`.+## 0.10.1.0+	* Set @threaded@ flag to @True@ by default.+	* Replaced use of @threaded@-flag in source-code, with CABAL CPP-macro.+	* Relocated **RegExDot.Options** to **ToolShed.Options**.+## 0.10.2.0+	* Renamed package from **RegExDot** to **regexdot**, for compatibility with Debian's *.deb*-format.+## 0.10.2.1+	* Added manually controlled **llvm** flag to the *.cabal*-file.+	* Changed identifier for type-parameters, to better reflect its role.+	* Reacted to the creation of module **ToolShed.Defaultable**.+	* Uploaded to [Hackage](http://hackage.haskell.org/packages/hackage.html).+## 0.10.2.2+	* Amended the *.cabal*-file to more correctly specify dependency on package **toolshed**.+	* Used new module **ToolShed.Pair** from package **toolshed-0.12.0.0**.+	* Guarded **eager-blackholing** flag in the *.cabal*-file.+## 0.11.0.0+	* Replaced `(+ 1)` and `(- 1)` with the faster calls `succ` and `pred`, in **RegExDot.RegEx**.+	* Reacted to new module-hierarchy, creation of new module **ToolShed.Data.List.Splits** and addition of method `ToolShed.SelfValidate.getErrors`, in **toolshed-0.13.0.0**, and used it to improved error-reporting in `instance Read RegExDot.RegEx.ExtendedRegEx`.+	* Minor reworking of `RegExDot.Repeatable.repeatableParser`.+	* 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`.+## 0.11.1.1+	* Tested with **haskell-platform-2013.2.0.0**.+	* Replaced preprocessor-directives with **build-depends** constraints in the *.cabal*-file.+	* In function `RegExDot.RegEx.findMatch.findMatchSlave.matchPairList`, changed `fromIntegral` (which required a type-signature) to `toRational`.+	* Either replaced instances of `(<$>)` with `fmap` to avoid ambiguity between **Control.Applicative** & **Prelude** which (from **base-4.8**) also exports this symbol, or hid the symbol when importing the **Prelude**..+## 0.11.1.2+	* Added **Default-language**-specification to the *.cabal*-file.+	* Added file **README.markdown**.+	* Converted this file to markdown-format.+	* Renamed directory **src/** to **src-lib/** for consistency with other packages. +	* Used **CPP** to control the import of symbols from **Control.Applicative**.
@@ -5,7 +5,7 @@ 	Copyright (C) 2010-2011 Dr. Alistair Ward. All Rights Reserved.  Home-page:-	http://functionalley.eu+	http://functionalley.eu/RegExDot/regExDot.html  License: 	GNU GENERAL PUBLIC LICENSE Version 3; see '/usr/share/common-licenses/GPL-3' or '/usr/share/doc/licenses/gpl-3.0.txt' where available, or the local packaged file 'LICENSE'.
− makefile
@@ -1,59 +0,0 @@-# Copyright (C) 2010-2014 Dr. Alistair Ward-#-# This program is free software: you can redistribute it and/or modify-# it under the terms of the GNU General Public License as published by-# the Free Software Foundation, either version 3 of the License, or-# (at your option) any later version.-#-# This program is distributed in the hope that it will be useful,-# but WITHOUT ANY WARRANTY; without even the implied warranty of-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-# GNU General Public License for more details.-#-# You should have received a copy of the GNU General Public License-# along with this program.  If not, see <http://www.gnu.org/licenses/>.--.PHONY: all build check clean configure copy haddock help hlint install prof sdist--all: install--install: build haddock-	@[ -z "$$CABAL_INSTALL_OPTIONS" ] || echo "INFO: CABAL_INSTALL_OPTIONS='$$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 $@ $$CABAL_COPY_OPTIONS--build: configure-	@[ -z "$$CABAL_BUILD_OPTIONS" ] || echo "INFO: CABAL_BUILD_OPTIONS='$$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 $@ $$CABAL_CONFIGURE_OPTIONS	#--user--haddock: configure-	PATH=~/.cabal/bin:$$PATH runhaskell Setup $@ --hyperlink-source	#Amend path to find 'HsColour', as required for 'hyperlink-source'.--hlint:-	@$@ -i 'Use &&' src/ -i 'Use ||' +RTS -N--sdist:-	TAR_OPTIONS='--format=ustar' runhaskell Setup $@--check: sdist-	cabal upload --check --verbose=3 dist/*.tar.gz;--clean:-	runhaskell Setup $@-	find src -type f \( -name '*.hc' -o -name '*.hcr' -o -name '*.hi' -o -name '*.o' \) -delete--help:-	@grep '^[a-zA-Z].*:' makefile | sed -e 's/:.*//'--rpm: regexdot.rpm build-
regexdot.cabal view
@@ -1,30 +1,51 @@--- Package-properties-Name:			regexdot-Version:		0.11.1.1-Cabal-Version:		>= 1.6-Copyright:		(C) 2010-2015 Dr. Alistair Ward-License:		GPL-License-file:		LICENSE-Author:			Dr. Alistair Ward-Stability:		Unstable interface, incomplete features.-Synopsis:		A polymorphic, POSIX, extended regex-engine.-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, GHC == 7.4, GHC == 7.6, GHC == 7.10-Homepage:		http://functionalley.eu-Maintainer:		regexdot <at> functionalley <dot> eu-Bug-reports:		regexdot <at> functionalley <dot> eu-Extra-Source-Files:	changelog, copyright, makefile+-- This file is part of RegExDot.+--+-- RegExDot is free software: you can redistribute it and/or modify+-- it under the terms of the GNU General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- RegExDot is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+-- GNU General Public License for more details.+--+-- You should have received a copy of the GNU General Public License+-- along with RegExDot.  If not, see <http://www.gnu.org/licenses/>. --- Turn on using: 'runhaskell ./Setup.hs configure -f llvm'.+Name:		regexdot+Version:	0.11.1.2+Cabal-version:	>= 1.10+Copyright:	(C) 2010-2015 Dr. Alistair Ward+License:	GPL+License-file:	LICENSE+Author:		Dr. Alistair Ward+Stability:	stable+Synopsis:	A polymorphic, POSIX, extended regex-engine.+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, GHC == 7.4, GHC == 7.6, GHC == 7.8, GHC == 7.10+Homepage:	http://functionalley.eu/RegExDot/regExDot.html+Maintainer:	mailto <colon> regexdot <at> functionalley <dot> eu+Bug-reports:	mailto <colon> regexdot <at> functionalley <dot> eu++-- None of these files are needed at run-time.+Extra-source-files:+    changelog.markdown+    copyright+    README.markdown++-- Enable using: 'cabal configure -f llvm'. flag llvm     Description:	Whether the 'llvm' compiler-backend has been installed and is required for code-generation.-    manual:		True-    default:		False+    Manual:		True+    Default:		False  Library-    hs-source-dirs:	src+    Default-language:	Haskell2010+    GHC-options:	-Wall -O2 -fno-warn-tabs+    Hs-source-dirs:	src-lib      Exposed-modules:         RegExDot.Anchor@@ -54,8 +75,6 @@         parallel >= 3.0,         parsec == 3.*,         toolshed >= 0.13--    GHC-options:	-Wall -O2 -fno-warn-tabs      if impl(ghc >= 7.4.1)         GHC-prof-options:	-prof -fprof-auto -fprof-cafs
+ src-lib/RegExDot/Anchor.hs view
@@ -0,0 +1,90 @@+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]++	* A type of /zero-width assertion/:+	the 'Bow'-predicate is 'True' if no input-data has yet been consumed; the 'Stern'-predicate is 'True' if no input-data remains to be consumed.++	* Whilst at the top-level of the regex, the /absence/ of an anchor is equivalent to a non-capturing @.*?@,+	tempting one to implement them using this property,+	it doesn't apply to those anchors encountered at either end of a sub-expression.+	So in all cases, an anchor constrains the permissible consumption of input-data,+	but at the top level it also prevents the regex drifting away from either end of the input-data.+-}++module RegExDot.Anchor(+-- * Types+-- ** Data-types+	Anchor(..),+-- ** Type-synonyms+	AnchorSpecification,+-- * Constants+	bowToken,+	sternToken,+	tokens,+	unanchored+) where++import qualified	Control.DeepSeq++-- | Defines the types on /anchor/ by which a /regex/ can be moored to a part of the input-data.+data Anchor =+	Bow	-- ^ Matches only if no input data has yet been consumed. Can only exist at the start of the entire regex, or (in theory) the start of any /alternative/.+	| Stern	-- ^ Matches only if no input data remains to be consumed. Can only exist at the end of the entire regex, or (in theory) the end of any /alternative/.+	deriving (+		Eq+--		Read,	-- See specialisation below.+--		Show	-- See specialisation below.+	)++instance Show Anchor	where+	showsPrec _ Bow		= showChar bowToken+	showsPrec _ Stern	= showChar sternToken++instance Read Anchor	where+	readsPrec _ []		= []	-- No parse.+	readsPrec _ (' ' : s)	= reads s	-- Consume white-space.+	readsPrec _ ('\t' : s)	= reads s	-- Consume white-space.+	readsPrec _ (c : s)	= case c `lookup` [(bowToken, Bow), (sternToken, Stern)] of+		Just anchor	-> [(anchor, s)]+		_		-> []	-- No parse.++instance Control.DeepSeq.NFData Anchor	where+	rnf _	= ()++-- | The conventional token used to denote a 'Bow'-anchor, when in 'String'-form.+bowToken :: Char+bowToken	= '^'++-- | The conventional token used to denote a 'Stern'-anchor, when in 'String'-form.+sternToken :: Char+sternToken	= '$'++-- | 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-lib/RegExDot/BracketExpression.hs view
@@ -0,0 +1,63 @@+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]++	This concept was traditionally known as a /Character-class/,+	but to distinguish it from the more recently introduced & narrower /POSIX Character-class/, it has been renamed /Bracket-expression/.+-}++module RegExDot.BracketExpression(+-- * Types+-- ** Type-synonyms+	BracketExpression,+-- * Constants+	delimiterTokens,+	negationToken,+	tokens,+-- * Functions+-- ** Predicates+	containsMatch+) where++import			RegExDot.BracketExpressionMember((=~))+import qualified	RegExDot.BracketExpressionMember	as BracketExpressionMember++-- | A /Bracket-expression/ consists of a list of 'BracketExpressionMember.Member's.+type BracketExpression m	= [BracketExpressionMember.Member m]++-- | True if the specified datum matches any 'BracketExpressionMember.Member' of the 'BracketExpression'.+containsMatch :: Eq m+	=> m			-- ^ The input datum.+	-> BracketExpression m	-- ^ The list of bracket-expression members within which to find a match.+	-> Bool+containsMatch datum	= any (datum =~)++-- | The delimiters of a /bracket-expression/, when in 'String'-form.+delimiterTokens :: (Char, Char)+delimiterTokens	= ('[', ']')++-- | Used to denote a negated /bracket-expression/, when in 'String'-form.+negationToken :: Char+negationToken	= '^'++-- | The set of 'Char' to which a specific meaning is attributed, when reading from 'String'.+tokens :: String+tokens	= [fst delimiterTokens, snd delimiterTokens, negationToken]+
+ src-lib/RegExDot/BracketExpressionMember.hs view
@@ -0,0 +1,93 @@+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]++	Permits /Perl-style shortcut/s to be canned & assigned a single-'Char' mnemonic for subsequent reference;+	the implementation of 'Read' looks for a back-slashed 'Char', for which it expects there to be a corresponding canned 'ShowablePredicate.ShowablePredicate'.++ [@CAVEATS@]++	Since the underlying polymorphic data-type isn't required to implement neither 'Enum' nor 'Ord', the implementation of 'Read' can't cope with range-specifications.+	Lacking this, Bracket-expression members must be enumerated exhaustively.+-}++module RegExDot.BracketExpressionMember(+-- * Type-classes+	ShortcutExpander(..),+-- * Types+-- ** Data-types+	Member(..),+-- * Functions+-- ** Operators+	(=~)+) where++import qualified	Control.Arrow+import qualified	Control.DeepSeq+import qualified	RegExDot.ShowablePredicate	as ShowablePredicate++infix 4 =~	-- Same as (==).++{- |+	* The interface via which /Perl-style shortcut/s are expanded (when they occur within a /bracket-expression/), in a manner appropriate to the chosen type-parameter.++	* The expansion of /Perl-style shortcut/s, is more restricted inside than outside, a /bracket-expression/,+	& consequently are merely represented here by a 'ShowablePredicate.ShowablePredicate', rather than providing a more general form suitable also for those /Perl-style shortcuts/ found outside /bracket-expression/s.++	* This interface is implemented elsewhere, where the specific type-parameter & consequently the appropriate set of /Perl-style shortcut/s, are defined.+-}+class ShortcutExpander a	where+	findPredicate	:: Char -> Maybe (ShowablePredicate.ShowablePredicate a)	-- ^ Attempt to find the appropriate 'ShowablePredicate.ShowablePredicate' to implement this /Perl-style shortcut/.++{- |+	* A /BracketExpression/ can contain either a literal, a range of literals given @(Enum a, Ord a)@, a /Perl-style shortcut/, or when 'Char' is the type-parameter, a /POSIX Character-class/.++	* This data-type reduces the representation of all these possibilities to either a predicate or a literal.+-}+data Member m	=+	Predicate (ShowablePredicate.ShowablePredicate m)	-- ^ This 'Member' is described using a /predicate/, which is run to determine whether the datum conforms & is a member of the "BracketExpression".+	| Literal m						-- ^ This 'Member' is defined literally, using an item of the polymorphic type.+	deriving Eq++instance Show m => Show (Member m)	where+	showsPrec _ (Predicate showablePredicate)	= shows showablePredicate+	showsPrec _ (Literal literal)			= shows literal++instance (ShortcutExpander m, Read m) => Read (Member m)	where+	readsPrec _ []				= []		-- No parse.+	readsPrec _ (' ' : s)			= reads s	-- Consume white-space.+	readsPrec _ ('\t' : s)			= reads s	-- Consume white-space.+	readsPrec _ ('\\' : shortcut : s)	= case findPredicate shortcut of+		Just showablePredicate	-> [(Predicate showablePredicate, s)]+		_			-> error $ "readsPrec RegExDot.BracketExpressionMember.Member:\tfindPredicate failed for shortcut " ++ show shortcut+	readsPrec _ literal			= Control.Arrow.first Literal `map` reads literal++instance Control.DeepSeq.NFData m => Control.DeepSeq.NFData (Member m)	where+	rnf (Predicate showablePredicate)	= Control.DeepSeq.rnf showablePredicate+	rnf (Literal literal)			= Control.DeepSeq.rnf literal++-- | Match-operator.+(=~) :: Eq m+	=> m		-- ^ The input datum.+	-> Member m	-- ^ The member of the bracket-expression against which the input-datum is to be matched.+	-> Bool		-- ^ The result of the match-operation.+datum =~ Predicate showablePredicate	= ShowablePredicate.predicate showablePredicate datum+datum =~ Literal literal		= datum == literal+
+ src-lib/RegExDot/CompilationOptions.hs view
@@ -0,0 +1,48 @@+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]++	* Defines those options relating to compile-time.++	* These tend to be options which affect the result, rather than the means by which it is achieved.++	* Whilst similar structures are present in other regex-implementations, there's no standardisation of the fields.+-}++module RegExDot.CompilationOptions(+-- * Types+-- ** Data-types+	CompilationOptions(..)+) where++import qualified	ToolShed.Defaultable+import qualified	ToolShed.Options++-- | The switch(es) used to control compilation of the /regex/-engine.+data CompilationOptions	= MkCompilationOptions {+	complyStrictlyWithPosix	:: Bool		-- ^ Define the offset of captured data, corresponding to a sub-expression which matched zero times, as the artificial value @-1@ specified by POSIX.+} deriving (Eq, Show)++instance ToolShed.Defaultable.Defaultable CompilationOptions	where+	defaultValue	= ToolShed.Options.blankValue { complyStrictlyWithPosix	= True }++instance ToolShed.Options.Options CompilationOptions	where+	blankValue	= MkCompilationOptions { complyStrictlyWithPosix = undefined }+
+ src-lib/RegExDot/Consumer.hs view
@@ -0,0 +1,105 @@+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]++	* Define the requirements & capabilities of a data-type which is designed to consume data.++	* This module also facilitates investigation of group-behaviour, by defining operations on either concatenations or alternations of 'Consumer's.++	* This module is specifically for use in a /regex/-engine,+	in which the meta-data from which the /regex/ is composed, are considered to /consume/ input data,+	whilst concatenation & alternation of such elements, behave as a different types of groups of consumer.+-}++module RegExDot.Consumer(+-- * Type-classes+	Consumer(..),+-- * Types+-- ** Type-synonyms+--	StarHeight,+-- * Functions+	accumulateConsumptionProfiles,+--	accumulateConsumptionProfilesFrom,+	aggregateConsumptionProfilesFromAlternatives,+	aggregateConsumptionProfilesFromConcatenation,+-- ** Query+	getConsumptionBounds,+	getFewest,+	getHasSpecificRequirement+) where++import			RegExDot.ConsumptionProfile((<>), (|+|))+import qualified	Data.List+import qualified	RegExDot.ConsumptionBounds	as ConsumptionBounds+import qualified	RegExDot.ConsumptionProfile	as ConsumptionProfile++{- |+	* A measure of the complexity of a /regex/, which has some baring on either the time-complexity or the space-complexity of the solution.++	* <http://en.wikipedia.org/wiki/Star_height>+-}+type StarHeight	= Int++{- |+	* The interface, to which types, which have a data-requirement, may conform.++	* Regrettably, methods requiring reference to the type of the consumable, would require multi-parameter type-classes.++	* 'consumptionProfile' defines the quantity-range of data which may be consumed, & any specific data-requirements.++	* 'starHeight' measures the complexity of the 'Consumer'.+-}+class Consumer c	where+	consumptionProfile	:: c -> ConsumptionProfile.ConsumptionProfile+	starHeight		:: c -> StarHeight++instance Consumer c => Consumer [c] where+	consumptionProfile	= undefined+	starHeight		= Data.List.foldl' (\l -> max l . starHeight) 0++-- | Convenience-function, to query the 'ConsumptionBounds.ConsumptionBounds' of a 'Consumer'.+getConsumptionBounds :: Consumer c => c -> ConsumptionBounds.ConsumptionBounds+getConsumptionBounds	= ConsumptionProfile.consumptionBounds . consumptionProfile++-- | Determine the minimum acceptable quantity of data.+getFewest :: Consumer c => c -> ConsumptionBounds.DataLength+getFewest	= fst . getConsumptionBounds++-- | Determine whether the specified 'Consumer', has a specific requirement.+getHasSpecificRequirement :: Consumer c => c -> Bool+getHasSpecificRequirement	= ConsumptionProfile.hasSpecificRequirement . consumptionProfile++-- | The aggregate of the specified alternation of 'ConsumptionProfile.ConsumptionProfile's.+aggregateConsumptionProfilesFromAlternatives :: Consumer c => [c] -> ConsumptionProfile.ConsumptionProfile+aggregateConsumptionProfilesFromAlternatives []	= error "RegExDot.Consumer.aggregateConsumptionProfilesFromAlternatives:\tnull list"+aggregateConsumptionProfilesFromAlternatives l	= Data.List.foldl1' (<>) $ consumptionProfile `map` l++-- | Get the 'ConsumptionProfile.ConsumptionProfile's for the specified list of 'Consumer's, then find the net effect of concatenating them.+aggregateConsumptionProfilesFromConcatenation :: Consumer c => [c] -> ConsumptionProfile.ConsumptionProfile+aggregateConsumptionProfilesFromConcatenation	= Data.List.foldl' (\acc -> (acc |+|) . consumptionProfile) ConsumptionProfile.zero {-initial value-}++-- | Get the 'ConsumptionProfile.ConsumptionProfile' for the specified list of 'Consumer's, then accumulate them, from the specified initial value.+accumulateConsumptionProfilesFrom :: Consumer c => ConsumptionProfile.ConsumptionProfile -> [c] -> ConsumptionProfile.AccumulatedConsumptionProfiles+accumulateConsumptionProfilesFrom	= scanr ((|+|) . consumptionProfile)++-- | Get the 'ConsumptionProfile.ConsumptionProfile' for the specified list of 'Consumer's, then accumulate them.+accumulateConsumptionProfiles :: Consumer c => [c] -> ConsumptionProfile.AccumulatedConsumptionProfiles+accumulateConsumptionProfiles	= {-init .-} accumulateConsumptionProfilesFrom ConsumptionProfile.zero	-- It's useful to leave the initial value at the end of the list.+
+ src-lib/RegExDot/ConsumptionBounds.hs view
@@ -0,0 +1,48 @@+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]	Describes the bounds of data-consumption.+-}++module RegExDot.ConsumptionBounds(+-- * Types+-- ** Type-synonyms+	ConsumptionBounds,+	DataLength,+-- * Constants+	zero,+-- * Functions+-- ** Predicates+	isPrecise+) where++-- | A measure of the quantity of an unspecified type of data.+type DataLength	= Int++-- | The minimum & maximum bounds of potential data-consumption.+type ConsumptionBounds	= (DataLength, Maybe DataLength)++-- | Exactly zero consumption.+zero :: ConsumptionBounds+zero	= (0, Just 0)++-- | Predicate which is 'True' if only a precise quantity of data is consumable; no more, no less.+isPrecise :: ConsumptionBounds -> Bool+isPrecise (fewest, most)	= Just fewest == most+
+ src-lib/RegExDot/ConsumptionProfile.hs view
@@ -0,0 +1,151 @@+{-# LANGUAGE CPP #-}+{-+	Copyright (C) 2010-2015 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]++	A 'RegExDot.ConsumptionProfile' is composed from both a capacity to consume, & an ability to discriminate based on the specific data.+	The former is described by 'consumptionBounds' & the latter by both 'hasSpecificRequirement' & 'canConsumeAnything'.+-}++module RegExDot.ConsumptionProfile(+-- * Types+-- ** Type-synonyms+	AccumulatedConsumptionProfiles,+-- ** Data-types+	ConsumptionProfile(..),+-- * Constants+	zero,+-- * Functions+	accumulateFrom,+	aggregateFromConcatenation,+-- ** Operators+	(|+|),+	(<>),+-- ** Predicates+--	isPrecise,+	withinConsumptionBounds+) where++import			Control.Arrow((***))+import qualified	Data.List+import qualified	Data.Maybe+import qualified	RegExDot.ConsumptionBounds	as ConsumptionBounds+import qualified	ToolShed.SelfValidate++#if !MIN_VERSION_base(4,8,0)+import	Control.Applicative((<$>), (<*>))+#endif++infixr 5 |+|	-- Same as (++).+infixr 2 <>	-- Same as (||).++{- |+	* A 'Consumer' is considered to have a 'ConsumptionProfile' composed from both a capacity to consume, & an ability to discriminate.++	* Whilst 'hasSpecificRequirement' & 'canConsumeAnything' look like opposites, they can assume independent values; the instance can be both, but only be neither when empty.+-}+data ConsumptionProfile	= MkConsumptionProfile {+	consumptionBounds	:: ConsumptionBounds.ConsumptionBounds,	-- ^ The permissible quantity of data, which can be consumed.+	hasSpecificRequirement	:: Bool,				-- ^ Whether at least one specific input datum is required.+	canConsumeAnything	:: Bool					-- ^ Whether at least one arbitrary input datum can be consumed.+} deriving (Eq, Read, Show)++instance ToolShed.SelfValidate.SelfValidator ConsumptionProfile	where+	getErrors c@(+		MkConsumptionProfile {+			consumptionBounds	= (fewest, most),+			hasSpecificRequirement	= hasSpecificRequirement',+			canConsumeAnything	= canConsumeAnything'+		}+	 ) = ToolShed.SelfValidate.extractErrors [+		(fewest < 0, "Negative fewest=" ++ show fewest ++ "."),+		(+			case most of+				Just m	-> m < fewest+				_	-> False,+			"Invalid 'most'; " ++ show c ++ "."+		),+		(not $ or [c == zero, hasSpecificRequirement', canConsumeAnything'], "Invalid " ++ show c ++ ".")+	 ]++{-+-- | True if there's no choice in the quantity of data to consume.+isPrecise :: ConsumptionProfile -> Bool+isPrecise	= ConsumptionBounds.isPrecise . consumptionBounds+-}++-- | Predicate, which is 'True' if the specified data-length, falls within the specified 'ConsumptionBounds.ConsumptionBounds'.+withinConsumptionBounds+	:: ConsumptionBounds.DataLength	-- ^ The actual quantity of data consumed.+	-> ConsumptionProfile		-- ^ The bounds within which data-consumption is required to fall.+	-> Bool+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+zero	= MkConsumptionProfile {+	consumptionBounds	= ConsumptionBounds.zero,+	hasSpecificRequirement	= False,+	canConsumeAnything	= False+}++-- | The net effect of two concatenated 'ConsumptionProfile's.+(|+|) :: ConsumptionProfile -> ConsumptionProfile -> ConsumptionProfile+MkConsumptionProfile {+	consumptionBounds	= (lf, ls),+	hasSpecificRequirement	= lh,+	canConsumeAnything	= lc+} |+| MkConsumptionProfile {+	consumptionBounds	= (rf, rs),+	hasSpecificRequirement	= rh,+	canConsumeAnything	= rc+} = MkConsumptionProfile {+	consumptionBounds	= (lf + rf, (+) <$> ls <*> rs),	-- The sum of those of the concatenation.+	hasSpecificRequirement	= lh || rh,			-- The concatenation mandates consumption of at least one specific input datum, if either 'ConsumptionProfile' does.+	canConsumeAnything	= lc || rc			-- The concatenation can consume at least one arbitrary input datum, if either 'ConsumptionProfile' can.+}++-- | The net effect of two alternative 'ConsumptionProfile's.+(<>) :: ConsumptionProfile -> ConsumptionProfile -> ConsumptionProfile+MkConsumptionProfile {+	consumptionBounds	= (lf, ls),+	hasSpecificRequirement	= lh,+	canConsumeAnything	= lc+} <> MkConsumptionProfile {+	consumptionBounds	= (rf, rs),+	hasSpecificRequirement	= rh,+	canConsumeAnything	= rc+} = MkConsumptionProfile {+	consumptionBounds	= (lf `min` rf, max <$> ls <*> rs),	-- Stretched to envelope alternatives.+	hasSpecificRequirement	= lh && rh,				-- The alternation mandates consumption of at least one specific input datum, if both 'ConsumptionProfile's do.+	canConsumeAnything	= lc || rc				-- The alternation can consume at least one arbitrary input datum, if either 'ConsumptionProfile' can.+}++-- | The aggregate of the specified concatenation of 'ConsumptionProfile's.+aggregateFromConcatenation :: [ConsumptionProfile] -> ConsumptionProfile+aggregateFromConcatenation	= Data.List.foldl' (|+|) zero {-initial value-}++-- | The 'ConsumptionProfile's of all the aggregated tails of a list of 'Consumer's.+type AccumulatedConsumptionProfiles	= [ConsumptionProfile]++-- | Accumulate the specified list of 'ConsumptionProfile's, from the specified initial value.+accumulateFrom :: ConsumptionProfile -> [ConsumptionProfile] -> AccumulatedConsumptionProfiles+accumulateFrom	= scanr (|+|)+
+ src-lib/RegExDot/DSL.hs view
@@ -0,0 +1,117 @@+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]++	* Defines /binary/ operators, to form a /Domain-specific Language/, by which to compose 'RegEx.Concatenations' from 'RegEx.Pattern's.++	* Each operator quantifies the specified 'RegEx.Pattern', & prepends it to the specified 'RegEx.Concatenation'.++ [@TODO@]	Could @[]@ & 'Meta' be instances of a type-class which has methods (-:) etc.,+		allowing the former to prepend 'RegEx.captureGroup' & the latter 'RegEx.Require' ?++-}++module RegExDot.DSL(+-- * Functions+-- ** Operators+	(-:),+	(?:),+	(??:),+	(*:),+	(*?:),+	(+:),+	(+?:),+	( #->#:),+	( #->#?:),+	( #->:),+	( #->?:),+	( #:),+	(<~>)+) where++import			RegExDot.Repeatable((^#->#), (^#->#?), (^#->), (^#->?), (^#))+import qualified	RegExDot.Anchor		as Anchor+import qualified	RegExDot.RegEx		as RegEx+import qualified	RegExDot.Repeatable	as Repeatable++infixr 5 -:, ?:, ??:, *:, *?:, +:, +?:,#->#:, #->#?:, #->:, #->?:, #:, <~>	-- Same as for ':', & lower than Repeatable's operators.++-- | Prepend an unrepeated 'RegEx.Pattern', to the specified 'RegEx.Concatenation'.+(-:) :: RegEx.Pattern a -> RegEx.Concatenation a -> RegEx.Concatenation a+(-:) pattern	= (Repeatable.one pattern :)++-- | Prepend an optional 'RegEx.Pattern', to the specified 'RegEx.Concatenation'.+(?:) :: RegEx.Pattern a -> RegEx.Concatenation a -> RegEx.Concatenation a+(?:) pattern	= (Repeatable.zeroOrOne pattern :)++-- | A /non-greedy/ version of '?:'.+(??:) :: RegEx.Pattern a -> RegEx.Concatenation a -> RegEx.Concatenation a+(??:) pattern	= (Repeatable.zeroOrOne' pattern :)++-- | Prepend a 'RegEx.Pattern', repeatable zero or more times, to the specified 'RegEx.Concatenation'.+(*:) :: RegEx.Pattern a -> RegEx.Concatenation a -> RegEx.Concatenation a+(*:) pattern	= (Repeatable.zeroOrMore pattern :)++-- | A /non-greedy/ version of '*:'.+(*?:) :: RegEx.Pattern a -> RegEx.Concatenation a -> RegEx.Concatenation a+(*?:) pattern	= (Repeatable.zeroOrMore' pattern :)++-- | Prepend a 'RegEx.Pattern', repeatable one or more times, to the specified 'RegEx.Concatenation'.+(+:) :: RegEx.Pattern a -> RegEx.Concatenation a -> RegEx.Concatenation a+(+:) pattern	= (Repeatable.oneOrMore pattern :)++-- | A /non-greedy/ version of '+:'.+(+?:) :: RegEx.Pattern a -> RegEx.Concatenation a -> RegEx.Concatenation a+(+?:) pattern	= (Repeatable.oneOrMore' pattern :)++-- | Prepend a 'RegEx.Pattern', repeated a range of times, to the specified 'RegEx.Concatenation'.+( #->#:) :: (RegEx.Pattern a, Repeatable.RepetitionBounds) -> RegEx.Concatenation a -> RegEx.Concatenation a+( #->#:) (pattern, bounds)	= (pattern ^#-># bounds :)++-- | A /non-greedy/ version of '#->#:'.+( #->#?:) :: (RegEx.Pattern a, Repeatable.RepetitionBounds) -> RegEx.Concatenation a -> RegEx.Concatenation a+( #->#?:) (pattern, bounds)	= (pattern ^#->#? bounds :)++-- | Prepend a 'RegEx.Pattern', repeated at least a specified number of times, to the specified 'RegEx.Concatenation'.+( #->:) :: (RegEx.Pattern a, Repeatable.Repetitions) -> RegEx.Concatenation a -> RegEx.Concatenation a+( #->:) (pattern, fewest)	= (pattern ^#-> fewest :)++-- | A /non-greedy/ version of '#->:'.+( #->?:) :: (RegEx.Pattern a, Repeatable.Repetitions) -> RegEx.Concatenation a -> RegEx.Concatenation a+( #->?:) (pattern, fewest)	= (pattern ^#->? fewest :)++-- | Prepend a 'RegEx.Pattern', repeated a precise number of times, to the specified 'RegEx.Concatenation'.+( #:) :: (RegEx.Pattern a, Repeatable.Repetitions) -> RegEx.Concatenation a -> RegEx.Concatenation a+( #:) (pattern, r)	= (pattern ^# r :)++{- |+	* Sandwiches a 'RegEx.Concatenation' between optional 'Anchor.Anchor's to construct a 'RegEx.ExtendedRegEx'.++	* Accounts for the unusual requirement to place an 'Anchor.Stern' at the start of the 'RegEx.Concatenation', or an 'Anchor.Bow' at the end.+-}+(<~>)+	:: 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,+	RegEx.sternAnchor	= stern+}
+ src-lib/RegExDot/DataSpan.hs view
@@ -0,0 +1,61 @@+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]	Augments a 'Span.Span', with the specific data concerned.+-}++module RegExDot.DataSpan(+-- * Types+-- ** Type-synonyms+	DataSpan,+-- * Functions+	after,+	empty,+	join+) where++import			Control.Arrow((***))+import qualified	RegExDot.ConsumptionBounds	as ConsumptionBounds+import qualified	RegExDot.Span			as Span++{- |+	* Augment 'Span.Span' with a copy of the data to which it refers.++	* Similar to <http://hackage.haskell.org/packages/archive/regex-base/latest/doc/html/Text-Regex-Base-RegexLike.html#t%3AMatchText>.+-}+type DataSpan a	= ([a], Span.Span)++-- | Constructs an empty instance, located at the specified offset.+empty :: ConsumptionBounds.DataLength -> DataSpan a+empty offset	= ([], Span.empty offset)++-- | Returns the furthest extent, i.e. the offset just after the end.+after :: DataSpan a -> ConsumptionBounds.DataLength+after	= Span.after . snd++{- |+	* Condenses a list into a single value.++	* Expects a gap-free, sorted list.+-}+join+	:: ConsumptionBounds.DataLength	-- ^ The offset at which the concatenated match is considered to have occurred.+	-> [DataSpan a]			-- ^ The list to be concatenated.+	-> DataSpan a+join offset	= (concat *** Span.join offset) . unzip
+ src-lib/RegExDot/DataSpanTree.hs view
@@ -0,0 +1,108 @@+{-+	Copyright (C) 2010-2015 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]	Permits transformation of 'RegEx.MatchList', to facilitate standardisation.+-}++module RegExDot.DataSpanTree(+-- * Types+-- ** Type-synonyms+--	DataSpanTree,+--	DataSpanTreeList,+-- * Functions+	extractCaptureGroups,+	flattenTreeList,+	toTreeList+) where++import qualified	Data.Foldable+import qualified	RegExDot.ConsumptionBounds	as ConsumptionBounds+import qualified	RegExDot.DataSpan		as DataSpan+import qualified	RegExDot.RegEx			as RegEx+import qualified	RegExDot.Tree			as Tree++-- | Defines a similar 'Tree.Tree' to 'RegEx.Match', but with different 'Tree.Leaf's.+type DataSpanTree a	= Tree.Tree (DataSpan.DataSpan a)++-- | Defines a similar structure to 'RegEx.MatchList'.+type DataSpanTreeList a	= [DataSpanTree a]++-- | Converts a 'RegEx.MatchList' into a 'DataSpanTreeList', by transforming the 'Tree.Leaf's.+toTreeList :: RegEx.MatchList a -> DataSpanTreeList a+toTreeList	= map toTree	where+	toTree :: RegEx.Match a -> DataSpanTree a+	toTree	= fmap toDataSpan	where+		toDataSpan :: RegEx.MatchedData a -> DataSpan.DataSpan a+		toDataSpan (_, inputDataOffset, inputData)	= (inputData, (inputDataOffset, length inputData))++-- | Condenses a 'DataSpanTreeList's into a list of 'DataSpan.DataSpan's, using 'DataSpan.join'.+flattenTreeList+	:: ConsumptionBounds.DataLength	-- ^ The offset into the input-data at which a match occurred.+	-> DataSpanTreeList a		-- ^ The tree to flatten.+	-> [DataSpan.DataSpan a]+flattenTreeList _ []				= []+flattenTreeList offset (tree : treeList)	= flattenedTree : flattenTreeList (DataSpan.after flattenedTree) treeList	where+--	flattenedTree :: DataSpan.DataSpan a+	flattenedTree	= DataSpan.join offset $ Data.Foldable.toList tree++{- |+	* POSIX describes the contents of /capture-groups/, as summarised in <http://www2.research.att.com/~gsf/testregex/>.++	* 'RegEx.Result', is a complete description of the match between 'RegEx.InputData' & RegEx.ExtendedRegEx'; this function extracts a POSIX-conformant list from it.++	* The major differences are, that:++		Only data from parenthesized sub-expressions ('RegEx.Alternatives') is captured.++		Only the /last/ repetition of a repeated sub-expression is returned.+		<http://www.opengroup.org/onlinepubs/009695399/functions/regcomp.html>.++		The data captured within each parenthesized sub-expression, is summarised as a single 'DataSpan.DataSpan'.++		POSIX specifies a 'Span.Span'-offset of @-1@, for sub-expressions which match zero times; cf sub-expressions which consume nothing, once.+		<http://www.opengroup.org/onlinepubs/009695399/functions/regcomp.html>.+		@+			("ace" Text.Regex.Posix.=~ "a(b)*c(d)?e") :: Text.Regex.Base.RegexLike.MatchArray+			array (0,2) [(0,(0,3)),(1,(-1,0)),(2,(-1,0))]++			("ace" Text.Regex.Posix.=~ "a(b*)c(d?)e") :: Text.Regex.Base.RegexLike.MatchArray+			array (0,2) [(0,(0,3)),(1,(1,0)),(2,(2,0))]+		@+		I consider this a poor convention, resulting from the focus of POSIX on C, which makes subsequent calculation from the list of 'DataSpan's difficult & error-prone.+-}+extractCaptureGroups+	:: Bool			-- ^ Whether to strictly comply with /POSIX/.+	-> DataSpanTreeList a	-- ^ The tree-structure from which to extract the capture-groups.+	-> [DataSpan.DataSpan a]+extractCaptureGroups complyStrictlyWithPosix	= extractCaptureGroups' 0	where+	extractCaptureGroups' :: ConsumptionBounds.DataLength -> DataSpanTreeList a -> [DataSpan.DataSpan a]+	extractCaptureGroups' _ []			= []+	extractCaptureGroups' offset (tree : treeList)	= let+--		recurseHorizontallyFrom :: ConsumptionBounds.DataLength -> [DataSpan.DataSpan a]+		recurseHorizontallyFrom	= (`extractCaptureGroups'` treeList)+	 in case tree of+		Tree.Leaf dataSpan	-> recurseHorizontallyFrom $ DataSpan.after dataSpan+		Tree.Node []		-> DataSpan.empty (if complyStrictlyWithPosix then -1 else offset) : recurseHorizontallyFrom offset	-- POSIX specifies an Span-offset of -1, for sub-expressions which match 0 times; cf sub-expressions which consumes nothing, once.+		Tree.Node treeLists	-> joinedFlattenedTreeList : (extractCaptureGroups' offset lastMatch {-recurse vertically-} ++ recurseHorizontallyFrom (DataSpan.after joinedFlattenedTreeList))	where+--			lastMatch :: DataSpanTreeList a+			lastMatch	= last treeLists	-- <http://www.opengroup.org/onlinepubs/009695399/functions/regcomp.html>.++--			joinedFlattenedTreeList :: DataSpan.DataSpan a+			joinedFlattenedTreeList	= DataSpan.join offset $ flattenTreeList offset lastMatch+
+ src-lib/RegExDot/ExecutionOptions.hs view
@@ -0,0 +1,102 @@+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]++	* Defines those options relating to the "RegExDot.RegEx"-runtime.++	* These tend to be options which don't affect the result, only the means by which it is achieved.++	* Whilst similar structures are present in other regex-implementations, there's no standardisation of the fields.+-}++module RegExDot.ExecutionOptions(+-- * Types+-- ** Data-types+	ExecutionOptions(..),+-- * Functions+-- ** Mutators+	setVerbose+) where++import qualified	ToolShed.Defaultable+import qualified	ToolShed.Options++-- | The switches used to control execution of the /regex/-engine.+data ExecutionOptions	= MkExecutionOptions {+	abortTrialRepetitionsOnInherentFailure	:: Bool,	-- ^ If an /alternative/ can't match, irrespective of the subsequent /RegExDot.RegEx.Concatenation/, then avoid futile trial /RegExDot.Repeatable.Repetitions/. The converse of 'checkForUnconsumableData'.+	abortTrialRepetitionsOnZeroConsumption	:: Bool,	-- ^ Check for zero data-consumption by the @n@-th /RegExDot.Repeatable.Repeatable RegExDot.RegEx.CaptureGroup/, before attempting @n+1@.+	bypassInputDataForLiberalConsumer	:: Bool,	-- ^ Whether to bypass reading of the input data, if the mapping to specific /RegExDot.RegEx.RepeatablePattern/s isn't required, & the /RegExDot.RegEx.ExtendedRegEx/ can consume the required quantity of anything.+	catchIncompatibleAnchors		:: Bool,	-- ^ Avoid futile trial solutions, involving repetitions of anchored alternatives, which must consume data.+	checkExistenceOfInelasticTail		:: Bool,	-- ^ If the /RegExDot.RegEx.ExtendedRegEx/ ends in an inelastic (zero /Star-height/) tail, confirm its existence at the end of the /RegExDot.RegEx.InputData/.+	checkForUnconsumableData		:: Bool,	-- ^ Check whether there's no possibility of consuming some of the input data. The converse of 'abortTrialRepetitionsOnInherentFailure'.+	moderateGreed				:: Bool,	-- ^ Greedily consume data, only up to the limit beyond which, future requirements would be compromised.+	permitReorderingOfAlternatives		:: Bool,	-- ^ Permit /RegExDot.RegEx.Alternatives/ to be re-ordered, in an attempt to more quickly locate a result.+	preferAlternativesWhichFeedTheGreedy	:: Bool,	-- ^ Within the /RegExDot.RegEx.MatchedData/ from which each candidate /RegExDot.RegEx.Match/ amongst sequences of /RegExDot.RegEx.Alternatives/, is ultimately composed, prefer /RegExDot.ConsumptionBounds.ConsumptionBounds/ of /RegExDot.RegEx.InputData/, beyond /RegExDot.Repeatable.getFewest/, by /RegExDot.Repeatable.isGreedy RegExDot.RegEx.RepeatablePattern/s.+	preferAlternativesWhichMimickUnrolling	:: Bool,	-- ^ Compare /RegExDot.ConsumptionBounds.ConsumptionBounds/ on successive /RegExDot.Repeatable.Repetitions/ of /RegExDot.RegEx.CaptureGroup/, between candidate /RegExDot.RegEx.Match/es, to mimic the behaviour of the unrolled /RegExDot.Repeatable.Repetitions/.+	preferFewerRepeatedAlternatives		:: Bool,	-- ^ Prefer fewer /RegExDot.Repeatable.Repetitions/ of /RegExDot.RegEx.Alternatives/, to discourage the capture of null lists of /RegExDot.RegEx.InputData/.+	requireMatchList			:: Bool,	-- ^ If merely interested in a 'Bool' result, rather than the optimal mapping of input data to /RegExDot.RegEx.RepeatablePattern/s, avoid unnecessary evaluation of the /RegEx.Match/.+	unrollRepeatedSingletonAlternative	:: Bool,	-- ^ Check whether /RegExDot.RegEx.Alternatives/ consists of just a singleton /RegExDot.RegEx.ExtendedRegEx/, & has therefore been used merely as a capture-group. Though this doesn't affect the result, it vastly improves efficiency.+	useFirstMatchAmongAlternatives		:: Bool,	-- ^ Rather than performing an exhaustive search for the optimal choice amongst /RegExDot.RegEx.Alternatives/, merely select the first that matches; conform to /Perl/ rather than /POSIX/.+	validateMinConsumptionOfAlternatives	:: Bool		-- ^ When the number of repetitions of a /RegExDot.RegEx.CaptureGroup/ is precisely specified, check whether the resulting minimum data-requirement is available.+} deriving (Eq, Show)++instance ToolShed.Defaultable.Defaultable ExecutionOptions	where+	defaultValue	= setVerbose False $ ToolShed.Options.blankValue {+		abortTrialRepetitionsOnInherentFailure	= True,		-- Regrettably, this slightly reduces performance for most non-pathological patterns.+		catchIncompatibleAnchors		= True,+		checkExistenceOfInelasticTail		= True,+		checkForUnconsumableData		= True,		-- Expensive, particularly when (not requireMatchList), & only typically useful in failure-scenarios.+		moderateGreed				= True,		-- Cost may exceed benefit. TODO: confirm.+		preferAlternativesWhichFeedTheGreedy	= True,+		preferAlternativesWhichMimickUnrolling	= True,+		preferFewerRepeatedAlternatives		= True,+		unrollRepeatedSingletonAlternative	= True,		-- Affects only efficiency, not the result.+		useFirstMatchAmongAlternatives		= False,	-- Perl-style matching may be faster, but may also yield a sub-optimal Match.+		validateMinConsumptionOfAlternatives	= False		-- The cost outweighs the small infrequent dividend.+	}++instance ToolShed.Options.Options ExecutionOptions	where+	blankValue	= MkExecutionOptions {+		abortTrialRepetitionsOnInherentFailure	= undefined,+		abortTrialRepetitionsOnZeroConsumption	= undefined,+		bypassInputDataForLiberalConsumer	= undefined,+		catchIncompatibleAnchors		= undefined,+		checkExistenceOfInelasticTail		= undefined,+		checkForUnconsumableData		= undefined,+		moderateGreed				= undefined,+		permitReorderingOfAlternatives		= undefined,+		preferAlternativesWhichFeedTheGreedy	= undefined,+		preferAlternativesWhichMimickUnrolling	= undefined,+		preferFewerRepeatedAlternatives		= undefined,+		requireMatchList			= undefined,+		unrollRepeatedSingletonAlternative	= undefined,+		useFirstMatchAmongAlternatives		= undefined,+		validateMinConsumptionOfAlternatives	= undefined+	}++-- | Sets those fields which depend crucially on whether the caller wants to retrieve any /RegExDot.RegEx.MatchList/ from the /RegExDot.RegEx.Result/, or just query whether there is one.+setVerbose :: Bool -> ExecutionOptions -> ExecutionOptions+setVerbose verbose e	= e {+	abortTrialRepetitionsOnZeroConsumption	= verbose,	-- The corresponding check, involves evaluation of a /RegExDot.RegEx.MatchList/, which is too expensive if the /RegExDot.RegEx.Matchlist/ isn't otherwise required.+	bypassInputDataForLiberalConsumer	= not verbose,	-- Potentially bypasses reading of /RegExDot.RegEx.InputData/, which is inappropriate if the mapping into a /RegExDot.RegEx.Result/ is required.+	permitReorderingOfAlternatives		= not verbose,	-- Doesn't help when 'requireMatchList', since an exhaustive search of /RegExDot.RegEx.Alternatives/, for the optimal solution, is performed.+	requireMatchList			= verbose+}+
+ src-lib/RegExDot/InstanceInt.hs view
@@ -0,0 +1,43 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]	Provides a default implementation of the type-classes, required for 'RegEx.ExtendedRegEx' Int.++ [@CAVEATS@]++	In contrast to the traditional regex, there's no reason to escape a 'Char', other than when referring to a /shortcut/.+	Since no /shortcuts/ are defined in this minimal definition, it's always an error to escape a character.+-}++module RegExDot.InstanceInt() where++import qualified	RegExDot.BracketExpressionMember	as BracketExpressionMember+import qualified	RegExDot.Meta				as Meta+import qualified	RegExDot.RegEx				as RegEx++instance BracketExpressionMember.ShortcutExpander Int	where+	findPredicate _	= Nothing++instance Meta.ShortcutExpander Int	where+	expand c	= error $ "RegExDot.Meta.ShortcutExpander.expand RegExDot.InstanceInt:\tunrecognised shortcut '" ++ show c ++ "'."	-- A shortcut is the only legitimate reason to escape a character.++instance RegEx.ShortcutExpander Int	where+	expand c	= error $ "RegExDot.RegEx.ShortcutExpander.expand RegExDot.InstanceInt:\tunrecognised shortcut '" ++ show c ++ "'."	-- A shortcut is the only legitimate reason to escape a character.+
+ src-lib/RegExDot/Meta.hs view
@@ -0,0 +1,144 @@+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]++	* Describes the set of polymorphic data, which can be matched.++	* Only permits a match against exactly one polymorphic datum, which distinguishes it from a /zero-width assertion/, like an /anchor/, /word-boundary/, or /look-ahead assertion/.++	* Designed to be used by a polymorphic /regex/-engine, to implement the traditional meta-characters; @. [] [^]@.++	* Permits /Perl-style shortcuts/ for commonly used 'Meta'-data, to be canned & assigned a single-'Char' mnemonic for subsequent reference;+	the implementation of 'Read' looks for a back-slashed 'Char', for which it expects there to be a corresponding canned 'Meta'.+	Since this class is polymorphic, it has no knowledge of what shortcuts might be appropriate for the chosen type-parameter,+	so the expansion from the back-slashed 'Char' to corresponding 'Meta'-data, is performed through the 'expand' interface of the 'ShortcutExpander' class, which should be implemented elsewhere.+-}++module RegExDot.Meta(+-- * Type-classes+	ShortcutExpander(..),+-- * Types+-- ** Data-types+	Meta(..),+-- * Constants+	shortcutToken,+	anyToken,+	tokens,+-- * Functions+-- ** Predicates+	isMatch+) where++import qualified	Control.Arrow+import qualified	Control.DeepSeq+import qualified	RegExDot.BracketExpression		as BracketExpression+import qualified	RegExDot.BracketExpressionMember	as BracketExpressionMember+import qualified	RegExDot.Consumer			as Consumer+import qualified	RegExDot.ConsumptionProfile		as ConsumptionProfile+import qualified	RegExDot.ShowablePredicate		as ShowablePredicate+import qualified	ToolShed.SelfValidate++{- |+	* The interface via which /Perl-style shortcut/s are expanded, in a manner appropriate to the chosen type-parameter.++	* Since the expansion of /Perl-style shortcut/s, is more restricted inside than outside a 'BracketExpression.BracketExpression',+	the former is considered to be a superclass, providing a base from which to build alternative implementations.+-}+class BracketExpressionMember.ShortcutExpander m => ShortcutExpander m	where+	expand	:: Char -> Meta m	-- ^ Expand a /Perl-style shortcut/.++-- | Declares a polymorphic data-type.+data Meta m =+	Any							-- ^ Any datum matches. Equivalent to @NoneOf []@, but more efficient. CAVEAT: independent of the type-parameter @a@.+	| Literal m						-- ^ The datum matches, if it's equal to the specified value. Equivalent to @AnyOf [BracketExpression.Literal x]@, but more efficient.+	| AnyOf (BracketExpression.BracketExpression m)		-- ^ The datum matches, if 'BracketExpression.containsMatch'.+	| NoneOf (BracketExpression.BracketExpression m)	-- ^ The datum matches, if @not BracketExpression.containsMatch@.+	| Predicate (ShowablePredicate.ShowablePredicate m)	-- ^ The datum matches if 'ShowablePredicate.ShowablePredicate'.+	deriving (+		Eq+--		Read,	-- Specialised below.+--		Show	-- Specialised below.+	)++instance ToolShed.SelfValidate.SelfValidator (Meta m)	where+	getErrors _	= []++instance Show m => Show (Meta m)	where+	showsPrec _ Any					= showChar anyToken+	showsPrec _ (Literal m)				= shows m+	showsPrec _ (AnyOf bracketExpression)		= shows bracketExpression+	showsPrec _ (NoneOf bracketExpression)		= showChar x . showChar BracketExpression.negationToken . showString xs	where (x : xs)	= show $ AnyOf bracketExpression+	showsPrec _ (Predicate showablePredicate)	= shows showablePredicate++instance (ShortcutExpander m, Read m) => Read (Meta m)	where+	readsPrec _ []				= []		-- No parse.+	readsPrec _ (' ' : s)			= reads s	-- Consume white-space.+	readsPrec _ ('\t' : s)			= reads s	-- Consume white-space.+	readsPrec _ ('.' : s)			= [(Any, s)]+	readsPrec _ ('[' : '^' : noneOf)	= Control.Arrow.first NoneOf `map` reads (fst BracketExpression.delimiterTokens : noneOf) {-Reconstruct without negation, & recurse-}+	readsPrec _ anyOf@('[' : _)		= Control.Arrow.first AnyOf `map` reads anyOf {-singleton-}+	readsPrec _ ('\\' : c : s)		= [(expand c, s)]+	readsPrec _ literal			= Control.Arrow.first Literal `map` reads literal {-singleton-}++instance Consumer.Consumer (Meta m)	where+	consumptionProfile meta	= let+		hasSpecificDataRequirement :: Bool+		hasSpecificDataRequirement	= case meta of+			Any		-> False+			NoneOf []	-> False+			_		-> True+	 in ConsumptionProfile.MkConsumptionProfile {+		ConsumptionProfile.consumptionBounds		= (1, Just 1),+		ConsumptionProfile.hasSpecificRequirement	= hasSpecificDataRequirement,+		ConsumptionProfile.canConsumeAnything		= not hasSpecificDataRequirement+	 }++	starHeight _	= 0++instance Control.DeepSeq.NFData m => Control.DeepSeq.NFData (Meta m)	where+	rnf Any					= ()+	rnf (Literal m)				= Control.DeepSeq.rnf m+	rnf (AnyOf bracketExpression)		= Control.DeepSeq.rnf bracketExpression+	rnf (NoneOf bracketExpression)		= Control.DeepSeq.rnf bracketExpression+	rnf (Predicate showablePredicate)	= Control.DeepSeq.rnf showablePredicate++-- | True if the specified datum matches.+isMatch :: Eq m+	=> m		-- ^ The input datum.+	-> Meta m	-- ^ The meta-entity against which the input datum is to be matched.+	-> Bool		-- ^ The result of the match-operation.+isMatch _ Any					= True+isMatch datum (Literal literal)			= datum == literal+isMatch datum (AnyOf bracketExpression)		= datum `BracketExpression.containsMatch` bracketExpression+isMatch datum (NoneOf bracketExpression)	= not $ datum `isMatch` AnyOf bracketExpression	-- This implementation leverages future enhancements to 'AnyOf'.+isMatch datum (Predicate showablePredicate)	= ShowablePredicate.predicate showablePredicate datum++-- | The token used to precede a /Perl-style shortcut/, when in the 'String'-form.+shortcutToken :: Char+shortcutToken	= '\\'++-- | The token used to denote 'Any', when in the 'String'-form.+anyToken :: Char+anyToken	= '.'++-- | The set of 'Char' to which a specific meaning is attributed, when reading from 'String'.+tokens :: String+tokens	= [shortcutToken, anyToken] ++ BracketExpression.tokens+
+ src-lib/RegExDot/RegEx.hs view
@@ -0,0 +1,1198 @@+{-+	Copyright (C) 2010-2015 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@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@.++	* 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.+	Each element of this 'Concatenation' is a 'RepeatablePattern', which describes a permissible match against 'InputData'.++	* 'RepeatablePattern' can take one of two forms.+	In the simplest case, it matches just a single item of the underlying polymorphic type, perhaps literally, though looser specifications also exist:+	@.@ matches any input datum; @[x, y, z]@ matches any of @x@, @y@, or @z@; @[^x, y, z]@ matches anything but @x@, @y@, or @z@.+	To support POSIX /ERE/s, 'RepeatablePattern' can also be a list 'Alternatives', each of which is recursively defined as an 'ExtendedRegEx', to form a tree-structure.++	* Each 'Pattern', can optionally be /quantified/ by either a traditional /greedy/, or a /Perl/-style /non-greedy/, suffix, e.g.; @[*, +, ?, {n, m}, {n,}, {n}, *?, +?, ??, {n, m}?, {n,}?]@.++	* For convenience, common specifications can be canned & assigned a single 'Char' mnemonic, for subsequent reference.+	Since 'ExtendedRegEx' is polymorphic, the set of abbreviations appropriate in the context of the unspecified base-type,+	must be implemented externally through the 'Meta.ShortcutExpander' interface.+	This permits the use, when the type-parameter is 'Char', of /Perl-style shortcuts/ @[\\d\\D\\s\\S\\w\\W]@.++	* The algorithm, is the classic /back-tracking/ one, rather than either a /DFA/ or /NFA/.+	This permits construction of 'Result' via which one can discover the deep mapping of 'InputData' into 'ExtendedRegEx',+	& provides the flexibility to add the features now expected by modern /regex/-engines.+	Since the type-parameter is unknown, & may represent a large object, the exponential space-complexity of creating a /DFA/ may present additional problems.+	The exponential time-complexity of the /back-tracking/ algorithm is partially tamed by targeting obvious inefficiencies with specific optimisations.++	* Char-based regexen, traditionally overload the delimiters of a set of 'Alternatives' (parentheses), as a request for data-capture.+	Here, in contrast, all 'RepeatablePattern's capture data, & repeated sub-expressions capture a list of data,+	rather than arbitrarily recording just the last (<http://www.opengroup.org/onlinepubs/009695399/functions/regcomp.html>) item.++ [@REFERENCES@]++	* <http://en.wikipedia.org/wiki/Regular_expression>++	* <http://swtch.com/~rsc/regexp/regexp1.html>++	* <http://docstore.mik.ua/orelly/perl/prog/ch02_04.htm#PERL2-CH-2-SECT-4.1.2>++	* <http://www.macs.hw.ac.uk/~dsg/gph/papers/html/Strategies/strategies.html>++	* <http://hackage.haskell.org/packages/archive/regex-posix/latest/doc/html/Text-Regex-Posix.html>++	* <http://www.haskell.org/haskellwiki/Regular_expressions>++	* <http://www2.research.att.com/~gsf/testregex/re-interpretation.html>++ [@CAVEATS@]++	* Because of the definition of mutually recursive data-types, it is difficult to split this annoyingly large module, & preserve compatibility across compilers,+	but it may be possible to break this cyclic dependency, by defining an interface to which one of the data-types defined here conforms.++	* Doesn't implement /Back-references/, making the definition of the 'ExtendedRegEx' context-free.++	* There's no integration with the type-classes defined in "Text.Regex.Base.RegexLike", which assumes 'Char'-based 'InputData';+	though this could be added to a specialised instance.++	* When 'Alternatives' are defined, 'Result' becomes a tree-like structure.+	Unless the alternative is a singleton, the specific alternative selected in the solution is typically unknown, & therefore the /structure/ of the branch of this tree is also unknown.+	This lack of clarity is compounded when the 'Alternatives' are 'Repeatable.Repeatable', since a different one may be selected on each successive repetition.+	Consequently, the user can't navigate this portion of the structure in a statically defined manner, to acquire the captured data.+	Despite this, & in contrast to other /regex/-engines, access to the whole data-structure is available, since it doesn't seem advantage to hide it.+	The user can then either use 'extractDataFromMatch' for that element of 'Result', thus aggregating the data from sections of unknown structure, or 'show' it, as an aid to debugging.++ [@TODO@]++	* Test parallel-operation, on a 3 or more processor machine.+	If 'rnf' is less effective than 'rwhnf',+	then the 'Control.DeepSeq.NFData' context can be removed,+	reducing the requirements imposed on the type-parameter 'a'.++	* Try 'Data.List.Stream' (stream-fusion), a faster drop-in replacement for 'Data.List'; possibly integrated in GHC-6.12.++	* 'ExecutionOptions.bypassInputDataForLiberalConsumer' is too restrictive.+	More generally, we can test whether the set of different 'a' in 'InputData', is a subset of those common to all remaining terms in the 'ExtendedRegEx'.+	Using this rule, we can infer @"aaa ..." =~ MkExtendedRegEx [a,a+,a?,[ab]{2,3}]@, given compatible 'ConsumptionProfile.consumptionBounds'.++	* Nested repetitions, where nothing has been added to the expression, result in repeated trials of the same expression,+	e.g.; @"(x{i,}){j,}"@ results in the same expansion for @(i, j) in [(2, 3), (3, 2), (6, 1), (1, 6)]@.+	The resulting 'MatchList' may be different, but if the first such trial fails, so will all the remainder.++	* Should cope with empty sets of 'Alternatives' & zero repetitions, neither of which can ever match, but the wider pattern can, e.g. @(()|x{0}|y)@.++	* By removing 'RepeatablePattern' from 'Match', it can be isolated in a new module.+	This would result in a significant loss of discoverability.++	* Expand repeated 'Anchor.Bow' with @fewest - 1@ null matches followed by recursive 'findMatch'-call with @repetitions = 1@.+-}++module RegExDot.RegEx(+-- * Type-classes+	ShortcutExpander(..),+-- * Types+-- ** Type-synonyms+--	BasicRegEx,+	Concatenation,+	ExternalMatch,+	InputData,+	MatchedData,+	MatchList,+--	MetaDataList,+	RepeatablePattern,+--	Transformation,+-- ** Data-types+	Alternatives(..),+	Match,+	ExtendedRegEx(..),+	Pattern(..),+	Result,+-- * Constants+	alternativeExtendedRegExSeparatorToken,+--	anyDatum,+	captureGroupDelimiters,+	tokens,+-- * Functions+--	accumulateDistinctInputData,+--	accumulateDistinctMetaDataFrom,+	dock,+--	drift,+--	findMatch,+	captureGroup,+--	mkNullMatchFromConcatenation,+--	mkNullMatchFromExtendedRegEx,+--	mkNullMatchFromRepeatablePattern,+--	safeReciprocal,+--	shiftMatchedData,+--	shiftMatch,+	shiftMatchList,+	showsMaybeAnchor,+	simply,+	transformExtendedRegEx,+-- ** Accessors (Deconstructors)+	deconstructAlternatives,+--	getInputData,+-- ** Operators+	(+~),+	(=~),+	(/~),+	(.*),+	(.*?),+--	(/+)+-- ** Predicates+--	hasBowAnchor,+--	hasSternAnchor,+	isDefined,+	isCaptureGroup,+	isSingletonAlternatives,+--	isUnconsumableByAnyOf,+-- ** Query+--	getDistinctMetaDataFromAlternatives,+--	getDistinctMetaDataFromConcatenation,+--	getDistinctMetaDataFromExtendedRegEx,+--	getDistinctMetaDataFromPattern,+--	getDistinctMetaDataFromRepeatablePattern,+	externalMatchLength,+	extractDataFromMatch,+	extractDataFromMatch',+	extractDataFromMatchList+) where++import			Control.Arrow((&&&))+import qualified	Control.Arrow+import qualified	Control.DeepSeq+import qualified	Control.Parallel.Strategies+import qualified	Data.Char+import qualified	Data.Foldable+import qualified	Data.List+import qualified	Data.Maybe+import qualified	Data.Ord+import qualified	RegExDot.Anchor			as Anchor+import qualified	RegExDot.Consumer		as Consumer+import qualified	RegExDot.ConsumptionBounds	as ConsumptionBounds+import qualified	RegExDot.ConsumptionProfile	as ConsumptionProfile+import qualified	RegExDot.ExecutionOptions	as ExecutionOptions+import qualified	RegExDot.Meta			as Meta+import qualified	RegExDot.RegExOpts		as RegExOpts+import qualified	RegExDot.Repeatable		as Repeatable+import			RegExDot.Repeatable((^#->))+import qualified	RegExDot.Tree			as Tree+import qualified	ToolShed.Data.List+import qualified	ToolShed.Data.List.Splits+import qualified	ToolShed.SelfValidate++infix 4 +~, =~, /~	-- Same as (==) & (/=).++-- | The type of a /regex/, in which there's no provision for either 'Alternatives' or 'Anchor.Anchor's.+type BasicRegEx m	= [Repeatable.Repeatable (Meta.Meta m)]++{- |+	* Defines the method required to expand a mnemonic into an 'ExtendedRegEx'.++	* CAVEAT: this interface must be declared locally, since it references 'ExtendedRegEx', & 'ExtendedRegEx' references it.+-}+class ShortcutExpander m	where+	expand	:: Char -> ExtendedRegEx m	-- ^ Expand a single-'Char' mnemonic into the corresponding 'ExtendedRegEx'.++{- |+	* Represents the /alternation/ feature of 'ExtendedRegEx's.++	* One could amalgamate this with 'Pattern', since it seems to exist merely as a peg to hang /instance/-declarations from.+-}+newtype Alternatives m	= MkAlternatives [ExtendedRegEx m]	deriving Eq++-- | Accessor, to expose the guts.+deconstructAlternatives :: Alternatives m -> [ExtendedRegEx m]+deconstructAlternatives (MkAlternatives extendedRegExList)	= extendedRegExList++-- | Similar to 'fmap', but operates on ['ExtendedRegEx'], rather than just @a@.+transformAlternatives+	:: ([ExtendedRegEx m] -> [ExtendedRegEx m])	-- ^ The transformation function to apply to the list of data behind the constructor.+	-> Alternatives m+	-> Alternatives m+transformAlternatives f	= MkAlternatives . f . deconstructAlternatives++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+		[(extendedRegEx, s1)]	-> case dropWhile Data.Char.isSpace s1 of+			('|' : s2)	-> Control.Arrow.first (transformAlternatives (extendedRegEx :)) `map` reads s2 {-singleton-}+			_		-> [(MkAlternatives [extendedRegEx], s1)]+		_			-> []	-- No parse.++instance Show m => Show (Alternatives m)	where+	showsPrec _	= foldl (.) (showString "") . Data.List.intersperse (showChar alternativeExtendedRegExSeparatorToken) . map shows . deconstructAlternatives	-- Replace the default list-format, with 'egrep'-syntax.++instance Consumer.Consumer (Alternatives m)	where+	consumptionProfile	= Consumer.aggregateConsumptionProfilesFromAlternatives . deconstructAlternatives+	starHeight		= Consumer.starHeight . deconstructAlternatives	-- Must evaluate all Alternatives to determine best.++instance ToolShed.SelfValidate.SelfValidator (Alternatives m)	where+	getErrors	= ToolShed.SelfValidate.getErrors . deconstructAlternatives++instance Control.DeepSeq.NFData m => Control.DeepSeq.NFData (Alternatives m)	where+	rnf	= Control.DeepSeq.rnf . deconstructAlternatives++-- | 'Alternatives' can be employed as a simple /capture-group/ as well as a switch, under which circumstances there's no choice amongst multiple 'Alternatives'.+isSingletonAlternatives :: Alternatives m -> Bool+isSingletonAlternatives	= (== 1) . length . deconstructAlternatives++-- | A set of 'Meta.Meta' which can be consumed.+type MetaDataList m	= [Meta.Meta m]++{- |+	* '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,+	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.+	-> MetaDataList m	-- ^ The list of meta-data against any of which a match can occur.+	-> Bool+isUnconsumableByAnyOf i	= not . any (Meta.isMatch i)++-- | The set of distinct 'Meta.Meta', in the specified 'Alternatives'.+getDistinctMetaDataFromAlternatives :: Eq m => Alternatives m -> MetaDataList m+getDistinctMetaDataFromAlternatives	= foldr (Data.List.union . getDistinctMetaDataFromExtendedRegEx) [] . deconstructAlternatives++-- | Defines either a simple 'Meta.Meta', which can match exactly one datum, or a set of 'Alternatives', each of which is recursively defined above, as an 'ExtendedRegEx'.+data Pattern m	=+	Require (Meta.Meta m)		-- ^ Describes a requirement for a simple scalar datum of the polymorphic type.+	| CaptureGroup (Alternatives m)	-- ^ A sub-expression containing a selection of recursively defined alternatives, thus forming a tree-structure.+	deriving Eq++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+		[(alternatives, s1)]	-> case dropWhile Data.Char.isSpace s1 of+			(')' : s2)	-> [(CaptureGroup alternatives, s2)]+			_		-> []	-- No parse.+		_			-> []	-- No parse.+	readsPrec _ s		= case reads s of+		[pair]	-> [Control.Arrow.first Require pair]+		_	-> []	-- No parse.++instance Show m => Show (Pattern m)	where+	showsPrec _ (Require meta)		= shows meta+	showsPrec _ (CaptureGroup alternatives)	= showChar (fst captureGroupDelimiters) . shows alternatives . showChar (snd captureGroupDelimiters)++instance Consumer.Consumer (Pattern m)	where+	consumptionProfile (Require meta)		= Consumer.consumptionProfile meta+	consumptionProfile (CaptureGroup alternatives)	= Consumer.consumptionProfile alternatives++	starHeight (Require meta)		= Consumer.starHeight meta+	starHeight (CaptureGroup alternatives)	= Consumer.starHeight alternatives++instance ToolShed.SelfValidate.SelfValidator (Pattern m)	where+	getErrors (Require meta)		= ToolShed.SelfValidate.getErrors meta+	getErrors (CaptureGroup alternatives)	= ToolShed.SelfValidate.getErrors alternatives++instance Control.DeepSeq.NFData m => Control.DeepSeq.NFData (Pattern m)	where+	rnf (Require meta)		= Control.DeepSeq.rnf meta+	rnf (CaptureGroup alternatives)	= Control.DeepSeq.rnf alternatives++-- | Convenience-function to build a 'CaptureGroup' from a list of alternative 'ExtendedRegEx's.+captureGroup :: [ExtendedRegEx m] -> Pattern m+captureGroup	= CaptureGroup . MkAlternatives++-- | True if the 'Pattern' was constructed via 'CaptureGroup'.+isCaptureGroup :: Pattern m -> Bool+isCaptureGroup (CaptureGroup _)	= True+isCaptureGroup _		= False++-- | The set of distinct 'Meta.Meta', in the specified 'Pattern'.+getDistinctMetaDataFromPattern :: Eq m => Pattern m -> MetaDataList m+getDistinctMetaDataFromPattern pattern	= case pattern of+	Require meta			-> [meta]+	CaptureGroup alternatives	-> getDistinctMetaDataFromAlternatives alternatives++-- | Constant pattern, representing a lax 'Require'ment.+anyDatum :: Pattern a+anyDatum	= Require Meta.Any++-- | Make 'Pattern's, 'Repeatable.Repeatable'.+type RepeatablePattern m	= Repeatable.Repeatable (Pattern m)++-- | Construct a null 'Match' by assuming that the specified 'RepeatablePattern' consumes zero 'InputData'; of which the caller should ensure it's capable.+mkNullMatchFromRepeatablePattern+	:: ConsumptionBounds.DataLength	-- ^ The offset into the list of input data at which the zero-length match occurred.+	-> RepeatablePattern m		-- ^ The pattern that the input data matched.+	-> Match m			-- ^ The resulting match-structure.+mkNullMatchFromRepeatablePattern offset r	= case Repeatable.base r of+	Require _	-> Tree.Leaf (r, offset, [])+	CaptureGroup _	-> Tree.Node $ Repeatable.getFewest r `replicate` []++-- | The set of distinct 'Meta.Meta', in the specified 'RepeatablePattern'.+getDistinctMetaDataFromRepeatablePattern :: Eq m => RepeatablePattern m -> MetaDataList m+getDistinctMetaDataFromRepeatablePattern	= getDistinctMetaDataFromPattern . Repeatable.base++{- |+	* Represents a black hole, which will greedily consume all data.++	* CAVEAT: nullary, i.e. a constant.+-}+(.*) :: RepeatablePattern m+(.*)	= Repeatable.zeroOrMore anyDatum++-- | A /non-greedy/ version of '.*'.+(.*?) :: RepeatablePattern m+(.*?)	= Repeatable.zeroOrMore' anyDatum++-- | Convenience-function, to build a 'RepeatablePattern' from an unrepeated instance of the specified 'Meta.Meta'-datum.+simply :: Meta.Meta m -> RepeatablePattern m+simply	= Repeatable.one . Require++-- | Represents the /concatenation/ aspect of 'ExtendedRegEx's.+type Concatenation m	= [RepeatablePattern m]++-- | Construct a null 'Match' by assuming that the specified 'Concatenation' consumes zero 'InputData'.+mkNullMatchFromConcatenation+	:: ConsumptionBounds.DataLength	-- ^ The offset into the list of input data at which the zero-length match occurred.+	-> Concatenation m		-- ^ The list of repeatable patterns that the input data matched.+	-> MatchList m			-- ^ The resulting match-structure.+mkNullMatchFromConcatenation offset	= map $ mkNullMatchFromRepeatablePattern offset++-- | The set of distinct 'Meta.Meta', in the specified 'Concatenation'.+getDistinctMetaDataFromConcatenation :: Eq m => Concatenation m -> MetaDataList m+getDistinctMetaDataFromConcatenation	= foldr (Data.List.union . getDistinctMetaDataFromRepeatablePattern) []++-- | The accumulating set of 'MetaDataList', resulting from each successive 'RepeatablePattern' in the specified 'Concatenation'.+accumulateDistinctMetaDataFrom :: Eq m => MetaDataList m -> Concatenation m -> [MetaDataList m]+accumulateDistinctMetaDataFrom	= scanr (Data.List.union . getDistinctMetaDataFromRepeatablePattern)++-- | Constructs an 'ExtendedRegEx', by surrounding a 'Concatenation' with optional 'Anchor.Anchor's.+data ExtendedRegEx m	= MkExtendedRegEx {+	bowAnchor	:: Maybe Anchor.Anchor,	-- ^ An option to anchor the /regex/ to the start of the 'InputData'.+	concatenation	:: Concatenation m,	-- ^ The sequence of 'RepeatablePattern's defining the 'Require'ments that the 'InputData' must meet.+	sternAnchor	:: Maybe Anchor.Anchor	-- ^ An option to anchor the /regex/ to the end of the 'InputData'.+} deriving Eq++-- | The type of a function which transforms an 'ExtendedRegEx'.+type Transformation m	= ExtendedRegEx m -> ExtendedRegEx m++-- | Similar to 'fmap', but operates on 'Concatenation', rather than just @a@.+transformExtendedRegEx+	:: (Concatenation m -> Concatenation m)	-- ^ The function used to transform the data behind the constructor.+	-> Transformation m+transformExtendedRegEx f extendedRegEx	= extendedRegEx { concatenation	= f $ concatenation extendedRegEx }++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.+	readsPrec _ ('\\' : c : s)	= [(expand c, s)]+	readsPrec _ s			= case singleton of+		[(extendedRegEx, _)]+			| ToolShed.SelfValidate.isValid extendedRegEx	-> singleton+			| otherwise					-> error $ ToolShed.SelfValidate.getFirstError extendedRegEx	-- Parsed OK, but invalid.+		_							-> []								-- No parse.+		where+--			singleton :: (Eq m, Meta.ShortcutExpander m, Read m, ShortcutExpander m) => [(ExtendedRegEx m, String)]+			singleton = [+				(+					MkExtendedRegEx {+						bowAnchor	= maybeBowAnchor,+						concatenation	= concatenation',+						sternAnchor	= maybeSternAnchor+					},+					remainder+				) |+					(maybeBowAnchor, s1)		<- readsMaybeAnchor s,+					(concatenation', s2)		<- reads s1,+					(maybeSternAnchor, remainder)	<- readsMaybeAnchor s2+			 ] {-list-comprehension-} where+				readsMaybeAnchor :: ReadS (Maybe Anchor.Anchor)+				readsMaybeAnchor s'	= return {-to List-monad-} $ case reads s' of+					[pair]	-> Control.Arrow.first Just pair+					_	-> (Nothing, s')++-- | Shows either the specified 'Anchor.Anchor', or a null string where 'Nothing' is specified.+showsMaybeAnchor :: Maybe Anchor.Anchor -> String -> String+showsMaybeAnchor maybeAnchor+	| Data.Maybe.isJust maybeAnchor	= shows $ Data.Maybe.fromJust maybeAnchor+	| otherwise			= id++instance Show m => Show (ExtendedRegEx m)	where+	showsPrec _ MkExtendedRegEx {+		bowAnchor	= maybeBowAnchor,	-- CAVEAT: this could be 'Nothing' or perversely an 'Anchor.Stern'.+		concatenation	= concatenation',+		sternAnchor	= maybeSternAnchor	-- CAVEAT: this could be 'Nothing' or perversely an 'Anchor.Bow'.+	} = showsMaybeAnchor maybeBowAnchor . shows concatenation' . showsMaybeAnchor maybeSternAnchor++instance Consumer.Consumer (ExtendedRegEx m)	where+	consumptionProfile	= Consumer.aggregateConsumptionProfilesFromConcatenation . concatenation+	starHeight		= Consumer.starHeight . concatenation++instance ToolShed.SelfValidate.SelfValidator (ExtendedRegEx m)	where+	getErrors	= ToolShed.SelfValidate.getErrors . concatenation++instance Control.DeepSeq.NFData m => Control.DeepSeq.NFData (ExtendedRegEx m)	where+	rnf MkExtendedRegEx {+		bowAnchor	= maybeBowAnchor,+		concatenation	= concatenation',+		sternAnchor	= maybeSternAnchor+	} = Control.DeepSeq.rnf (maybeBowAnchor, concatenation', maybeSternAnchor)++-- | Drop 'Anchor.Anchor's at both bow & stern of the specified 'ExtendedRegEx'.+dock :: Transformation m+dock e	= e {+	bowAnchor	= Just Anchor.Bow,+	sternAnchor	= Just Anchor.Stern+}++-- | Amend the 'Concatenation' in the specified 'ExtendedRegEx', by prepending '.*' where there's a missing top-level 'Anchor.Bow' & appending '.*?' where there's a missing top-level 'Anchor.Stern'.+drift :: Transformation m+drift extendedRegEx	= transformExtendedRegEx (+	(+		if hasBowAnchor extendedRegEx then id else {-no anchor-} ((.*?) :)+	) . (+		if hasSternAnchor extendedRegEx then id else {-no anchor-} (++ [(.*)])+	)+ ) extendedRegEx++-- | True if the specified 'ExtendedRegEx' has an initial 'Anchor.Bow'. CAVEAT: though typically the alternative is no 'Anchor.Anchor', there may perversely be an initial 'Anchor.Stern', but neither qualify.+hasBowAnchor :: ExtendedRegEx m -> Bool+hasBowAnchor	= (== Just Anchor.Bow) . bowAnchor++-- | True if the specified 'ExtendedRegEx' has a terminal 'Anchor.Stern'. CAVEAT: though typically the alternative is no 'Anchor.Anchor', there may perversely be a terminal 'Anchor.Bow', but neither qualify.+hasSternAnchor :: ExtendedRegEx m -> Bool+hasSternAnchor	= (== Just Anchor.Stern) . sternAnchor++-- | 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++-- | Construct a null 'Match' by assuming that the specified 'ExtendedRegEx' consumes zero 'InputData'.+mkNullMatchFromExtendedRegEx :: ConsumptionBounds.DataLength -> ExtendedRegEx m -> MatchList m+mkNullMatchFromExtendedRegEx offset	= mkNullMatchFromConcatenation offset . concatenation++-- | The set of distinct (i.e. unique) 'Meta.Meta', in the specified 'ExtendedRegEx'.+getDistinctMetaDataFromExtendedRegEx :: Eq m => ExtendedRegEx m -> MetaDataList m+getDistinctMetaDataFromExtendedRegEx	= getDistinctMetaDataFromConcatenation . concatenation++{- |+	* The input-data is just a list.++	* Whilst typically this list is also a 'String', & could therefore be more efficiently implemented using "Data.ByteString",+	we can't assume that the polymorphic base-type is always 'Char'.+-}+type InputData m = [m]++-- | The accumulating sets of distinct input data.+accumulateDistinctInputData :: Eq m => InputData m -> [InputData m]+accumulateDistinctInputData	= scanr (Data.List.union . return) []++-- | Tag the 'InputData' with the 'RepeatablePattern' it matched (which unfortunately confines the definition to this (bloated) module), & the offset from the start of the data;+type MatchedData m	= (RepeatablePattern m, ConsumptionBounds.DataLength, InputData m)++-- | Shifts the offset of the specified 'MatchedData'.+shiftMatchedData+	:: ConsumptionBounds.DataLength	-- ^ The offset by which to shift the position into the input-data at which a match occurred.+	-> MatchedData m		-- ^ The match-structure whose offset is to be shifted.+	-> MatchedData m+shiftMatchedData i (r, offset, d)	= (r, offset + i, d)++-- | Accessor.+getInputData :: MatchedData m -> InputData m+getInputData (_, _, inputData)	= inputData++-- | Describes the manner in which a 'RepeatablePattern' successfully consumed 'InputData'.+type Match m	= Tree.Tree (MatchedData m)++-- | Shifts the offsets of all the 'MatchedData' contained in the specified 'Match'.+shiftMatch+	:: ConsumptionBounds.DataLength	-- ^ The offset by which to shift the position into the input-data at which a match occurred.+	-> Match m			-- ^ The match-structure whose offset is to be shifted.+	-> Match m+shiftMatch i	= (shiftMatchedData i `fmap`)++-- | Extract & concatenate, the 'InputData' from a 'Match'.+extractDataFromMatch :: Match m -> InputData m+extractDataFromMatch	= Data.Foldable.foldMap getInputData	-- Uses the List-monoid's associative binary operator (++), to concatenate the values returned by the specified function.++-- | Extract & concatenate, the 'InputData' from a 'Match'; null if it didn't match any.+extractDataFromMatch' :: Maybe (Match m) -> InputData m+extractDataFromMatch' (Just match)	= extractDataFromMatch match+extractDataFromMatch' _			= []++-- | Describes the manner in which a 'Concatenation' successfully consumed 'InputData'.+type MatchList m	= [Match m]++-- | Shifts the offsets of all the 'MatchedData' contained in the specified 'MatchList'.+shiftMatchList+	:: ConsumptionBounds.DataLength	-- ^ The offset by which to shift the position into the input-data at which a each listed match occurred.+	-> MatchList m			-- ^ The list of match-structures, each of whose offsets are to be shifted.+	-> MatchList m+shiftMatchList i	= map (shiftMatch i)++-- | Extract & concatenate, the 'InputData', from the 'MatchList'.+extractDataFromMatchList :: MatchList m -> InputData m+extractDataFromMatchList	= concatMap extractDataFromMatch	-- CAVEAT: too fine-grain for effective data-parallelism.++-- | At the top-level of an 'ExtendedRegEx', the lack of an 'Anchor.Anchor' allows the 'ExtendedRegEx' to drift away from the corresponding end of the input-data; this data-gap is captured here.+type ExternalMatch m	= Maybe (Match m)++-- | Captures the list of input-data consumed by the 'Concatenation', bracketed by any data-prefix or data-suffix.+type Result m	= (ExternalMatch m, Maybe (MatchList m), ExternalMatch m)++-- | Returns the length of data consumed by the specified 'ExternalMatch'.+externalMatchLength :: ExternalMatch m -> ConsumptionBounds.DataLength+externalMatchLength	= length . extractDataFromMatch'++{- |+	* Similar to '+~', but exposes 'ExecutionOptions.ExecutionOptions', permitting greater control; & enough rope to hang yourself.++	* The parameter-order facilitates partial application, to acquire a matcher-function for a specific 'ExtendedRegEx', which can be applied repeatedly to different 'InputData'.++	* Performs some one-off @O(n)@ time-complexity preparation, then delegates the grunt-work to a private recursive function.++	* 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, Control.DeepSeq.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)+findMatch regExOpts@RegExOpts.MkRegExOpts {+	RegExOpts.executionOptions	= executionOptions,+	RegExOpts.regEx			= extendedRegEx+} originalInputData+	| let+		extractLowStarHeightTail :: ExtendedRegEx m -> Concatenation m+		extractLowStarHeightTail	= {-#SCC "extractLowStarHeightTail" #-} fromConcatenation . reverse . concatenation	where+			fromConcatenation :: Concatenation m -> Concatenation m+			fromConcatenation []					= []+			fromConcatenation (repeatablePattern : concatenation')+				| Consumer.starHeight repeatablePattern == 0	= repeatablePattern : fromConcatenation {-recurse-} concatenation'+				| Repeatable.getFewest repeatablePattern > 0	= case Repeatable.base repeatablePattern of+					CaptureGroup alternatives+						| isSingletonAlternatives alternatives	-> extractLowStarHeightTail {-recurse-} . head $ deconstructAlternatives alternatives+						| otherwise				-> {-non-singleton-} []+					_				-> []	-- Zero 'Consumer.StarHeight' would have been detected by 'fromConcatenation'.+				| otherwise					= []++--		lowStarHeightTail :: Concatenation a+		lowStarHeightTail	= extractLowStarHeightTail extendedRegEx+	in and [+		ExecutionOptions.checkExistenceOfInelasticTail executionOptions,+		not $ null lowStarHeightTail,	-- Prevent infinite recursion.+		reverse originalInputData /~ RegExOpts.mkRegEx MkExtendedRegEx {+			bowAnchor	= Just Anchor.Bow,+			concatenation	= lowStarHeightTail,+			sternAnchor	= Nothing+		} -- Check for mismatch with the corresponding tail of 'InputData'.+	]		= Nothing+	| otherwise	= findMatchSlave originalConcatenation (+		Consumer.accumulateConsumptionProfiles originalConcatenation+	) (+		accumulateDistinctMetaDataFrom [] originalConcatenation+	) originalInputData originalInputDataLength (+		accumulateDistinctInputData originalInputData+	)+	where+--		originalConcatenation :: Concatenation a+		originalConcatenation	= concatenation extendedRegEx++		originalInputDataLength :: ConsumptionBounds.DataLength+		originalInputDataLength	= length originalInputData+{-+ This beast takes a lot of parameters.+ 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, Control.DeepSeq.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 [] _ _ [] _ _	= 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	= 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.+				not $ ExecutionOptions.requireMatchList executionOptions,				-- Otherwise the precise mapping of the 'inputData' to 'RepeatablePattern's must be determined.+				not $ ConsumptionProfile.hasSpecificRequirement accumulatedConsumptionProfileHead	-- Otherwise a match for the specific 'Meta'-data must be found.+			] = Just undefined {-shouldn't be evaluated according to 'ExecutionOptions.requireMatchList'-}+			| and [+				ExecutionOptions.checkForUnconsumableData executionOptions,+				not $ ConsumptionProfile.canConsumeAnything accumulatedConsumptionProfileHead,	-- Otherwise the subsequent test will always fail.+				(`isUnconsumableByAnyOf` distinctMetaDataHead) `any` distinctInputDataHead	-- Occasionally failure is both inevitable & obvious.+			] = Nothing+			| otherwise	= {-#SCC "findMatchSlave" #-} let+				tailConsumptionProfile :: ConsumptionProfile.ConsumptionProfile+				tailConsumptionProfile@(+					ConsumptionProfile.MkConsumptionProfile {+						ConsumptionProfile.consumptionBounds	= (minConsumptionConcatenationTail, maybeMaxConsumptionConcatenationTail)+					}+				 ) = 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 Nothing+				else {-inputData is sufficient for concatenationTail-} let+					Repeatable.MkRepeatable {+						Repeatable.base			= base,+						Repeatable.repetitionBounds	= (fewest, most),+						Repeatable.isGreedy		= isGreedy+					} = 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 (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'.+ The resulting 'BasicRegEx', with /Kleene Star/s weeded-out & 'Alternatives' pruned, can be matched in O(n)-time, to verify minimal requirements.+-}+						extractMinimumRequirementFromConcatenation :: Concatenation m -> BasicRegEx m+						extractMinimumRequirementFromConcatenation	= concatMap (+							\repeatablePattern -> case Repeatable.base repeatablePattern of+								Require datum	-> return {-to List-monad-} repeatablePattern { Repeatable.base = datum }+								CaptureGroup a+									| isSingletonAlternatives a	-> concat . replicate (Repeatable.getFewest repeatablePattern) . extractMinimumRequirementFromConcatenation {-recurse-} . concatenation . head $ deconstructAlternatives a+									| otherwise			-> []+						 ) . filter ((> 0) . Repeatable.getFewest)++						maximumDataAfterPegs :: Eq m => BasicRegEx m -> InputData m -> Maybe (InputData m)+						maximumDataAfterPegs pegList@(+							Repeatable.MkRepeatable {+								Repeatable.base			= metaPeg,+								Repeatable.repetitionBounds	= (fewestPeg, _)+							} : pegListTail+						 ) inputData'+							| null remainingInputData && length candidateMatchedInputData < fewestPeg		= Nothing							-- Insufficient data to ever match.+							| fewestPeg == 1 || (`Meta.isMatch` metaPeg) `all` tail candidateMatchedInputData	= pegListTail `maximumDataAfterPegs` remainingInputData		-- Success => recurse.+							| otherwise										= pegList `maximumDataAfterPegs` tail candidateInputData	-- Failed candidate => recurse.+							where+--								candidateInputData, candidateMatchedInputData, remainingInputData :: InputData a+								candidateInputData				= dropWhile (not . (`Meta.isMatch` metaPeg)) inputData'+								(candidateMatchedInputData, remainingInputData)	= fewestPeg `splitAt` candidateInputData+						maximumDataAfterPegs [] inputData'	= Just inputData'	-- All the pegs have been matched.+{-+ Each 'repeatablePatternHead' can either be a 'Require' or a 'CaptureGroup'.+ The former case needs further reduction & we can proceed to construct a 'Tree.Leaf' from any matching 'inputData'.+ We attempt to reduce the latter case to an instance of the former, by expanding all combinations of repetitions of various 'Alternatives', constructing a new 'ExtendedRegEx' from each, & recursing.+-}+				in case base of+					Require meta+						| null maybeMatchList	-> Nothing+						| otherwise		-> head {-most suitable-} maybeMatchList+						where+{-+ Attempt to narrow the repetition-range, from the potentially unbounded specification, to that which is possible according to,+ the 'inputDataLength', & the specified consumption-bounds of the remainder of the /regex/.+ 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 . map (+								\(consumedInputData, unconsumedInputData)	-> let+									consumedInputDataLength :: ConsumptionBounds.DataLength+									consumedInputDataLength	= length consumedInputData+								in (+									Tree.Leaf (repeatablePatternHead, inputDataOffset, consumedInputData) :+								) `fmap` {-apply to Maybe Functor-} findMatchSlave concatenationTail accumulatedConsumptionProfilesTail distinctMetaDataTail unconsumedInputData (+									inputDataLength - consumedInputDataLength+								) (+									consumedInputDataLength `drop` distinctInputData+								) -- Recurse, to check whether the 'unconsumedInputData' tail also matches.+							 ) $ let+								fewestData, mostData, mostData' :: Repeatable.Repetitions+								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" #-} 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.+										_		-> maxDataAvailable++--									measureUnmatchableTail :: InputData m -> ConsumptionBounds.DataLength -> ConsumptionBounds.DataLength+									measureUnmatchableTail _ 0	= 0						-- Have matched all the data.+									measureUnmatchableTail (a : as) unmatched+										| a `Meta.isMatch` meta	= measureUnmatchableTail as $ pred unmatched	-- Recurse.+										| otherwise		= unmatched					-- Return the unmatchable tail-length.+									measureUnmatchableTail [] _	= error "RegExDot.RegEx.findMatch.findMatchSlave.maybeMatchList.mostData.measureUnmatchableTail:\tdata unexpectedly exhausted."+{-+ 'most' has been extracted from 'repeatablePatternHead',+ & reduced according to the length of 'inputData' which actually matches, to form 'mostData',+ but if it still exceeds 'fewestData', the resulting repetition-range may trigger backtracking.+ So, attempt to further reduce this ill-defined repetition-range, according to the competing requirements of 'concatenationTail'.+-}+								mostData'+									| and [+										ExecutionOptions.moderateGreed executionOptions,+										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'	-> 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.+							 ) `take` (+								if isGreedy+									then ToolShed.Data.List.Splits.splitsLeftFrom mostData'+									else ToolShed.Data.List.Splits.splitsRightFrom fewestData+							 ) inputData++					CaptureGroup alternatives+						| and [+							not isGreedy,+							fewest <= 0,	-- Zero repetitions permissible.+							Data.Maybe.isJust tailMatch+						] -> zeroRepetitions	-- Zero repetitions is the optimal solution, rendering the choice of Alternative irrelevant.+						| and [+							ExecutionOptions.catchIncompatibleAnchors executionOptions,+							fewest > 1,			-- Multiple repetitions.+							minConsumptionAlternatives > 0,	-- InputData required.+							($ extendedRegExAlternatives) `any` [(hasBowAnchor `all`), (hasSternAnchor `all`)]+						]							-> Nothing	-- 'extendedRegExFromAlternative' requires 'InputData', therefore 'Anchor.Bow' can only pass on the 1st repetition & 'Anchor.Stern' can only pass on the last.+						| otherwise						-> let+{-+ We're about to try all repetitions of one Alternative before progressing to the next.+ One could alternatively transpose this search-pattern, & investigate all 'Alternatives' permissible at n repetitions, before trying (n + 1); I've not pursued this option.+ 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 . concat . Control.Parallel.Strategies.parMap Control.Parallel.Strategies.rseq {-Particularly effective when 'not ExecutionOptions.requireMatchList' -} (+--								:: ExtendedRegEx m -> [Maybe (Match m, MatchList m)]+								\extendedRegExFromAlternative@MkExtendedRegEx {+									concatenation	= concatenationFromAlternative+								} -> let+									components :: Int+									components	= length concatenationFromAlternative++									consumptionProfileConcatenationFromAlternative :: [ConsumptionProfile.ConsumptionProfile]+									consumptionProfileConcatenationFromAlternative	= map Consumer.consumptionProfile concatenationFromAlternative++									minConsumptionConcatenationFromAlternative	:: ConsumptionBounds.DataLength+									maybeMaxConsumptionConcatenationFromAlternative	:: Maybe ConsumptionBounds.DataLength+									(minConsumptionConcatenationFromAlternative, maybeMaxConsumptionConcatenationFromAlternative)	= ConsumptionProfile.consumptionBounds $ ConsumptionProfile.aggregateFromConcatenation consumptionProfileConcatenationFromAlternative+								in (+									if ExecutionOptions.abortTrialRepetitionsOnZeroConsumption executionOptions && Data.Maybe.isNothing most {-constrained only by 'inputDataLength'-}+										then {-#SCC "abortTrialRepetitionsOnZeroConsumption" #-} ToolShed.Data.List.takeUntil $ \maybeMatchPair -> case maybeMatchPair of+											Just (match, _)	-> ($ Tree.pop match) `all` [+												(>= fewest) . length,			-- Must achieve 'Repeatable.getFewest', regardless of data-consumption.+												null . extractDataFromMatchList . last	-- If the n-th repetition consumed nothing, so will the (n + 1)-th.+											 ]+											_		-> False			-- Failure with n repetitions, doesn't preclude success with n + 1.+										else {-optimisation not required-} id+								) . (+--									:: Maybe (Match m, MatchList m) -> Maybe (Match m, MatchList m)+									\maybeMatchPairList	-> if ExecutionOptions.abortTrialRepetitionsOnInherentFailure executionOptions+{-+ We're iterating through the identified range of possible repetitions, of one possible 'concatenationFromAlternative'.+ If failure can be proven, for an arbitrary number of repetitions, then we can bypass the futile remainder.+ If the first trial succeeded, then clearly there's no such inherent problem with 'concatenationFromAlternative', & it's necessary to evaluate all subsequent trial-repetitions.+ Otherwise, assuming there're subsequent trials to be bypassed, attempt to prove that failure is inevitable, by launching a pilot-study,+ using a single repetition of 'concatenationFromAlternative' followed by a replacement infallible 'concatenationTail'.+-}+										then {-#SCC "abortTrialRepetitionsOnInherentFailure" #-} case maybeMatchPairList of+											(Nothing {-failed attempt-} : _ {-subsequent attempt worth bypassing-} : _)	-> if (+												minConsumptionConcatenationFromAlternative == 0	-- Can delegate consumption of unconsumable data to the infallible tail => unprovable culpability.+											 ) || inputData =~ RegExOpts.mkRegEx MkExtendedRegEx {+												bowAnchor	= Just Anchor.Bow,+												concatenation	= concatenationFromAlternative ++ [anyDatum ^#-> minConsumptionConcatenationTail],	-- Infallible tail.+												sternAnchor	= Just Anchor.Stern+											 }+												then maybeMatchPairList+												else []	-- Abandon further repetitions of 'Alternatives'.+											_ {-Alternative is OK, or there's no subsequent attempt to bypass-}		-> maybeMatchPairList+										else {-optimisation not required-} maybeMatchPairList+								) . map (+--									:: Repeatable.Repetitions -> Maybe (Match m, MatchList m)+									\repetitions	-> if hasSternAnchor extendedRegExFromAlternative+										then if (repetitions == 1 || minConsumptionConcatenationFromAlternative == 0) && minConsumptionConcatenationTail == 0+											then (+												shiftMatch inputDataOffset . Tree.Node . (+													: replicate (+														pred repetitions	-- Typically degenerates to zero, since anchored sub-expressions aren't normally repeatable.+													) (+														mkNullMatchFromExtendedRegEx 0 extendedRegExFromAlternative	-- Other Alternatives may be available & suitable.+													) -- Repeated null match may, lacking other suitable Alternatives, correspond to this stern-anchored ExtendedRegEx, so data must be consumed before them.+												) &&& const (mkNullMatchFromConcatenation inputDataOffset concatenationTail)+											) `fmap` findMatch regExOpts { RegExOpts.regEx = extendedRegExFromAlternative } inputData	-- Recurse, either consuming all inputData or failing.+											else Nothing+										else {-no Anchor.Stern-} let+{-+ Expand the 'Repeatable CaptureGroup', so that after recursion it is ultimately reduced in complexity, to just a 'Concatenation' of 'Repeatable Require', & can be handled above as a POSIX BRE.++ There're two special cases; one repetition of n 'Alternatives' & n repetitions of one Alternative.+ If there's only one repetition, we can reduce the implementation-logic substantially, to the point where the implementation's independent of whether there's a choice of 'Alternatives'.+ If there only one Alternative, then the entire repetition-sequence can be immediately expanded, vastly reducing the required number of recursions,+ otherwise we can only expand the first repetition, leaving the option of matching a different Alternative on subsequent repetitions.++ In all cases, we must subsequently re-compose any 'MatchList' resulting of the recursive call, into one corresponding to the structure of original ExtendedRegEx.+ The expansion-process & corresponding subsequent collation, are associated in a tuple, in which the consumption-profile of the expansion is also contained.+-}+--											collater					:: MatchList m -> (Match m, MatchList m)+											consumptionProfileExpandedConcatenationPrefix	:: [ConsumptionProfile.ConsumptionProfile]+--											expandedConcatenationPrefix			:: Concatenation a+											(collater, consumptionProfileExpandedConcatenationPrefix, expandedConcatenationPrefix)+												| repetitions == 1	= (+													Control.Arrow.first (+														Tree.Node . return {-to List-monad-}+													) . splitAt components,	-- Bisect the 'MatchList', into the part resulting from the Alternative, & that for 'concatenationTail'.+													consumptionProfileConcatenationFromAlternative,+													concatenationFromAlternative+												) -- Both tuples below, can degenerate to this simple case; it isn't a fundamentally different algorithm.+												| and [+													ExecutionOptions.unrollRepeatedSingletonAlternative executionOptions,+													isSingletonAlternative,+													not $ hasBowAnchor extendedRegExFromAlternative+												] = (+													Control.Arrow.first (+														Tree.Node . ToolShed.Data.List.chunk components+													) . splitAt (+														components * repetitions+													), -- Bisect the 'MatchList', into the part resulting from (Alternative){n}, & that for 'concatenationTail'.+													concat $ replicate repetitions consumptionProfileConcatenationFromAlternative,+													concat $ replicate repetitions concatenationFromAlternative	-- Expand all repetitions of the single alternative.+												)+												| otherwise {-choice of Alternatives or has Anchor.Bow-}	=  let+													remainingRepetitions :: Repeatable.Repetitions+													remainingRepetitions	= pred repetitions	-- Expand just the first repetition of the set of 'Alternatives'.++--													singletonRepeatable :: RepeatablePattern a+													singletonRepeatable	= Repeatable.toSingleton repeatablePatternHead+												in (+													Control.Arrow.first (+														Tree.Node . uncurry (:) . Control.Arrow.second (+															concatMap Tree.pop		-- Amalgamate the repeated singleton 'MatchList's.+														) . splitAt components			-- Bisect first 'MatchList', into the part resulting from 'concatenationFromAlternative', & that for 'replicate remainingRepetitions'.+													) . splitAt (+														components + remainingRepetitions	-- Bisect the 'MatchList', into the part resulting from (Alternatives){n}, & that for 'concatenationTail'.+													),+													consumptionProfileConcatenationFromAlternative ++ replicate remainingRepetitions (Consumer.consumptionProfile singletonRepeatable),+													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 `fmap` {-apply to Maybe Functor-} findMatchSlave (+										expandedConcatenationPrefix ++ concatenationTail+									) (+										ConsumptionProfile.accumulateFrom tailConsumptionProfile {-initial value-} consumptionProfileExpandedConcatenationPrefix ++ tail accumulatedConsumptionProfilesTail+									) (+										accumulateDistinctMetaDataFrom (head distinctMetaDataTail) {-initial value-} expandedConcatenationPrefix ++ tail distinctMetaDataTail+									) inputData inputDataLength distinctInputData	-- Recurse to get 'Maybe (MatchList a)'.+								) $ 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 []	-- Failure is inevitable.+										else [fewest]+									else {-imprecise range-} let+{-+ Attempt to narrow the repetition-range, from the potentially unbounded specification, to that which is possible according to (data available / consumption-requirement),+ where there're bounds for both numerator & denominator.+ Additionally, one must account for the possibility of the denominator being zero, & if so, select the optimal value if the numerator is also zero.+ 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" #-} 1 {-zero is handled later-} `max` if any Data.Maybe.isNothing [maybeMaxConsumptionConcatenationTail, maybeMaxConsumptionAlternatives]+											then fewest+											else {-no unlimited capacities-} let+												minDataAvailable, maxConsumptionAlternatives :: ConsumptionBounds.DataLength+												minDataAvailable		= inputDataLength - Data.Maybe.fromJust maybeMaxConsumptionConcatenationTail	-- CAVEAT: can be negative.+												maxConsumptionAlternatives	= Data.Maybe.fromJust maybeMaxConsumptionAlternatives+											in if isSingletonAlternative+												then if maxConsumptionAlternatives == 0							-- Denominator.+													then {-sole Alternative can't consume anything ?!-} if minDataAvailable <= 0	-- Numerator.+														then {-tail may consume all data-} fewest	-- Repeat to meet the minimum requirement.+														else {-unconsumable data-} maxBound		-- Failure is inevitable.+													else {-non-zero => can divide-} max fewest $ minDataAvailable /+ maxConsumptionAlternatives+												else {-choice of Alternatives-} let+													minDataAvailable' :: ConsumptionBounds.DataLength+													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-} 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" #-} case most of+											Just cap	-> cap `min` mostPermissibleRepetitions	-- Create a ceiling above which the calculated number of permissible repetitions can't rise.+											_		-> mostPermissibleRepetitions+											where+												mostPermissibleRepetitions :: Repeatable.Repetitions+												mostPermissibleRepetitions+													| isSingletonAlternative		= if minConsumptionAlternatives == 0							-- Denominator.+														then {-sole Alternative can consume zero data-} if maxDataAvailable == 0	-- Numerator.+															then {-zero data available-} fewest		-- Repeat to meet the minimum requirement.+															else {-data available-} maxDataAvailable	-- Either each repetition consumes something, or (n - 1) repetitions is a better solution.+														else {-non-zero => can divide-} maxDataAvailable `div` minConsumptionAlternatives	-- Divide & round down.+													| otherwise {-choice of Alternatives-}	= 1 {-account for instance expanded as 'concatenationFromAlternative'-} + let+														maxDataAvailable' :: ConsumptionBounds.DataLength+														maxDataAvailable'	= maxDataAvailable - minConsumptionConcatenationFromAlternative	-- CAVEAT: potentially negative.+													in if minConsumptionAlternatives == 0							-- Denominator.+														then {-@ least one Alternative can consume zero-} if maxDataAvailable' == 0	-- Numerator.+															then {-zero data available $-} pred fewestAlternatives	-- Repeat as required. CAVEAT: an annoying dependency, which prevents parallel-evaluation.+															else {-data available-} if and [+																False,	-- The cost outweighs the small infrequent dividend.+																minConsumptionConcatenationFromAlternative == 0,+																maxDataAvailable > fewest+															] -- If 'fewest' can be met, without unconsuming repetitions of 'concatenationFromAlternative', then stop short of permitting it.+																then pred maxDataAvailable	-- Any greater, & one repetition (possibly that currently expanded) must needlessly consume nothing.+																else maxDataAvailable'		-- Either all n repetitions consumes something, or (n - 1) is a better solution.+														else {-non-zero => can divide-} maxDataAvailable' `div` minConsumptionAlternatives	-- Divide & round down.+{-+ 'most' has been extracted from 'repeatablePatternHead',+ & reduced according to the number of times it can fit into the maximum available data, to form 'mostAlternatives',+ but if it still exceeds 'fewestAlternatives', the resulting repetition-range may trigger backtracking.+ So, attempt to further reduce this ill-defined repetition-range, according to the competing requirements of 'concatenationTail'.+ CAVEAT: this pays-off infrequently, but not necessarily insignificantly.+-}+										mostAlternatives'	= {-#SCC "mostAlternatives'" #-} if and [+											ExecutionOptions.moderateGreed executionOptions,+											mostAlternatives > fewestAlternatives,	-- Otherwise, there's no unbridled greed to moderate.+											minConsumptionAlternatives > 0		-- Otherwise, any number of repetitions can occur without necessarily triggering back-tracking.+										 ]+											then case maximumDataBeforePegs of+												Just maximumDataBeforePegs'	-> mostAlternatives `min` (+													length maximumDataBeforePegs' `div` minConsumptionAlternatives+												 ) -- Cap upper bound.+												_				-> 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.+							 ) . (+								if ExecutionOptions.permitReorderingOfAlternatives executionOptions+{-+ The order in which 'Alternatives' are evaluated, should make no difference to the final result; unless 'ExecutionOptions.useFirstMatchAmongAlternatives'.+ This order can be manipulated in an attempt to more quickly locate any 'Just MatchList', though if 'ExecutionOptions.requireMatchList', an exhaustive search for the optimal solution must be performed anyway.+ Tackling lower-complexity 'Alternatives' first makes sense, in the hope of finding any easy solution before becoming entrenched fighting some hideous monster.+ Preferring 'Alternatives' which require more data also makes sense, regardless of 'Repeatable.isGreedy' which just a preference for a type of solution, because fewer repetitions are then permissible;+ this is rather like trying to pack a box & preferring to try the biggest items first.+-}+									then Data.List.sortBy $ Data.Ord.comparing (+										Consumer.starHeight &&& safeReciprocal . (fromIntegral :: ConsumptionBounds.DataLength -> Rational) . Consumer.getFewest+									) -- Firstly increasing complexity, then decreasing minimum data-capacity, otherwise stable.+									else id+							 ) $ filter (+								\e	-> (+									not (hasBowAnchor e) || inputDataOffset == 0			-- Necessary & sufficient.+								) && (+									not (hasSternAnchor e) || minConsumptionConcatenationTail == 0	-- Necessary but insufficient, since though minimum consumption is zero, maximum isn't necessarily.+								)+							 ) extendedRegExAlternatives+						in if null matchPairList+							then if fewest <= 0+								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',+ & if the caller reduces the 'Just MatchList' to 'True', that'd be sufficient, but otherwise one must identify the optimal 'MatchList'.+ The primary selection-criterion used is the POSIX one, based on total maximum (or minimum for non-'Repeatable.isGreedy' capture-groups) data-consumption.+ /Perl/'s "first-past-the-post" policy is available as an alternative.+-}+								if ExecutionOptions.useFirstMatchAmongAlternatives executionOptions || length matchPairList == 1+									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.+									) . map (+										(+{-+ If the primary selection-criterion doesn't resolve the choice between candidate 'Match'es, I employ these ad-hoc criteria.++	* Fewer repetitions are preferred, which discourages the capture of null lists of 'InputData'.++	* Within the 'MatchedData', from which a candidate 'Match' is ultimately composed, consumption of 'InputData' beyond 'Repeatable.getFewest' by 'Repeatable.isGreedy' 'RepeatablePattern's, is preferred to non-greedy ones.++	* The consumption on each successive repetition is compared between candidate 'Match'es (which are now known to have used an equal number of repetitions);+	this causes the data-consumption to flow towards earlier repetitions for greedy capture-groups, & towards later repetitions in non-greedy ones,+	thus mimicking the behaviour of the unrolled repetition.++	* PS: more precise criteria are defined in <http://www2.research.att.com/~gsf/testregex/re-interpretation.html>.+-}+											\match -> let+												dataLengthCriterion :: InputData m -> Rational+												dataLengthCriterion	= (+													if isGreedy+														then id+														else safeReciprocal	-- Prefer less data.+												 ) . fromIntegral . length++--												matchLists :: [MatchList m]+												matchLists	= Tree.pop match+											in (+												dataLengthCriterion $ extractDataFromMatch match,+												if ExecutionOptions.preferFewerRepeatedAlternatives executionOptions+													then safeReciprocal . toRational $ length matchLists+													else 0,+												if ExecutionOptions.preferAlternativesWhichFeedTheGreedy executionOptions+													then let+														excessConsumption :: MatchedData m -> ConsumptionBounds.DataLength+														excessConsumption (+															Repeatable.MkRepeatable {+																Repeatable.repetitionBounds	= (f, _),+																Repeatable.isGreedy		= g+															},+															_,+															consumedInputData+														 )+															| g		= l - f+															| otherwise	= f - l+															where+																l :: ConsumptionBounds.DataLength+																l	= length consumedInputData+													in Data.Foldable.foldl' (\l -> (l +) . excessConsumption) 0 match+													else 0,+												if ExecutionOptions.preferAlternativesWhichMimickUnrolling executionOptions+													then map (dataLengthCriterion . extractDataFromMatchList) matchLists+													else []+											) -- Create a tuple of selection-criteria, for simultaneous assessment.+										) . fst {-focus the choice on the Alternatives-} &&& id	-- Prepend a selection-criterion to each result.+									)+							) matchPairList+						where+--							tailMatch, zeroRepetitions :: Maybe (MatchList m)+							tailMatch	= findMatchSlave concatenationTail accumulatedConsumptionProfilesTail distinctMetaDataTail inputData inputDataLength distinctInputData	-- Recurse.+							zeroRepetitions	= (Tree.Node [] :) `fmap` {-apply to Maybe Functor-} tailMatch	-- Prepend a null 'MatchList'.++--							extendedRegExAlternatives :: [ExtendedRegEx m]+							extendedRegExAlternatives	= Data.List.nub $ deconstructAlternatives alternatives++							isSingletonAlternative :: Bool+							isSingletonAlternative	= length extendedRegExAlternatives == 1++							minConsumptionAlternatives	:: ConsumptionBounds.DataLength+							maybeMaxConsumptionAlternatives	:: Maybe ConsumptionBounds.DataLength+							(minConsumptionAlternatives, maybeMaxConsumptionAlternatives)	= Consumer.getConsumptionBounds alternatives	-- Independent of the choice of Alternative.+			where+				inputDataOffset :: ConsumptionBounds.DataLength+				inputDataOffset	= originalInputDataLength - inputDataLength+		findMatchSlave _ _ _ _ _ _	= error "RegExDot.RegEx.findMatch.findMatchSlave:\tunexpected parameter-pattern."++{- |+	* Operator's name was chosen to suggest something more than '=~'.++	* 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, Control.DeepSeq.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+inputData +~ regExOpts	= (+	if hasBowAnchor' then Nothing else fmap head maybeMatchList,	-- Record the first 'Match', consumed in the absence of a top-level 'Anchor.Bow'.+	fmap (+		(+			if hasBowAnchor' then id else tail			-- Remove the first 'Match', since this wasn't consumed by the 'Concatenation'.+		) . (+			if hasSternAnchor' then id else init			-- Remove the last 'Match', since this wasn't consumed by the 'Concatenation'.+		)+	) maybeMatchList,+	if hasSternAnchor' then Nothing else fmap last maybeMatchList	-- Record the last 'Match', consumed in the absence of a top-level 'Anchor.Stern'.+ ) where+--	extendedRegEx :: ExtendedRegEx a+	extendedRegEx	= RegExOpts.regEx regExOpts++	hasBowAnchor', hasSternAnchor' :: Bool+	hasBowAnchor'	= hasBowAnchor extendedRegEx+	hasSternAnchor'	= hasSternAnchor extendedRegEx++--	maybeMatchList :: Maybe (MatchList m)+	maybeMatchList	= fmap drift (RegExOpts.setVerbose True regExOpts) `findMatch` inputData++{- |+	* Pattern-match operator.++	* Identifier & parameter-order follow the lead of /Perl/'s pattern-match operator.++	* Considerably more efficient than '+~', since even though they are both implemented via 'findMatch',+	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, Control.DeepSeq.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, Control.DeepSeq.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	= not . (inputData =~)++-- | The delimiters of 'Alternatives', when in the 'String'-form.+captureGroupDelimiters :: (Char, Char)+captureGroupDelimiters	= ('(', ')')++-- | The token used to separate alternative 'ExtendedRegEx's, when in the 'String'-form.+alternativeExtendedRegExSeparatorToken :: Char+alternativeExtendedRegExSeparatorToken	= '|'++-- | The set of 'Char' to which a specific meaning is attributed, when reading from 'String'.+tokens :: String+tokens	= [fst captureGroupDelimiters, snd captureGroupDelimiters, alternativeExtendedRegExSeparatorToken]++{- |+	* Invert the specified fraction, but return 'maxBound' if the result would otherwise be infinite.++	* CAVEAT: this is an awful concept, and therefore intended for internal use only.+-}+safeReciprocal :: (Eq f, Fractional f) => f -> f+safeReciprocal 0	= fromIntegral (maxBound :: Int)	-- Handle divide-by-zero error.+safeReciprocal f	= recip f++infixl 7 /+	-- Same as (/).++{- |+	* Integral division, with any fractional remainder rounded-up.++	* A rather dubious requirement, so internal use only.+-}+-- (/+) :: Integral i => i -> i -> i+(/+)+	:: Int	-- ^ Numerator.+	-> Int	-- ^ Denominator.+	-> Int	-- 10% faster in unoptimised code, & more in optimised.+_ /+ 0				= error "RegExDot.RegEx.(/+):\tzero denominator => infinity"+-- numerator /+ denominator	= ceiling ((fromIntegral numerator / fromIntegral denominator) :: Double)+numerator /+ denominator	= uncurry (+) . Control.Arrow.second signum $ quotRem numerator denominator	-- Slightly faster.
+ src-lib/RegExDot/RegExOpts.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE MultiParamTypeClasses, TypeSynonymInstances, FlexibleInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]++	* Provides a complete job-specification.++	* 'regEx' is polymorphic, allowing use of either /RegExDot.RegEx.ExtendedRegEx/ (which is also polymorphic) or /RegExChar.ExtendedRegExChar.ExtendedRegExChar/.+-}++module RegExDot.RegExOpts(+-- * Types+-- ** Data-types+	RegExOpts(..),+-- * Functions+	setVerbose,+-- ** Constructors+	mkRegEx+) where++import qualified	RegExDot.CompilationOptions	as CompilationOptions+import qualified	RegExDot.ExecutionOptions	as ExecutionOptions+import qualified	ToolShed.Defaultable++-- | Aggregates both 'ExecutionOptions.ExecutionOptions' & 'CompilationOptions.CompilationOptions' with a polymorphic /regex/, to form a complete job-description.+data RegExOpts a	= MkRegExOpts {+	compilationOptions	:: CompilationOptions.CompilationOptions,	-- ^ Parameters governing the result, rather than the implementation.+	executionOptions	:: ExecutionOptions.ExecutionOptions,		-- ^ Parameters governing the implementation, rather than the result.+	regEx			:: a						-- ^ Polymorphic, to permit specialisation either of the type of /regex/ or the type of input-data.+} deriving Show++instance Functor RegExOpts	where+	fmap f regExOpts	= regExOpts { regEx = f $ regEx regExOpts }++-- | Smart constructor using 'ToolShed.Defaultable.defaultValue'.+mkRegEx :: a -> RegExOpts a+mkRegEx	r	= MkRegExOpts {+	compilationOptions	= ToolShed.Defaultable.defaultValue,+	executionOptions	= ToolShed.Defaultable.defaultValue,+	regEx			= r+}++-- | Mutator, which sets an appropriate 'ExecutionOptions.ExecutionOptions', for the depth to which the caller wants to probe the resulting match.+setVerbose :: Bool -> RegExOpts a -> RegExOpts a+setVerbose verbose regExOpts	= regExOpts { executionOptions = ExecutionOptions.setVerbose verbose $ executionOptions regExOpts }+
+ src-lib/RegExDot/Repeatable.hs view
@@ -0,0 +1,400 @@+{-# LANGUAGE CPP #-}+{-+	Copyright (C) 2010-2015 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]++	* A data-type, which represents the permissible range of instances, of the underlying polymorphic datum.++	* Designed for use in a polymorphic /regex/-engine, which specifies patterns composed of repeated /greedy/ & /non-greedy/ sequences of /Meta/-data;++ >		*	+	?	{fewest, most}	{fewest,}	{fewest}+ >		*?	+?	??	{fewest, most}?	{fewest,}?++	* In the context of /regex/es, this concept is known as /Quantification/.++	* /regex/es evolved from the minimal ability to optionally qualify the datum with a <http://en.wikipedia.org/wiki/Kleene_star> suffix.+	More exotic repetition-specifications could be composed by concatenating these atomic building-blocks.+	Here, I've taken the contrary top-down view, & assumed that all data are qualified by a full 'RepetitionBounds', which in most cases will degenerate into a simpler form.++	* The type of entity which is being repeated, isn't the domain of this data-type; it's polymorphic.+-}++module RegExDot.Repeatable(+-- * Types+-- ** Type-synonyms+	Repetitions,+	RepetitionBounds,+-- ** Data-types+	Repeatable(..),+-- * Constants+--	nonGreedyToken,+	oneOrMoreToken,+	rangeDelimiters,+	rangeSeparatorToken,+	tokens,+	zeroOrMoreToken,+	zeroOrOneToken,+-- * Functions+	one,+	oneOrMore,+	oneOrMore',+	zeroOrMore,+	zeroOrMore',+	zeroOrOne,+	zeroOrOne',+--	precisely,+	repeatableParser,+	showSuffix,+-- ** Accessors+	getFewest,+	getMost,+-- ** Mutators+	focus,+--	setNonGreedy,+	toSingleton,+-- ** Operators+	(^#->#),+	(^#->#?),+	(^#->),+	(^#->?),+	(^#),+-- ** Predicates+	isPrecise,+	hasPreciseBounds+) where++import			Control.Arrow((***))+import qualified	Control.DeepSeq+import qualified	Data.List+import qualified	RegExDot.Consumer		as Consumer+import qualified	RegExDot.ConsumptionProfile	as ConsumptionProfile+import qualified	Text.ParserCombinators.Parsec	as Parsec+import			Text.ParserCombinators.Parsec((<?>))+import qualified	ToolShed.Data.Pair+import qualified	ToolShed.SelfValidate++#if !MIN_VERSION_base(4,8,0)+import	Control.Applicative((<$>), (<*>))+#endif++infix 6 ^#->#, ^#->#?, ^#->, ^#->?, ^#	-- A notch tighter than "DSL"s binary operators.++-- | A number of repetitions.+type Repetitions	= Int++-- | Defines the bounds of a range of permissible repetitions.+type RepetitionBounds	= (Repetitions, Maybe Repetitions)++-- | Creates a precise 'RepetitionBounds', i.e. both lower & upper bounds on the number of 'Repetitions' are equal to the same value.+precisely :: Repetitions -> RepetitionBounds+precisely i	= (i, Just i)++-- | Predicate which is 'True' if exactly one value is permissible, ie lower & upper bounds on the number of 'Repetitions' are identical.+hasPreciseBounds :: RepetitionBounds -> Bool+hasPreciseBounds (fewest, most)	= Just fewest == most++-- | Declares a polymorphic data-type, which augments the underlying 'base' datum, with the range of times it may be used.+data Repeatable a	= MkRepeatable {+	base			:: a,			-- ^ The underlying polymorphic datum.+	repetitionBounds	:: RepetitionBounds,	-- ^ The bounds delimiting the range of permissible repetitions, of 'base'.+	isGreedy		:: Bool			-- ^ Whether to demand as many matching instances of 'base' as possible; or as few (AKA /lazy quantification/).+} deriving Eq++instance Functor Repeatable	where+	fmap f repeatable	= repeatable { base = f $ base repeatable }++-- | True if there's no choice in the number of repetitions; implemented via 'isPrecise'.+isPrecise :: Repeatable a -> Bool+isPrecise	= hasPreciseBounds . repetitionBounds++-- | Builds a parser for a specification of the number of permissible instances of the specified polymorphic parameter.+repeatableParser :: a -> Parsec.Parser (Repeatable a)+repeatableParser b	= Parsec.option (+	one b	-- The default; there's no concept of greediness here.+ ) $ do+	repeatable	<- Parsec.choice [+		(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")+				most	<- Parsec.spaces >> Parsec.option (+					Just fewest	-- The default.+				 ) (+					do+						i	<- (+							Parsec.char rangeSeparatorToken			<?> "Repeatable.rangeSeparatorToken " ++ show rangeSeparatorToken+						 ) >> Parsec.spaces >> Parsec.option Nothing {-default to open-ended range-} (+							Just . read <$> Parsec.many1 Parsec.digit	<?> "Repetition-range maximum"+						 )++						Parsec.spaces >> return {-to ParsecT-monad-} i+				 )++				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 ParsecT-monad-} False++	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+		[(base', s1)]	-> (error . ("readsPrec Repeatable:\tparse-error; " ++) . show) `either` return $ Parsec.parse ((,) <$> repeatableParser base' <*> Parsec.getInput) "Repeatable" s1+		_		-> []	-- No parse.++{- |+	* A 'ShowS'-function for the suffix, denoting the permissible repetitions, of 'base'.++	* This function converts the internal, into the tradition /greedy/ & /non-greedy/ quantifiers of various specific varieties.+-}+showSuffix :: Repeatable a -> ShowS+showSuffix repeatable	= let+	showRange :: ShowS -> ShowS+	showRange x	= (\(begin, end) -> begin . x . end) $ ToolShed.Data.Pair.mirror showChar rangeDelimiters+ in (+	case repetitionBounds repeatable of+		(0, Nothing)		-> showChar zeroOrMoreToken+		(1, Nothing)		-> showChar oneOrMoreToken+		(fewest, Nothing)	-> showRange $ shows fewest . showChar rangeSeparatorToken+		(0, Just 1)		-> showChar zeroOrOneToken+		(1, Just 1)		-> id	-- CAVEAT: since there's no explicit repetition-operator, the non-greedy modifier can't be appended.+		(fewest, Just most)	-> showRange $ if fewest == most+			then shows fewest	-- Single-valued range.+			else shows fewest . showChar rangeSeparatorToken . shows most+ ) . if ($ repeatable) `any` [isGreedy, isPrecise] {-without a range of possibilities, non-greediness is irrelevant-}+	then id+	else showChar nonGreedyToken	-- This can only be appended, if there a previous repetition-operator for it to modify.++-- Replicate the syntax, for repetition, as used in a POSIX-standard /regex/.+instance Show a => Show (Repeatable a)	where+	showsPrec _ repeatable	= shows (base repeatable) . showSuffix repeatable++instance Consumer.Consumer a => Consumer.Consumer (Repeatable a)	where+	consumptionProfile MkRepeatable {+		base			= b,+		repetitionBounds	= (fewest, most)+	} = baseConsumptionProfile {+		ConsumptionProfile.consumptionBounds	= (fewest *) *** ((*) <$> most <*>) $ ConsumptionProfile.consumptionBounds baseConsumptionProfile	-- CAVEAT: special cases exist, where one or both halves of this calculation degenerate to a simpler form, but special treatment, in an attempt to improve performance, proved counterproductive.+	} where+		baseConsumptionProfile :: ConsumptionProfile.ConsumptionProfile+		baseConsumptionProfile	= Consumer.consumptionProfile b++	starHeight MkRepeatable {+		base			= b,+		repetitionBounds	= r+	} = Consumer.starHeight b + if hasPreciseBounds r then 0 else 1++instance ToolShed.SelfValidate.SelfValidator a => ToolShed.SelfValidate.SelfValidator (Repeatable a)	where+	getErrors MkRepeatable {+		base			= b,+		repetitionBounds	= (fewest, most),+		isGreedy		= g+	}+		| not $ ToolShed.SelfValidate.isValid b	= ToolShed.SelfValidate.getErrors b	-- Delegate.+		| otherwise				= ToolShed.SelfValidate.extractErrors [+			(fewest < 0, "Negative fewest=" ++ show fewest ++ "."),+			(+				case most of+					Just m	-> m < fewest+					_	-> False,+				"Invalid repetition-range; '" ++ show (fewest, most) ++ "'."+			), (+				not g && case most of+					Just m	-> fewest >= m	-- There ought to be potential for non-greediness, where specified: the converse isn't true, since greediness isn't explicit, & may not have been wanted.+					_	-> False,+				"Invalid non-greedy repetition-range; '" ++ show (fewest, most) ++ "'."+			)+		]++instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Repeatable a)	where+	rnf MkRepeatable {+		base			= b,+		repetitionBounds	= r,+		isGreedy		= g+	} = Control.DeepSeq.rnf (b, r, g)++-- | Mutator.+setNonGreedy :: Repeatable a -> Repeatable a+setNonGreedy r	= r { isGreedy = False }++{- |+	* Construct a greedy 'Repeatable', from a polymorphic datum, with the specified range of permissible instances.++	* The /#/s in the identifier represent the two bounds.++	* /a{f, m}/+-}+(^#->#)+	:: a			-- ^ The polymorphic payload from which to construct the 'Repeatable'.+	-> RepetitionBounds	-- ^ The permissible repetition-bounds for the polymorphic data.+	-> Repeatable a+b ^#-># bounds	= MkRepeatable {+	base			= b,+	repetitionBounds	= bounds,+	isGreedy		= True+}++{- |+	* Construct a non-greedy version of '^#->#'.++	* /a{f, m}?/+-}+(^#->#?)+	:: a			-- ^ The polymorphic payload from which to construct the 'Repeatable'.+	-> RepetitionBounds	-- ^ The permissible repetition-bounds for the polymorphic data.+	-> Repeatable a+b ^#->#? bounds	= setNonGreedy (b ^#-># bounds)++{- |+	* Construct a greedy 'Repeatable', tailored for data repeated at least the specified number of times.++	* The /#/ in the identifier represents the single bound.++	* /a{f,}/+-}+(^#->)+	:: a		-- ^ The polymorphic payload from which to construct the 'Repeatable'.+	-> Repetitions	-- ^ The minimum permissible repetitions of the polymorphic data.+	-> Repeatable a+b ^#-> fewest	= b ^#-># (fewest, Nothing)++{- |+	* Construct a non-greedy version of '^#->'.++	* /a{f,}?/+-}+(^#->?)+	:: a		-- ^ The polymorphic payload from which to construct the 'Repeatable'.+	-> Repetitions	-- ^ The minimum permissible repetitions of the polymorphic data.+	-> Repeatable a+b ^#->? fewest	= setNonGreedy (b ^#-> fewest)++{- |+	* Construct a 'Repeatable', tailored for data repeated a precise number of times.++	* The /#/ in the identifier represents the single bound.++	* /a{f}/+-}+(^#)+	:: a		-- ^ The polymorphic payload from which to construct the 'Repeatable'.+	-> Repetitions	-- ^ The precise number of repetitions of the polymorphic data which is required.+	-> Repeatable a+b ^# r	= b ^#-># precisely r++{- |+	* Construct a 'Repeatable', tailored for unrepeated data.++	* A degenerate case of '^#'.+-}+one :: a -> Repeatable a+one	= (^# 1)++{- |+	* Construct a greedy 'Repeatable', from a polymorphic datum, with 'fewest' == 0 & 'most' == 1.++	* A specific case of '^#->#'.+-}+zeroOrOne :: a -> Repeatable a+zeroOrOne	= (^#-># (0, Just 1))++-- | Construct a non-greedy version of 'zeroOrOne'.+zeroOrOne' :: a -> Repeatable a+zeroOrOne'	= setNonGreedy . zeroOrOne++{- |+	* Construct a greedy 'Repeatable', from a polymorphic datum, with 'fewest' == 0.++	* A specific case of '^#->'.+-}+zeroOrMore :: a -> Repeatable a+zeroOrMore	= (^#-> 0)++-- | Construct a non-greedy version of 'zeroOrMore'.+zeroOrMore' :: a -> Repeatable a+zeroOrMore'	= setNonGreedy . zeroOrMore++{- |+	* Construct a greedy 'Repeatable', from a polymorphic datum, with lower 'RepetitionBounds' == one.++	* A specific case of '^#->'.+-}+oneOrMore :: a -> Repeatable a+oneOrMore	= (^#-> 1)++-- | Construct a non-greedy version of 'oneOrMore'.+oneOrMore' :: a -> Repeatable a+oneOrMore'	= setNonGreedy . oneOrMore++-- | Reduces a 'Repeatable', with a range of 'RepetitionBounds', to a precise number of repetitions.+focus :: Repeatable a -> Repetitions -> Repeatable a+focus r i	= r { repetitionBounds = precisely i }++{- |+	* Reduces a 'Repeatable', with a range of 'RepetitionBounds', to a singleton.++	* A degenerate case of 'focus'.+-}+toSingleton :: Repeatable a -> Repeatable a+toSingleton	= (`focus` 1)++-- | Accessor.+getFewest :: Repeatable a -> Repetitions+getFewest MkRepeatable { repetitionBounds = (f, _) }	= f++-- | Accessor.+getMost :: Repeatable a -> Maybe Repetitions+getMost MkRepeatable { repetitionBounds = (_, m) }	= m++-- | The token used to denote /non-greedy/, when in the 'String'-form.+nonGreedyToken :: Char+nonGreedyToken	= '?'++{- |+	* The token used to denote 'zeroOrMore', when in the 'String'-form.++	* AKA /Kleene Star/.+-}+zeroOrMoreToken :: Char+zeroOrMoreToken	= '*'++-- | The token used to denote 'zeroOrOne', when in the 'String'-form.+zeroOrOneToken :: Char+zeroOrOneToken	= '?'++-- | The token used to denote 'oneOrMore', when in the 'String'-form.+oneOrMoreToken :: Char+oneOrMoreToken	= '+'++-- | The delimiters of '^#->#', when in the 'String'-form.+rangeDelimiters :: (Char, Char)+rangeDelimiters	= ('{', '}')++-- | The token used to separate 'RepetitionBounds', when in the 'String'-form.+rangeSeparatorToken :: Char+rangeSeparatorToken	= ','++-- | The set of 'Char' to which a specific meaning is attributed, when reading from 'String'.+tokens :: String+tokens	= Data.List.nub [nonGreedyToken, zeroOrMoreToken, zeroOrOneToken, oneOrMoreToken, fst rangeDelimiters, snd rangeDelimiters, rangeSeparatorToken]+
+ src-lib/RegExDot/Result.hs view
@@ -0,0 +1,60 @@+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]	Tools to manipulate a 'RegEx.Result'.++ [@CAVEAT@]	The data-definition remains in "RegEx", since it references 'RegEx.Match'.+-}++module RegExDot.Result(+-- * Functions+-- ** Accessors (Deconstructors)+	getMatchList,+	getPreMatch,+	getPostMatch,+-- ** Predicates+	isMatch,+-- ** Query+	countMatches+) where++import qualified	Data.Maybe+import qualified	RegExDot.RegEx	as RegEx++-- | Accessor.+getPreMatch :: RegEx.Result a -> RegEx.ExternalMatch a+getPreMatch (externalMatch, _, _)	= externalMatch++-- | Accessor.+getPostMatch :: RegEx.Result a -> RegEx.ExternalMatch a+getPostMatch (_, _, externalMatch)	= externalMatch++-- | Accessor.+getMatchList :: RegEx.Result a -> Maybe (RegEx.MatchList a)+getMatchList (_, maybeMatchList, _)	= maybeMatchList++-- | True if the 'RegEx.InputData' matched the 'RegEx.ExtendedRegEx'.+isMatch :: RegEx.Result a -> Bool+isMatch	= Data.Maybe.isJust . getMatchList++-- | Counts the number of top-level 'RegEx.Match'es.+countMatches :: RegEx.Result a -> Int+countMatches (_, Just matchList, _)	= length matchList+countMatches _				= 0+
+ src-lib/RegExDot/ShowablePredicate.hs view
@@ -0,0 +1,53 @@+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]	Used to implement /Perl-shortcut/s, /BracketExpression/-ranges & /POSIX Character-classes/.+-}++module RegExDot.ShowablePredicate(+-- * Types+	Predicate,+-- ** Data-types+	ShowablePredicate(+		MkShowablePredicate,+		name,+		predicate+	)+) where++import qualified	Control.DeepSeq++-- | An arbitrary polymorphic predicate function.+type Predicate a	= a -> Bool++-- | Container for both the /predicate/, & the name used in the implementation of 'Show'.+data ShowablePredicate a	= MkShowablePredicate {+	name		:: String,	-- ^ The identifier of this predicate when in string-form.+	predicate	:: Predicate a	-- ^ The function used to determine whether an input datum matches.+}++instance Show (ShowablePredicate a)	where+	showsPrec _	= showString . name++instance Eq (ShowablePredicate a)	where+	l == r	= name l == name r	-- Ignore 'predicate'.++instance Control.DeepSeq.NFData (ShowablePredicate a)	where+	rnf	= Control.DeepSeq.rnf . name	-- Ignore 'predicate'.+
+ src-lib/RegExDot/Span.hs view
@@ -0,0 +1,64 @@+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]	Delimits a data-sequence, via its offset & length, within a wider list.+-}++module RegExDot.Span(+-- * Types+-- ** Type-synonyms+	Span,+-- * Functions+	after,+	empty,+	join+) where++import			Control.Arrow((***))+import qualified	RegExDot.ConsumptionBounds	as ConsumptionBounds++{- |+	* The offset & length of a specific data-sequence, within a wider list.++	* Similar to <http://hackage.haskell.org/packages/archive/regex-base/latest/doc/html/Text-Regex-Base-RegexLike.html#t%3AMatchArray>.+-}+type Span	= (ConsumptionBounds.DataLength {-offset-}, ConsumptionBounds.DataLength)++-- | An empty instance, located at the specified offset.+empty :: ConsumptionBounds.DataLength -> Span+empty offset	= (offset, 0)++-- | Returns the furthest extent, i.e. the offset just after the end.+after :: Span -> ConsumptionBounds.DataLength+after	= uncurry (+)++{- |+	* Condenses a list into a single value.++	* Expects a gap-free, sorted list.+-}+join+	:: ConsumptionBounds.DataLength	-- ^ The offset into the list of input-data to use when a null list of spans is received.+	-> [Span]+	-> Span+join offset []		= empty offset		-- The offset can't be deduced from a null list, so use the value provided.+join _ [singleton]	= singleton		-- Merely for efficiency.+join _ spanList		= head *** sum $ unzip spanList++
+ src-lib/RegExDot/Tree.hs view
@@ -0,0 +1,80 @@+{-+	Copyright (C) 2010 Dr. Alistair Ward++	This program is free software: you can redistribute it and/or modify+	it under the terms of the GNU General Public License as published by+	the Free Software Foundation, either version 3 of the License, or+	(at your option) any later version.++	This program is distributed in the hope that it will be useful,+	but WITHOUT ANY WARRANTY; without even the implied warranty of+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+	GNU General Public License for more details.++	You should have received a copy of the GNU General Public License+	along with this program.  If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@]	Dr. Alistair Ward++ [@DESCRIPTION@]++	* Defines a specific type of tree-structure, which is composed from either a datum, or a list of Tree-lists; <http://en.wikipedia.org/wiki/Rose_Tree>.++	* This more general tree-structure has the shape of a regular-expression match.++	* This contrasts with the typical binary-tree, which is either empty, or contains a (left Tree, datum, right Tree).+-}++module RegExDot.Tree(+-- * Types+-- ** Type-synonyms+--	TreeList,+-- ** Data-types+	Tree(..),+-- * Functions+	pop+) where++import qualified	Control.Arrow+import qualified	Control.DeepSeq+import qualified	Data.Foldable+import qualified	Data.List+import qualified	Data.Monoid++-- | A general purpose tree-type structure.+data Tree a	=+	Leaf a			-- ^ The payload.+	| Node [TreeList a]	-- ^ Recurse. NB: a list of lists is required to contain the /MatchLists/ resulting from repeated /Alternatives/.+	deriving Eq++-- | A list of 'Tree's; significant only because it is the essence of the recursive nature of 'Tree'.+type TreeList a	= [Tree a]++instance Show a => Show (Tree a)	where+	showsPrec _ (Leaf a)		= shows a+	showsPrec _ (Node treeLists)	= showList treeLists++instance Read a => Read (Tree a)	where+	readsPrec _ node@('[' : _)	= Control.Arrow.first Node `map` readList node {-singleton-}+	readsPrec _ leaf		= Control.Arrow.first Leaf `map` reads leaf {-singleton-}++instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Tree a)	where+	rnf (Leaf a)	= Control.DeepSeq.rnf a+	rnf (Node l)	= Control.DeepSeq.rnf l++instance Functor Tree	where+	fmap f (Leaf a)		= Leaf $ f a+	fmap f (Node treeLists)	= Node $ map (fmap {-recurse-} f `map`) treeLists++instance Data.Foldable.Foldable Tree where+	foldMap f (Leaf a)		= f a	-- CAVEAT: 'f' should be Associative, as required by a Monoid.+	foldMap f (Node treeLists)	= Data.List.foldl' (Data.List.foldl' (\monoid -> (monoid `Data.Monoid.mappend`) . Data.Foldable.foldMap f)) Data.Monoid.mempty treeLists++-- | Deconstruct the specified 'Node'; i.e. lop the apex from the 'Tree', leaving a flat top.+pop+	:: Tree a	-- ^ The tree from which to extract the list of trees hanging immediately beneath its apex.+	-> [TreeList a]+pop (Node treeLists)	= treeLists+pop _			= error "RegExDot.Tree.pop:\tunexpected Leaf"+
− src/RegExDot/Anchor.hs
@@ -1,90 +0,0 @@-{--	Copyright (C) 2010 Dr. Alistair Ward--	This program is free software: you can redistribute it and/or modify-	it under the terms of the GNU General Public License as published by-	the Free Software Foundation, either version 3 of the License, or-	(at your option) any later version.--	This program is distributed in the hope that it will be useful,-	but WITHOUT ANY WARRANTY; without even the implied warranty of-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-	GNU General Public License for more details.--	You should have received a copy of the GNU General Public License-	along with this program.  If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@]	Dr. Alistair Ward-- [@DESCRIPTION@]--	* A type of /zero-width assertion/:-	the 'Bow'-predicate is 'True' if no input-data has yet been consumed; the 'Stern'-predicate is 'True' if no input-data remains to be consumed.--	* Whilst at the top-level of the regex, the /absence/ of an anchor is equivalent to a non-capturing @.*?@,-	tempting one to implement them using this property,-	it doesn't apply to those anchors encountered at either end of a sub-expression.-	So in all cases, an anchor constrains the permissible consumption of input-data,-	but at the top level it also prevents the regex drifting away from either end of the input-data.--}--module RegExDot.Anchor(--- * Types--- ** Data-types-	Anchor(..),--- ** Type-synonyms-	AnchorSpecification,--- * Constants-	bowToken,-	sternToken,-	tokens,-	unanchored-) where--import qualified	Control.DeepSeq---- | Defines the types on /anchor/ by which a /regex/ can be moored to a part of the input-data.-data Anchor =-	Bow	-- ^ Matches only if no input data has yet been consumed. Can only exist at the start of the entire regex, or (in theory) the start of any /alternative/.-	| Stern	-- ^ Matches only if no input data remains to be consumed. Can only exist at the end of the entire regex, or (in theory) the end of any /alternative/.-	deriving (-		Eq---		Read,	-- See specialisation below.---		Show	-- See specialisation below.-	)--instance Show Anchor	where-	showsPrec _ Bow		= showChar bowToken-	showsPrec _ Stern	= showChar sternToken--instance Read Anchor	where-	readsPrec _ []		= []	-- No parse.-	readsPrec _ (' ' : s)	= reads s	-- Consume white-space.-	readsPrec _ ('\t' : s)	= reads s	-- Consume white-space.-	readsPrec _ (c : s)	= case c `lookup` [(bowToken, Bow), (sternToken, Stern)] of-		Just anchor	-> [(anchor, s)]-		_		-> []	-- No parse.--instance Control.DeepSeq.NFData Anchor	where-	rnf _	= ()---- | The conventional token used to denote a 'Bow'-anchor, when in 'String'-form.-bowToken :: Char-bowToken	= '^'---- | The conventional token used to denote a 'Stern'-anchor, when in 'String'-form.-sternToken :: Char-sternToken	= '$'---- | 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/BracketExpression.hs
@@ -1,63 +0,0 @@-{--	Copyright (C) 2010 Dr. Alistair Ward--	This program is free software: you can redistribute it and/or modify-	it under the terms of the GNU General Public License as published by-	the Free Software Foundation, either version 3 of the License, or-	(at your option) any later version.--	This program is distributed in the hope that it will be useful,-	but WITHOUT ANY WARRANTY; without even the implied warranty of-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-	GNU General Public License for more details.--	You should have received a copy of the GNU General Public License-	along with this program.  If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@]	Dr. Alistair Ward-- [@DESCRIPTION@]--	This concept was traditionally known as a /Character-class/,-	but to distinguish it from the more recently introduced & narrower /POSIX Character-class/, it has been renamed /Bracket-expression/.--}--module RegExDot.BracketExpression(--- * Types--- ** Type-synonyms-	BracketExpression,--- * Constants-	delimiterTokens,-	negationToken,-	tokens,--- * Functions--- ** Predicates-	containsMatch-) where--import			RegExDot.BracketExpressionMember((=~))-import qualified	RegExDot.BracketExpressionMember	as BracketExpressionMember---- | A /Bracket-expression/ consists of a list of 'BracketExpressionMember.Member's.-type BracketExpression m	= [BracketExpressionMember.Member m]---- | True if the specified datum matches any 'BracketExpressionMember.Member' of the 'BracketExpression'.-containsMatch :: Eq m-	=> m			-- ^ The input datum.-	-> BracketExpression m	-- ^ The list of bracket-expression members within which to find a match.-	-> Bool-containsMatch datum	= any (datum =~)---- | The delimiters of a /bracket-expression/, when in 'String'-form.-delimiterTokens :: (Char, Char)-delimiterTokens	= ('[', ']')---- | Used to denote a negated /bracket-expression/, when in 'String'-form.-negationToken :: Char-negationToken	= '^'---- | The set of 'Char' to which a specific meaning is attributed, when reading from 'String'.-tokens :: String-tokens	= [fst delimiterTokens, snd delimiterTokens, negationToken]-
− src/RegExDot/BracketExpressionMember.hs
@@ -1,93 +0,0 @@-{--	Copyright (C) 2010 Dr. Alistair Ward--	This program is free software: you can redistribute it and/or modify-	it under the terms of the GNU General Public License as published by-	the Free Software Foundation, either version 3 of the License, or-	(at your option) any later version.--	This program is distributed in the hope that it will be useful,-	but WITHOUT ANY WARRANTY; without even the implied warranty of-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-	GNU General Public License for more details.--	You should have received a copy of the GNU General Public License-	along with this program.  If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@]	Dr. Alistair Ward-- [@DESCRIPTION@]--	Permits /Perl-style shortcut/s to be canned & assigned a single-'Char' mnemonic for subsequent reference;-	the implementation of 'Read' looks for a back-slashed 'Char', for which it expects there to be a corresponding canned 'ShowablePredicate.ShowablePredicate'.-- [@CAVEATS@]--	Since the underlying polymorphic data-type isn't required to implement neither 'Enum' nor 'Ord', the implementation of 'Read' can't cope with range-specifications.-	Lacking this, Bracket-expression members must be enumerated exhaustively.--}--module RegExDot.BracketExpressionMember(--- * Type-classes-	ShortcutExpander(..),--- * Types--- ** Data-types-	Member(..),--- * Functions--- ** Operators-	(=~)-) where--import qualified	Control.Arrow-import qualified	Control.DeepSeq-import qualified	RegExDot.ShowablePredicate	as ShowablePredicate--infix 4 =~	-- Same as (==).--{- |-	* The interface via which /Perl-style shortcut/s are expanded (when they occur within a /bracket-expression/), in a manner appropriate to the chosen type-parameter.--	* The expansion of /Perl-style shortcut/s, is more restricted inside than outside, a /bracket-expression/,-	& consequently are merely represented here by a 'ShowablePredicate.ShowablePredicate', rather than providing a more general form suitable also for those /Perl-style shortcuts/ found outside /bracket-expression/s.--	* This interface is implemented elsewhere, where the specific type-parameter & consequently the appropriate set of /Perl-style shortcut/s, are defined.--}-class ShortcutExpander a	where-	findPredicate	:: Char -> Maybe (ShowablePredicate.ShowablePredicate a)	-- ^ Attempt to find the appropriate 'ShowablePredicate.ShowablePredicate' to implement this /Perl-style shortcut/.--{- |-	* A /BracketExpression/ can contain either a literal, a range of literals given @(Enum a, Ord a)@, a /Perl-style shortcut/, or when 'Char' is the type-parameter, a /POSIX Character-class/.--	* This data-type reduces the representation of all these possibilities to either a predicate or a literal.--}-data Member m	=-	Predicate (ShowablePredicate.ShowablePredicate m)	-- ^ This 'Member' is described using a /predicate/, which is run to determine whether the datum conforms & is a member of the "BracketExpression".-	| Literal m						-- ^ This 'Member' is defined literally, using an item of the polymorphic type.-	deriving Eq--instance Show m => Show (Member m)	where-	showsPrec _ (Predicate showablePredicate)	= shows showablePredicate-	showsPrec _ (Literal literal)			= shows literal--instance (ShortcutExpander m, Read m) => Read (Member m)	where-	readsPrec _ []				= []		-- No parse.-	readsPrec _ (' ' : s)			= reads s	-- Consume white-space.-	readsPrec _ ('\t' : s)			= reads s	-- Consume white-space.-	readsPrec _ ('\\' : shortcut : s)	= case findPredicate shortcut of-		Just showablePredicate	-> [(Predicate showablePredicate, s)]-		_			-> error $ "readsPrec RegExDot.BracketExpressionMember.Member:\tfindPredicate failed for shortcut " ++ show shortcut-	readsPrec _ literal			= Control.Arrow.first Literal `map` reads literal--instance Control.DeepSeq.NFData m => Control.DeepSeq.NFData (Member m)	where-	rnf (Predicate showablePredicate)	= Control.DeepSeq.rnf showablePredicate-	rnf (Literal literal)			= Control.DeepSeq.rnf literal---- | Match-operator.-(=~) :: Eq m-	=> m		-- ^ The input datum.-	-> Member m	-- ^ The member of the bracket-expression against which the input-datum is to be matched.-	-> Bool		-- ^ The result of the match-operation.-datum =~ Predicate showablePredicate	= ShowablePredicate.predicate showablePredicate datum-datum =~ Literal literal		= datum == literal-
− src/RegExDot/CompilationOptions.hs
@@ -1,48 +0,0 @@-{--	Copyright (C) 2010 Dr. Alistair Ward--	This program is free software: you can redistribute it and/or modify-	it under the terms of the GNU General Public License as published by-	the Free Software Foundation, either version 3 of the License, or-	(at your option) any later version.--	This program is distributed in the hope that it will be useful,-	but WITHOUT ANY WARRANTY; without even the implied warranty of-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-	GNU General Public License for more details.--	You should have received a copy of the GNU General Public License-	along with this program.  If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@]	Dr. Alistair Ward-- [@DESCRIPTION@]--	* Defines those options relating to compile-time.--	* These tend to be options which affect the result, rather than the means by which it is achieved.--	* Whilst similar structures are present in other regex-implementations, there's no standardisation of the fields.--}--module RegExDot.CompilationOptions(--- * Types--- ** Data-types-	CompilationOptions(..)-) where--import qualified	ToolShed.Defaultable-import qualified	ToolShed.Options---- | The switch(es) used to control compilation of the /regex/-engine.-data CompilationOptions	= MkCompilationOptions {-	complyStrictlyWithPosix	:: Bool		-- ^ Define the offset of captured data, corresponding to a sub-expression which matched zero times, as the artificial value @-1@ specified by POSIX.-} deriving (Eq, Show)--instance ToolShed.Defaultable.Defaultable CompilationOptions	where-	defaultValue	= ToolShed.Options.blankValue { complyStrictlyWithPosix	= True }--instance ToolShed.Options.Options CompilationOptions	where-	blankValue	= MkCompilationOptions { complyStrictlyWithPosix = undefined }-
− src/RegExDot/Consumer.hs
@@ -1,105 +0,0 @@-{--	Copyright (C) 2010 Dr. Alistair Ward--	This program is free software: you can redistribute it and/or modify-	it under the terms of the GNU General Public License as published by-	the Free Software Foundation, either version 3 of the License, or-	(at your option) any later version.--	This program is distributed in the hope that it will be useful,-	but WITHOUT ANY WARRANTY; without even the implied warranty of-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-	GNU General Public License for more details.--	You should have received a copy of the GNU General Public License-	along with this program.  If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@]	Dr. Alistair Ward-- [@DESCRIPTION@]--	* Define the requirements & capabilities of a data-type which is designed to consume data.--	* This module also facilitates investigation of group-behaviour, by defining operations on either concatenations or alternations of 'Consumer's.--	* This module is specifically for use in a /regex/-engine,-	in which the meta-data from which the /regex/ is composed, are considered to /consume/ input data,-	whilst concatenation & alternation of such elements, behave as a different types of groups of consumer.--}--module RegExDot.Consumer(--- * Type-classes-	Consumer(..),--- * Types--- ** Type-synonyms---	StarHeight,--- * Functions-	accumulateConsumptionProfiles,---	accumulateConsumptionProfilesFrom,-	aggregateConsumptionProfilesFromAlternatives,-	aggregateConsumptionProfilesFromConcatenation,--- ** Query-	getConsumptionBounds,-	getFewest,-	getHasSpecificRequirement-) where--import			RegExDot.ConsumptionProfile((<>), (|+|))-import qualified	Data.List-import qualified	RegExDot.ConsumptionBounds	as ConsumptionBounds-import qualified	RegExDot.ConsumptionProfile	as ConsumptionProfile--{- |-	* A measure of the complexity of a /regex/, which has some baring on either the time-complexity or the space-complexity of the solution.--	* <http://en.wikipedia.org/wiki/Star_height>--}-type StarHeight	= Int--{- |-	* The interface, to which types, which have a data-requirement, may conform.--	* Regrettably, methods requiring reference to the type of the consumable, would require multi-parameter type-classes.--	* 'consumptionProfile' defines the quantity-range of data which may be consumed, & any specific data-requirements.--	* 'starHeight' measures the complexity of the 'Consumer'.--}-class Consumer c	where-	consumptionProfile	:: c -> ConsumptionProfile.ConsumptionProfile-	starHeight		:: c -> StarHeight--instance Consumer c => Consumer [c] where-	consumptionProfile	= undefined-	starHeight		= Data.List.foldl' (\l -> max l . starHeight) 0---- | Convenience-function, to query the 'ConsumptionBounds.ConsumptionBounds' of a 'Consumer'.-getConsumptionBounds :: Consumer c => c -> ConsumptionBounds.ConsumptionBounds-getConsumptionBounds	= ConsumptionProfile.consumptionBounds . consumptionProfile---- | Determine the minimum acceptable quantity of data.-getFewest :: Consumer c => c -> ConsumptionBounds.DataLength-getFewest	= fst . getConsumptionBounds---- | Determine whether the specified 'Consumer', has a specific requirement.-getHasSpecificRequirement :: Consumer c => c -> Bool-getHasSpecificRequirement	= ConsumptionProfile.hasSpecificRequirement . consumptionProfile---- | The aggregate of the specified alternation of 'ConsumptionProfile.ConsumptionProfile's.-aggregateConsumptionProfilesFromAlternatives :: Consumer c => [c] -> ConsumptionProfile.ConsumptionProfile-aggregateConsumptionProfilesFromAlternatives []	= error "RegExDot.Consumer.aggregateConsumptionProfilesFromAlternatives:\tnull list"-aggregateConsumptionProfilesFromAlternatives l	= Data.List.foldl1' (<>) $ consumptionProfile `map` l---- | Get the 'ConsumptionProfile.ConsumptionProfile's for the specified list of 'Consumer's, then find the net effect of concatenating them.-aggregateConsumptionProfilesFromConcatenation :: Consumer c => [c] -> ConsumptionProfile.ConsumptionProfile-aggregateConsumptionProfilesFromConcatenation	= Data.List.foldl' (\acc -> (acc |+|) . consumptionProfile) ConsumptionProfile.zero {-initial value-}---- | Get the 'ConsumptionProfile.ConsumptionProfile' for the specified list of 'Consumer's, then accumulate them, from the specified initial value.-accumulateConsumptionProfilesFrom :: Consumer c => ConsumptionProfile.ConsumptionProfile -> [c] -> ConsumptionProfile.AccumulatedConsumptionProfiles-accumulateConsumptionProfilesFrom	= scanr ((|+|) . consumptionProfile)---- | Get the 'ConsumptionProfile.ConsumptionProfile' for the specified list of 'Consumer's, then accumulate them.-accumulateConsumptionProfiles :: Consumer c => [c] -> ConsumptionProfile.AccumulatedConsumptionProfiles-accumulateConsumptionProfiles	= {-init .-} accumulateConsumptionProfilesFrom ConsumptionProfile.zero	-- It's useful to leave the initial value at the end of the list.-
− src/RegExDot/ConsumptionBounds.hs
@@ -1,48 +0,0 @@-{--	Copyright (C) 2010 Dr. Alistair Ward--	This program is free software: you can redistribute it and/or modify-	it under the terms of the GNU General Public License as published by-	the Free Software Foundation, either version 3 of the License, or-	(at your option) any later version.--	This program is distributed in the hope that it will be useful,-	but WITHOUT ANY WARRANTY; without even the implied warranty of-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-	GNU General Public License for more details.--	You should have received a copy of the GNU General Public License-	along with this program.  If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@]	Dr. Alistair Ward-- [@DESCRIPTION@]	Describes the bounds of data-consumption.--}--module RegExDot.ConsumptionBounds(--- * Types--- ** Type-synonyms-	ConsumptionBounds,-	DataLength,--- * Constants-	zero,--- * Functions--- ** Predicates-	isPrecise-) where---- | A measure of the quantity of an unspecified type of data.-type DataLength	= Int---- | The minimum & maximum bounds of potential data-consumption.-type ConsumptionBounds	= (DataLength, Maybe DataLength)---- | Exactly zero consumption.-zero :: ConsumptionBounds-zero	= (0, Just 0)---- | Predicate which is 'True' if only a precise quantity of data is consumable; no more, no less.-isPrecise :: ConsumptionBounds -> Bool-isPrecise (fewest, most)	= Just fewest == most-
− src/RegExDot/ConsumptionProfile.hs
@@ -1,148 +0,0 @@-{--	Copyright (C) 2010-2015 Dr. Alistair Ward--	This program is free software: you can redistribute it and/or modify-	it under the terms of the GNU General Public License as published by-	the Free Software Foundation, either version 3 of the License, or-	(at your option) any later version.--	This program is distributed in the hope that it will be useful,-	but WITHOUT ANY WARRANTY; without even the implied warranty of-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-	GNU General Public License for more details.--	You should have received a copy of the GNU General Public License-	along with this program.  If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@]	Dr. Alistair Ward-- [@DESCRIPTION@]--	A 'RegExDot.ConsumptionProfile' is composed from both a capacity to consume, & an ability to discriminate based on the specific data.-	The former is described by 'consumptionBounds' & the latter by both 'hasSpecificRequirement' & 'canConsumeAnything'.--}--module RegExDot.ConsumptionProfile(--- * Types--- ** Type-synonyms-	AccumulatedConsumptionProfiles,--- ** Data-types-	ConsumptionProfile(..),--- * Constants-	zero,--- * Functions-	accumulateFrom,-	aggregateFromConcatenation,--- ** Operators-	(|+|),-	(<>),--- ** Predicates---	isPrecise,-	withinConsumptionBounds-) where--import Prelude hiding ((<$>), (<*>))	-- The "Prelude" from 'base-4.8' exports this symbol.-import			Control.Applicative((<$>), (<*>))-import			Control.Arrow((***))-import qualified	Data.List-import qualified	Data.Maybe-import qualified	RegExDot.ConsumptionBounds	as ConsumptionBounds-import qualified	ToolShed.SelfValidate--infixr 5 |+|	-- Same as (++).-infixr 2 <>	-- Same as (||).--{- |-	* A 'Consumer' is considered to have a 'ConsumptionProfile' composed from both a capacity to consume, & an ability to discriminate.--	* Whilst 'hasSpecificRequirement' & 'canConsumeAnything' look like opposites, they can assume independent values; the instance can be both, but only be neither when empty.--}-data ConsumptionProfile	= MkConsumptionProfile {-	consumptionBounds	:: ConsumptionBounds.ConsumptionBounds,	-- ^ The permissible quantity of data, which can be consumed.-	hasSpecificRequirement	:: Bool,				-- ^ Whether at least one specific input datum is required.-	canConsumeAnything	:: Bool					-- ^ Whether at least one arbitrary input datum can be consumed.-} deriving (Eq, Read, Show)--instance ToolShed.SelfValidate.SelfValidator ConsumptionProfile	where-	getErrors c@(-		MkConsumptionProfile {-			consumptionBounds	= (fewest, most),-			hasSpecificRequirement	= hasSpecificRequirement',-			canConsumeAnything	= canConsumeAnything'-		}-	 ) = ToolShed.SelfValidate.extractErrors [-		(fewest < 0, "Negative fewest=" ++ show fewest ++ "."),-		(-			case most of-				Just m	-> m < fewest-				_	-> False,-			"Invalid 'most'; " ++ show c ++ "."-		),-		(not $ or [c == zero, hasSpecificRequirement', canConsumeAnything'], "Invalid " ++ show c ++ ".")-	 ]--{---- | True if there's no choice in the quantity of data to consume.-isPrecise :: ConsumptionProfile -> Bool-isPrecise	= ConsumptionBounds.isPrecise . consumptionBounds--}---- | Predicate, which is 'True' if the specified data-length, falls within the specified 'ConsumptionBounds.ConsumptionBounds'.-withinConsumptionBounds-	:: ConsumptionBounds.DataLength	-- ^ The actual quantity of data consumed.-	-> ConsumptionProfile		-- ^ The bounds within which data-consumption is required to fall.-	-> Bool-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-zero	= MkConsumptionProfile {-	consumptionBounds	= ConsumptionBounds.zero,-	hasSpecificRequirement	= False,-	canConsumeAnything	= False-}---- | The net effect of two concatenated 'ConsumptionProfile's.-(|+|) :: ConsumptionProfile -> ConsumptionProfile -> ConsumptionProfile-MkConsumptionProfile {-	consumptionBounds	= (lf, ls),-	hasSpecificRequirement	= lh,-	canConsumeAnything	= lc-} |+| MkConsumptionProfile {-	consumptionBounds	= (rf, rs),-	hasSpecificRequirement	= rh,-	canConsumeAnything	= rc-} = MkConsumptionProfile {-	consumptionBounds	= (lf + rf, (+) <$> ls <*> rs),	-- The sum of those of the concatenation.-	hasSpecificRequirement	= lh || rh,			-- The concatenation mandates consumption of at least one specific input datum, if either 'ConsumptionProfile' does.-	canConsumeAnything	= lc || rc			-- The concatenation can consume at least one arbitrary input datum, if either 'ConsumptionProfile' can.-}---- | The net effect of two alternative 'ConsumptionProfile's.-(<>) :: ConsumptionProfile -> ConsumptionProfile -> ConsumptionProfile-MkConsumptionProfile {-	consumptionBounds	= (lf, ls),-	hasSpecificRequirement	= lh,-	canConsumeAnything	= lc-} <> MkConsumptionProfile {-	consumptionBounds	= (rf, rs),-	hasSpecificRequirement	= rh,-	canConsumeAnything	= rc-} = MkConsumptionProfile {-	consumptionBounds	= (lf `min` rf, max <$> ls <*> rs),	-- Stretched to envelope alternatives.-	hasSpecificRequirement	= lh && rh,				-- The alternation mandates consumption of at least one specific input datum, if both 'ConsumptionProfile's do.-	canConsumeAnything	= lc || rc				-- The alternation can consume at least one arbitrary input datum, if either 'ConsumptionProfile' can.-}---- | The aggregate of the specified concatenation of 'ConsumptionProfile's.-aggregateFromConcatenation :: [ConsumptionProfile] -> ConsumptionProfile-aggregateFromConcatenation	= Data.List.foldl' (|+|) zero {-initial value-}---- | The 'ConsumptionProfile's of all the aggregated tails of a list of 'Consumer's.-type AccumulatedConsumptionProfiles	= [ConsumptionProfile]---- | Accumulate the specified list of 'ConsumptionProfile's, from the specified initial value.-accumulateFrom :: ConsumptionProfile -> [ConsumptionProfile] -> AccumulatedConsumptionProfiles-accumulateFrom	= scanr (|+|)-
− src/RegExDot/DSL.hs
@@ -1,117 +0,0 @@-{--	Copyright (C) 2010 Dr. Alistair Ward--	This program is free software: you can redistribute it and/or modify-	it under the terms of the GNU General Public License as published by-	the Free Software Foundation, either version 3 of the License, or-	(at your option) any later version.--	This program is distributed in the hope that it will be useful,-	but WITHOUT ANY WARRANTY; without even the implied warranty of-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-	GNU General Public License for more details.--	You should have received a copy of the GNU General Public License-	along with this program.  If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@]	Dr. Alistair Ward-- [@DESCRIPTION@]--	* Defines /binary/ operators, to form a /Domain-specific Language/, by which to compose 'RegEx.Concatenations' from 'RegEx.Pattern's.--	* Each operator quantifies the specified 'RegEx.Pattern', & prepends it to the specified 'RegEx.Concatenation'.-- [@TODO@]	Could @[]@ & 'Meta' be instances of a type-class which has methods (-:) etc.,-		allowing the former to prepend 'RegEx.captureGroup' & the latter 'RegEx.Require' ?---}--module RegExDot.DSL(--- * Functions--- ** Operators-	(-:),-	(?:),-	(??:),-	(*:),-	(*?:),-	(+:),-	(+?:),-	( #->#:),-	( #->#?:),-	( #->:),-	( #->?:),-	( #:),-	(<~>)-) where--import			RegExDot.Repeatable((^#->#), (^#->#?), (^#->), (^#->?), (^#))-import qualified	RegExDot.Anchor		as Anchor-import qualified	RegExDot.RegEx		as RegEx-import qualified	RegExDot.Repeatable	as Repeatable--infixr 5 -:, ?:, ??:, *:, *?:, +:, +?:,#->#:, #->#?:, #->:, #->?:, #:, <~>	-- Same as for ':', & lower than Repeatable's operators.---- | Prepend an unrepeated 'RegEx.Pattern', to the specified 'RegEx.Concatenation'.-(-:) :: RegEx.Pattern a -> RegEx.Concatenation a -> RegEx.Concatenation a-(-:) pattern	= (Repeatable.one pattern :)---- | Prepend an optional 'RegEx.Pattern', to the specified 'RegEx.Concatenation'.-(?:) :: RegEx.Pattern a -> RegEx.Concatenation a -> RegEx.Concatenation a-(?:) pattern	= (Repeatable.zeroOrOne pattern :)---- | A /non-greedy/ version of '?:'.-(??:) :: RegEx.Pattern a -> RegEx.Concatenation a -> RegEx.Concatenation a-(??:) pattern	= (Repeatable.zeroOrOne' pattern :)---- | Prepend a 'RegEx.Pattern', repeatable zero or more times, to the specified 'RegEx.Concatenation'.-(*:) :: RegEx.Pattern a -> RegEx.Concatenation a -> RegEx.Concatenation a-(*:) pattern	= (Repeatable.zeroOrMore pattern :)---- | A /non-greedy/ version of '*:'.-(*?:) :: RegEx.Pattern a -> RegEx.Concatenation a -> RegEx.Concatenation a-(*?:) pattern	= (Repeatable.zeroOrMore' pattern :)---- | Prepend a 'RegEx.Pattern', repeatable one or more times, to the specified 'RegEx.Concatenation'.-(+:) :: RegEx.Pattern a -> RegEx.Concatenation a -> RegEx.Concatenation a-(+:) pattern	= (Repeatable.oneOrMore pattern :)---- | A /non-greedy/ version of '+:'.-(+?:) :: RegEx.Pattern a -> RegEx.Concatenation a -> RegEx.Concatenation a-(+?:) pattern	= (Repeatable.oneOrMore' pattern :)---- | Prepend a 'RegEx.Pattern', repeated a range of times, to the specified 'RegEx.Concatenation'.-( #->#:) :: (RegEx.Pattern a, Repeatable.RepetitionBounds) -> RegEx.Concatenation a -> RegEx.Concatenation a-( #->#:) (pattern, bounds)	= (pattern ^#-># bounds :)---- | A /non-greedy/ version of '#->#:'.-( #->#?:) :: (RegEx.Pattern a, Repeatable.RepetitionBounds) -> RegEx.Concatenation a -> RegEx.Concatenation a-( #->#?:) (pattern, bounds)	= (pattern ^#->#? bounds :)---- | Prepend a 'RegEx.Pattern', repeated at least a specified number of times, to the specified 'RegEx.Concatenation'.-( #->:) :: (RegEx.Pattern a, Repeatable.Repetitions) -> RegEx.Concatenation a -> RegEx.Concatenation a-( #->:) (pattern, fewest)	= (pattern ^#-> fewest :)---- | A /non-greedy/ version of '#->:'.-( #->?:) :: (RegEx.Pattern a, Repeatable.Repetitions) -> RegEx.Concatenation a -> RegEx.Concatenation a-( #->?:) (pattern, fewest)	= (pattern ^#->? fewest :)---- | Prepend a 'RegEx.Pattern', repeated a precise number of times, to the specified 'RegEx.Concatenation'.-( #:) :: (RegEx.Pattern a, Repeatable.Repetitions) -> RegEx.Concatenation a -> RegEx.Concatenation a-( #:) (pattern, r)	= (pattern ^# r :)--{- |-	* Sandwiches a 'RegEx.Concatenation' between optional 'Anchor.Anchor's to construct a 'RegEx.ExtendedRegEx'.--	* Accounts for the unusual requirement to place an 'Anchor.Stern' at the start of the 'RegEx.Concatenation', or an 'Anchor.Bow' at the end.--}-(<~>)-	:: 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,-	RegEx.sternAnchor	= stern-}
− src/RegExDot/DataSpan.hs
@@ -1,61 +0,0 @@-{--	Copyright (C) 2010 Dr. Alistair Ward--	This program is free software: you can redistribute it and/or modify-	it under the terms of the GNU General Public License as published by-	the Free Software Foundation, either version 3 of the License, or-	(at your option) any later version.--	This program is distributed in the hope that it will be useful,-	but WITHOUT ANY WARRANTY; without even the implied warranty of-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-	GNU General Public License for more details.--	You should have received a copy of the GNU General Public License-	along with this program.  If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@]	Dr. Alistair Ward-- [@DESCRIPTION@]	Augments a 'Span.Span', with the specific data concerned.--}--module RegExDot.DataSpan(--- * Types--- ** Type-synonyms-	DataSpan,--- * Functions-	after,-	empty,-	join-) where--import			Control.Arrow((***))-import qualified	RegExDot.ConsumptionBounds	as ConsumptionBounds-import qualified	RegExDot.Span			as Span--{- |-	* Augment 'Span.Span' with a copy of the data to which it refers.--	* Similar to <http://hackage.haskell.org/packages/archive/regex-base/latest/doc/html/Text-Regex-Base-RegexLike.html#t%3AMatchText>.--}-type DataSpan a	= ([a], Span.Span)---- | Constructs an empty instance, located at the specified offset.-empty :: ConsumptionBounds.DataLength -> DataSpan a-empty offset	= ([], Span.empty offset)---- | Returns the furthest extent, i.e. the offset just after the end.-after :: DataSpan a -> ConsumptionBounds.DataLength-after	= Span.after . snd--{- |-	* Condenses a list into a single value.--	* Expects a gap-free, sorted list.--}-join-	:: ConsumptionBounds.DataLength	-- ^ The offset at which the concatenated match is considered to have occurred.-	-> [DataSpan a]			-- ^ The list to be concatenated.-	-> DataSpan a-join offset	= (concat *** Span.join offset) . unzip
− src/RegExDot/DataSpanTree.hs
@@ -1,108 +0,0 @@-{--	Copyright (C) 2010-2015 Dr. Alistair Ward--	This program is free software: you can redistribute it and/or modify-	it under the terms of the GNU General Public License as published by-	the Free Software Foundation, either version 3 of the License, or-	(at your option) any later version.--	This program is distributed in the hope that it will be useful,-	but WITHOUT ANY WARRANTY; without even the implied warranty of-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-	GNU General Public License for more details.--	You should have received a copy of the GNU General Public License-	along with this program.  If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@]	Dr. Alistair Ward-- [@DESCRIPTION@]	Permits transformation of 'RegEx.MatchList', to facilitate standardisation.--}--module RegExDot.DataSpanTree(--- * Types--- ** Type-synonyms---	DataSpanTree,---	DataSpanTreeList,--- * Functions-	extractCaptureGroups,-	flattenTreeList,-	toTreeList-) where--import qualified	Data.Foldable-import qualified	RegExDot.ConsumptionBounds	as ConsumptionBounds-import qualified	RegExDot.DataSpan		as DataSpan-import qualified	RegExDot.RegEx			as RegEx-import qualified	RegExDot.Tree			as Tree---- | Defines a similar 'Tree.Tree' to 'RegEx.Match', but with different 'Tree.Leaf's.-type DataSpanTree a	= Tree.Tree (DataSpan.DataSpan a)---- | Defines a similar structure to 'RegEx.MatchList'.-type DataSpanTreeList a	= [DataSpanTree a]---- | Converts a 'RegEx.MatchList' into a 'DataSpanTreeList', by transforming the 'Tree.Leaf's.-toTreeList :: RegEx.MatchList a -> DataSpanTreeList a-toTreeList	= map toTree	where-	toTree :: RegEx.Match a -> DataSpanTree a-	toTree	= fmap toDataSpan	where-		toDataSpan :: RegEx.MatchedData a -> DataSpan.DataSpan a-		toDataSpan (_, inputDataOffset, inputData)	= (inputData, (inputDataOffset, length inputData))---- | Condenses a 'DataSpanTreeList's into a list of 'DataSpan.DataSpan's, using 'DataSpan.join'.-flattenTreeList-	:: ConsumptionBounds.DataLength	-- ^ The offset into the input-data at which a match occurred.-	-> DataSpanTreeList a		-- ^ The tree to flatten.-	-> [DataSpan.DataSpan a]-flattenTreeList _ []				= []-flattenTreeList offset (tree : treeList)	= flattenedTree : flattenTreeList (DataSpan.after flattenedTree) treeList	where---	flattenedTree :: DataSpan.DataSpan a-	flattenedTree	= DataSpan.join offset $ Data.Foldable.toList tree--{- |-	* POSIX describes the contents of /capture-groups/, as summarised in <http://www2.research.att.com/~gsf/testregex/>.--	* 'RegEx.Result', is a complete description of the match between 'RegEx.InputData' & RegEx.ExtendedRegEx'; this function extracts a POSIX-conformant list from it.--	* The major differences are, that:--		Only data from parenthesized sub-expressions ('RegEx.Alternatives') is captured.--		Only the /last/ repetition of a repeated sub-expression is returned.-		<http://www.opengroup.org/onlinepubs/009695399/functions/regcomp.html>.--		The data captured within each parenthesized sub-expression, is summarised as a single 'DataSpan.DataSpan'.--		POSIX specifies a 'Span.Span'-offset of @-1@, for sub-expressions which match zero times; cf sub-expressions which consume nothing, once.-		<http://www.opengroup.org/onlinepubs/009695399/functions/regcomp.html>.-		@-			("ace" Text.Regex.Posix.=~ "a(b)*c(d)?e") :: Text.Regex.Base.RegexLike.MatchArray-			array (0,2) [(0,(0,3)),(1,(-1,0)),(2,(-1,0))]--			("ace" Text.Regex.Posix.=~ "a(b*)c(d?)e") :: Text.Regex.Base.RegexLike.MatchArray-			array (0,2) [(0,(0,3)),(1,(1,0)),(2,(2,0))]-		@-		I consider this a poor convention, resulting from the focus of POSIX on C, which makes subsequent calculation from the list of 'DataSpan's difficult & error-prone.--}-extractCaptureGroups-	:: Bool			-- ^ Whether to strictly comply with /POSIX/.-	-> DataSpanTreeList a	-- ^ The tree-structure from which to extract the capture-groups.-	-> [DataSpan.DataSpan a]-extractCaptureGroups complyStrictlyWithPosix	= extractCaptureGroups' 0	where-	extractCaptureGroups' :: ConsumptionBounds.DataLength -> DataSpanTreeList a -> [DataSpan.DataSpan a]-	extractCaptureGroups' _ []			= []-	extractCaptureGroups' offset (tree : treeList)	= let---		recurseHorizontallyFrom :: ConsumptionBounds.DataLength -> [DataSpan.DataSpan a]-		recurseHorizontallyFrom	= (`extractCaptureGroups'` treeList)-	 in case tree of-		Tree.Leaf dataSpan	-> recurseHorizontallyFrom $ DataSpan.after dataSpan-		Tree.Node []		-> DataSpan.empty (if complyStrictlyWithPosix then -1 else offset) : recurseHorizontallyFrom offset	-- POSIX specifies an Span-offset of -1, for sub-expressions which match 0 times; cf sub-expressions which consumes nothing, once.-		Tree.Node treeLists	-> joinedFlattenedTreeList : (extractCaptureGroups' offset lastMatch {-recurse vertically-} ++ recurseHorizontallyFrom (DataSpan.after joinedFlattenedTreeList))	where---			lastMatch :: DataSpanTreeList a-			lastMatch	= last treeLists	-- <http://www.opengroup.org/onlinepubs/009695399/functions/regcomp.html>.----			joinedFlattenedTreeList :: DataSpan.DataSpan a-			joinedFlattenedTreeList	= DataSpan.join offset $ flattenTreeList offset lastMatch-
− src/RegExDot/ExecutionOptions.hs
@@ -1,102 +0,0 @@-{--	Copyright (C) 2010 Dr. Alistair Ward--	This program is free software: you can redistribute it and/or modify-	it under the terms of the GNU General Public License as published by-	the Free Software Foundation, either version 3 of the License, or-	(at your option) any later version.--	This program is distributed in the hope that it will be useful,-	but WITHOUT ANY WARRANTY; without even the implied warranty of-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-	GNU General Public License for more details.--	You should have received a copy of the GNU General Public License-	along with this program.  If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@]	Dr. Alistair Ward-- [@DESCRIPTION@]--	* Defines those options relating to the "RegExDot.RegEx"-runtime.--	* These tend to be options which don't affect the result, only the means by which it is achieved.--	* Whilst similar structures are present in other regex-implementations, there's no standardisation of the fields.--}--module RegExDot.ExecutionOptions(--- * Types--- ** Data-types-	ExecutionOptions(..),--- * Functions--- ** Mutators-	setVerbose-) where--import qualified	ToolShed.Defaultable-import qualified	ToolShed.Options---- | The switches used to control execution of the /regex/-engine.-data ExecutionOptions	= MkExecutionOptions {-	abortTrialRepetitionsOnInherentFailure	:: Bool,	-- ^ If an /alternative/ can't match, irrespective of the subsequent /RegExDot.RegEx.Concatenation/, then avoid futile trial /RegExDot.Repeatable.Repetitions/. The converse of 'checkForUnconsumableData'.-	abortTrialRepetitionsOnZeroConsumption	:: Bool,	-- ^ Check for zero data-consumption by the @n@-th /RegExDot.Repeatable.Repeatable RegExDot.RegEx.CaptureGroup/, before attempting @n+1@.-	bypassInputDataForLiberalConsumer	:: Bool,	-- ^ Whether to bypass reading of the input data, if the mapping to specific /RegExDot.RegEx.RepeatablePattern/s isn't required, & the /RegExDot.RegEx.ExtendedRegEx/ can consume the required quantity of anything.-	catchIncompatibleAnchors		:: Bool,	-- ^ Avoid futile trial solutions, involving repetitions of anchored alternatives, which must consume data.-	checkExistenceOfInelasticTail		:: Bool,	-- ^ If the /RegExDot.RegEx.ExtendedRegEx/ ends in an inelastic (zero /Star-height/) tail, confirm its existence at the end of the /RegExDot.RegEx.InputData/.-	checkForUnconsumableData		:: Bool,	-- ^ Check whether there's no possibility of consuming some of the input data. The converse of 'abortTrialRepetitionsOnInherentFailure'.-	moderateGreed				:: Bool,	-- ^ Greedily consume data, only up to the limit beyond which, future requirements would be compromised.-	permitReorderingOfAlternatives		:: Bool,	-- ^ Permit /RegExDot.RegEx.Alternatives/ to be re-ordered, in an attempt to more quickly locate a result.-	preferAlternativesWhichFeedTheGreedy	:: Bool,	-- ^ Within the /RegExDot.RegEx.MatchedData/ from which each candidate /RegExDot.RegEx.Match/ amongst sequences of /RegExDot.RegEx.Alternatives/, is ultimately composed, prefer /RegExDot.ConsumptionBounds.ConsumptionBounds/ of /RegExDot.RegEx.InputData/, beyond /RegExDot.Repeatable.getFewest/, by /RegExDot.Repeatable.isGreedy RegExDot.RegEx.RepeatablePattern/s.-	preferAlternativesWhichMimickUnrolling	:: Bool,	-- ^ Compare /RegExDot.ConsumptionBounds.ConsumptionBounds/ on successive /RegExDot.Repeatable.Repetitions/ of /RegExDot.RegEx.CaptureGroup/, between candidate /RegExDot.RegEx.Match/es, to mimic the behaviour of the unrolled /RegExDot.Repeatable.Repetitions/.-	preferFewerRepeatedAlternatives		:: Bool,	-- ^ Prefer fewer /RegExDot.Repeatable.Repetitions/ of /RegExDot.RegEx.Alternatives/, to discourage the capture of null lists of /RegExDot.RegEx.InputData/.-	requireMatchList			:: Bool,	-- ^ If merely interested in a 'Bool' result, rather than the optimal mapping of input data to /RegExDot.RegEx.RepeatablePattern/s, avoid unnecessary evaluation of the /RegEx.Match/.-	unrollRepeatedSingletonAlternative	:: Bool,	-- ^ Check whether /RegExDot.RegEx.Alternatives/ consists of just a singleton /RegExDot.RegEx.ExtendedRegEx/, & has therefore been used merely as a capture-group. Though this doesn't affect the result, it vastly improves efficiency.-	useFirstMatchAmongAlternatives		:: Bool,	-- ^ Rather than performing an exhaustive search for the optimal choice amongst /RegExDot.RegEx.Alternatives/, merely select the first that matches; conform to /Perl/ rather than /POSIX/.-	validateMinConsumptionOfAlternatives	:: Bool		-- ^ When the number of repetitions of a /RegExDot.RegEx.CaptureGroup/ is precisely specified, check whether the resulting minimum data-requirement is available.-} deriving (Eq, Show)--instance ToolShed.Defaultable.Defaultable ExecutionOptions	where-	defaultValue	= setVerbose False $ ToolShed.Options.blankValue {-		abortTrialRepetitionsOnInherentFailure	= True,		-- Regrettably, this slightly reduces performance for most non-pathological patterns.-		catchIncompatibleAnchors		= True,-		checkExistenceOfInelasticTail		= True,-		checkForUnconsumableData		= True,		-- Expensive, particularly when (not requireMatchList), & only typically useful in failure-scenarios.-		moderateGreed				= True,		-- Cost may exceed benefit. TODO: confirm.-		preferAlternativesWhichFeedTheGreedy	= True,-		preferAlternativesWhichMimickUnrolling	= True,-		preferFewerRepeatedAlternatives		= True,-		unrollRepeatedSingletonAlternative	= True,		-- Affects only efficiency, not the result.-		useFirstMatchAmongAlternatives		= False,	-- Perl-style matching may be faster, but may also yield a sub-optimal Match.-		validateMinConsumptionOfAlternatives	= False		-- The cost outweighs the small infrequent dividend.-	}--instance ToolShed.Options.Options ExecutionOptions	where-	blankValue	= MkExecutionOptions {-		abortTrialRepetitionsOnInherentFailure	= undefined,-		abortTrialRepetitionsOnZeroConsumption	= undefined,-		bypassInputDataForLiberalConsumer	= undefined,-		catchIncompatibleAnchors		= undefined,-		checkExistenceOfInelasticTail		= undefined,-		checkForUnconsumableData		= undefined,-		moderateGreed				= undefined,-		permitReorderingOfAlternatives		= undefined,-		preferAlternativesWhichFeedTheGreedy	= undefined,-		preferAlternativesWhichMimickUnrolling	= undefined,-		preferFewerRepeatedAlternatives		= undefined,-		requireMatchList			= undefined,-		unrollRepeatedSingletonAlternative	= undefined,-		useFirstMatchAmongAlternatives		= undefined,-		validateMinConsumptionOfAlternatives	= undefined-	}---- | Sets those fields which depend crucially on whether the caller wants to retrieve any /RegExDot.RegEx.MatchList/ from the /RegExDot.RegEx.Result/, or just query whether there is one.-setVerbose :: Bool -> ExecutionOptions -> ExecutionOptions-setVerbose verbose e	= e {-	abortTrialRepetitionsOnZeroConsumption	= verbose,	-- The corresponding check, involves evaluation of a /RegExDot.RegEx.MatchList/, which is too expensive if the /RegExDot.RegEx.Matchlist/ isn't otherwise required.-	bypassInputDataForLiberalConsumer	= not verbose,	-- Potentially bypasses reading of /RegExDot.RegEx.InputData/, which is inappropriate if the mapping into a /RegExDot.RegEx.Result/ is required.-	permitReorderingOfAlternatives		= not verbose,	-- Doesn't help when 'requireMatchList', since an exhaustive search of /RegExDot.RegEx.Alternatives/, for the optimal solution, is performed.-	requireMatchList			= verbose-}-
− src/RegExDot/InstanceInt.hs
@@ -1,43 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-{--	Copyright (C) 2010 Dr. Alistair Ward--	This program is free software: you can redistribute it and/or modify-	it under the terms of the GNU General Public License as published by-	the Free Software Foundation, either version 3 of the License, or-	(at your option) any later version.--	This program is distributed in the hope that it will be useful,-	but WITHOUT ANY WARRANTY; without even the implied warranty of-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-	GNU General Public License for more details.--	You should have received a copy of the GNU General Public License-	along with this program.  If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@]	Dr. Alistair Ward-- [@DESCRIPTION@]	Provides a default implementation of the type-classes, required for 'RegEx.ExtendedRegEx' Int.-- [@CAVEATS@]--	In contrast to the traditional regex, there's no reason to escape a 'Char', other than when referring to a /shortcut/.-	Since no /shortcuts/ are defined in this minimal definition, it's always an error to escape a character.--}--module RegExDot.InstanceInt() where--import qualified	RegExDot.BracketExpressionMember	as BracketExpressionMember-import qualified	RegExDot.Meta				as Meta-import qualified	RegExDot.RegEx				as RegEx--instance BracketExpressionMember.ShortcutExpander Int	where-	findPredicate _	= Nothing--instance Meta.ShortcutExpander Int	where-	expand c	= error $ "RegExDot.Meta.ShortcutExpander.expand RegExDot.InstanceInt:\tunrecognised shortcut '" ++ show c ++ "'."	-- A shortcut is the only legitimate reason to escape a character.--instance RegEx.ShortcutExpander Int	where-	expand c	= error $ "RegExDot.RegEx.ShortcutExpander.expand RegExDot.InstanceInt:\tunrecognised shortcut '" ++ show c ++ "'."	-- A shortcut is the only legitimate reason to escape a character.-
− src/RegExDot/Meta.hs
@@ -1,144 +0,0 @@-{--	Copyright (C) 2010 Dr. Alistair Ward--	This program is free software: you can redistribute it and/or modify-	it under the terms of the GNU General Public License as published by-	the Free Software Foundation, either version 3 of the License, or-	(at your option) any later version.--	This program is distributed in the hope that it will be useful,-	but WITHOUT ANY WARRANTY; without even the implied warranty of-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-	GNU General Public License for more details.--	You should have received a copy of the GNU General Public License-	along with this program.  If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@]	Dr. Alistair Ward-- [@DESCRIPTION@]--	* Describes the set of polymorphic data, which can be matched.--	* Only permits a match against exactly one polymorphic datum, which distinguishes it from a /zero-width assertion/, like an /anchor/, /word-boundary/, or /look-ahead assertion/.--	* Designed to be used by a polymorphic /regex/-engine, to implement the traditional meta-characters; @. [] [^]@.--	* Permits /Perl-style shortcuts/ for commonly used 'Meta'-data, to be canned & assigned a single-'Char' mnemonic for subsequent reference;-	the implementation of 'Read' looks for a back-slashed 'Char', for which it expects there to be a corresponding canned 'Meta'.-	Since this class is polymorphic, it has no knowledge of what shortcuts might be appropriate for the chosen type-parameter,-	so the expansion from the back-slashed 'Char' to corresponding 'Meta'-data, is performed through the 'expand' interface of the 'ShortcutExpander' class, which should be implemented elsewhere.--}--module RegExDot.Meta(--- * Type-classes-	ShortcutExpander(..),--- * Types--- ** Data-types-	Meta(..),--- * Constants-	shortcutToken,-	anyToken,-	tokens,--- * Functions--- ** Predicates-	isMatch-) where--import qualified	Control.Arrow-import qualified	Control.DeepSeq-import qualified	RegExDot.BracketExpression		as BracketExpression-import qualified	RegExDot.BracketExpressionMember	as BracketExpressionMember-import qualified	RegExDot.Consumer			as Consumer-import qualified	RegExDot.ConsumptionProfile		as ConsumptionProfile-import qualified	RegExDot.ShowablePredicate		as ShowablePredicate-import qualified	ToolShed.SelfValidate--{- |-	* The interface via which /Perl-style shortcut/s are expanded, in a manner appropriate to the chosen type-parameter.--	* Since the expansion of /Perl-style shortcut/s, is more restricted inside than outside a 'BracketExpression.BracketExpression',-	the former is considered to be a superclass, providing a base from which to build alternative implementations.--}-class BracketExpressionMember.ShortcutExpander m => ShortcutExpander m	where-	expand	:: Char -> Meta m	-- ^ Expand a /Perl-style shortcut/.---- | Declares a polymorphic data-type.-data Meta m =-	Any							-- ^ Any datum matches. Equivalent to @NoneOf []@, but more efficient. CAVEAT: independent of the type-parameter @a@.-	| Literal m						-- ^ The datum matches, if it's equal to the specified value. Equivalent to @AnyOf [BracketExpression.Literal x]@, but more efficient.-	| AnyOf (BracketExpression.BracketExpression m)		-- ^ The datum matches, if 'BracketExpression.containsMatch'.-	| NoneOf (BracketExpression.BracketExpression m)	-- ^ The datum matches, if @not BracketExpression.containsMatch@.-	| Predicate (ShowablePredicate.ShowablePredicate m)	-- ^ The datum matches if 'ShowablePredicate.ShowablePredicate'.-	deriving (-		Eq---		Read,	-- Specialised below.---		Show	-- Specialised below.-	)--instance ToolShed.SelfValidate.SelfValidator (Meta m)	where-	getErrors _	= []--instance Show m => Show (Meta m)	where-	showsPrec _ Any					= showChar anyToken-	showsPrec _ (Literal m)				= shows m-	showsPrec _ (AnyOf bracketExpression)		= shows bracketExpression-	showsPrec _ (NoneOf bracketExpression)		= showChar x . showChar BracketExpression.negationToken . showString xs	where (x : xs)	= show $ AnyOf bracketExpression-	showsPrec _ (Predicate showablePredicate)	= shows showablePredicate--instance (ShortcutExpander m, Read m) => Read (Meta m)	where-	readsPrec _ []				= []		-- No parse.-	readsPrec _ (' ' : s)			= reads s	-- Consume white-space.-	readsPrec _ ('\t' : s)			= reads s	-- Consume white-space.-	readsPrec _ ('.' : s)			= [(Any, s)]-	readsPrec _ ('[' : '^' : noneOf)	= Control.Arrow.first NoneOf `map` reads (fst BracketExpression.delimiterTokens : noneOf) {-Reconstruct without negation, & recurse-}-	readsPrec _ anyOf@('[' : _)		= Control.Arrow.first AnyOf `map` reads anyOf {-singleton-}-	readsPrec _ ('\\' : c : s)		= [(expand c, s)]-	readsPrec _ literal			= Control.Arrow.first Literal `map` reads literal {-singleton-}--instance Consumer.Consumer (Meta m)	where-	consumptionProfile meta	= let-		hasSpecificDataRequirement :: Bool-		hasSpecificDataRequirement	= case meta of-			Any		-> False-			NoneOf []	-> False-			_		-> True-	 in ConsumptionProfile.MkConsumptionProfile {-		ConsumptionProfile.consumptionBounds		= (1, Just 1),-		ConsumptionProfile.hasSpecificRequirement	= hasSpecificDataRequirement,-		ConsumptionProfile.canConsumeAnything		= not hasSpecificDataRequirement-	 }--	starHeight _	= 0--instance Control.DeepSeq.NFData m => Control.DeepSeq.NFData (Meta m)	where-	rnf Any					= ()-	rnf (Literal m)				= Control.DeepSeq.rnf m-	rnf (AnyOf bracketExpression)		= Control.DeepSeq.rnf bracketExpression-	rnf (NoneOf bracketExpression)		= Control.DeepSeq.rnf bracketExpression-	rnf (Predicate showablePredicate)	= Control.DeepSeq.rnf showablePredicate---- | True if the specified datum matches.-isMatch :: Eq m-	=> m		-- ^ The input datum.-	-> Meta m	-- ^ The meta-entity against which the input datum is to be matched.-	-> Bool		-- ^ The result of the match-operation.-isMatch _ Any					= True-isMatch datum (Literal literal)			= datum == literal-isMatch datum (AnyOf bracketExpression)		= datum `BracketExpression.containsMatch` bracketExpression-isMatch datum (NoneOf bracketExpression)	= not $ datum `isMatch` AnyOf bracketExpression	-- This implementation leverages future enhancements to 'AnyOf'.-isMatch datum (Predicate showablePredicate)	= ShowablePredicate.predicate showablePredicate datum---- | The token used to precede a /Perl-style shortcut/, when in the 'String'-form.-shortcutToken :: Char-shortcutToken	= '\\'---- | The token used to denote 'Any', when in the 'String'-form.-anyToken :: Char-anyToken	= '.'---- | The set of 'Char' to which a specific meaning is attributed, when reading from 'String'.-tokens :: String-tokens	= [shortcutToken, anyToken] ++ BracketExpression.tokens-
− src/RegExDot/RegEx.hs
@@ -1,1198 +0,0 @@-{--	Copyright (C) 2010-2015 Dr. Alistair Ward--	This program is free software: you can redistribute it and/or modify-	it under the terms of the GNU General Public License as published by-	the Free Software Foundation, either version 3 of the License, or-	(at your option) any later version.--	This program is distributed in the hope that it will be useful,-	but WITHOUT ANY WARRANTY; without even the implied warranty of-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-	GNU General Public License for more details.--	You should have received a copy of the GNU General Public License-	along with this program.  If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@]	Dr. Alistair Ward-- [@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@.--	* 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.-	Each element of this 'Concatenation' is a 'RepeatablePattern', which describes a permissible match against 'InputData'.--	* 'RepeatablePattern' can take one of two forms.-	In the simplest case, it matches just a single item of the underlying polymorphic type, perhaps literally, though looser specifications also exist:-	@.@ matches any input datum; @[x, y, z]@ matches any of @x@, @y@, or @z@; @[^x, y, z]@ matches anything but @x@, @y@, or @z@.-	To support POSIX /ERE/s, 'RepeatablePattern' can also be a list 'Alternatives', each of which is recursively defined as an 'ExtendedRegEx', to form a tree-structure.--	* Each 'Pattern', can optionally be /quantified/ by either a traditional /greedy/, or a /Perl/-style /non-greedy/, suffix, e.g.; @[*, +, ?, {n, m}, {n,}, {n}, *?, +?, ??, {n, m}?, {n,}?]@.--	* For convenience, common specifications can be canned & assigned a single 'Char' mnemonic, for subsequent reference.-	Since 'ExtendedRegEx' is polymorphic, the set of abbreviations appropriate in the context of the unspecified base-type,-	must be implemented externally through the 'Meta.ShortcutExpander' interface.-	This permits the use, when the type-parameter is 'Char', of /Perl-style shortcuts/ @[\\d\\D\\s\\S\\w\\W]@.--	* The algorithm, is the classic /back-tracking/ one, rather than either a /DFA/ or /NFA/.-	This permits construction of 'Result' via which one can discover the deep mapping of 'InputData' into 'ExtendedRegEx',-	& provides the flexibility to add the features now expected by modern /regex/-engines.-	Since the type-parameter is unknown, & may represent a large object, the exponential space-complexity of creating a /DFA/ may present additional problems.-	The exponential time-complexity of the /back-tracking/ algorithm is partially tamed by targeting obvious inefficiencies with specific optimisations.--	* Char-based regexen, traditionally overload the delimiters of a set of 'Alternatives' (parentheses), as a request for data-capture.-	Here, in contrast, all 'RepeatablePattern's capture data, & repeated sub-expressions capture a list of data,-	rather than arbitrarily recording just the last (<http://www.opengroup.org/onlinepubs/009695399/functions/regcomp.html>) item.-- [@REFERENCES@]--	* <http://en.wikipedia.org/wiki/Regular_expression>--	* <http://swtch.com/~rsc/regexp/regexp1.html>--	* <http://docstore.mik.ua/orelly/perl/prog/ch02_04.htm#PERL2-CH-2-SECT-4.1.2>--	* <http://www.macs.hw.ac.uk/~dsg/gph/papers/html/Strategies/strategies.html>--	* <http://hackage.haskell.org/packages/archive/regex-posix/latest/doc/html/Text-Regex-Posix.html>--	* <http://www.haskell.org/haskellwiki/Regular_expressions>--	* <http://www2.research.att.com/~gsf/testregex/re-interpretation.html>-- [@CAVEATS@]--	* Because of the definition of mutually recursive data-types, it is difficult to split this annoyingly large module, & preserve compatibility across compilers,-	but it may be possible to break this cyclic dependency, by defining an interface to which one of the data-types defined here conforms.--	* Doesn't implement /Back-references/, making the definition of the 'ExtendedRegEx' context-free.--	* There's no integration with the type-classes defined in "Text.Regex.Base.RegexLike", which assumes 'Char'-based 'InputData';-	though this could be added to a specialised instance.--	* When 'Alternatives' are defined, 'Result' becomes a tree-like structure.-	Unless the alternative is a singleton, the specific alternative selected in the solution is typically unknown, & therefore the /structure/ of the branch of this tree is also unknown.-	This lack of clarity is compounded when the 'Alternatives' are 'Repeatable.Repeatable', since a different one may be selected on each successive repetition.-	Consequently, the user can't navigate this portion of the structure in a statically defined manner, to acquire the captured data.-	Despite this, & in contrast to other /regex/-engines, access to the whole data-structure is available, since it doesn't seem advantage to hide it.-	The user can then either use 'extractDataFromMatch' for that element of 'Result', thus aggregating the data from sections of unknown structure, or 'show' it, as an aid to debugging.-- [@TODO@]--	* Test parallel-operation, on a 3 or more processor machine.-	If 'rnf' is less effective than 'rwhnf',-	then the 'Control.DeepSeq.NFData' context can be removed,-	reducing the requirements imposed on the type-parameter 'a'.--	* Try 'Data.List.Stream' (stream-fusion), a faster drop-in replacement for 'Data.List'; possibly integrated in GHC-6.12.--	* 'ExecutionOptions.bypassInputDataForLiberalConsumer' is too restrictive.-	More generally, we can test whether the set of different 'a' in 'InputData', is a subset of those common to all remaining terms in the 'ExtendedRegEx'.-	Using this rule, we can infer @"aaa ..." =~ MkExtendedRegEx [a,a+,a?,[ab]{2,3}]@, given compatible 'ConsumptionProfile.consumptionBounds'.--	* Nested repetitions, where nothing has been added to the expression, result in repeated trials of the same expression,-	e.g.; @"(x{i,}){j,}"@ results in the same expansion for @(i, j) in [(2, 3), (3, 2), (6, 1), (1, 6)]@.-	The resulting 'MatchList' may be different, but if the first such trial fails, so will all the remainder.--	* Should cope with empty sets of 'Alternatives' & zero repetitions, neither of which can ever match, but the wider pattern can, e.g. @(()|x{0}|y)@.--	* By removing 'RepeatablePattern' from 'Match', it can be isolated in a new module.-	This would result in a significant loss of discoverability.--	* Expand repeated 'Anchor.Bow' with @fewest - 1@ null matches followed by recursive 'findMatch'-call with @repetitions = 1@.--}--module RegExDot.RegEx(--- * Type-classes-	ShortcutExpander(..),--- * Types--- ** Type-synonyms---	BasicRegEx,-	Concatenation,-	ExternalMatch,-	InputData,-	MatchedData,-	MatchList,---	MetaDataList,-	RepeatablePattern,---	Transformation,--- ** Data-types-	Alternatives(..),-	Match,-	ExtendedRegEx(..),-	Pattern(..),-	Result,--- * Constants-	alternativeExtendedRegExSeparatorToken,---	anyDatum,-	captureGroupDelimiters,-	tokens,--- * Functions---	accumulateDistinctInputData,---	accumulateDistinctMetaDataFrom,-	dock,---	drift,---	findMatch,-	captureGroup,---	mkNullMatchFromConcatenation,---	mkNullMatchFromExtendedRegEx,---	mkNullMatchFromRepeatablePattern,---	safeReciprocal,---	shiftMatchedData,---	shiftMatch,-	shiftMatchList,-	showsMaybeAnchor,-	simply,-	transformExtendedRegEx,--- ** Accessors (Deconstructors)-	deconstructAlternatives,---	getInputData,--- ** Operators-	(+~),-	(=~),-	(/~),-	(.*),-	(.*?),---	(/+)--- ** Predicates---	hasBowAnchor,---	hasSternAnchor,-	isDefined,-	isCaptureGroup,-	isSingletonAlternatives,---	isUnconsumableByAnyOf,--- ** Query---	getDistinctMetaDataFromAlternatives,---	getDistinctMetaDataFromConcatenation,---	getDistinctMetaDataFromExtendedRegEx,---	getDistinctMetaDataFromPattern,---	getDistinctMetaDataFromRepeatablePattern,-	externalMatchLength,-	extractDataFromMatch,-	extractDataFromMatch',-	extractDataFromMatchList-) where--import			Control.Arrow((&&&))-import qualified	Control.Arrow-import qualified	Control.DeepSeq-import qualified	Control.Parallel.Strategies-import qualified	Data.Char-import qualified	Data.Foldable-import qualified	Data.List-import qualified	Data.Maybe-import qualified	Data.Ord-import qualified	RegExDot.Anchor			as Anchor-import qualified	RegExDot.Consumer		as Consumer-import qualified	RegExDot.ConsumptionBounds	as ConsumptionBounds-import qualified	RegExDot.ConsumptionProfile	as ConsumptionProfile-import qualified	RegExDot.ExecutionOptions	as ExecutionOptions-import qualified	RegExDot.Meta			as Meta-import qualified	RegExDot.RegExOpts		as RegExOpts-import qualified	RegExDot.Repeatable		as Repeatable-import			RegExDot.Repeatable((^#->))-import qualified	RegExDot.Tree			as Tree-import qualified	ToolShed.Data.List-import qualified	ToolShed.Data.List.Splits-import qualified	ToolShed.SelfValidate--infix 4 +~, =~, /~	-- Same as (==) & (/=).---- | The type of a /regex/, in which there's no provision for either 'Alternatives' or 'Anchor.Anchor's.-type BasicRegEx m	= [Repeatable.Repeatable (Meta.Meta m)]--{- |-	* Defines the method required to expand a mnemonic into an 'ExtendedRegEx'.--	* CAVEAT: this interface must be declared locally, since it references 'ExtendedRegEx', & 'ExtendedRegEx' references it.--}-class ShortcutExpander m	where-	expand	:: Char -> ExtendedRegEx m	-- ^ Expand a single-'Char' mnemonic into the corresponding 'ExtendedRegEx'.--{- |-	* Represents the /alternation/ feature of 'ExtendedRegEx's.--	* One could amalgamate this with 'Pattern', since it seems to exist merely as a peg to hang /instance/-declarations from.--}-newtype Alternatives m	= MkAlternatives [ExtendedRegEx m]	deriving Eq---- | Accessor, to expose the guts.-deconstructAlternatives :: Alternatives m -> [ExtendedRegEx m]-deconstructAlternatives (MkAlternatives extendedRegExList)	= extendedRegExList---- | Similar to 'fmap', but operates on ['ExtendedRegEx'], rather than just @a@.-transformAlternatives-	:: ([ExtendedRegEx m] -> [ExtendedRegEx m])	-- ^ The transformation function to apply to the list of data behind the constructor.-	-> Alternatives m-	-> Alternatives m-transformAlternatives f	= MkAlternatives . f . deconstructAlternatives--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-		[(extendedRegEx, s1)]	-> case dropWhile Data.Char.isSpace s1 of-			('|' : s2)	-> Control.Arrow.first (transformAlternatives (extendedRegEx :)) `map` reads s2 {-singleton-}-			_		-> [(MkAlternatives [extendedRegEx], s1)]-		_			-> []	-- No parse.--instance Show m => Show (Alternatives m)	where-	showsPrec _	= foldl (.) (showString "") . Data.List.intersperse (showChar alternativeExtendedRegExSeparatorToken) . map shows . deconstructAlternatives	-- Replace the default list-format, with 'egrep'-syntax.--instance Consumer.Consumer (Alternatives m)	where-	consumptionProfile	= Consumer.aggregateConsumptionProfilesFromAlternatives . deconstructAlternatives-	starHeight		= Consumer.starHeight . deconstructAlternatives	-- Must evaluate all Alternatives to determine best.--instance ToolShed.SelfValidate.SelfValidator (Alternatives m)	where-	getErrors	= ToolShed.SelfValidate.getErrors . deconstructAlternatives--instance Control.DeepSeq.NFData m => Control.DeepSeq.NFData (Alternatives m)	where-	rnf	= Control.DeepSeq.rnf . deconstructAlternatives---- | 'Alternatives' can be employed as a simple /capture-group/ as well as a switch, under which circumstances there's no choice amongst multiple 'Alternatives'.-isSingletonAlternatives :: Alternatives m -> Bool-isSingletonAlternatives	= (== 1) . length . deconstructAlternatives---- | A set of 'Meta.Meta' which can be consumed.-type MetaDataList m	= [Meta.Meta m]--{- |-	* '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,-	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.-	-> MetaDataList m	-- ^ The list of meta-data against any of which a match can occur.-	-> Bool-isUnconsumableByAnyOf i	= not . any (Meta.isMatch i)---- | The set of distinct 'Meta.Meta', in the specified 'Alternatives'.-getDistinctMetaDataFromAlternatives :: Eq m => Alternatives m -> MetaDataList m-getDistinctMetaDataFromAlternatives	= foldr (Data.List.union . getDistinctMetaDataFromExtendedRegEx) [] . deconstructAlternatives---- | Defines either a simple 'Meta.Meta', which can match exactly one datum, or a set of 'Alternatives', each of which is recursively defined above, as an 'ExtendedRegEx'.-data Pattern m	=-	Require (Meta.Meta m)		-- ^ Describes a requirement for a simple scalar datum of the polymorphic type.-	| CaptureGroup (Alternatives m)	-- ^ A sub-expression containing a selection of recursively defined alternatives, thus forming a tree-structure.-	deriving Eq--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-		[(alternatives, s1)]	-> case dropWhile Data.Char.isSpace s1 of-			(')' : s2)	-> [(CaptureGroup alternatives, s2)]-			_		-> []	-- No parse.-		_			-> []	-- No parse.-	readsPrec _ s		= case reads s of-		[pair]	-> [Control.Arrow.first Require pair]-		_	-> []	-- No parse.--instance Show m => Show (Pattern m)	where-	showsPrec _ (Require meta)		= shows meta-	showsPrec _ (CaptureGroup alternatives)	= showChar (fst captureGroupDelimiters) . shows alternatives . showChar (snd captureGroupDelimiters)--instance Consumer.Consumer (Pattern m)	where-	consumptionProfile (Require meta)		= Consumer.consumptionProfile meta-	consumptionProfile (CaptureGroup alternatives)	= Consumer.consumptionProfile alternatives--	starHeight (Require meta)		= Consumer.starHeight meta-	starHeight (CaptureGroup alternatives)	= Consumer.starHeight alternatives--instance ToolShed.SelfValidate.SelfValidator (Pattern m)	where-	getErrors (Require meta)		= ToolShed.SelfValidate.getErrors meta-	getErrors (CaptureGroup alternatives)	= ToolShed.SelfValidate.getErrors alternatives--instance Control.DeepSeq.NFData m => Control.DeepSeq.NFData (Pattern m)	where-	rnf (Require meta)		= Control.DeepSeq.rnf meta-	rnf (CaptureGroup alternatives)	= Control.DeepSeq.rnf alternatives---- | Convenience-function to build a 'CaptureGroup' from a list of alternative 'ExtendedRegEx's.-captureGroup :: [ExtendedRegEx m] -> Pattern m-captureGroup	= CaptureGroup . MkAlternatives---- | True if the 'Pattern' was constructed via 'CaptureGroup'.-isCaptureGroup :: Pattern m -> Bool-isCaptureGroup (CaptureGroup _)	= True-isCaptureGroup _		= False---- | The set of distinct 'Meta.Meta', in the specified 'Pattern'.-getDistinctMetaDataFromPattern :: Eq m => Pattern m -> MetaDataList m-getDistinctMetaDataFromPattern pattern	= case pattern of-	Require meta			-> [meta]-	CaptureGroup alternatives	-> getDistinctMetaDataFromAlternatives alternatives---- | Constant pattern, representing a lax 'Require'ment.-anyDatum :: Pattern a-anyDatum	= Require Meta.Any---- | Make 'Pattern's, 'Repeatable.Repeatable'.-type RepeatablePattern m	= Repeatable.Repeatable (Pattern m)---- | Construct a null 'Match' by assuming that the specified 'RepeatablePattern' consumes zero 'InputData'; of which the caller should ensure it's capable.-mkNullMatchFromRepeatablePattern-	:: ConsumptionBounds.DataLength	-- ^ The offset into the list of input data at which the zero-length match occurred.-	-> RepeatablePattern m		-- ^ The pattern that the input data matched.-	-> Match m			-- ^ The resulting match-structure.-mkNullMatchFromRepeatablePattern offset r	= case Repeatable.base r of-	Require _	-> Tree.Leaf (r, offset, [])-	CaptureGroup _	-> Tree.Node $ Repeatable.getFewest r `replicate` []---- | The set of distinct 'Meta.Meta', in the specified 'RepeatablePattern'.-getDistinctMetaDataFromRepeatablePattern :: Eq m => RepeatablePattern m -> MetaDataList m-getDistinctMetaDataFromRepeatablePattern	= getDistinctMetaDataFromPattern . Repeatable.base--{- |-	* Represents a black hole, which will greedily consume all data.--	* CAVEAT: nullary, i.e. a constant.--}-(.*) :: RepeatablePattern m-(.*)	= Repeatable.zeroOrMore anyDatum---- | A /non-greedy/ version of '.*'.-(.*?) :: RepeatablePattern m-(.*?)	= Repeatable.zeroOrMore' anyDatum---- | Convenience-function, to build a 'RepeatablePattern' from an unrepeated instance of the specified 'Meta.Meta'-datum.-simply :: Meta.Meta m -> RepeatablePattern m-simply	= Repeatable.one . Require---- | Represents the /concatenation/ aspect of 'ExtendedRegEx's.-type Concatenation m	= [RepeatablePattern m]---- | Construct a null 'Match' by assuming that the specified 'Concatenation' consumes zero 'InputData'.-mkNullMatchFromConcatenation-	:: ConsumptionBounds.DataLength	-- ^ The offset into the list of input data at which the zero-length match occurred.-	-> Concatenation m		-- ^ The list of repeatable patterns that the input data matched.-	-> MatchList m			-- ^ The resulting match-structure.-mkNullMatchFromConcatenation offset	= map $ mkNullMatchFromRepeatablePattern offset---- | The set of distinct 'Meta.Meta', in the specified 'Concatenation'.-getDistinctMetaDataFromConcatenation :: Eq m => Concatenation m -> MetaDataList m-getDistinctMetaDataFromConcatenation	= foldr (Data.List.union . getDistinctMetaDataFromRepeatablePattern) []---- | The accumulating set of 'MetaDataList', resulting from each successive 'RepeatablePattern' in the specified 'Concatenation'.-accumulateDistinctMetaDataFrom :: Eq m => MetaDataList m -> Concatenation m -> [MetaDataList m]-accumulateDistinctMetaDataFrom	= scanr (Data.List.union . getDistinctMetaDataFromRepeatablePattern)---- | Constructs an 'ExtendedRegEx', by surrounding a 'Concatenation' with optional 'Anchor.Anchor's.-data ExtendedRegEx m	= MkExtendedRegEx {-	bowAnchor	:: Maybe Anchor.Anchor,	-- ^ An option to anchor the /regex/ to the start of the 'InputData'.-	concatenation	:: Concatenation m,	-- ^ The sequence of 'RepeatablePattern's defining the 'Require'ments that the 'InputData' must meet.-	sternAnchor	:: Maybe Anchor.Anchor	-- ^ An option to anchor the /regex/ to the end of the 'InputData'.-} deriving Eq---- | The type of a function which transforms an 'ExtendedRegEx'.-type Transformation m	= ExtendedRegEx m -> ExtendedRegEx m---- | Similar to 'fmap', but operates on 'Concatenation', rather than just @a@.-transformExtendedRegEx-	:: (Concatenation m -> Concatenation m)	-- ^ The function used to transform the data behind the constructor.-	-> Transformation m-transformExtendedRegEx f extendedRegEx	= extendedRegEx { concatenation	= f $ concatenation extendedRegEx }--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.-	readsPrec _ ('\\' : c : s)	= [(expand c, s)]-	readsPrec _ s			= case singleton of-		[(extendedRegEx, _)]-			| ToolShed.SelfValidate.isValid extendedRegEx	-> singleton-			| otherwise					-> error $ ToolShed.SelfValidate.getFirstError extendedRegEx	-- Parsed OK, but invalid.-		_							-> []								-- No parse.-		where---			singleton :: (Eq m, Meta.ShortcutExpander m, Read m, ShortcutExpander m) => [(ExtendedRegEx m, String)]-			singleton = [-				(-					MkExtendedRegEx {-						bowAnchor	= maybeBowAnchor,-						concatenation	= concatenation',-						sternAnchor	= maybeSternAnchor-					},-					remainder-				) |-					(maybeBowAnchor, s1)		<- readsMaybeAnchor s,-					(concatenation', s2)		<- reads s1,-					(maybeSternAnchor, remainder)	<- readsMaybeAnchor s2-			 ] {-list-comprehension-} where-				readsMaybeAnchor :: ReadS (Maybe Anchor.Anchor)-				readsMaybeAnchor s'	= return {-to List-monad-} $ case reads s' of-					[pair]	-> Control.Arrow.first Just pair-					_	-> (Nothing, s')---- | Shows either the specified 'Anchor.Anchor', or a null string where 'Nothing' is specified.-showsMaybeAnchor :: Maybe Anchor.Anchor -> String -> String-showsMaybeAnchor maybeAnchor-	| Data.Maybe.isJust maybeAnchor	= shows $ Data.Maybe.fromJust maybeAnchor-	| otherwise			= id--instance Show m => Show (ExtendedRegEx m)	where-	showsPrec _ MkExtendedRegEx {-		bowAnchor	= maybeBowAnchor,	-- CAVEAT: this could be 'Nothing' or perversely an 'Anchor.Stern'.-		concatenation	= concatenation',-		sternAnchor	= maybeSternAnchor	-- CAVEAT: this could be 'Nothing' or perversely an 'Anchor.Bow'.-	} = showsMaybeAnchor maybeBowAnchor . shows concatenation' . showsMaybeAnchor maybeSternAnchor--instance Consumer.Consumer (ExtendedRegEx m)	where-	consumptionProfile	= Consumer.aggregateConsumptionProfilesFromConcatenation . concatenation-	starHeight		= Consumer.starHeight . concatenation--instance ToolShed.SelfValidate.SelfValidator (ExtendedRegEx m)	where-	getErrors	= ToolShed.SelfValidate.getErrors . concatenation--instance Control.DeepSeq.NFData m => Control.DeepSeq.NFData (ExtendedRegEx m)	where-	rnf MkExtendedRegEx {-		bowAnchor	= maybeBowAnchor,-		concatenation	= concatenation',-		sternAnchor	= maybeSternAnchor-	} = Control.DeepSeq.rnf (maybeBowAnchor, concatenation', maybeSternAnchor)---- | Drop 'Anchor.Anchor's at both bow & stern of the specified 'ExtendedRegEx'.-dock :: Transformation m-dock e	= e {-	bowAnchor	= Just Anchor.Bow,-	sternAnchor	= Just Anchor.Stern-}---- | Amend the 'Concatenation' in the specified 'ExtendedRegEx', by prepending '.*' where there's a missing top-level 'Anchor.Bow' & appending '.*?' where there's a missing top-level 'Anchor.Stern'.-drift :: Transformation m-drift extendedRegEx	= transformExtendedRegEx (-	(-		if hasBowAnchor extendedRegEx then id else {-no anchor-} ((.*?) :)-	) . (-		if hasSternAnchor extendedRegEx then id else {-no anchor-} (++ [(.*)])-	)- ) extendedRegEx---- | True if the specified 'ExtendedRegEx' has an initial 'Anchor.Bow'. CAVEAT: though typically the alternative is no 'Anchor.Anchor', there may perversely be an initial 'Anchor.Stern', but neither qualify.-hasBowAnchor :: ExtendedRegEx m -> Bool-hasBowAnchor	= (== Just Anchor.Bow) . bowAnchor---- | True if the specified 'ExtendedRegEx' has a terminal 'Anchor.Stern'. CAVEAT: though typically the alternative is no 'Anchor.Anchor', there may perversely be a terminal 'Anchor.Bow', but neither qualify.-hasSternAnchor :: ExtendedRegEx m -> Bool-hasSternAnchor	= (== Just Anchor.Stern) . sternAnchor---- | 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---- | Construct a null 'Match' by assuming that the specified 'ExtendedRegEx' consumes zero 'InputData'.-mkNullMatchFromExtendedRegEx :: ConsumptionBounds.DataLength -> ExtendedRegEx m -> MatchList m-mkNullMatchFromExtendedRegEx offset	= mkNullMatchFromConcatenation offset . concatenation---- | The set of distinct (i.e. unique) 'Meta.Meta', in the specified 'ExtendedRegEx'.-getDistinctMetaDataFromExtendedRegEx :: Eq m => ExtendedRegEx m -> MetaDataList m-getDistinctMetaDataFromExtendedRegEx	= getDistinctMetaDataFromConcatenation . concatenation--{- |-	* The input-data is just a list.--	* Whilst typically this list is also a 'String', & could therefore be more efficiently implemented using "Data.ByteString",-	we can't assume that the polymorphic base-type is always 'Char'.--}-type InputData m = [m]---- | The accumulating sets of distinct input data.-accumulateDistinctInputData :: Eq m => InputData m -> [InputData m]-accumulateDistinctInputData	= scanr (Data.List.union . return) []---- | Tag the 'InputData' with the 'RepeatablePattern' it matched (which unfortunately confines the definition to this (bloated) module), & the offset from the start of the data;-type MatchedData m	= (RepeatablePattern m, ConsumptionBounds.DataLength, InputData m)---- | Shifts the offset of the specified 'MatchedData'.-shiftMatchedData-	:: ConsumptionBounds.DataLength	-- ^ The offset by which to shift the position into the input-data at which a match occurred.-	-> MatchedData m		-- ^ The match-structure whose offset is to be shifted.-	-> MatchedData m-shiftMatchedData i (r, offset, d)	= (r, offset + i, d)---- | Accessor.-getInputData :: MatchedData m -> InputData m-getInputData (_, _, inputData)	= inputData---- | Describes the manner in which a 'RepeatablePattern' successfully consumed 'InputData'.-type Match m	= Tree.Tree (MatchedData m)---- | Shifts the offsets of all the 'MatchedData' contained in the specified 'Match'.-shiftMatch-	:: ConsumptionBounds.DataLength	-- ^ The offset by which to shift the position into the input-data at which a match occurred.-	-> Match m			-- ^ The match-structure whose offset is to be shifted.-	-> Match m-shiftMatch i	= (shiftMatchedData i `fmap`)---- | Extract & concatenate, the 'InputData' from a 'Match'.-extractDataFromMatch :: Match m -> InputData m-extractDataFromMatch	= Data.Foldable.foldMap getInputData	-- Uses the List-monoid's associative binary operator (++), to concatenate the values returned by the specified function.---- | Extract & concatenate, the 'InputData' from a 'Match'; null if it didn't match any.-extractDataFromMatch' :: Maybe (Match m) -> InputData m-extractDataFromMatch' (Just match)	= extractDataFromMatch match-extractDataFromMatch' _			= []---- | Describes the manner in which a 'Concatenation' successfully consumed 'InputData'.-type MatchList m	= [Match m]---- | Shifts the offsets of all the 'MatchedData' contained in the specified 'MatchList'.-shiftMatchList-	:: ConsumptionBounds.DataLength	-- ^ The offset by which to shift the position into the input-data at which a each listed match occurred.-	-> MatchList m			-- ^ The list of match-structures, each of whose offsets are to be shifted.-	-> MatchList m-shiftMatchList i	= map (shiftMatch i)---- | Extract & concatenate, the 'InputData', from the 'MatchList'.-extractDataFromMatchList :: MatchList m -> InputData m-extractDataFromMatchList	= concatMap extractDataFromMatch	-- CAVEAT: too fine-grain for effective data-parallelism.---- | At the top-level of an 'ExtendedRegEx', the lack of an 'Anchor.Anchor' allows the 'ExtendedRegEx' to drift away from the corresponding end of the input-data; this data-gap is captured here.-type ExternalMatch m	= Maybe (Match m)---- | Captures the list of input-data consumed by the 'Concatenation', bracketed by any data-prefix or data-suffix.-type Result m	= (ExternalMatch m, Maybe (MatchList m), ExternalMatch m)---- | Returns the length of data consumed by the specified 'ExternalMatch'.-externalMatchLength :: ExternalMatch m -> ConsumptionBounds.DataLength-externalMatchLength	= length . extractDataFromMatch'--{- |-	* Similar to '+~', but exposes 'ExecutionOptions.ExecutionOptions', permitting greater control; & enough rope to hang yourself.--	* The parameter-order facilitates partial application, to acquire a matcher-function for a specific 'ExtendedRegEx', which can be applied repeatedly to different 'InputData'.--	* Performs some one-off @O(n)@ time-complexity preparation, then delegates the grunt-work to a private recursive function.--	* 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, Control.DeepSeq.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)-findMatch regExOpts@RegExOpts.MkRegExOpts {-	RegExOpts.executionOptions	= executionOptions,-	RegExOpts.regEx			= extendedRegEx-} originalInputData-	| let-		extractLowStarHeightTail :: ExtendedRegEx m -> Concatenation m-		extractLowStarHeightTail	= {-#SCC "extractLowStarHeightTail" #-} fromConcatenation . reverse . concatenation	where-			fromConcatenation :: Concatenation m -> Concatenation m-			fromConcatenation []					= []-			fromConcatenation (repeatablePattern : concatenation')-				| Consumer.starHeight repeatablePattern == 0	= repeatablePattern : fromConcatenation {-recurse-} concatenation'-				| Repeatable.getFewest repeatablePattern > 0	= case Repeatable.base repeatablePattern of-					CaptureGroup alternatives-						| isSingletonAlternatives alternatives	-> extractLowStarHeightTail {-recurse-} . head $ deconstructAlternatives alternatives-						| otherwise				-> {-non-singleton-} []-					_				-> []	-- Zero 'Consumer.StarHeight' would have been detected by 'fromConcatenation'.-				| otherwise					= []----		lowStarHeightTail :: Concatenation a-		lowStarHeightTail	= extractLowStarHeightTail extendedRegEx-	in and [-		ExecutionOptions.checkExistenceOfInelasticTail executionOptions,-		not $ null lowStarHeightTail,	-- Prevent infinite recursion.-		reverse originalInputData /~ RegExOpts.mkRegEx MkExtendedRegEx {-			bowAnchor	= Just Anchor.Bow,-			concatenation	= lowStarHeightTail,-			sternAnchor	= Nothing-		} -- Check for mismatch with the corresponding tail of 'InputData'.-	]		= Nothing-	| otherwise	= findMatchSlave originalConcatenation (-		Consumer.accumulateConsumptionProfiles originalConcatenation-	) (-		accumulateDistinctMetaDataFrom [] originalConcatenation-	) originalInputData originalInputDataLength (-		accumulateDistinctInputData originalInputData-	)-	where---		originalConcatenation :: Concatenation a-		originalConcatenation	= concatenation extendedRegEx--		originalInputDataLength :: ConsumptionBounds.DataLength-		originalInputDataLength	= length originalInputData-{-- This beast takes a lot of parameters.- 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, Control.DeepSeq.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 [] _ _ [] _ _	= 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	= 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.-				not $ ExecutionOptions.requireMatchList executionOptions,				-- Otherwise the precise mapping of the 'inputData' to 'RepeatablePattern's must be determined.-				not $ ConsumptionProfile.hasSpecificRequirement accumulatedConsumptionProfileHead	-- Otherwise a match for the specific 'Meta'-data must be found.-			] = Just undefined {-shouldn't be evaluated according to 'ExecutionOptions.requireMatchList'-}-			| and [-				ExecutionOptions.checkForUnconsumableData executionOptions,-				not $ ConsumptionProfile.canConsumeAnything accumulatedConsumptionProfileHead,	-- Otherwise the subsequent test will always fail.-				(`isUnconsumableByAnyOf` distinctMetaDataHead) `any` distinctInputDataHead	-- Occasionally failure is both inevitable & obvious.-			] = Nothing-			| otherwise	= {-#SCC "findMatchSlave" #-} let-				tailConsumptionProfile :: ConsumptionProfile.ConsumptionProfile-				tailConsumptionProfile@(-					ConsumptionProfile.MkConsumptionProfile {-						ConsumptionProfile.consumptionBounds	= (minConsumptionConcatenationTail, maybeMaxConsumptionConcatenationTail)-					}-				 ) = 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 Nothing-				else {-inputData is sufficient for concatenationTail-} let-					Repeatable.MkRepeatable {-						Repeatable.base			= base,-						Repeatable.repetitionBounds	= (fewest, most),-						Repeatable.isGreedy		= isGreedy-					} = 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 (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'.- The resulting 'BasicRegEx', with /Kleene Star/s weeded-out & 'Alternatives' pruned, can be matched in O(n)-time, to verify minimal requirements.--}-						extractMinimumRequirementFromConcatenation :: Concatenation m -> BasicRegEx m-						extractMinimumRequirementFromConcatenation	= concatMap (-							\repeatablePattern -> case Repeatable.base repeatablePattern of-								Require datum	-> return {-to List-monad-} repeatablePattern { Repeatable.base = datum }-								CaptureGroup a-									| isSingletonAlternatives a	-> concat . replicate (Repeatable.getFewest repeatablePattern) . extractMinimumRequirementFromConcatenation {-recurse-} . concatenation . head $ deconstructAlternatives a-									| otherwise			-> []-						 ) . filter ((> 0) . Repeatable.getFewest)--						maximumDataAfterPegs :: Eq m => BasicRegEx m -> InputData m -> Maybe (InputData m)-						maximumDataAfterPegs pegList@(-							Repeatable.MkRepeatable {-								Repeatable.base			= metaPeg,-								Repeatable.repetitionBounds	= (fewestPeg, _)-							} : pegListTail-						 ) inputData'-							| null remainingInputData && length candidateMatchedInputData < fewestPeg		= Nothing							-- Insufficient data to ever match.-							| fewestPeg == 1 || (`Meta.isMatch` metaPeg) `all` tail candidateMatchedInputData	= pegListTail `maximumDataAfterPegs` remainingInputData		-- Success => recurse.-							| otherwise										= pegList `maximumDataAfterPegs` tail candidateInputData	-- Failed candidate => recurse.-							where---								candidateInputData, candidateMatchedInputData, remainingInputData :: InputData a-								candidateInputData				= dropWhile (not . (`Meta.isMatch` metaPeg)) inputData'-								(candidateMatchedInputData, remainingInputData)	= fewestPeg `splitAt` candidateInputData-						maximumDataAfterPegs [] inputData'	= Just inputData'	-- All the pegs have been matched.-{-- Each 'repeatablePatternHead' can either be a 'Require' or a 'CaptureGroup'.- The former case needs further reduction & we can proceed to construct a 'Tree.Leaf' from any matching 'inputData'.- We attempt to reduce the latter case to an instance of the former, by expanding all combinations of repetitions of various 'Alternatives', constructing a new 'ExtendedRegEx' from each, & recursing.--}-				in case base of-					Require meta-						| null maybeMatchList	-> Nothing-						| otherwise		-> head {-most suitable-} maybeMatchList-						where-{-- Attempt to narrow the repetition-range, from the potentially unbounded specification, to that which is possible according to,- the 'inputDataLength', & the specified consumption-bounds of the remainder of the /regex/.- 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 . map (-								\(consumedInputData, unconsumedInputData)	-> let-									consumedInputDataLength :: ConsumptionBounds.DataLength-									consumedInputDataLength	= length consumedInputData-								in (-									Tree.Leaf (repeatablePatternHead, inputDataOffset, consumedInputData) :-								) `fmap` {-apply to Maybe Functor-} findMatchSlave concatenationTail accumulatedConsumptionProfilesTail distinctMetaDataTail unconsumedInputData (-									inputDataLength - consumedInputDataLength-								) (-									consumedInputDataLength `drop` distinctInputData-								) -- Recurse, to check whether the 'unconsumedInputData' tail also matches.-							 ) $ let-								fewestData, mostData, mostData' :: Repeatable.Repetitions-								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" #-} 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.-										_		-> maxDataAvailable----									measureUnmatchableTail :: InputData m -> ConsumptionBounds.DataLength -> ConsumptionBounds.DataLength-									measureUnmatchableTail _ 0	= 0						-- Have matched all the data.-									measureUnmatchableTail (a : as) unmatched-										| a `Meta.isMatch` meta	= measureUnmatchableTail as $ pred unmatched	-- Recurse.-										| otherwise		= unmatched					-- Return the unmatchable tail-length.-									measureUnmatchableTail [] _	= error "RegExDot.RegEx.findMatch.findMatchSlave.maybeMatchList.mostData.measureUnmatchableTail:\tdata unexpectedly exhausted."-{-- 'most' has been extracted from 'repeatablePatternHead',- & reduced according to the length of 'inputData' which actually matches, to form 'mostData',- but if it still exceeds 'fewestData', the resulting repetition-range may trigger backtracking.- So, attempt to further reduce this ill-defined repetition-range, according to the competing requirements of 'concatenationTail'.--}-								mostData'-									| and [-										ExecutionOptions.moderateGreed executionOptions,-										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'	-> 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.-							 ) `take` (-								if isGreedy-									then ToolShed.Data.List.Splits.splitsLeftFrom mostData'-									else ToolShed.Data.List.Splits.splitsRightFrom fewestData-							 ) inputData--					CaptureGroup alternatives-						| and [-							not isGreedy,-							fewest <= 0,	-- Zero repetitions permissible.-							Data.Maybe.isJust tailMatch-						] -> zeroRepetitions	-- Zero repetitions is the optimal solution, rendering the choice of Alternative irrelevant.-						| and [-							ExecutionOptions.catchIncompatibleAnchors executionOptions,-							fewest > 1,			-- Multiple repetitions.-							minConsumptionAlternatives > 0,	-- InputData required.-							($ extendedRegExAlternatives) `any` [(hasBowAnchor `all`), (hasSternAnchor `all`)]-						]							-> Nothing	-- 'extendedRegExFromAlternative' requires 'InputData', therefore 'Anchor.Bow' can only pass on the 1st repetition & 'Anchor.Stern' can only pass on the last.-						| otherwise						-> let-{-- We're about to try all repetitions of one Alternative before progressing to the next.- One could alternatively transpose this search-pattern, & investigate all 'Alternatives' permissible at n repetitions, before trying (n + 1); I've not pursued this option.- 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 . concat . Control.Parallel.Strategies.parMap Control.Parallel.Strategies.rseq {-Particularly effective when 'not ExecutionOptions.requireMatchList' -} (---								:: ExtendedRegEx m -> [Maybe (Match m, MatchList m)]-								\extendedRegExFromAlternative@MkExtendedRegEx {-									concatenation	= concatenationFromAlternative-								} -> let-									components :: Int-									components	= length concatenationFromAlternative--									consumptionProfileConcatenationFromAlternative :: [ConsumptionProfile.ConsumptionProfile]-									consumptionProfileConcatenationFromAlternative	= map Consumer.consumptionProfile concatenationFromAlternative--									minConsumptionConcatenationFromAlternative	:: ConsumptionBounds.DataLength-									maybeMaxConsumptionConcatenationFromAlternative	:: Maybe ConsumptionBounds.DataLength-									(minConsumptionConcatenationFromAlternative, maybeMaxConsumptionConcatenationFromAlternative)	= ConsumptionProfile.consumptionBounds $ ConsumptionProfile.aggregateFromConcatenation consumptionProfileConcatenationFromAlternative-								in (-									if ExecutionOptions.abortTrialRepetitionsOnZeroConsumption executionOptions && Data.Maybe.isNothing most {-constrained only by 'inputDataLength'-}-										then {-#SCC "abortTrialRepetitionsOnZeroConsumption" #-} ToolShed.Data.List.takeUntil $ \maybeMatchPair -> case maybeMatchPair of-											Just (match, _)	-> ($ Tree.pop match) `all` [-												(>= fewest) . length,			-- Must achieve 'Repeatable.getFewest', regardless of data-consumption.-												null . extractDataFromMatchList . last	-- If the n-th repetition consumed nothing, so will the (n + 1)-th.-											 ]-											_		-> False			-- Failure with n repetitions, doesn't preclude success with n + 1.-										else {-optimisation not required-} id-								) . (---									:: Maybe (Match m, MatchList m) -> Maybe (Match m, MatchList m)-									\maybeMatchPairList	-> if ExecutionOptions.abortTrialRepetitionsOnInherentFailure executionOptions-{-- We're iterating through the identified range of possible repetitions, of one possible 'concatenationFromAlternative'.- If failure can be proven, for an arbitrary number of repetitions, then we can bypass the futile remainder.- If the first trial succeeded, then clearly there's no such inherent problem with 'concatenationFromAlternative', & it's necessary to evaluate all subsequent trial-repetitions.- Otherwise, assuming there're subsequent trials to be bypassed, attempt to prove that failure is inevitable, by launching a pilot-study,- using a single repetition of 'concatenationFromAlternative' followed by a replacement infallible 'concatenationTail'.--}-										then {-#SCC "abortTrialRepetitionsOnInherentFailure" #-} case maybeMatchPairList of-											(Nothing {-failed attempt-} : _ {-subsequent attempt worth bypassing-} : _)	-> if (-												minConsumptionConcatenationFromAlternative == 0	-- Can delegate consumption of unconsumable data to the infallible tail => unprovable culpability.-											 ) || inputData =~ RegExOpts.mkRegEx MkExtendedRegEx {-												bowAnchor	= Just Anchor.Bow,-												concatenation	= concatenationFromAlternative ++ [anyDatum ^#-> minConsumptionConcatenationTail],	-- Infallible tail.-												sternAnchor	= Just Anchor.Stern-											 }-												then maybeMatchPairList-												else []	-- Abandon further repetitions of 'Alternatives'.-											_ {-Alternative is OK, or there's no subsequent attempt to bypass-}		-> maybeMatchPairList-										else {-optimisation not required-} maybeMatchPairList-								) . map (---									:: Repeatable.Repetitions -> Maybe (Match m, MatchList m)-									\repetitions	-> if hasSternAnchor extendedRegExFromAlternative-										then if (repetitions == 1 || minConsumptionConcatenationFromAlternative == 0) && minConsumptionConcatenationTail == 0-											then (-												shiftMatch inputDataOffset . Tree.Node . (-													: replicate (-														pred repetitions	-- Typically degenerates to zero, since anchored sub-expressions aren't normally repeatable.-													) (-														mkNullMatchFromExtendedRegEx 0 extendedRegExFromAlternative	-- Other Alternatives may be available & suitable.-													) -- Repeated null match may, lacking other suitable Alternatives, correspond to this stern-anchored ExtendedRegEx, so data must be consumed before them.-												) &&& const (mkNullMatchFromConcatenation inputDataOffset concatenationTail)-											) `fmap` findMatch regExOpts { RegExOpts.regEx = extendedRegExFromAlternative } inputData	-- Recurse, either consuming all inputData or failing.-											else Nothing-										else {-no Anchor.Stern-} let-{-- Expand the 'Repeatable CaptureGroup', so that after recursion it is ultimately reduced in complexity, to just a 'Concatenation' of 'Repeatable Require', & can be handled above as a POSIX BRE.-- There're two special cases; one repetition of n 'Alternatives' & n repetitions of one Alternative.- If there's only one repetition, we can reduce the implementation-logic substantially, to the point where the implementation's independent of whether there's a choice of 'Alternatives'.- If there only one Alternative, then the entire repetition-sequence can be immediately expanded, vastly reducing the required number of recursions,- otherwise we can only expand the first repetition, leaving the option of matching a different Alternative on subsequent repetitions.-- In all cases, we must subsequently re-compose any 'MatchList' resulting of the recursive call, into one corresponding to the structure of original ExtendedRegEx.- The expansion-process & corresponding subsequent collation, are associated in a tuple, in which the consumption-profile of the expansion is also contained.--}---											collater					:: MatchList m -> (Match m, MatchList m)-											consumptionProfileExpandedConcatenationPrefix	:: [ConsumptionProfile.ConsumptionProfile]---											expandedConcatenationPrefix			:: Concatenation a-											(collater, consumptionProfileExpandedConcatenationPrefix, expandedConcatenationPrefix)-												| repetitions == 1	= (-													Control.Arrow.first (-														Tree.Node . return {-to List-monad-}-													) . splitAt components,	-- Bisect the 'MatchList', into the part resulting from the Alternative, & that for 'concatenationTail'.-													consumptionProfileConcatenationFromAlternative,-													concatenationFromAlternative-												) -- Both tuples below, can degenerate to this simple case; it isn't a fundamentally different algorithm.-												| and [-													ExecutionOptions.unrollRepeatedSingletonAlternative executionOptions,-													isSingletonAlternative,-													not $ hasBowAnchor extendedRegExFromAlternative-												] = (-													Control.Arrow.first (-														Tree.Node . ToolShed.Data.List.chunk components-													) . splitAt (-														components * repetitions-													), -- Bisect the 'MatchList', into the part resulting from (Alternative){n}, & that for 'concatenationTail'.-													concat $ replicate repetitions consumptionProfileConcatenationFromAlternative,-													concat $ replicate repetitions concatenationFromAlternative	-- Expand all repetitions of the single alternative.-												)-												| otherwise {-choice of Alternatives or has Anchor.Bow-}	=  let-													remainingRepetitions :: Repeatable.Repetitions-													remainingRepetitions	= pred repetitions	-- Expand just the first repetition of the set of 'Alternatives'.----													singletonRepeatable :: RepeatablePattern a-													singletonRepeatable	= Repeatable.toSingleton repeatablePatternHead-												in (-													Control.Arrow.first (-														Tree.Node . uncurry (:) . Control.Arrow.second (-															concatMap Tree.pop		-- Amalgamate the repeated singleton 'MatchList's.-														) . splitAt components			-- Bisect first 'MatchList', into the part resulting from 'concatenationFromAlternative', & that for 'replicate remainingRepetitions'.-													) . splitAt (-														components + remainingRepetitions	-- Bisect the 'MatchList', into the part resulting from (Alternatives){n}, & that for 'concatenationTail'.-													),-													consumptionProfileConcatenationFromAlternative ++ replicate remainingRepetitions (Consumer.consumptionProfile singletonRepeatable),-													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 `fmap` {-apply to Maybe Functor-} findMatchSlave (-										expandedConcatenationPrefix ++ concatenationTail-									) (-										ConsumptionProfile.accumulateFrom tailConsumptionProfile {-initial value-} consumptionProfileExpandedConcatenationPrefix ++ tail accumulatedConsumptionProfilesTail-									) (-										accumulateDistinctMetaDataFrom (head distinctMetaDataTail) {-initial value-} expandedConcatenationPrefix ++ tail distinctMetaDataTail-									) inputData inputDataLength distinctInputData	-- Recurse to get 'Maybe (MatchList a)'.-								) $ 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 []	-- Failure is inevitable.-										else [fewest]-									else {-imprecise range-} let-{-- Attempt to narrow the repetition-range, from the potentially unbounded specification, to that which is possible according to (data available / consumption-requirement),- where there're bounds for both numerator & denominator.- Additionally, one must account for the possibility of the denominator being zero, & if so, select the optimal value if the numerator is also zero.- 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" #-} 1 {-zero is handled later-} `max` if any Data.Maybe.isNothing [maybeMaxConsumptionConcatenationTail, maybeMaxConsumptionAlternatives]-											then fewest-											else {-no unlimited capacities-} let-												minDataAvailable, maxConsumptionAlternatives :: ConsumptionBounds.DataLength-												minDataAvailable		= inputDataLength - Data.Maybe.fromJust maybeMaxConsumptionConcatenationTail	-- CAVEAT: can be negative.-												maxConsumptionAlternatives	= Data.Maybe.fromJust maybeMaxConsumptionAlternatives-											in if isSingletonAlternative-												then if maxConsumptionAlternatives == 0							-- Denominator.-													then {-sole Alternative can't consume anything ?!-} if minDataAvailable <= 0	-- Numerator.-														then {-tail may consume all data-} fewest	-- Repeat to meet the minimum requirement.-														else {-unconsumable data-} maxBound		-- Failure is inevitable.-													else {-non-zero => can divide-} max fewest $ minDataAvailable /+ maxConsumptionAlternatives-												else {-choice of Alternatives-} let-													minDataAvailable' :: ConsumptionBounds.DataLength-													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-} 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" #-} case most of-											Just cap	-> cap `min` mostPermissibleRepetitions	-- Create a ceiling above which the calculated number of permissible repetitions can't rise.-											_		-> mostPermissibleRepetitions-											where-												mostPermissibleRepetitions :: Repeatable.Repetitions-												mostPermissibleRepetitions-													| isSingletonAlternative		= if minConsumptionAlternatives == 0							-- Denominator.-														then {-sole Alternative can consume zero data-} if maxDataAvailable == 0	-- Numerator.-															then {-zero data available-} fewest		-- Repeat to meet the minimum requirement.-															else {-data available-} maxDataAvailable	-- Either each repetition consumes something, or (n - 1) repetitions is a better solution.-														else {-non-zero => can divide-} maxDataAvailable `div` minConsumptionAlternatives	-- Divide & round down.-													| otherwise {-choice of Alternatives-}	= 1 {-account for instance expanded as 'concatenationFromAlternative'-} + let-														maxDataAvailable' :: ConsumptionBounds.DataLength-														maxDataAvailable'	= maxDataAvailable - minConsumptionConcatenationFromAlternative	-- CAVEAT: potentially negative.-													in if minConsumptionAlternatives == 0							-- Denominator.-														then {-@ least one Alternative can consume zero-} if maxDataAvailable' == 0	-- Numerator.-															then {-zero data available $-} pred fewestAlternatives	-- Repeat as required. CAVEAT: an annoying dependency, which prevents parallel-evaluation.-															else {-data available-} if and [-																False,	-- The cost outweighs the small infrequent dividend.-																minConsumptionConcatenationFromAlternative == 0,-																maxDataAvailable > fewest-															] -- If 'fewest' can be met, without unconsuming repetitions of 'concatenationFromAlternative', then stop short of permitting it.-																then pred maxDataAvailable	-- Any greater, & one repetition (possibly that currently expanded) must needlessly consume nothing.-																else maxDataAvailable'		-- Either all n repetitions consumes something, or (n - 1) is a better solution.-														else {-non-zero => can divide-} maxDataAvailable' `div` minConsumptionAlternatives	-- Divide & round down.-{-- 'most' has been extracted from 'repeatablePatternHead',- & reduced according to the number of times it can fit into the maximum available data, to form 'mostAlternatives',- but if it still exceeds 'fewestAlternatives', the resulting repetition-range may trigger backtracking.- So, attempt to further reduce this ill-defined repetition-range, according to the competing requirements of 'concatenationTail'.- CAVEAT: this pays-off infrequently, but not necessarily insignificantly.--}-										mostAlternatives'	= {-#SCC "mostAlternatives'" #-} if and [-											ExecutionOptions.moderateGreed executionOptions,-											mostAlternatives > fewestAlternatives,	-- Otherwise, there's no unbridled greed to moderate.-											minConsumptionAlternatives > 0		-- Otherwise, any number of repetitions can occur without necessarily triggering back-tracking.-										 ]-											then case maximumDataBeforePegs of-												Just maximumDataBeforePegs'	-> mostAlternatives `min` (-													length maximumDataBeforePegs' `div` minConsumptionAlternatives-												 ) -- Cap upper bound.-												_				-> 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.-							 ) . (-								if ExecutionOptions.permitReorderingOfAlternatives executionOptions-{-- The order in which 'Alternatives' are evaluated, should make no difference to the final result; unless 'ExecutionOptions.useFirstMatchAmongAlternatives'.- This order can be manipulated in an attempt to more quickly locate any 'Just MatchList', though if 'ExecutionOptions.requireMatchList', an exhaustive search for the optimal solution must be performed anyway.- Tackling lower-complexity 'Alternatives' first makes sense, in the hope of finding any easy solution before becoming entrenched fighting some hideous monster.- Preferring 'Alternatives' which require more data also makes sense, regardless of 'Repeatable.isGreedy' which just a preference for a type of solution, because fewer repetitions are then permissible;- this is rather like trying to pack a box & preferring to try the biggest items first.--}-									then Data.List.sortBy $ Data.Ord.comparing (-										Consumer.starHeight &&& safeReciprocal . (fromIntegral :: ConsumptionBounds.DataLength -> Rational) . Consumer.getFewest-									) -- Firstly increasing complexity, then decreasing minimum data-capacity, otherwise stable.-									else id-							 ) $ filter (-								\e	-> (-									not (hasBowAnchor e) || inputDataOffset == 0			-- Necessary & sufficient.-								) && (-									not (hasSternAnchor e) || minConsumptionConcatenationTail == 0	-- Necessary but insufficient, since though minimum consumption is zero, maximum isn't necessarily.-								)-							 ) extendedRegExAlternatives-						in if null matchPairList-							then if fewest <= 0-								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',- & if the caller reduces the 'Just MatchList' to 'True', that'd be sufficient, but otherwise one must identify the optimal 'MatchList'.- The primary selection-criterion used is the POSIX one, based on total maximum (or minimum for non-'Repeatable.isGreedy' capture-groups) data-consumption.- /Perl/'s "first-past-the-post" policy is available as an alternative.--}-								if ExecutionOptions.useFirstMatchAmongAlternatives executionOptions || length matchPairList == 1-									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.-									) . map (-										(-{-- If the primary selection-criterion doesn't resolve the choice between candidate 'Match'es, I employ these ad-hoc criteria.--	* Fewer repetitions are preferred, which discourages the capture of null lists of 'InputData'.--	* Within the 'MatchedData', from which a candidate 'Match' is ultimately composed, consumption of 'InputData' beyond 'Repeatable.getFewest' by 'Repeatable.isGreedy' 'RepeatablePattern's, is preferred to non-greedy ones.--	* The consumption on each successive repetition is compared between candidate 'Match'es (which are now known to have used an equal number of repetitions);-	this causes the data-consumption to flow towards earlier repetitions for greedy capture-groups, & towards later repetitions in non-greedy ones,-	thus mimicking the behaviour of the unrolled repetition.--	* PS: more precise criteria are defined in <http://www2.research.att.com/~gsf/testregex/re-interpretation.html>.--}-											\match -> let-												dataLengthCriterion :: InputData m -> Rational-												dataLengthCriterion	= (-													if isGreedy-														then id-														else safeReciprocal	-- Prefer less data.-												 ) . fromIntegral . length----												matchLists :: [MatchList m]-												matchLists	= Tree.pop match-											in (-												dataLengthCriterion $ extractDataFromMatch match,-												if ExecutionOptions.preferFewerRepeatedAlternatives executionOptions-													then safeReciprocal . toRational $ length matchLists-													else 0,-												if ExecutionOptions.preferAlternativesWhichFeedTheGreedy executionOptions-													then let-														excessConsumption :: MatchedData m -> ConsumptionBounds.DataLength-														excessConsumption (-															Repeatable.MkRepeatable {-																Repeatable.repetitionBounds	= (f, _),-																Repeatable.isGreedy		= g-															},-															_,-															consumedInputData-														 )-															| g		= l - f-															| otherwise	= f - l-															where-																l :: ConsumptionBounds.DataLength-																l	= length consumedInputData-													in Data.Foldable.foldl' (\l -> (l +) . excessConsumption) 0 match-													else 0,-												if ExecutionOptions.preferAlternativesWhichMimickUnrolling executionOptions-													then map (dataLengthCriterion . extractDataFromMatchList) matchLists-													else []-											) -- Create a tuple of selection-criteria, for simultaneous assessment.-										) . fst {-focus the choice on the Alternatives-} &&& id	-- Prepend a selection-criterion to each result.-									)-							) matchPairList-						where---							tailMatch, zeroRepetitions :: Maybe (MatchList m)-							tailMatch	= findMatchSlave concatenationTail accumulatedConsumptionProfilesTail distinctMetaDataTail inputData inputDataLength distinctInputData	-- Recurse.-							zeroRepetitions	= (Tree.Node [] :) `fmap` {-apply to Maybe Functor-} tailMatch	-- Prepend a null 'MatchList'.----							extendedRegExAlternatives :: [ExtendedRegEx m]-							extendedRegExAlternatives	= Data.List.nub $ deconstructAlternatives alternatives--							isSingletonAlternative :: Bool-							isSingletonAlternative	= length extendedRegExAlternatives == 1--							minConsumptionAlternatives	:: ConsumptionBounds.DataLength-							maybeMaxConsumptionAlternatives	:: Maybe ConsumptionBounds.DataLength-							(minConsumptionAlternatives, maybeMaxConsumptionAlternatives)	= Consumer.getConsumptionBounds alternatives	-- Independent of the choice of Alternative.-			where-				inputDataOffset :: ConsumptionBounds.DataLength-				inputDataOffset	= originalInputDataLength - inputDataLength-		findMatchSlave _ _ _ _ _ _	= error "RegExDot.RegEx.findMatch.findMatchSlave:\tunexpected parameter-pattern."--{- |-	* Operator's name was chosen to suggest something more than '=~'.--	* 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, Control.DeepSeq.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-inputData +~ regExOpts	= (-	if hasBowAnchor' then Nothing else fmap head maybeMatchList,	-- Record the first 'Match', consumed in the absence of a top-level 'Anchor.Bow'.-	fmap (-		(-			if hasBowAnchor' then id else tail			-- Remove the first 'Match', since this wasn't consumed by the 'Concatenation'.-		) . (-			if hasSternAnchor' then id else init			-- Remove the last 'Match', since this wasn't consumed by the 'Concatenation'.-		)-	) maybeMatchList,-	if hasSternAnchor' then Nothing else fmap last maybeMatchList	-- Record the last 'Match', consumed in the absence of a top-level 'Anchor.Stern'.- ) where---	extendedRegEx :: ExtendedRegEx a-	extendedRegEx	= RegExOpts.regEx regExOpts--	hasBowAnchor', hasSternAnchor' :: Bool-	hasBowAnchor'	= hasBowAnchor extendedRegEx-	hasSternAnchor'	= hasSternAnchor extendedRegEx----	maybeMatchList :: Maybe (MatchList m)-	maybeMatchList	= fmap drift (RegExOpts.setVerbose True regExOpts) `findMatch` inputData--{- |-	* Pattern-match operator.--	* Identifier & parameter-order follow the lead of /Perl/'s pattern-match operator.--	* Considerably more efficient than '+~', since even though they are both implemented via 'findMatch',-	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, Control.DeepSeq.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, Control.DeepSeq.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	= not . (inputData =~)---- | The delimiters of 'Alternatives', when in the 'String'-form.-captureGroupDelimiters :: (Char, Char)-captureGroupDelimiters	= ('(', ')')---- | The token used to separate alternative 'ExtendedRegEx's, when in the 'String'-form.-alternativeExtendedRegExSeparatorToken :: Char-alternativeExtendedRegExSeparatorToken	= '|'---- | The set of 'Char' to which a specific meaning is attributed, when reading from 'String'.-tokens :: String-tokens	= [fst captureGroupDelimiters, snd captureGroupDelimiters, alternativeExtendedRegExSeparatorToken]--{- |-	* Invert the specified fraction, but return 'maxBound' if the result would otherwise be infinite.--	* CAVEAT: this is an awful concept, and therefore intended for internal use only.--}-safeReciprocal :: (Eq f, Fractional f) => f -> f-safeReciprocal 0	= fromIntegral (maxBound :: Int)	-- Handle divide-by-zero error.-safeReciprocal f	= recip f--infixl 7 /+	-- Same as (/).--{- |-	* Integral division, with any fractional remainder rounded-up.--	* A rather dubious requirement, so internal use only.--}--- (/+) :: Integral i => i -> i -> i-(/+)-	:: Int	-- ^ Numerator.-	-> Int	-- ^ Denominator.-	-> Int	-- 10% faster in unoptimised code, & more in optimised.-_ /+ 0				= error "RegExDot.RegEx.(/+):\tzero denominator => infinity"--- numerator /+ denominator	= ceiling ((fromIntegral numerator / fromIntegral denominator) :: Double)-numerator /+ denominator	= uncurry (+) . Control.Arrow.second signum $ quotRem numerator denominator	-- Slightly faster.
− src/RegExDot/RegExOpts.hs
@@ -1,65 +0,0 @@-{-# LANGUAGE MultiParamTypeClasses, TypeSynonymInstances, FlexibleInstances #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--{--	Copyright (C) 2010 Dr. Alistair Ward--	This program is free software: you can redistribute it and/or modify-	it under the terms of the GNU General Public License as published by-	the Free Software Foundation, either version 3 of the License, or-	(at your option) any later version.--	This program is distributed in the hope that it will be useful,-	but WITHOUT ANY WARRANTY; without even the implied warranty of-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-	GNU General Public License for more details.--	You should have received a copy of the GNU General Public License-	along with this program.  If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@]	Dr. Alistair Ward-- [@DESCRIPTION@]--	* Provides a complete job-specification.--	* 'regEx' is polymorphic, allowing use of either /RegExDot.RegEx.ExtendedRegEx/ (which is also polymorphic) or /RegExChar.ExtendedRegExChar.ExtendedRegExChar/.--}--module RegExDot.RegExOpts(--- * Types--- ** Data-types-	RegExOpts(..),--- * Functions-	setVerbose,--- ** Constructors-	mkRegEx-) where--import qualified	RegExDot.CompilationOptions	as CompilationOptions-import qualified	RegExDot.ExecutionOptions	as ExecutionOptions-import qualified	ToolShed.Defaultable---- | Aggregates both 'ExecutionOptions.ExecutionOptions' & 'CompilationOptions.CompilationOptions' with a polymorphic /regex/, to form a complete job-description.-data RegExOpts a	= MkRegExOpts {-	compilationOptions	:: CompilationOptions.CompilationOptions,	-- ^ Parameters governing the result, rather than the implementation.-	executionOptions	:: ExecutionOptions.ExecutionOptions,		-- ^ Parameters governing the implementation, rather than the result.-	regEx			:: a						-- ^ Polymorphic, to permit specialisation either of the type of /regex/ or the type of input-data.-} deriving Show--instance Functor RegExOpts	where-	fmap f regExOpts	= regExOpts { regEx = f $ regEx regExOpts }---- | Smart constructor using 'ToolShed.Defaultable.defaultValue'.-mkRegEx :: a -> RegExOpts a-mkRegEx	r	= MkRegExOpts {-	compilationOptions	= ToolShed.Defaultable.defaultValue,-	executionOptions	= ToolShed.Defaultable.defaultValue,-	regEx			= r-}---- | Mutator, which sets an appropriate 'ExecutionOptions.ExecutionOptions', for the depth to which the caller wants to probe the resulting match.-setVerbose :: Bool -> RegExOpts a -> RegExOpts a-setVerbose verbose regExOpts	= regExOpts { executionOptions = ExecutionOptions.setVerbose verbose $ executionOptions regExOpts }-
− src/RegExDot/Repeatable.hs
@@ -1,397 +0,0 @@-{--	Copyright (C) 2010-2015 Dr. Alistair Ward--	This program is free software: you can redistribute it and/or modify-	it under the terms of the GNU General Public License as published by-	the Free Software Foundation, either version 3 of the License, or-	(at your option) any later version.--	This program is distributed in the hope that it will be useful,-	but WITHOUT ANY WARRANTY; without even the implied warranty of-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-	GNU General Public License for more details.--	You should have received a copy of the GNU General Public License-	along with this program.  If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@]	Dr. Alistair Ward-- [@DESCRIPTION@]--	* A data-type, which represents the permissible range of instances, of the underlying polymorphic datum.--	* Designed for use in a polymorphic /regex/-engine, which specifies patterns composed of repeated /greedy/ & /non-greedy/ sequences of /Meta/-data;-- >		*	+	?	{fewest, most}	{fewest,}	{fewest}- >		*?	+?	??	{fewest, most}?	{fewest,}?--	* In the context of /regex/es, this concept is known as /Quantification/.--	* /regex/es evolved from the minimal ability to optionally qualify the datum with a <http://en.wikipedia.org/wiki/Kleene_star> suffix.-	More exotic repetition-specifications could be composed by concatenating these atomic building-blocks.-	Here, I've taken the contrary top-down view, & assumed that all data are qualified by a full 'RepetitionBounds', which in most cases will degenerate into a simpler form.--	* The type of entity which is being repeated, isn't the domain of this data-type; it's polymorphic.--}--module RegExDot.Repeatable(--- * Types--- ** Type-synonyms-	Repetitions,-	RepetitionBounds,--- ** Data-types-	Repeatable(..),--- * Constants---	nonGreedyToken,-	oneOrMoreToken,-	rangeDelimiters,-	rangeSeparatorToken,-	tokens,-	zeroOrMoreToken,-	zeroOrOneToken,--- * Functions-	one,-	oneOrMore,-	oneOrMore',-	zeroOrMore,-	zeroOrMore',-	zeroOrOne,-	zeroOrOne',---	precisely,-	repeatableParser,-	showSuffix,--- ** Accessors-	getFewest,-	getMost,--- ** Mutators-	focus,---	setNonGreedy,-	toSingleton,--- ** Operators-	(^#->#),-	(^#->#?),-	(^#->),-	(^#->?),-	(^#),--- ** Predicates-	isPrecise,-	hasPreciseBounds-) where--import Prelude hiding ((<$>), (<*>))	-- The "Prelude" from 'base-4.8' exports this symbol.-import			Control.Applicative((<$>), (<*>))-import			Control.Arrow((***))-import qualified	Control.DeepSeq-import qualified	Data.List-import qualified	RegExDot.Consumer		as Consumer-import qualified	RegExDot.ConsumptionProfile	as ConsumptionProfile-import qualified	Text.ParserCombinators.Parsec	as Parsec-import			Text.ParserCombinators.Parsec((<?>))-import qualified	ToolShed.Data.Pair-import qualified	ToolShed.SelfValidate--infix 6 ^#->#, ^#->#?, ^#->, ^#->?, ^#	-- A notch tighter than "DSL"s binary operators.---- | A number of repetitions.-type Repetitions	= Int---- | Defines the bounds of a range of permissible repetitions.-type RepetitionBounds	= (Repetitions, Maybe Repetitions)---- | Creates a precise 'RepetitionBounds', i.e. both lower & upper bounds on the number of 'Repetitions' are equal to the same value.-precisely :: Repetitions -> RepetitionBounds-precisely i	= (i, Just i)---- | Predicate which is 'True' if exactly one value is permissible, ie lower & upper bounds on the number of 'Repetitions' are identical.-hasPreciseBounds :: RepetitionBounds -> Bool-hasPreciseBounds (fewest, most)	= Just fewest == most---- | Declares a polymorphic data-type, which augments the underlying 'base' datum, with the range of times it may be used.-data Repeatable a	= MkRepeatable {-	base			:: a,			-- ^ The underlying polymorphic datum.-	repetitionBounds	:: RepetitionBounds,	-- ^ The bounds delimiting the range of permissible repetitions, of 'base'.-	isGreedy		:: Bool			-- ^ Whether to demand as many matching instances of 'base' as possible; or as few (AKA /lazy quantification/).-} deriving Eq--instance Functor Repeatable	where-	fmap f repeatable	= repeatable { base = f $ base repeatable }---- | True if there's no choice in the number of repetitions; implemented via 'isPrecise'.-isPrecise :: Repeatable a -> Bool-isPrecise	= hasPreciseBounds . repetitionBounds---- | Builds a parser for a specification of the number of permissible instances of the specified polymorphic parameter.-repeatableParser :: a -> Parsec.Parser (Repeatable a)-repeatableParser b	= Parsec.option (-	one b	-- The default; there's no concept of greediness here.- ) $ do-	repeatable	<- Parsec.choice [-		(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")-				most	<- Parsec.spaces >> Parsec.option (-					Just fewest	-- The default.-				 ) (-					do-						i	<- (-							Parsec.char rangeSeparatorToken			<?> "Repeatable.rangeSeparatorToken " ++ show rangeSeparatorToken-						 ) >> Parsec.spaces >> Parsec.option Nothing {-default to open-ended range-} (-							Just . read <$> Parsec.many1 Parsec.digit	<?> "Repetition-range maximum"-						 )--						Parsec.spaces >> return {-to ParsecT-monad-} i-				 )--				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 ParsecT-monad-} False--	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-		[(base', s1)]	-> (error . ("readsPrec Repeatable:\tparse-error; " ++) . show) `either` return $ Parsec.parse ((,) <$> repeatableParser base' <*> Parsec.getInput) "Repeatable" s1-		_		-> []	-- No parse.--{- |-	* A 'ShowS'-function for the suffix, denoting the permissible repetitions, of 'base'.--	* This function converts the internal, into the tradition /greedy/ & /non-greedy/ quantifiers of various specific varieties.--}-showSuffix :: Repeatable a -> ShowS-showSuffix repeatable	= let-	showRange :: ShowS -> ShowS-	showRange x	= (\(begin, end) -> begin . x . end) $ ToolShed.Data.Pair.mirror showChar rangeDelimiters- in (-	case repetitionBounds repeatable of-		(0, Nothing)		-> showChar zeroOrMoreToken-		(1, Nothing)		-> showChar oneOrMoreToken-		(fewest, Nothing)	-> showRange $ shows fewest . showChar rangeSeparatorToken-		(0, Just 1)		-> showChar zeroOrOneToken-		(1, Just 1)		-> id	-- CAVEAT: since there's no explicit repetition-operator, the non-greedy modifier can't be appended.-		(fewest, Just most)	-> showRange $ if fewest == most-			then shows fewest	-- Single-valued range.-			else shows fewest . showChar rangeSeparatorToken . shows most- ) . if ($ repeatable) `any` [isGreedy, isPrecise] {-without a range of possibilities, non-greediness is irrelevant-}-	then id-	else showChar nonGreedyToken	-- This can only be appended, if there a previous repetition-operator for it to modify.---- Replicate the syntax, for repetition, as used in a POSIX-standard /regex/.-instance Show a => Show (Repeatable a)	where-	showsPrec _ repeatable	= shows (base repeatable) . showSuffix repeatable--instance Consumer.Consumer a => Consumer.Consumer (Repeatable a)	where-	consumptionProfile MkRepeatable {-		base			= b,-		repetitionBounds	= (fewest, most)-	} = baseConsumptionProfile {-		ConsumptionProfile.consumptionBounds	= (fewest *) *** ((*) <$> most <*>) $ ConsumptionProfile.consumptionBounds baseConsumptionProfile	-- CAVEAT: special cases exist, where one or both halves of this calculation degenerate to a simpler form, but special treatment, in an attempt to improve performance, proved counterproductive.-	} where-		baseConsumptionProfile :: ConsumptionProfile.ConsumptionProfile-		baseConsumptionProfile	= Consumer.consumptionProfile b--	starHeight MkRepeatable {-		base			= b,-		repetitionBounds	= r-	} = Consumer.starHeight b + if hasPreciseBounds r then 0 else 1--instance ToolShed.SelfValidate.SelfValidator a => ToolShed.SelfValidate.SelfValidator (Repeatable a)	where-	getErrors MkRepeatable {-		base			= b,-		repetitionBounds	= (fewest, most),-		isGreedy		= g-	}-		| not $ ToolShed.SelfValidate.isValid b	= ToolShed.SelfValidate.getErrors b	-- Delegate.-		| otherwise				= ToolShed.SelfValidate.extractErrors [-			(fewest < 0, "Negative fewest=" ++ show fewest ++ "."),-			(-				case most of-					Just m	-> m < fewest-					_	-> False,-				"Invalid repetition-range; '" ++ show (fewest, most) ++ "'."-			), (-				not g && case most of-					Just m	-> fewest >= m	-- There ought to be potential for non-greediness, where specified: the converse isn't true, since greediness isn't explicit, & may not have been wanted.-					_	-> False,-				"Invalid non-greedy repetition-range; '" ++ show (fewest, most) ++ "'."-			)-		]--instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Repeatable a)	where-	rnf MkRepeatable {-		base			= b,-		repetitionBounds	= r,-		isGreedy		= g-	} = Control.DeepSeq.rnf (b, r, g)---- | Mutator.-setNonGreedy :: Repeatable a -> Repeatable a-setNonGreedy r	= r { isGreedy = False }--{- |-	* Construct a greedy 'Repeatable', from a polymorphic datum, with the specified range of permissible instances.--	* The /#/s in the identifier represent the two bounds.--	* /a{f, m}/--}-(^#->#)-	:: a			-- ^ The polymorphic payload from which to construct the 'Repeatable'.-	-> RepetitionBounds	-- ^ The permissible repetition-bounds for the polymorphic data.-	-> Repeatable a-b ^#-># bounds	= MkRepeatable {-	base			= b,-	repetitionBounds	= bounds,-	isGreedy		= True-}--{- |-	* Construct a non-greedy version of '^#->#'.--	* /a{f, m}?/--}-(^#->#?)-	:: a			-- ^ The polymorphic payload from which to construct the 'Repeatable'.-	-> RepetitionBounds	-- ^ The permissible repetition-bounds for the polymorphic data.-	-> Repeatable a-b ^#->#? bounds	= setNonGreedy (b ^#-># bounds)--{- |-	* Construct a greedy 'Repeatable', tailored for data repeated at least the specified number of times.--	* The /#/ in the identifier represents the single bound.--	* /a{f,}/--}-(^#->)-	:: a		-- ^ The polymorphic payload from which to construct the 'Repeatable'.-	-> Repetitions	-- ^ The minimum permissible repetitions of the polymorphic data.-	-> Repeatable a-b ^#-> fewest	= b ^#-># (fewest, Nothing)--{- |-	* Construct a non-greedy version of '^#->'.--	* /a{f,}?/--}-(^#->?)-	:: a		-- ^ The polymorphic payload from which to construct the 'Repeatable'.-	-> Repetitions	-- ^ The minimum permissible repetitions of the polymorphic data.-	-> Repeatable a-b ^#->? fewest	= setNonGreedy (b ^#-> fewest)--{- |-	* Construct a 'Repeatable', tailored for data repeated a precise number of times.--	* The /#/ in the identifier represents the single bound.--	* /a{f}/--}-(^#)-	:: a		-- ^ The polymorphic payload from which to construct the 'Repeatable'.-	-> Repetitions	-- ^ The precise number of repetitions of the polymorphic data which is required.-	-> Repeatable a-b ^# r	= b ^#-># precisely r--{- |-	* Construct a 'Repeatable', tailored for unrepeated data.--	* A degenerate case of '^#'.--}-one :: a -> Repeatable a-one	= (^# 1)--{- |-	* Construct a greedy 'Repeatable', from a polymorphic datum, with 'fewest' == 0 & 'most' == 1.--	* A specific case of '^#->#'.--}-zeroOrOne :: a -> Repeatable a-zeroOrOne	= (^#-># (0, Just 1))---- | Construct a non-greedy version of 'zeroOrOne'.-zeroOrOne' :: a -> Repeatable a-zeroOrOne'	= setNonGreedy . zeroOrOne--{- |-	* Construct a greedy 'Repeatable', from a polymorphic datum, with 'fewest' == 0.--	* A specific case of '^#->'.--}-zeroOrMore :: a -> Repeatable a-zeroOrMore	= (^#-> 0)---- | Construct a non-greedy version of 'zeroOrMore'.-zeroOrMore' :: a -> Repeatable a-zeroOrMore'	= setNonGreedy . zeroOrMore--{- |-	* Construct a greedy 'Repeatable', from a polymorphic datum, with lower 'RepetitionBounds' == one.--	* A specific case of '^#->'.--}-oneOrMore :: a -> Repeatable a-oneOrMore	= (^#-> 1)---- | Construct a non-greedy version of 'oneOrMore'.-oneOrMore' :: a -> Repeatable a-oneOrMore'	= setNonGreedy . oneOrMore---- | Reduces a 'Repeatable', with a range of 'RepetitionBounds', to a precise number of repetitions.-focus :: Repeatable a -> Repetitions -> Repeatable a-focus r i	= r { repetitionBounds = precisely i }--{- |-	* Reduces a 'Repeatable', with a range of 'RepetitionBounds', to a singleton.--	* A degenerate case of 'focus'.--}-toSingleton :: Repeatable a -> Repeatable a-toSingleton	= (`focus` 1)---- | Accessor.-getFewest :: Repeatable a -> Repetitions-getFewest MkRepeatable { repetitionBounds = (f, _) }	= f---- | Accessor.-getMost :: Repeatable a -> Maybe Repetitions-getMost MkRepeatable { repetitionBounds = (_, m) }	= m---- | The token used to denote /non-greedy/, when in the 'String'-form.-nonGreedyToken :: Char-nonGreedyToken	= '?'--{- |-	* The token used to denote 'zeroOrMore', when in the 'String'-form.--	* AKA /Kleene Star/.--}-zeroOrMoreToken :: Char-zeroOrMoreToken	= '*'---- | The token used to denote 'zeroOrOne', when in the 'String'-form.-zeroOrOneToken :: Char-zeroOrOneToken	= '?'---- | The token used to denote 'oneOrMore', when in the 'String'-form.-oneOrMoreToken :: Char-oneOrMoreToken	= '+'---- | The delimiters of '^#->#', when in the 'String'-form.-rangeDelimiters :: (Char, Char)-rangeDelimiters	= ('{', '}')---- | The token used to separate 'RepetitionBounds', when in the 'String'-form.-rangeSeparatorToken :: Char-rangeSeparatorToken	= ','---- | The set of 'Char' to which a specific meaning is attributed, when reading from 'String'.-tokens :: String-tokens	= Data.List.nub [nonGreedyToken, zeroOrMoreToken, zeroOrOneToken, oneOrMoreToken, fst rangeDelimiters, snd rangeDelimiters, rangeSeparatorToken]-
− src/RegExDot/Result.hs
@@ -1,60 +0,0 @@-{--	Copyright (C) 2010 Dr. Alistair Ward--	This program is free software: you can redistribute it and/or modify-	it under the terms of the GNU General Public License as published by-	the Free Software Foundation, either version 3 of the License, or-	(at your option) any later version.--	This program is distributed in the hope that it will be useful,-	but WITHOUT ANY WARRANTY; without even the implied warranty of-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-	GNU General Public License for more details.--	You should have received a copy of the GNU General Public License-	along with this program.  If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@]	Dr. Alistair Ward-- [@DESCRIPTION@]	Tools to manipulate a 'RegEx.Result'.-- [@CAVEAT@]	The data-definition remains in "RegEx", since it references 'RegEx.Match'.--}--module RegExDot.Result(--- * Functions--- ** Accessors (Deconstructors)-	getMatchList,-	getPreMatch,-	getPostMatch,--- ** Predicates-	isMatch,--- ** Query-	countMatches-) where--import qualified	Data.Maybe-import qualified	RegExDot.RegEx	as RegEx---- | Accessor.-getPreMatch :: RegEx.Result a -> RegEx.ExternalMatch a-getPreMatch (externalMatch, _, _)	= externalMatch---- | Accessor.-getPostMatch :: RegEx.Result a -> RegEx.ExternalMatch a-getPostMatch (_, _, externalMatch)	= externalMatch---- | Accessor.-getMatchList :: RegEx.Result a -> Maybe (RegEx.MatchList a)-getMatchList (_, maybeMatchList, _)	= maybeMatchList---- | True if the 'RegEx.InputData' matched the 'RegEx.ExtendedRegEx'.-isMatch :: RegEx.Result a -> Bool-isMatch	= Data.Maybe.isJust . getMatchList---- | Counts the number of top-level 'RegEx.Match'es.-countMatches :: RegEx.Result a -> Int-countMatches (_, Just matchList, _)	= length matchList-countMatches _				= 0-
− src/RegExDot/ShowablePredicate.hs
@@ -1,53 +0,0 @@-{--	Copyright (C) 2010 Dr. Alistair Ward--	This program is free software: you can redistribute it and/or modify-	it under the terms of the GNU General Public License as published by-	the Free Software Foundation, either version 3 of the License, or-	(at your option) any later version.--	This program is distributed in the hope that it will be useful,-	but WITHOUT ANY WARRANTY; without even the implied warranty of-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-	GNU General Public License for more details.--	You should have received a copy of the GNU General Public License-	along with this program.  If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@]	Dr. Alistair Ward-- [@DESCRIPTION@]	Used to implement /Perl-shortcut/s, /BracketExpression/-ranges & /POSIX Character-classes/.--}--module RegExDot.ShowablePredicate(--- * Types-	Predicate,--- ** Data-types-	ShowablePredicate(-		MkShowablePredicate,-		name,-		predicate-	)-) where--import qualified	Control.DeepSeq---- | An arbitrary polymorphic predicate function.-type Predicate a	= a -> Bool---- | Container for both the /predicate/, & the name used in the implementation of 'Show'.-data ShowablePredicate a	= MkShowablePredicate {-	name		:: String,	-- ^ The identifier of this predicate when in string-form.-	predicate	:: Predicate a	-- ^ The function used to determine whether an input datum matches.-}--instance Show (ShowablePredicate a)	where-	showsPrec _	= showString . name--instance Eq (ShowablePredicate a)	where-	l == r	= name l == name r	-- Ignore 'predicate'.--instance Control.DeepSeq.NFData (ShowablePredicate a)	where-	rnf	= Control.DeepSeq.rnf . name	-- Ignore 'predicate'.-
− src/RegExDot/Span.hs
@@ -1,64 +0,0 @@-{--	Copyright (C) 2010 Dr. Alistair Ward--	This program is free software: you can redistribute it and/or modify-	it under the terms of the GNU General Public License as published by-	the Free Software Foundation, either version 3 of the License, or-	(at your option) any later version.--	This program is distributed in the hope that it will be useful,-	but WITHOUT ANY WARRANTY; without even the implied warranty of-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-	GNU General Public License for more details.--	You should have received a copy of the GNU General Public License-	along with this program.  If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@]	Dr. Alistair Ward-- [@DESCRIPTION@]	Delimits a data-sequence, via its offset & length, within a wider list.--}--module RegExDot.Span(--- * Types--- ** Type-synonyms-	Span,--- * Functions-	after,-	empty,-	join-) where--import			Control.Arrow((***))-import qualified	RegExDot.ConsumptionBounds	as ConsumptionBounds--{- |-	* The offset & length of a specific data-sequence, within a wider list.--	* Similar to <http://hackage.haskell.org/packages/archive/regex-base/latest/doc/html/Text-Regex-Base-RegexLike.html#t%3AMatchArray>.--}-type Span	= (ConsumptionBounds.DataLength {-offset-}, ConsumptionBounds.DataLength)---- | An empty instance, located at the specified offset.-empty :: ConsumptionBounds.DataLength -> Span-empty offset	= (offset, 0)---- | Returns the furthest extent, i.e. the offset just after the end.-after :: Span -> ConsumptionBounds.DataLength-after	= uncurry (+)--{- |-	* Condenses a list into a single value.--	* Expects a gap-free, sorted list.--}-join-	:: ConsumptionBounds.DataLength	-- ^ The offset into the list of input-data to use when a null list of spans is received.-	-> [Span]-	-> Span-join offset []		= empty offset		-- The offset can't be deduced from a null list, so use the value provided.-join _ [singleton]	= singleton		-- Merely for efficiency.-join _ spanList		= head *** sum $ unzip spanList--
− src/RegExDot/Tree.hs
@@ -1,80 +0,0 @@-{--	Copyright (C) 2010 Dr. Alistair Ward--	This program is free software: you can redistribute it and/or modify-	it under the terms of the GNU General Public License as published by-	the Free Software Foundation, either version 3 of the License, or-	(at your option) any later version.--	This program is distributed in the hope that it will be useful,-	but WITHOUT ANY WARRANTY; without even the implied warranty of-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-	GNU General Public License for more details.--	You should have received a copy of the GNU General Public License-	along with this program.  If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@]	Dr. Alistair Ward-- [@DESCRIPTION@]--	* Defines a specific type of tree-structure, which is composed from either a datum, or a list of Tree-lists; <http://en.wikipedia.org/wiki/Rose_Tree>.--	* This more general tree-structure has the shape of a regular-expression match.--	* This contrasts with the typical binary-tree, which is either empty, or contains a (left Tree, datum, right Tree).--}--module RegExDot.Tree(--- * Types--- ** Type-synonyms---	TreeList,--- ** Data-types-	Tree(..),--- * Functions-	pop-) where--import qualified	Control.Arrow-import qualified	Control.DeepSeq-import qualified	Data.Foldable-import qualified	Data.List-import qualified	Data.Monoid---- | A general purpose tree-type structure.-data Tree a	=-	Leaf a			-- ^ The payload.-	| Node [TreeList a]	-- ^ Recurse. NB: a list of lists is required to contain the /MatchLists/ resulting from repeated /Alternatives/.-	deriving Eq---- | A list of 'Tree's; significant only because it is the essence of the recursive nature of 'Tree'.-type TreeList a	= [Tree a]--instance Show a => Show (Tree a)	where-	showsPrec _ (Leaf a)		= shows a-	showsPrec _ (Node treeLists)	= showList treeLists--instance Read a => Read (Tree a)	where-	readsPrec _ node@('[' : _)	= Control.Arrow.first Node `map` readList node {-singleton-}-	readsPrec _ leaf		= Control.Arrow.first Leaf `map` reads leaf {-singleton-}--instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Tree a)	where-	rnf (Leaf a)	= Control.DeepSeq.rnf a-	rnf (Node l)	= Control.DeepSeq.rnf l--instance Functor Tree	where-	fmap f (Leaf a)		= Leaf $ f a-	fmap f (Node treeLists)	= Node $ map (fmap {-recurse-} f `map`) treeLists--instance Data.Foldable.Foldable Tree where-	foldMap f (Leaf a)		= f a	-- CAVEAT: 'f' should be Associative, as required by a Monoid.-	foldMap f (Node treeLists)	= Data.List.foldl' (Data.List.foldl' (\monoid -> (monoid `Data.Monoid.mappend`) . Data.Foldable.foldMap f)) Data.Monoid.mempty treeLists---- | Deconstruct the specified 'Node'; i.e. lop the apex from the 'Tree', leaving a flat top.-pop-	:: Tree a	-- ^ The tree from which to extract the list of trees hanging immediately beneath its apex.-	-> [TreeList a]-pop (Node treeLists)	= treeLists-pop _			= error "RegExDot.Tree.pop:\tunexpected Leaf"-