regexchar 0.9.0.7 → 0.9.0.8
raw patch · 26 files changed
+1188/−1175 lines, 26 filesdep ~toolshed
Dependency ranges changed: toolshed
Files
- changelog +6/−1
- makefile +5/−1
- regexchar.cabal +18/−15
- regexchar.spec +1/−1
- src/Grecce/Assert/RegExOptsChar.hs +0/−92
- src/Grecce/Grep.hs +1/−1
- src/Grecce/Performance/ExtendedRegEx.hs +0/−144
- src/Grecce/Performance/ExtendedRegExTest.hs +0/−51
- src/Grecce/Performance/ExtendedRegExTestsNegative.hs +0/−85
- src/Grecce/Performance/ExtendedRegExTestsPositive.hs +0/−329
- src/Grecce/QC/ExtendedRegExChar.hs +0/−205
- src/Grecce/QC/MetaChar.hs +0/−78
- src/Grecce/QC/QuickChecks.hs +0/−52
- src/Grecce/QC/RepeatableMetaChar.hs +0/−98
- src/Grecce/Test/Assert/RegExOptsChar.hs +92/−0
- src/Grecce/Test/Performance/ExtendedRegEx.hs +144/−0
- src/Grecce/Test/Performance/ExtendedRegExTest.hs +51/−0
- src/Grecce/Test/Performance/ExtendedRegExTestsNegative.hs +85/−0
- src/Grecce/Test/Performance/ExtendedRegExTestsPositive.hs +329/−0
- src/Grecce/Test/QC/ExtendedRegExChar.hs +205/−0
- src/Grecce/Test/QC/MetaChar.hs +78/−0
- src/Grecce/Test/QC/QuickChecks.hs +52/−0
- src/Grecce/Test/QC/RepeatableMetaChar.hs +98/−0
- src/Main.hs +18/−18
- src/RegExChar/ExtendedRegExChar.hs +2/−2
- src/RegExChar/MetaChar.hs +3/−2
changelog view
@@ -36,4 +36,9 @@ * Changed identifier for type-parameters, to better reflect its role. * Removed dependency on archaic module "ToolShed.Arithmetic". * Reacted to the creation of module "ToolShed.Defaultable".-+ * Uploaded to <http://hackage.haskell.org/packages/hackage.html>.+0.9.0.8+ * Amended 'regexchar.cabal' to more correctly specify dependency on package 'toolshed'.+ * Used new module "ToolShed.Pair" from package toolshed-0.12.0.0.+ * Relocated modules "Grecce.Assert", "Grecce.Performance" and "Grecce.QC", into new layer "Test", in module-hierarchy.+ * Guarded 'eager-blackholing' flag in cabal file.
makefile view
@@ -13,7 +13,7 @@ # 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 clean clobber configure copy deb haddock help hlint install lintian prof rpm rpmlint sdist+.PHONY: all build check clean clobber configure copy deb haddock help hlint install lintian prof rpm rpmlint sdist all: install @@ -45,6 +45,9 @@ sdist: configure runhaskell Setup.hs $@ +check: sdist+ cabal upload --check --verbose=3 dist/*.tar.gz;+ rpm: regexchar.spec rpmbuild -bb -- $^ mv -- `find /usr/src/packages/ -type f -name 'regexchar*.rpm' -mtime 0 -print` ./@@ -67,6 +70,7 @@ clean: runhaskell Setup.hs $@ rm -rf 'debian/usr/' 'debian/DEBIAN/md5sums'+ find src -type f \( -name '*.o' -o -name '*.hi' \) -delete clobber: clean rm -f -- *.rpm *.deb
regexchar.cabal view
@@ -1,15 +1,15 @@ --Package-properties Name: regexchar-Version: 0.9.0.7+Version: 0.9.0.8 Cabal-Version: >= 1.6 Copyright: (C) 2010 Dr. Alistair Ward License: GPL License-file: LICENSE Author: Dr. Alistair Ward Stability: Stable interface, incomplete features.-Synopsis: A POSIX extended regular-expression engine.+Synopsis: A POSIX, extended regex-engine. Build-Type: Simple-Description: Provides a POSIX, extended regex-engine, specialised from the underlying polymorphic engine /regexdot/, to merely process character-lists, as is typically required.+Description: Provides a POSIX, extended regex-engine, specialised from the underlying /polymorphic/ package /regexdot/, to merely process the traditional character-lists. Category: Regex, Text Tested-With: GHC == 6.10, GHC == 6.12, GHC == 7.0 Homepage: http://functionalley.eu@@ -23,7 +23,7 @@ default: True flag llvm- Description: Whether the 'llvm' backend has been installed and is required for code-generation.+ Description: Whether the 'llvm' compiler-backend has been installed and is required for code-generation. manual: True default: False @@ -41,7 +41,7 @@ haskell98, parsec == 3.*, regexdot == 0.10.*,- toolshed == 0.11.*+ toolshed >= 0.12 GHC-options: -Wall GHC-prof-options: -prof -auto-all -caf-all@@ -53,17 +53,17 @@ hs-source-dirs: src Main-Is: Main.hs Other-modules:- Grecce.Assert.RegExOptsChar Grecce.CommandOptions Grecce.Grep- Grecce.Performance.ExtendedRegEx- Grecce.Performance.ExtendedRegExTest- Grecce.Performance.ExtendedRegExTestsNegative- Grecce.Performance.ExtendedRegExTestsPositive- Grecce.QC.ExtendedRegExChar- Grecce.QC.MetaChar- Grecce.QC.QuickChecks- Grecce.QC.RepeatableMetaChar+ Grecce.Test.Assert.RegExOptsChar+ Grecce.Test.Performance.ExtendedRegEx+ Grecce.Test.Performance.ExtendedRegExTest+ Grecce.Test.Performance.ExtendedRegExTestsNegative+ Grecce.Test.Performance.ExtendedRegExTestsPositive+ Grecce.Test.QC.ExtendedRegExChar+ Grecce.Test.QC.MetaChar+ Grecce.Test.QC.QuickChecks+ Grecce.Test.QC.RepeatableMetaChar Build-depends: array,@@ -77,7 +77,10 @@ if flag(threaded) Build-depends: parallel >= 3.0- GHC-options: -threaded -feager-blackholing+ GHC-options: -threaded++ if impl(ghc >= 6.12)+ GHC-options: -feager-blackholing else Build-depends: parallel
regexchar.spec view
@@ -8,7 +8,7 @@ Summary: A re-implementation of egrep, using a specialisation of a polymorphic regex-engine. Name: regexchar-Version: 0.9.0.7+Version: 0.9.0.8 Release: 1 License: GPLv3 #From '/usr/share/doc/packages/rpm/GROUPS'.
− src/Grecce/Assert/RegExOptsChar.hs
@@ -1,92 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}-{-- 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@]-- * Reads lines from the specified file.-- * Each line describes a 'Test'.-- * Each input-data string should match the /regex/ in the specified way.-- * Permits comparison with "Text.Regex.Posix.String".--}--module Grecce.Assert.RegExOptsChar(--- * Types--- ** Type-synonyms--- Test,--- TestDataAndResult,--- TestFilter,--- * Functions- findFailures,- findFailuresPosix,- readTests--- ** Predicates--- isMatch-) where--import Control.Applicative((<$>))-import Text.Regex.Posix()-import qualified Control.Arrow-import qualified RegExChar.ExtendedRegExChar as ExtendedRegExChar-import qualified RegExChar.RegExOptsChar as RegExOptsChar --CAVEAT: beware of the similar name.-import qualified RegExDot.CompilationOptions as CompilationOptions-import qualified RegExDot.ExecutionOptions as ExecutionOptions-import qualified Text.Regex.Base.RegexLike as RegexLike-import qualified Text.Regex.Posix.Wrap---- | Composed input data & list of captured 'RegExOptsChar.MatchSpan'.-type TestDataAndResult = (ExtendedRegExChar.InputData, [RegExOptsChar.MatchSpan])---- | Composed of the 'String' from which an unspecified type of regex is read, & a list of input data & results, to which it should respectively be applied & compared.-type Test = (String {-regex-source-}, [TestDataAndResult])---- | Filters a list of 'Test's, & returns a sub-set.-type TestFilter = [Test] -> [Test]---- | 'True' if the specified 'ExtendedRegExChar.InputData', is a member of the set defined by the polymorphic 'Text.Regex.Base.RegexLike.RegexLike' parameter.-isMatch :: RegexLike.RegexLike r ExtendedRegExChar.InputData => r -> TestDataAndResult -> Bool-isMatch regex (inputData, matchSpanList) = RegexLike.matchOnce regex inputData == Just (RegExOptsChar.toZeroIndexedArray matchSpanList)---- | Returns those 'Test's, which 'RegExOptsChar.RegExOptsChar' fails.-findFailures :: CompilationOptions.CompilationOptions -> ExecutionOptions.ExecutionOptions -> TestFilter-findFailures compilationOptions executionOptions = filter (not . uncurry all . Control.Arrow.first (isMatch . RegexLike.makeRegexOpts compilationOptions executionOptions))---- | Returns those 'Test's, which 'Text.Regex.Posix.Wrap.Regex' fails.-findFailuresPosix :: TestFilter-findFailuresPosix = filter (not . uncurry all . Control.Arrow.first ((isMatch :: Text.Regex.Posix.Wrap.Regex -> TestDataAndResult -> Bool) . RegexLike.makeRegex))--{- |- * Read 'Test's from file, ignoring blank lines & comments.-- * To facilitate parsing, each line in the file has the format of @show Test@.--}-readTests :: String -> IO [Test]-readTests fileName = map (- \line -> case reads line of- [(test, _)] -> test- _ -> error $ "Grecce.Assert.RegExOptsChar.readTests:\tfailed to read 'Test' from '" ++ line ++ "'"- ) . filter (- (/= '#') . head --Remove comments.- ) . filter (- not . null --Remove blank lines.- ) . lines <$> readFile fileName-
src/Grecce/Grep.hs view
@@ -37,12 +37,12 @@ import qualified Data.List import qualified Grecce.CommandOptions as CommandOptions import qualified RegExChar.ExtendedRegExChar as ExtendedRegExChar-import RegExChar.ExtendedRegExChar as ExtendedRegExChar((+~), (=~)) import qualified RegExDot.RegEx as RegEx import qualified RegExDot.RegExOpts as RegExOpts import qualified RegExDot.Result as Result import qualified System import qualified System.IO+import RegExChar.ExtendedRegExChar as ExtendedRegExChar((+~), (=~)) #if MIN_VERSION_parallel(3,0,0) import qualified Control.Parallel.Strategies
− src/Grecce/Performance/ExtendedRegEx.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@]-- * CPU-times the specified stress-test(s), over a hard-coded list of /regex/-engines,- providing each combination with a varied list of 'Performance.ExtendedRegExTest.TestGenerator's,- & a level of 'Performance.ExtendedRegExTest.TestComplexity' which increases linearly to the specified cap.-- * Either a simple 'Bool' result, or the full data-capture, can be requested.-- [@CAVEATS@]-- * For compatibility with the other regex-engines, uses the specialised instance 'ExtendedRegExChar.ExtendedRegExChar',- rather than the underlying polymorphic 'RegEx.ExtendedRegEx'.-- * "Text.Regex.Posix" stalls forever on 'Performance.ExtendedRegExTestsPositive.OVERLAPPING_ALTERNATIVES',- with 'Performance.ExtendedRegExTest.TestComplexity'=3 & data-capture requested.-- [@TODO@]-- * Utilise the standard interface in "RegExOptsChar", to permit identical treatment of different regex-engines.--}--module Grecce.Performance.ExtendedRegEx(--- * Types--- ** Type-synonyms--- GenerateAndRunTest,--- GetAndRunTestRange,--- * Constants--- solitary,--- * Functions- run--- tailorTest-) where--import Control.Applicative((<$>), (<*>))-import Control.Arrow((&&&))-import qualified Control.Arrow-import qualified Data.Maybe-import qualified Grecce.Performance.ExtendedRegExTest as Performance.ExtendedRegExTest-import qualified Grecce.Performance.ExtendedRegExTestsNegative as Performance.ExtendedRegExTestsNegative-import qualified Grecce.Performance.ExtendedRegExTestsPositive as Performance.ExtendedRegExTestsPositive-import qualified RegExChar.ExtendedRegExChar as ExtendedRegExChar-import qualified RegExDot.ExecutionOptions as ExecutionOptions-import qualified RegExDot.RegEx as RegEx-import qualified RegExDot.RegExOpts as RegExOpts-import qualified Text.Regex.Posix-import qualified ToolShed.Defaultable as Defaultable-import qualified ToolShed.TimePure as TimePure--solitary :: Bool-solitary = True --Currently required because of the disastrous performance of "Text.Regex.Posix" on 'Performance.ExtendedRegExTestsPositive.OVERLAPPING_ALTERNATIVES'.--type GetAndRunTestRange a = [a] -> IO ()-type GenerateAndRunTest = Performance.ExtendedRegExTest.TestGenerator Char -> Performance.ExtendedRegExTest.TestComplexity -> IO (ExtendedRegExChar.InputData, [(Double, String)])--{- |- * Print the CPU-seconds, required to run the range of 'Performance.ExtendedRegExTest.TestGenerator's corresponding to the specified test-names & a linearly increasing 'Performance.ExtendedRegExTest.TestComplexity'.-- * If the specified maximum 'Performance.ExtendedRegExTest.TestComplexity' is set to the special value '0', then it is increased linearly, without limit.--}-run :: ExecutionOptions.ExecutionOptions -> Performance.ExtendedRegExTest.TestComplexity -> Either [Performance.ExtendedRegExTestsNegative.TestName] [Performance.ExtendedRegExTestsPositive.TestName] -> IO ()-run executionOptions 0 testRange = run executionOptions maxBound testRange --Run the specified test-range, without any upper bound to 'Performance.ExtendedRegExTest.TestComplexity'.-run executionOptions maxTestComplexity (Left negativeTestRange) = runNegativeTestRange negativeTestRange where- runNegativeTestRange :: GetAndRunTestRange Performance.ExtendedRegExTestsNegative.TestName- runNegativeTestRange [] = runNegativeTestRange [minBound .. maxBound] --Run all the tests, to the specified maximum 'Performance.ExtendedRegExTest.TestComplexity'.- runNegativeTestRange testRange = sequence_ $ (- \complexity testGenerator -> cpuSecondsFor testGenerator complexity >>= print- ) <$> [1 .. maxTestComplexity] <*> Data.Maybe.mapMaybe Performance.ExtendedRegExTest.query testRange where- cpuSecondsFor :: GenerateAndRunTest- cpuSecondsFor testGenerator = (- \(inputData, (isMismatch, extendedRegExCharString)) -> (,) extendedRegExCharString <$> mapM (- TimePure.getCPUSeconds . ($ inputData) . (show .)- ) [- isMismatch,- if solitary- then const True --CAVEAT: should be 'undefined'.- else not . (Text.Regex.Posix.=~ extendedRegExCharString) :: String -> Bool --CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.- ]- ) . Control.Arrow.second (- (- \extendedRegEx -> (- RegEx./~ RegExOpts.MkRegExOpts { --CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.- RegExOpts.compilationOptions = Defaultable.defaultValue,- RegExOpts.executionOptions = executionOptions,- RegExOpts.regEx = extendedRegEx- }- )- ) &&& show . ExtendedRegExChar.MkExtendedRegExChar False {-hasNonCapturingTopLevelAlternatives-}- ) . testGenerator--run executionOptions maxTestComplexity (Right positiveTestRange) = runPositiveTestRange positiveTestRange where- runPositiveTestRange :: GetAndRunTestRange Performance.ExtendedRegExTestsPositive.TestName- runPositiveTestRange [] = runPositiveTestRange [minBound .. maxBound] --Run all the tests, to the specified maximum 'Performance.ExtendedRegExTest.TestComplexity'.- runPositiveTestRange testRange = sequence_ $ (- \complexity testGenerator -> cpuSecondsFor testGenerator complexity >>= print- ) <$> [1 .. maxTestComplexity] <*> Data.Maybe.mapMaybe Performance.ExtendedRegExTest.query testRange where- cpuSecondsFor :: GenerateAndRunTest- cpuSecondsFor testGenerator = (- \(inputData, (showMatch, extendedRegExCharString)) -> (,) extendedRegExCharString <$> (- (TimePure.getCPUSeconds . ($ inputData)) `mapM` [- showMatch,- if ExecutionOptions.requireMatchList executionOptions- then if solitary- then const "<untested>"--- else (Text.Regex.Posix.=~ extendedRegExCharString) :: String -> String --CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.- else (\(_, _, _, s) -> show s) . ((Text.Regex.Posix.=~ extendedRegExCharString) :: String -> (String, String, String, [String])) --CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.- else show . if solitary- then const True --CAVEAT: should be 'undefined'.- else (Text.Regex.Posix.=~ extendedRegExCharString) :: String -> Bool --CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.- ]- )- ) . Control.Arrow.second (- (- \extendedRegEx -> let- regExOpts :: RegExOpts.RegExOpts (RegEx.ExtendedRegEx Char)- regExOpts = RegExOpts.MkRegExOpts {- RegExOpts.compilationOptions = Defaultable.defaultValue,- RegExOpts.executionOptions = executionOptions,- RegExOpts.regEx = extendedRegEx- } --Similar to 'RegexLike.makeRegexOpts', but without the requirement to read the regex.- in if ExecutionOptions.requireMatchList executionOptions- then show . (RegEx.+~ regExOpts) --CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.- else show . (RegEx.=~ regExOpts) --CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.- ) &&& show . ExtendedRegExChar.MkExtendedRegExChar False {-hasNonCapturingTopLevelAlternatives-}- ) . testGenerator-
− src/Grecce/Performance/ExtendedRegExTest.hs
@@ -1,51 +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 the types used for performance-testing.-- * Defines an interface to permit queries on repositories of 'TestGenerator's.--}--module Grecce.Performance.ExtendedRegExTest(--- * Type-classes- TestGeneratorRepository(..),--- * Types--- ** Type-synonyms- TestComplexity,- Test,- TestGenerator-) where--import qualified RegExDot.RegEx as RegEx---- | Used to specify the level of complexity of /regex/ to generate.-type TestComplexity = Int---- | A pair defining the data & the /regex/ to which it should be fed.-type Test a = (RegEx.InputData a, RegEx.ExtendedRegEx a)---- | The type of a function, which accepts a 'TestComplexity', & returns a 'Test'.-type TestGenerator a = TestComplexity -> Test a---- | Implementer contracts to generate an appropriate 'Test'.-class TestGeneratorRepository a where- query :: a -> Maybe (TestGenerator Char) --CAVEAT: polymorphism reduced, to avoid requirement for non-portable Haskell-extensions.-
− src/Grecce/Performance/ExtendedRegExTestsNegative.hs
@@ -1,85 +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 'Performance.ExtendedRegExTest.TestGenerator's, for 'Performance.ExtendedRegExTest.Test's, which should fail.-- * Implements 'Performance.ExtendedRegExTest.TestGeneratorRepository'.--}--module Grecce.Performance.ExtendedRegExTestsNegative(--- * Types--- ** Data-types- TestName-) where--import qualified Control.Arrow-import qualified Data.Maybe-import qualified Grecce.Performance.ExtendedRegExTest as Performance.ExtendedRegExTest-import qualified Grecce.Performance.ExtendedRegExTestsPositive as Performance.ExtendedRegExTestsPositive---- | Defines an enumeration of keys, by which to reference 'Performance.ExtendedRegExTest.TestGenerator's.-data TestName =- INTERLEAVED_GREEDY_STAR_VS_MANDATORY- | LINEAR_FRACTAL_NONGREEDY_STAR- | OVERLAPPING_ALTERNATIVES- deriving (- Bounded,- Enum,- Eq,- Read,- Show- )--instance Performance.ExtendedRegExTest.TestGeneratorRepository TestName where- query = (- `lookup` [-{-- Explanation: based on 'Performance.ExtendedRegExTestsPositive.INTERLEAVED_GREEDY_STAR_VS_MANDATORY', but with twice the input data following by a mismatch.- Performance: vastly better performance than "Text.Regex.Posix", benefiting from 'ExecutionOptions.checkExistenceOfInelasticTail'.--}- (- INTERLEAVED_GREEDY_STAR_VS_MANDATORY,- Control.Arrow.first ((++ "!") . concat . replicate 2) . Data.Maybe.fromJust (- Performance.ExtendedRegExTest.query Performance.ExtendedRegExTestsPositive.INTERLEAVED_GREEDY_STAR_VS_MANDATORY- )- ),-{-- Explanation: based on 'Performance.ExtendedRegExTestsPositive.LINEAR_FRACTAL_NONGREEDY_STAR', but with mismatching input data, which causes the time-complexity explodes to O(n!).- Performance: vastly better performance than "Text.Regex.Posix", benefiting from 'ExecutionOptions.checkForUnconsumableData'.--}- (- LINEAR_FRACTAL_NONGREEDY_STAR,- Control.Arrow.first (++ "!") . Data.Maybe.fromJust (- Performance.ExtendedRegExTest.query Performance.ExtendedRegExTestsPositive.LINEAR_FRACTAL_NONGREEDY_STAR- )- ),-{-- Explanation: based on 'Performance.ExtendedRegExTestsPositive.OVERLAPPING_ALTERNATIVES', but with mismatching input data.- Performance: marginally better performance than "Text.Regex.Posix", benefiting from 'ExecutionOptions.checkForUnconsumableData' & 'ExecutionOptions.permitReorderingOfAlternatives'.--}- (- OVERLAPPING_ALTERNATIVES,- Control.Arrow.first (++ "!") . Data.Maybe.fromJust (- Performance.ExtendedRegExTest.query Performance.ExtendedRegExTestsPositive.OVERLAPPING_ALTERNATIVES- )- )- ]- )
− src/Grecce/Performance/ExtendedRegExTestsPositive.hs
@@ -1,329 +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 'Performance.ExtendedRegExTest.TestGenerator's, for 'Performance.ExtendedRegExTest.Test's, which should pass.-- * Implements 'Performance.ExtendedRegExTest.TestGeneratorRepository'.--}--module Grecce.Performance.ExtendedRegExTestsPositive(--- * Types--- ** Data-types- TestName(..)--- * Functions--- triangularSeries-) where--import Control.Arrow((&&&))-import RegExDot.DSL((-:), (?:), (*:), (<~>))-import RegExDot.RegEx((.*), (.*?))-import RegExDot.Repeatable((^#->#), (^#->), (^#))-import qualified Data.Char-import qualified Grecce.Performance.ExtendedRegExTest as Performance.ExtendedRegExTest-import qualified RegExDot.Anchor as Anchor-import qualified RegExDot.BracketExpressionMember as BracketExpressionMember-import qualified RegExDot.Meta as Meta-import qualified RegExDot.RegEx as RegEx-import qualified RegExDot.Repeatable as Repeatable---- | Defines an enumeration of keys, by which to reference 'Performance.ExtendedRegExTest.TestGenerator's.-data TestName =- BIPOLAR_GREEDY_OPTIONAL_VS_MANDATORY- | INTERLEAVED_GREEDY_STAR_VS_MANDATORY- | TRIANGULAR_CONSUMPTION- | BINARY_TREE- | REPEATABLE_ALTERNATIVES- | REPEATED_ALTERNATIVES- | LINEAR_FRACTAL_RANGES- | LINEAR_FRACTAL_NONGREEDY_STAR- | BINARY_TREE_FRACTAL- | FUTILE_REPEATABLE_ALTERNATIVES- | OVERLAPPING_ALTERNATIVES- | UNBRIDLED_GREED- | MULTIPLE_CHOICE- deriving (- Bounded,- Enum,- Eq,- Read,- Show- )--instance Performance.ExtendedRegExTest.TestGeneratorRepository TestName where- query = (- `lookup` [-{-- "^(<testChar>?){<complexity>}(<testChar>){<complexity>}$".- Explanation: each of the optional meta-data in the first half of the expression can capture data, but in doing so starves a mandatory meta-datum in the second half of the expression,- presenting the opportunity for O(2^n) time-complexity backtracking.- Performance: vastly better Boolean-result performance than "Text.Regex.Posix". Looks like O(n^2) time-complexity.--}- (- BIPOLAR_GREEDY_OPTIONAL_VS_MANDATORY,- (`replicate` testChar) &&& let- exactly :: Performance.ExtendedRegExTest.TestComplexity -> (RegEx.Pattern Char -> RegEx.RepeatablePattern Char) -> RegEx.RepeatablePattern Char- exactly n f = (RegEx.captureGroup . map ((Nothing, Nothing) <~>) . return {-to List-monad-} . return {-to List-monad-} $ f testRequirement) ^# n- in ((Just Anchor.Bow, Just Anchor.Stern) <~>) . (`map` [Repeatable.zeroOrOne, Repeatable.one]) . exactly- ),-{-- "^(.*z){<complexity>}$".- Explanation: each '.*' can consume all the remaining data, starving the interspersed mandatory meta data, presenting the opportunity for O(2^n) time-complexity backtracking.- Performance: marginally better Boolean-result & data-capture performance than "Text.Regex.Posix" with 'ExecutionOptions.moderateGreed'. Empirically O(n^3) time-complexity.--}- (- INTERLEAVED_GREEDY_STAR_VS_MANDATORY,- concat . (`replicate` (testChar : "z")) &&& RegEx.dock . (- `mkExtendedRegExFromRepeatableSingletonAlternative` ((Nothing, Nothing) <~>) [- (.*),- RegEx.simply $ Meta.Literal 'z'- ]- ) . flip (^#)- ),-{-- "^<testChar>*<testChar>+<testChar>{2,}<testChar>{3,} ... a{<complexity>,}$".- Explanation: each term greedily consumes all the data, starving all the remaining terms, until after much back-tracking, it consumes the just the minimum permissible.- The correct consumption-pattern is triangular, starting at the apex & spreading into a delta.- Performance: vastly better Boolean-result & data-capture performance than "Text.Regex.Posix".--}-- (- TRIANGULAR_CONSUMPTION,- (`replicate` testChar) . triangularSeries &&& ((Just Anchor.Bow, Just Anchor.Stern) <~>) . (`take` map (testRequirement ^#->) [0 ..]) . succ- ),-{-- "(<testChar>|<testChar>{2})*c".- Explanation: from <http://www.haskell.org/pipermail/libraries/2009-March/011379.html>- Performance: disastrous performance.--}- (- BINARY_TREE,- (++ "bc") . (`replicate` testChar) . (2 ^) &&& const (- (Nothing, Nothing) <~> RegEx.captureGroup (- map (- ((Nothing, Nothing) <~>) . return {-to List-monad-} . (testRequirement ^#) . (2 ^)- ) [0 :: Int .. 1]- ) *: RegEx.Require (Meta.Literal 'c') -: []- )- ),-{-- "^(<testChar>?|<testChar>{2}|<testChar>{4}|<testChar>{8}| ... |<testChar>{2^<complexity>})*$".- Explanation: all but one of the 'Alternatives' must be used, one on each successive repetition, in order to consume all the 'InputData'.- A correct implementation will match against the longest possible Alternative on each successive repetition, just as if the repetition were unrolled; see 'REPEATED_ALTERNATIVES'.- Performance: vastly worse Boolean-result performance (even with 'ExecutionOptions.permitReorderingOfAlternatives'),- & worse data-capture performance (even with 'ExecutionOptions.useFirstMatchAmongAlternatives'), than "Text.Regex.Posix".--}- (- REPEATABLE_ALTERNATIVES,- (`replicate` testChar) . pred {-requires several different Alternatives-} . (2 ^) &&& (- \i -> ((Just Anchor.Bow, Just Anchor.Stern) <~>) . return {-to List-monad-} . Repeatable.zeroOrMore . RegEx.captureGroup . map ((Nothing, Nothing) <~>) $ [Repeatable.zeroOrOne testRequirement] : map (- return {-to List-monad-} . (testRequirement ^#) . (2 ^)- ) [1 .. i]- )- ),-{-- "^(<testChar>?|<testChar>{2}|<testChar>{4}| ... |<testChar>{2^<complexity>})(<testChar>?|<testChar>{2}|<testChar>{4}| ... |<testChar>{2^<complexity>})(<testChar>?|<testChar>{2}|<testChar>{4}| ... |<testChar>{2^<complexity>})$".- Explanation: 'REPEATABLE_ALTERNATIVES' with the repetition unrolled, to permit inspection of individual capture-groups,- revealing the use by "Text.Regex.Posix", of Perl-style "1st-past-the-post" selection between Alternatives.- Performance: much faster than 'REPEATABLE_ALTERNATIVES'. Vastly better Boolean-result & data-capture performance than both 'REPEATABLE_ALTERNATIVES' & "Text.Regex.Posix".--}- (- REPEATED_ALTERNATIVES,- (`replicate` testChar) . pred {-requires several different Alternatives-} . (2 ^) &&& (- \i -> ((Just Anchor.Bow, Just Anchor.Stern) <~>) . replicate i . Repeatable.one . RegEx.captureGroup . map ((Nothing, Nothing) <~>) $ [Repeatable.zeroOrOne testRequirement] : map (- return {-to List-monad-} . (testRequirement ^#) . (2 ^)- ) [1 .. i]- )- ),-{-- "^((((<testChar>{0,2}){0,2}){0,2}){0,2}){0,2} ... $".- Explanation:- Performance: vastly better Boolean-result performance than "Text.Regex.Posix", which exhausts memory at n==10. Better data-capture performance too.- Stack-overflow at 'Performance.ExtendedRegExTest.TestComplexity'=14.--}- (- LINEAR_FRACTAL_RANGES,- let- base :: Repeatable.Repetitions- base = 2 --Arbitrarily.-- bounds :: Repeatable.RepetitionBounds- bounds = (0, Just base)- in (`replicate` testChar) . (base ^) &&& RegEx.dock . (- (- mkExtendedRegExFromRepeatableSingletonAlternative (^#-># bounds) `iterate` ((Nothing, Nothing) <~>) [testRequirement ^#-># bounds] {-initial value-}- ) !!- ) . pred- ),-{-- "^((((((((((((((<testChar>*?)*?)*?)*?)*?)*?)*?)*?)*?)*?)*?)*?)*?)*?)*? ... $".- Explanation: interesting because all 'RegEx.Alternatives' are expanded before the first comparison against input data, & the number possible repetitions of each expansion is very vague.- Performance: vastly better Boolean-result performance than "Text.Regex.Posix", but inferior data-capture performance.--}- (- LINEAR_FRACTAL_NONGREEDY_STAR,- const (replicate 8 {-arbitrary-} testChar) &&& RegEx.dock . (- (- mkExtendedRegExFromRepeatableSingletonAlternative Repeatable.zeroOrMore' `iterate` ((Nothing, Nothing) <~>) [Repeatable.zeroOrMore' testRequirement] {-initial value-}- ) !!- )- ),-{-- "^((([^<testChar>]|<testChar>)|(<testChar>|[^<testChar>]))|((<testChar>|[^<testChar>])|([^<testChar>]|<testChar>)))$".- Explanation: exhibits symmetry in the left & right branches of the binary tree of RegEx.Alternatives, & so the exponentially expanding set of leaves are equivalent & all match the data.- Performance: vastly better Boolean-result & comparable data.capture performance to "Text.Regex.Posix", which exhausts memory at complexity 5 to 6, & to egrep which lasts until complexity==6.--}- (- BINARY_TREE_FRACTAL,- let- base :: Repeatable.Repetitions- base = 2 --Arbitrarily.- in (`replicate` testChar) . (base ^) &&& RegEx.dock . head . (- (- iterate (- \extendedRegExList -> (- ((Nothing, Nothing) <~>) . return {-to List-monad-} . (^# base) . RegEx.captureGroup- ) `map` [extendedRegExList, reverse extendedRegExList]- ) $ map (- ((Nothing, Nothing) <~>) . return {-to List-monad-} . ($ RegEx.Require $ Meta.Literal testChar)- ) [Repeatable.one, Repeatable.zeroOrOne]- ) !!- )- ),-{-- "^(<testChar>1|<testChar>{2}2|<testChar>{3}3| ... |<testChar>{<complexity>}<complexity>)*<testChar>*$".- Explanation: none of the Alternatives can match, & consequently the group must match zero times, presenting the opportunity for many wasted trial repetitions.- Performance: comparable Boolean-result performance to "Text.Regex.Posix", & marginally superior data-capture performance.--}- (- FUTILE_REPEATABLE_ALTERNATIVES,- \alternatives -> (- replicate 1024 {-arbitrarily-} testChar,- ((Just Anchor.Bow, Just Anchor.Stern) <~>) $ map Repeatable.zeroOrMore [- RegEx.captureGroup . map ((Nothing, Nothing) <~>) $ (- \i -> ($ i) `map` [- (testRequirement ^#),- RegEx.simply . Meta.Literal . Data.Char.chr . (Data.Char.ord '0' +) . (`mod` 10)- ]- ) `map` [1 .. alternatives],- testRequirement- ]- )- ),-{-- "^a?((ab?)*|(bc?)*|(cd?)*| ...)*$".- Explanation: the zeroeth term matches greedily, consuming the datum mandated by the 1st Alternative, so the 2nd Alternative greedily consumes two data, including the datum mandated by the 3rd, ...- If there're an even number of Alternatives, then n/2 repetitions (one for each), will consume all the input data, but otherwise the final input datum isn't consumed.- The consumption back-tracks rapidly until the zeroeth term yields-up the initial datum, after which the odd Alternatives match consuming all the input data, in (n/2 + 1) repetitions.- This suggests a very tractable O(n) time-complexity, but Posix requires that we select the combination of Alternatives which consume most data,- & since there are two terms in the regex that can consume each input datum, it looks like there should be 2^n candidate solutions; in fact there's just one.- So, all O(Alternatives ^ repetitions) permutations may be searched in vain for a superior solution.- Performance: vastly worse Boolean-result performance than "Text.Regex.Posix", but the latter then stalls indefinitely (CPU-bound) at complexity=[13 (not verbose), 4 (verbose)].--}- (- OVERLAPPING_ALTERNATIVES,- let- bracket :: a -> [a] -> [a]- bracket terminal = (terminal :) . (++ [terminal])-- zeroOrMoreAlternatives :: [RegEx.Concatenation Char] -> RegEx.Concatenation Char- zeroOrMoreAlternatives = return {-to List-monad-} . Repeatable.zeroOrMore . RegEx.captureGroup . map ((Nothing, Nothing) <~>)- in (- id &&& (- ((Just Anchor.Bow, Just Anchor.Stern) <~>) . (RegEx.Require (Meta.Literal testChar) ?:) . zeroOrMoreAlternatives . map (- \(a, b) -> zeroOrMoreAlternatives $ return {-to List-monad-} [a, b]- ) . init . tail . uncurry zip . (- map Repeatable.one . init &&& map Repeatable.zeroOrOne . tail- ) . bracket undefined . map (- RegEx.Require . Meta.Literal- )- )- ) . (`take` (filter Data.Char.isAlphaNum $ filter Data.Char.isAscii [minBound .. maxBound])) . succ- ),-{-- "^(a|b|c)*(a.*?c){3}$".- Explanation: zero repetitions of the set of Alternatives on the LHS is the only solution, but because the greedy star, within which they're grouped, permits many more,- & because the RHS's requirement for all the input-data, isn't explicit, there's a temptation to over-consume.- Performance: empirically O(2^n) time-complexity, but linearised by 'ExecutionOptions.moderateGreed'.- Better Boolean-result & data-capture performance than "Text.Regex.Posix".--}- (- UNBRIDLED_GREED,- \complexity -> let- complexity' :: Performance.ExtendedRegExTest.TestComplexity- complexity' = 1 + complexity-- testSet :: String- testSet = take complexity' . filter Data.Char.isAlphaNum $ filter Data.Char.isAscii [minBound .. maxBound]- in (- concat $ replicate complexity' testSet,- ((Just Anchor.Bow, Just Anchor.Stern) <~>) $ (- RegEx.captureGroup . map ((Nothing, Nothing) <~>) $ map (- return {-to List-monad-} . RegEx.simply . Meta.Literal- ) testSet- ) *: [- (- RegEx.captureGroup . map ((Nothing, Nothing) <~>) . return {-to List-monad-} $ RegEx.Require (Meta.Literal $ head testSet) -: (.*?) : RegEx.Require (Meta.Literal $ last testSet) -: []- ) ^# complexity'- ]- )- ),-{-- "^(a*[^a]|[^b]|[^c]| ... |[^z])*$"- Explanation: The first alternative greedily consumes all the input data until finding a mismatch on the last, whilst all the remaining alternatives successfully consume a single input datum.- The same occurs on each successive repetition of the alternatives, of which there are as many as input data,- leading to a lot of wasted time with O(<alternatives> ^ <input data>) time-complexity.- Performance: poor Boolean-result & worse data-capture performance (benefiting from 'ExecutionOptions.useFirstMatchAmongAlternatives'), compared to "Text.Regex.Posix".--}- (- MULTIPLE_CHOICE,- const (replicate 8 {-arbitrary-} testChar) &&& (- ((Just Anchor.Bow, Just Anchor.Stern) <~>) . return {-to List-monad-} . Repeatable.zeroOrMore . RegEx.captureGroup . map ((Nothing, Nothing) <~>) . (- [- Repeatable.zeroOrMore testRequirement,- RegEx.simply . Meta.NoneOf $ map BracketExpressionMember.Literal [testChar]- ] :- ) . map (- return {-to List-monad-} . RegEx.simply . Meta.NoneOf . return {-to List-monad-} . BracketExpressionMember.Literal- ) . (- `take` tail [testChar ..]- )- )- )- ]- ) where- testChar :: Char- testChar = 'a'-- testRequirement :: RegEx.Pattern Char- testRequirement = RegEx.Require $ Meta.Literal testChar-- mkExtendedRegExFromRepeatableSingletonAlternative :: (RegEx.Pattern Char -> RegEx.RepeatablePattern Char) -> RegEx.ExtendedRegEx Char -> RegEx.ExtendedRegEx Char- mkExtendedRegExFromRepeatableSingletonAlternative makeRepetitions = ((Nothing, Nothing) <~>) . return {-to List-monad-} . makeRepetitions . RegEx.captureGroup . return {-to List-monad-}--{- |- * The sum of the sequence descending from the specified integer; @n + (n - 1) + (n - 2) + ... + 2 + 1 + 0@.-- * <http://en.wikipedia.org/wiki/Triangular_number>.-- * <http://mathworld.wolfram.com/TriangularNumber.html>.--}-triangularSeries :: Integral i => i -> i-triangularSeries i = i * (i + 1) `div` 2
− src/Grecce/QC/ExtendedRegExChar.hs
@@ -1,205 +0,0 @@-{-# LANGUAGE CPP #-}-{-# 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@] Implements 'Test.QuickCheck.Arbitrary' & defines tests based on it.--}--module Grecce.QC.ExtendedRegExChar(--- * Types--- ** Data-types--- AlternativesChar(..),--- PatternChar(..),--- RepeatablePatternChar(..),--- ** Type-synonyms--- Testable,--- * Functions--- deconstructAlternativesChar,--- deconstructPatternChar,--- deconstructRepeatablePatternChar,- quickChecks-) where--import Control.Applicative((<$>))-import Grecce.QC.MetaChar()-import RegExDot.DSL((-:), (?:), (+:), (<~>))-import RegExDot.RegEx((+~))-import Test.QuickCheck((==>))-import qualified RegExChar.ExtendedRegExChar as ExtendedRegExChar --CAVEAT: beware of the similar name.-import qualified RegExChar.MetaChar as MetaChar-import qualified RegExDot.Anchor as Anchor-import qualified RegExDot.BracketExpressionMember as BracketExpressionMember-import qualified RegExDot.Consumer as Consumer-import qualified RegExDot.ConsumptionBounds as ConsumptionBounds-import qualified RegExDot.ConsumptionProfile as ConsumptionProfile-import qualified RegExDot.Meta as Meta-import qualified RegExDot.RegEx as RegEx-import qualified RegExDot.RegExOpts as RegExOpts-import qualified RegExDot.Repeatable as Repeatable-import qualified RegExDot.Result as Result-import qualified Test.QuickCheck-import qualified ToolShed.SelfValidate as SelfValidate---- | A specialised instance, required to instantiate 'Test.QuickCheck.Arbitrary'.-newtype AlternativesChar = MkAlternativesChar (RegEx.Alternatives Char) deriving (Eq, Read, Show)---- | Accessor.-deconstructAlternativesChar :: AlternativesChar -> RegEx.Alternatives Char-deconstructAlternativesChar (MkAlternativesChar a) = a--instance SelfValidate.SelfValidator AlternativesChar where- isValid = SelfValidate.isValid . deconstructAlternativesChar--instance Consumer.Consumer AlternativesChar where- consumptionProfile = Consumer.consumptionProfile . deconstructAlternativesChar- starHeight = Consumer.starHeight . deconstructAlternativesChar--instance Test.QuickCheck.Arbitrary AlternativesChar where- arbitrary = MkAlternativesChar . RegEx.MkAlternatives . map ExtendedRegExChar.extendedRegEx <$> (- Test.QuickCheck.elements [1, 2] >>= Test.QuickCheck.vector {-[ExtendedRegExChar]-}- ) --TODO: permit zero alternatives.-#if !(MIN_VERSION_QuickCheck(2,1,0))- coarbitrary = undefined --CAVEAT: stops warnings from ghc.-#endif---- | A specialised instance, required to instantiate 'Test.QuickCheck.Arbitrary'.-newtype PatternChar = MkPatternChar (RegEx.Pattern Char) deriving (Eq, Read, Show)---- | Accessor.-deconstructPatternChar :: PatternChar -> RegEx.Pattern Char-deconstructPatternChar (MkPatternChar pattern) = pattern--instance SelfValidate.SelfValidator PatternChar where- isValid = SelfValidate.isValid . deconstructPatternChar--instance Consumer.Consumer PatternChar where- consumptionProfile = Consumer.consumptionProfile . deconstructPatternChar- starHeight = Consumer.starHeight . deconstructPatternChar--instance Test.QuickCheck.Arbitrary PatternChar where- arbitrary = MkPatternChar <$> Test.QuickCheck.frequency [- (4, RegEx.Require . MetaChar.deconstruct <$> Test.QuickCheck.arbitrary {-MetaChar-}),- (1, RegEx.CaptureGroup . deconstructAlternativesChar <$> Test.QuickCheck.arbitrary {-AlternativesChar-})- ] --CAVEAT: may recurse forever if 'RegEx.CaptureGroup / RegEx.Require' is too high.-#if ! (MIN_VERSION_QuickCheck(2,1,0))- coarbitrary = undefined --CAVEAT: stops warnings from ghc.-#endif---- | A specialised instance, required to instantiate 'Test.QuickCheck.Arbitrary'.-newtype RepeatablePatternChar = MkRepeatablePatternChar (Repeatable.Repeatable PatternChar) deriving (Eq, Read, Show)---- | Accessor.-deconstructRepeatablePatternChar :: RepeatablePatternChar -> Repeatable.Repeatable PatternChar-deconstructRepeatablePatternChar (MkRepeatablePatternChar repeatable) = repeatable--instance SelfValidate.SelfValidator RepeatablePatternChar where- isValid = SelfValidate.isValid . deconstructRepeatablePatternChar--instance Consumer.Consumer RepeatablePatternChar where- consumptionProfile = Consumer.consumptionProfile . deconstructRepeatablePatternChar- starHeight = Consumer.starHeight . deconstructRepeatablePatternChar--instance Test.QuickCheck.Arbitrary RepeatablePatternChar where- arbitrary = do- patternChar <- Test.QuickCheck.arbitrary- fewest <- Test.QuickCheck.elements [0 .. 9] --Could be more, but this is an adequate test.- most <- Test.QuickCheck.oneof [return {-to Gen-monad-} Nothing, Just <$> Test.QuickCheck.elements [max fewest 1 .. 9]]- isGreedy <- Test.QuickCheck.arbitrary {-Bool-}-- let- repetitionBounds :: Repeatable.RepetitionBounds- repetitionBounds = (fewest, most)-- return {-to Gen-monad-} $ MkRepeatablePatternChar Repeatable.MkRepeatable {- Repeatable.base = patternChar,- Repeatable.repetitionBounds = repetitionBounds,- Repeatable.isGreedy = Repeatable.hasPreciseBounds repetitionBounds || isGreedy --Only specify non-greedy where space exists.- }-#if ! (MIN_VERSION_QuickCheck(2,1,0))- coarbitrary = undefined --CAVEAT: stops warnings from ghc.-#endif--instance Test.QuickCheck.Arbitrary ExtendedRegExChar.ExtendedRegExChar where- arbitrary = do- hasBowAnchor <- Test.QuickCheck.arbitrary {-Bool-}- concatenation <- map ((deconstructPatternChar <$> {-replace base-}) . deconstructRepeatablePatternChar {-Repeatable.Repeatable PatternChar-}) <$> Test.QuickCheck.vector 2 {-[RepeatablePatternChar]-}- hasSternAnchor <- Test.QuickCheck.arbitrary {-Bool-}-- return {-to Gen-monad-} $ ExtendedRegExChar.MkExtendedRegExChar False RegEx.MkExtendedRegEx {- RegEx.bowAnchor = if hasBowAnchor then Just Anchor.Bow else Nothing,- RegEx.concatenation = concatenation,- RegEx.sternAnchor = if hasSternAnchor then Just Anchor.Stern else Nothing- }--#if ! (MIN_VERSION_QuickCheck(2,1,0))- coarbitrary = undefined --CAVEAT: stops warnings from ghc.-#endif--type Testable = ExtendedRegExChar.ExtendedRegExChar -> Test.QuickCheck.Property---- | Defines invariant properties, which must hold for any 'ExtendedRegExChar.ExtendedRegExChar'.-quickChecks :: (Testable -> IO ()) -> IO ()-quickChecks checker = quickChecks1 >> quickChecks2 where- quickChecks1 = checker `mapM_` [prop_consumptionProfile, prop_consumptionProfile2, prop_io, prop_io', prop_isValid, prop_starHeight] where- prop_consumptionProfile, prop_consumptionProfile2, prop_io, prop_io', prop_isValid, prop_starHeight :: Testable- prop_consumptionProfile r = Test.QuickCheck.label "prop_consumptionProfile" $ (- case maybeMaxData of- Nothing -> True- Just m -> m >= minData- ) && (- not b == all (not . Consumer.getHasSpecificRequirement) (RegEx.concatenation $ ExtendedRegExChar.extendedRegEx r)- )- where- ConsumptionProfile.MkConsumptionProfile {- ConsumptionProfile.consumptionBounds = (minData, maybeMaxData),- ConsumptionProfile.hasSpecificRequirement = b- } = Consumer.consumptionProfile r-- prop_consumptionProfile2 r = RegEx.isDefined (ExtendedRegExChar.extendedRegEx r) ==> Test.QuickCheck.label "prop_consumptionProfile2" $ or [hasSpecificRequirement, canConsumeAnything] --There's either; a requirement for at least one specific, or we can consume at least one arbitrary; input datum.- where- ConsumptionProfile.MkConsumptionProfile {- ConsumptionProfile.hasSpecificRequirement = hasSpecificRequirement,- ConsumptionProfile.canConsumeAnything = canConsumeAnything- } = Consumer.consumptionProfile r-- prop_io r = Test.QuickCheck.label "prop_io" $ read (show r) == r- prop_io' r = Test.QuickCheck.label "prop_io'" $ read (show r') == r' where r' = ExtendedRegExChar.extendedRegEx r --Check "RegEx.ExtendedRegEx Char" too.- prop_isValid r = Test.QuickCheck.label "prop_isValid" $ SelfValidate.isValid r- prop_starHeight r = Test.QuickCheck.label "prop_starHeight" $ (Consumer.starHeight r == 0) == ConsumptionBounds.isPrecise (Consumer.getConsumptionBounds r)-- quickChecks2 = Test.QuickCheck.quickCheck {-'checker' is technically the wrong type-} `mapM_` [prop_double] where- prop_double :: Double -> Test.QuickCheck.Property- prop_double d = Test.QuickCheck.label "prop_double" $ RegEx.extractDataFromMatchList `fmap` Result.getMatchList result == Just s where- s :: String- s = show $ d * 1e6 --Implementation of show, uses exponential notation, when the number is large.-- result :: RegEx.Result Char- result = s +~ RegExOpts.mkRegEx (- (Just Anchor.Bow, Just Anchor.Stern) <~> sign ?: digits +: map (- Repeatable.zeroOrOne . RegEx.captureGroup . map ((Nothing, Nothing) <~>) . return {-to List-monad-}- ) [- RegEx.Require (Meta.Literal '.') -: digits +: [],- RegEx.Require (Meta.AnyOf $ map BracketExpressionMember.Literal "eE") -: sign ?: digits +: []- ]- ) where- sign, digits :: RegEx.Pattern Char- sign = RegEx.Require . Meta.AnyOf $ map BracketExpressionMember.Literal "+-"- digits = RegEx.Require . Meta.AnyOf $ map BracketExpressionMember.Literal ['0' .. '9']-
− src/Grecce/QC/MetaChar.hs
@@ -1,78 +0,0 @@-{-# LANGUAGE CPP #-}-{-# 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@] Implements 'Test.QuickCheck.Arbitrary' & defines tests based on it.-- [@TODO@] Test /Perl-style shortcuts/ & /Posix Character-classes/.--}--module Grecce.QC.MetaChar(--- * Types--- ** Type-synonyms--- Testable,--- * Constants--- testCharacters,--- * Functions- quickChecks-) where--import Control.Applicative((<$>))-import qualified Data.List-import qualified RegExChar.MetaChar as MetaChar --CAVEAT: beware of the similar name.-import qualified RegExDot.BracketExpression as BracketExpression-import qualified RegExDot.BracketExpressionMember as BracketExpressionMember-import qualified RegExDot.Meta as Meta-import qualified Test.QuickCheck---- CAVEAT: module "Test.QuickCheck.Arbitrary" defines an instance of this from package "Quickcheck-2.1.0.1".-#if !(MIN_VERSION_QuickCheck(2,1,0))-instance Test.QuickCheck.Arbitrary Char where- arbitrary = Test.QuickCheck.elements testCharacters- coarbitrary = undefined --CAVEAT: stops warnings from ghc.-#endif--testCharacters :: String-testCharacters = filter (`notElem` [Meta.shortcutToken, BracketExpression.negationToken]) [' ' .. '~'] --TODO: permit all characters.--instance Test.QuickCheck.Arbitrary MetaChar.MetaChar where- arbitrary = Test.QuickCheck.oneof $ map (MetaChar.MkMetaChar <$>) [- return {-to Gen-monad-} Meta.Any,- Meta.Literal <$> Test.QuickCheck.elements testCharacters,- Meta.AnyOf . map BracketExpressionMember.Literal . Data.List.nub <$> arbitraryCharList 1 16, --Test.QuickCheck.vector 16 {-Char-},- Meta.NoneOf . map BracketExpressionMember.Literal . Data.List.nub <$> arbitraryCharList 1 4- ] where- arbitraryCharList :: Int -> Int -> Test.QuickCheck.Gen String- arbitraryCharList i j = do- l <- Test.QuickCheck.arbitrary {-Int-}-- take (min j $ i `max` l) . Data.List.nub . Data.List.filter (`elem` testCharacters) <$> Test.QuickCheck.vector maxBound {-Char-}-#if ! (MIN_VERSION_QuickCheck(2,1,0))- coarbitrary = undefined --CAVEAT: stops warnings from ghc.-#endif--type Testable = MetaChar.MetaChar -> Test.QuickCheck.Property---- | Defines invariant properties, which must hold for any 'MetaChar.MetaChar'.-quickChecks :: (Testable -> IO ()) -> IO ()-quickChecks checker = checker `mapM_` [prop_io] where- prop_io :: Testable- prop_io m = Test.QuickCheck.label "prop_io" $ read (show m) == m-
− src/Grecce/QC/QuickChecks.hs
@@ -1,52 +0,0 @@-{-# LANGUAGE CPP #-}-{-- 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@] Calls the /quickChecks/-functions for modules supporting this feature.--}--module Grecce.QC.QuickChecks(--- * Functions- run-) where--import qualified Grecce.QC.ExtendedRegExChar as QC.ExtendedRegExChar-import qualified Grecce.QC.MetaChar as QC.MetaChar-import qualified Grecce.QC.RepeatableMetaChar as QC.RepeatableMetaChar-import qualified Test.QuickCheck--{- | Run the /quickChecks/-functions for modules supporting this feature.- The single Boolean flag enables verbose output.--}-run :: Bool -> IO ()-run isVerbose =- putStrLn "MetaChar" >> QC.MetaChar.quickChecks checker- >> putStrLn "RepeatableMetaChar" >> QC.RepeatableMetaChar.quickChecks checker- >> putStrLn "ExtendedRegExChar" >> QC.ExtendedRegExChar.quickChecks checker- where- checker :: Test.QuickCheck.Testable t => t -> IO ()- checker- | isVerbose =-#if MIN_VERSION_QuickCheck(2,4,0)- Test.QuickCheck.verboseCheck-#else- error "Grecce.QC.QuickChecks:\t'Test.QuickCheck.verboseCheck' is only available as of 'QuickCheck-2.4'."-#endif- | otherwise = Test.QuickCheck.quickCheck-
− src/Grecce/QC/RepeatableMetaChar.hs
@@ -1,98 +0,0 @@-{-# LANGUAGE CPP #-}-{-- 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@] Implements 'Test.QuickCheck.Arbitrary' & defines tests based on it.--}--module Grecce.QC.RepeatableMetaChar(--- * Types--- ** Data-types--- RepeatableMetaChar,--- ** Type-synonyms--- Testable,--- * Functions--- deconstruct,- quickChecks-) where--import Control.Applicative((<$>))-import Grecce.QC.MetaChar()-import qualified RegExChar.MetaChar as MetaChar-import qualified RegExDot.Consumer as Consumer-import qualified RegExDot.ConsumptionBounds as ConsumptionBounds-import qualified RegExDot.ConsumptionProfile as ConsumptionProfile-import qualified RegExDot.Repeatable as Repeatable-import qualified Test.QuickCheck-import qualified ToolShed.SelfValidate as SelfValidate---- | A specialised instance, required to instantiate 'Test.QuickCheck.Arbitrary'.-newtype RepeatableMetaChar = MkRepeatableMetaChar (Repeatable.Repeatable MetaChar.MetaChar) deriving (Eq, Read, Show)---- | Accessor.-deconstruct :: RepeatableMetaChar -> Repeatable.Repeatable MetaChar.MetaChar-deconstruct (MkRepeatableMetaChar repeatableMetaChar) = repeatableMetaChar--instance SelfValidate.SelfValidator RepeatableMetaChar where- isValid = SelfValidate.isValid . deconstruct--instance Consumer.Consumer RepeatableMetaChar where- consumptionProfile = Consumer.consumptionProfile . deconstruct- starHeight = Consumer.starHeight . deconstruct--instance Test.QuickCheck.Arbitrary RepeatableMetaChar where- arbitrary = do- metaChar <- Test.QuickCheck.arbitrary- fewest <- Test.QuickCheck.elements [0 .. 9] --Could be more, but this is an adequate test.- most <- Test.QuickCheck.oneof [return Nothing, Just <$> Test.QuickCheck.elements [max fewest 1 .. 9]]- isGreedy <- Test.QuickCheck.arbitrary-- let- repetitionBounds :: Repeatable.RepetitionBounds- repetitionBounds = (fewest, most)-- return {-to Gen-monad-} . MkRepeatableMetaChar $ Repeatable.MkRepeatable {- Repeatable.base = metaChar,- Repeatable.repetitionBounds = repetitionBounds,- Repeatable.isGreedy = Repeatable.hasPreciseBounds repetitionBounds || isGreedy --Only specify 'non-greedy' where space exists.- }-#if !(MIN_VERSION_QuickCheck(2,1,0))- coarbitrary = undefined --CAVEAT: stops warnings from ghc.-#endif--type Testable = RepeatableMetaChar -> Test.QuickCheck.Property---- | Defines invariant properties, which must hold for any 'RepeatableMetaChar'.-quickChecks :: (Testable -> IO ()) -> IO ()-quickChecks checker = checker `mapM_` [prop_consumptionProfile, prop_io, prop_isValid, prop_starHeight] where- prop_consumptionProfile, prop_io, prop_isValid, prop_starHeight :: Testable- prop_consumptionProfile r = Test.QuickCheck.label "prop_consumptionProfile" $ (- case maybeMaxData of- Nothing -> True- Just m -> m >= minData- ) && (- b == Consumer.getHasSpecificRequirement (Repeatable.base $ deconstruct r)- ) where ConsumptionProfile.MkConsumptionProfile {- ConsumptionProfile.consumptionBounds = (minData, maybeMaxData),- ConsumptionProfile.hasSpecificRequirement = b- } = Consumer.consumptionProfile r- prop_io (MkRepeatableMetaChar r) = Test.QuickCheck.label "prop_io" $ read (show r) == r- prop_isValid r = Test.QuickCheck.label "prop_isValid" $ SelfValidate.isValid r- prop_starHeight (MkRepeatableMetaChar r) = Test.QuickCheck.label "prop_starHeight" $ Consumer.starHeight r == if ConsumptionBounds.isPrecise (Consumer.getConsumptionBounds r) then 0 else 1-
+ src/Grecce/Test/Assert/RegExOptsChar.hs view
@@ -0,0 +1,92 @@+{-# LANGUAGE FlexibleContexts #-}+{-+ 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@]++ * Reads lines from the specified file.++ * Each line describes a 'Test'.++ * Each input-data string should match the /regex/ in the specified way.++ * Permits comparison with "Text.Regex.Posix.String".+-}++module Grecce.Test.Assert.RegExOptsChar(+-- * Types+-- ** Type-synonyms+-- Test,+-- TestDataAndResult,+-- TestFilter,+-- * Functions+ findFailures,+ findFailuresPosix,+ readTests+-- ** Predicates+-- isMatch+) where++import Control.Applicative((<$>))+import qualified Control.Arrow+import qualified RegExChar.ExtendedRegExChar as ExtendedRegExChar+import qualified RegExChar.RegExOptsChar as RegExOptsChar --CAVEAT: beware of the similar name.+import qualified RegExDot.CompilationOptions as CompilationOptions+import qualified RegExDot.ExecutionOptions as ExecutionOptions+import qualified Text.Regex.Base.RegexLike as RegexLike+import qualified Text.Regex.Posix.Wrap+import Text.Regex.Posix()++-- | Composed input data & list of captured 'RegExOptsChar.MatchSpan'.+type TestDataAndResult = (ExtendedRegExChar.InputData, [RegExOptsChar.MatchSpan])++-- | Composed of the 'String' from which an unspecified type of regex is read, & a list of input data & results, to which it should respectively be applied & compared.+type Test = (String {-regex-source-}, [TestDataAndResult])++-- | Filters a list of 'Test's, & returns a sub-set.+type TestFilter = [Test] -> [Test]++-- | 'True' if the specified 'ExtendedRegExChar.InputData', is a member of the set defined by the polymorphic 'Text.Regex.Base.RegexLike.RegexLike' parameter.+isMatch :: RegexLike.RegexLike r ExtendedRegExChar.InputData => r -> TestDataAndResult -> Bool+isMatch regex (inputData, matchSpanList) = RegexLike.matchOnce regex inputData == Just (RegExOptsChar.toZeroIndexedArray matchSpanList)++-- | Returns those 'Test's, which 'RegExOptsChar.RegExOptsChar' fails.+findFailures :: CompilationOptions.CompilationOptions -> ExecutionOptions.ExecutionOptions -> TestFilter+findFailures compilationOptions executionOptions = filter (not . uncurry all . Control.Arrow.first (isMatch . RegexLike.makeRegexOpts compilationOptions executionOptions))++-- | Returns those 'Test's, which 'Text.Regex.Posix.Wrap.Regex' fails.+findFailuresPosix :: TestFilter+findFailuresPosix = filter (not . uncurry all . Control.Arrow.first ((isMatch :: Text.Regex.Posix.Wrap.Regex -> TestDataAndResult -> Bool) . RegexLike.makeRegex))++{- |+ * Read 'Test's from file, ignoring blank lines & comments.++ * To facilitate parsing, each line in the file has the format of @show Test@.+-}+readTests :: String -> IO [Test]+readTests fileName = map (+ \line -> case reads line of+ [(test, _)] -> test+ _ -> error $ "Grecce.Test.Assert.RegExOptsChar.readTests:\tfailed to read 'Test' from '" ++ line ++ "'"+ ) . filter (+ (/= '#') . head --Remove comments.+ ) . filter (+ not . null --Remove blank lines.+ ) . lines <$> readFile fileName+
+ src/Grecce/Test/Performance/ExtendedRegEx.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@]++ * CPU-times the specified stress-test(s), over a hard-coded list of /regex/-engines,+ providing each combination with a varied list of 'Performance.ExtendedRegExTest.TestGenerator's,+ & a level of 'Performance.ExtendedRegExTest.TestComplexity' which increases linearly to the specified cap.++ * Either a simple 'Bool' result, or the full data-capture, can be requested.++ [@CAVEATS@]++ * For compatibility with the other regex-engines, uses the specialised instance 'ExtendedRegExChar.ExtendedRegExChar',+ rather than the underlying polymorphic 'RegEx.ExtendedRegEx'.++ * "Text.Regex.Posix" stalls forever on 'Performance.ExtendedRegExTestsPositive.OVERLAPPING_ALTERNATIVES',+ with 'Performance.ExtendedRegExTest.TestComplexity'=3 & data-capture requested.++ [@TODO@]++ * Utilise the standard interface in "RegExOptsChar", to permit identical treatment of different regex-engines.+-}++module Grecce.Test.Performance.ExtendedRegEx(+-- * Types+-- ** Type-synonyms+-- GenerateAndRunTest,+-- GetAndRunTestRange,+-- * Constants+-- solitary,+-- * Functions+ run+-- tailorTest+) where++import Control.Applicative((<$>), (<*>))+import Control.Arrow((&&&))+import qualified Control.Arrow+import qualified Data.Maybe+import qualified Grecce.Test.Performance.ExtendedRegExTest as Test.Performance.ExtendedRegExTest+import qualified Grecce.Test.Performance.ExtendedRegExTestsNegative as Test.Performance.ExtendedRegExTestsNegative+import qualified Grecce.Test.Performance.ExtendedRegExTestsPositive as Test.Performance.ExtendedRegExTestsPositive+import qualified RegExChar.ExtendedRegExChar as ExtendedRegExChar+import qualified RegExDot.ExecutionOptions as ExecutionOptions+import qualified RegExDot.RegEx as RegEx+import qualified RegExDot.RegExOpts as RegExOpts+import qualified Text.Regex.Posix+import qualified ToolShed.Defaultable as Defaultable+import qualified ToolShed.TimePure as TimePure++solitary :: Bool+solitary = True --Currently required because of the disastrous performance of "Text.Regex.Posix" on 'Test.Performance.ExtendedRegExTestsPositive.OVERLAPPING_ALTERNATIVES'.++type GetAndRunTestRange a = [a] -> IO ()+type GenerateAndRunTest = Test.Performance.ExtendedRegExTest.TestGenerator Char -> Test.Performance.ExtendedRegExTest.TestComplexity -> IO (ExtendedRegExChar.InputData, [(Double, String)])++{- |+ * Print the CPU-seconds, required to run the range of 'Test.Performance.ExtendedRegExTest.TestGenerator's corresponding to the specified test-names & a linearly increasing 'Test.Performance.ExtendedRegExTest.TestComplexity'.++ * If the specified maximum 'Test.Performance.ExtendedRegExTest.TestComplexity' is set to the special value '0', then it is increased linearly, without limit.+-}+run :: ExecutionOptions.ExecutionOptions -> Test.Performance.ExtendedRegExTest.TestComplexity -> Either [Test.Performance.ExtendedRegExTestsNegative.TestName] [Test.Performance.ExtendedRegExTestsPositive.TestName] -> IO ()+run executionOptions 0 testRange = run executionOptions maxBound testRange --Run the specified test-range, without any upper bound to 'Test.Performance.ExtendedRegExTest.TestComplexity'.+run executionOptions maxTestComplexity (Left negativeTestRange) = runNegativeTestRange negativeTestRange where+ runNegativeTestRange :: GetAndRunTestRange Test.Performance.ExtendedRegExTestsNegative.TestName+ runNegativeTestRange [] = runNegativeTestRange [minBound .. maxBound] --Run all the tests, to the specified maximum 'Test.Performance.ExtendedRegExTest.TestComplexity'.+ runNegativeTestRange testRange = sequence_ $ (+ \complexity testGenerator -> cpuSecondsFor testGenerator complexity >>= print+ ) <$> [1 .. maxTestComplexity] <*> Data.Maybe.mapMaybe Test.Performance.ExtendedRegExTest.query testRange where+ cpuSecondsFor :: GenerateAndRunTest+ cpuSecondsFor testGenerator = (+ \(inputData, (isMismatch, extendedRegExCharString)) -> (,) extendedRegExCharString <$> mapM (+ TimePure.getCPUSeconds . ($ inputData) . (show .)+ ) [+ isMismatch,+ if solitary+ then const True --CAVEAT: should be 'undefined'.+ else not . (Text.Regex.Posix.=~ extendedRegExCharString) :: String -> Bool --CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.+ ]+ ) . Control.Arrow.second (+ (+ \extendedRegEx -> (+ RegEx./~ RegExOpts.MkRegExOpts { --CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.+ RegExOpts.compilationOptions = Defaultable.defaultValue,+ RegExOpts.executionOptions = executionOptions,+ RegExOpts.regEx = extendedRegEx+ }+ )+ ) &&& show . ExtendedRegExChar.MkExtendedRegExChar False {-hasNonCapturingTopLevelAlternatives-}+ ) . testGenerator++run executionOptions maxTestComplexity (Right positiveTestRange) = runPositiveTestRange positiveTestRange where+ runPositiveTestRange :: GetAndRunTestRange Test.Performance.ExtendedRegExTestsPositive.TestName+ runPositiveTestRange [] = runPositiveTestRange [minBound .. maxBound] --Run all the tests, to the specified maximum 'Test.Performance.ExtendedRegExTest.TestComplexity'.+ runPositiveTestRange testRange = sequence_ $ (+ \complexity testGenerator -> cpuSecondsFor testGenerator complexity >>= print+ ) <$> [1 .. maxTestComplexity] <*> Data.Maybe.mapMaybe Test.Performance.ExtendedRegExTest.query testRange where+ cpuSecondsFor :: GenerateAndRunTest+ cpuSecondsFor testGenerator = (+ \(inputData, (showMatch, extendedRegExCharString)) -> (,) extendedRegExCharString <$> (+ (TimePure.getCPUSeconds . ($ inputData)) `mapM` [+ showMatch,+ if ExecutionOptions.requireMatchList executionOptions+ then if solitary+ then const "<untested>"+-- else (Text.Regex.Posix.=~ extendedRegExCharString) :: String -> String --CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.+ else (\(_, _, _, s) -> show s) . ((Text.Regex.Posix.=~ extendedRegExCharString) :: String -> (String, String, String, [String])) --CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.+ else show . if solitary+ then const True --CAVEAT: should be 'undefined'.+ else (Text.Regex.Posix.=~ extendedRegExCharString) :: String -> Bool --CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.+ ]+ )+ ) . Control.Arrow.second (+ (+ \extendedRegEx -> let+ regExOpts :: RegExOpts.RegExOpts (RegEx.ExtendedRegEx Char)+ regExOpts = RegExOpts.MkRegExOpts {+ RegExOpts.compilationOptions = Defaultable.defaultValue,+ RegExOpts.executionOptions = executionOptions,+ RegExOpts.regEx = extendedRegEx+ } --Similar to 'RegexLike.makeRegexOpts', but without the requirement to read the regex.+ in if ExecutionOptions.requireMatchList executionOptions+ then show . (RegEx.+~ regExOpts) --CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.+ else show . (RegEx.=~ regExOpts) --CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.+ ) &&& show . ExtendedRegExChar.MkExtendedRegExChar False {-hasNonCapturingTopLevelAlternatives-}+ ) . testGenerator+
+ src/Grecce/Test/Performance/ExtendedRegExTest.hs view
@@ -0,0 +1,51 @@+{-+ 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 the types used for performance-testing.++ * Defines an interface to permit queries on repositories of 'TestGenerator's.+-}++module Grecce.Test.Performance.ExtendedRegExTest(+-- * Type-classes+ TestGeneratorRepository(..),+-- * Types+-- ** Type-synonyms+ TestComplexity,+ Test,+ TestGenerator+) where++import qualified RegExDot.RegEx as RegEx++-- | Used to specify the level of complexity of /regex/ to generate.+type TestComplexity = Int++-- | A pair defining the data & the /regex/ to which it should be fed.+type Test a = (RegEx.InputData a, RegEx.ExtendedRegEx a)++-- | The type of a function, which accepts a 'TestComplexity', & returns a 'Test'.+type TestGenerator a = TestComplexity -> Test a++-- | Implementer contracts to generate an appropriate 'Test'.+class TestGeneratorRepository a where+ query :: a -> Maybe (TestGenerator Char) --CAVEAT: polymorphism reduced, to avoid requirement for non-portable Haskell-extensions.+
+ src/Grecce/Test/Performance/ExtendedRegExTestsNegative.hs view
@@ -0,0 +1,85 @@+{-+ 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 'Performance.ExtendedRegExTest.TestGenerator's, for 'Performance.ExtendedRegExTest.Test's, which should fail.++ * Implements 'Performance.ExtendedRegExTest.TestGeneratorRepository'.+-}++module Grecce.Test.Performance.ExtendedRegExTestsNegative(+-- * Types+-- ** Data-types+ TestName+) where++import qualified Control.Arrow+import qualified Data.Maybe+import qualified Grecce.Test.Performance.ExtendedRegExTest as Test.Performance.ExtendedRegExTest+import qualified Grecce.Test.Performance.ExtendedRegExTestsPositive as Test.Performance.ExtendedRegExTestsPositive++-- | Defines an enumeration of keys, by which to reference 'Test.Performance.ExtendedRegExTest.TestGenerator's.+data TestName =+ INTERLEAVED_GREEDY_STAR_VS_MANDATORY+ | LINEAR_FRACTAL_NONGREEDY_STAR+ | OVERLAPPING_ALTERNATIVES+ deriving (+ Bounded,+ Enum,+ Eq,+ Read,+ Show+ )++instance Test.Performance.ExtendedRegExTest.TestGeneratorRepository TestName where+ query = (+ `lookup` [+{-+ Explanation: based on 'Test.Performance.ExtendedRegExTestsPositive.INTERLEAVED_GREEDY_STAR_VS_MANDATORY', but with twice the input data following by a mismatch.+ Performance: vastly better performance than "Text.Regex.Posix", benefiting from 'ExecutionOptions.checkExistenceOfInelasticTail'.+-}+ (+ INTERLEAVED_GREEDY_STAR_VS_MANDATORY,+ Control.Arrow.first ((++ "!") . concat . replicate 2) . Data.Maybe.fromJust (+ Test.Performance.ExtendedRegExTest.query Test.Performance.ExtendedRegExTestsPositive.INTERLEAVED_GREEDY_STAR_VS_MANDATORY+ )+ ),+{-+ Explanation: based on 'Test.Performance.ExtendedRegExTestsPositive.LINEAR_FRACTAL_NONGREEDY_STAR', but with mismatching input data, which causes the time-complexity explodes to O(n!).+ Performance: vastly better performance than "Text.Regex.Posix", benefiting from 'ExecutionOptions.checkForUnconsumableData'.+-}+ (+ LINEAR_FRACTAL_NONGREEDY_STAR,+ Control.Arrow.first (++ "!") . Data.Maybe.fromJust (+ Test.Performance.ExtendedRegExTest.query Test.Performance.ExtendedRegExTestsPositive.LINEAR_FRACTAL_NONGREEDY_STAR+ )+ ),+{-+ Explanation: based on 'Test.Performance.ExtendedRegExTestsPositive.OVERLAPPING_ALTERNATIVES', but with mismatching input data.+ Performance: marginally better performance than "Text.Regex.Posix", benefiting from 'ExecutionOptions.checkForUnconsumableData' & 'ExecutionOptions.permitReorderingOfAlternatives'.+-}+ (+ OVERLAPPING_ALTERNATIVES,+ Control.Arrow.first (++ "!") . Data.Maybe.fromJust (+ Test.Performance.ExtendedRegExTest.query Test.Performance.ExtendedRegExTestsPositive.OVERLAPPING_ALTERNATIVES+ )+ )+ ]+ )
+ src/Grecce/Test/Performance/ExtendedRegExTestsPositive.hs view
@@ -0,0 +1,329 @@+{-+ 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 'Performance.ExtendedRegExTest.TestGenerator's, for 'Performance.ExtendedRegExTest.Test's, which should pass.++ * Implements 'Performance.ExtendedRegExTest.TestGeneratorRepository'.+-}++module Grecce.Test.Performance.ExtendedRegExTestsPositive(+-- * Types+-- ** Data-types+ TestName(..)+-- * Functions+-- triangularSeries+) where++import Control.Arrow((&&&))+import qualified Data.Char+import qualified Grecce.Test.Performance.ExtendedRegExTest as Test.Performance.ExtendedRegExTest+import qualified RegExDot.Anchor as Anchor+import qualified RegExDot.BracketExpressionMember as BracketExpressionMember+import qualified RegExDot.Meta as Meta+import qualified RegExDot.RegEx as RegEx+import qualified RegExDot.Repeatable as Repeatable+import RegExDot.DSL((-:), (?:), (*:), (<~>))+import RegExDot.RegEx((.*), (.*?))+import RegExDot.Repeatable((^#->#), (^#->), (^#))++-- | Defines an enumeration of keys, by which to reference 'Test.Performance.ExtendedRegExTest.TestGenerator's.+data TestName =+ BIPOLAR_GREEDY_OPTIONAL_VS_MANDATORY+ | INTERLEAVED_GREEDY_STAR_VS_MANDATORY+ | TRIANGULAR_CONSUMPTION+ | BINARY_TREE+ | REPEATABLE_ALTERNATIVES+ | REPEATED_ALTERNATIVES+ | LINEAR_FRACTAL_RANGES+ | LINEAR_FRACTAL_NONGREEDY_STAR+ | BINARY_TREE_FRACTAL+ | FUTILE_REPEATABLE_ALTERNATIVES+ | OVERLAPPING_ALTERNATIVES+ | UNBRIDLED_GREED+ | MULTIPLE_CHOICE+ deriving (+ Bounded,+ Enum,+ Eq,+ Read,+ Show+ )++instance Test.Performance.ExtendedRegExTest.TestGeneratorRepository TestName where+ query = (+ `lookup` [+{-+ "^(<testChar>?){<complexity>}(<testChar>){<complexity>}$".+ Explanation: each of the optional meta-data in the first half of the expression can capture data, but in doing so starves a mandatory meta-datum in the second half of the expression,+ presenting the opportunity for O(2^n) time-complexity backtracking.+ Performance: vastly better Boolean-result performance than "Text.Regex.Posix". Looks like O(n^2) time-complexity.+-}+ (+ BIPOLAR_GREEDY_OPTIONAL_VS_MANDATORY,+ (`replicate` testChar) &&& let+ exactly :: Test.Performance.ExtendedRegExTest.TestComplexity -> (RegEx.Pattern Char -> RegEx.RepeatablePattern Char) -> RegEx.RepeatablePattern Char+ exactly n f = (RegEx.captureGroup . map ((Nothing, Nothing) <~>) . return {-to List-monad-} . return {-to List-monad-} $ f testRequirement) ^# n+ in ((Just Anchor.Bow, Just Anchor.Stern) <~>) . (`map` [Repeatable.zeroOrOne, Repeatable.one]) . exactly+ ),+{-+ "^(.*z){<complexity>}$".+ Explanation: each '.*' can consume all the remaining data, starving the interspersed mandatory meta data, presenting the opportunity for O(2^n) time-complexity backtracking.+ Performance: marginally better Boolean-result & data-capture performance than "Text.Regex.Posix" with 'ExecutionOptions.moderateGreed'. Empirically O(n^3) time-complexity.+-}+ (+ INTERLEAVED_GREEDY_STAR_VS_MANDATORY,+ concat . (`replicate` (testChar : "z")) &&& RegEx.dock . (+ `mkExtendedRegExFromRepeatableSingletonAlternative` ((Nothing, Nothing) <~>) [+ (.*),+ RegEx.simply $ Meta.Literal 'z'+ ]+ ) . flip (^#)+ ),+{-+ "^<testChar>*<testChar>+<testChar>{2,}<testChar>{3,} ... a{<complexity>,}$".+ Explanation: each term greedily consumes all the data, starving all the remaining terms, until after much back-tracking, it consumes the just the minimum permissible.+ The correct consumption-pattern is triangular, starting at the apex & spreading into a delta.+ Performance: vastly better Boolean-result & data-capture performance than "Text.Regex.Posix".+-}++ (+ TRIANGULAR_CONSUMPTION,+ (`replicate` testChar) . triangularSeries &&& ((Just Anchor.Bow, Just Anchor.Stern) <~>) . (`take` map (testRequirement ^#->) [0 ..]) . succ+ ),+{-+ "(<testChar>|<testChar>{2})*c".+ Explanation: from <http://www.haskell.org/pipermail/libraries/2009-March/011379.html>+ Performance: disastrous performance.+-}+ (+ BINARY_TREE,+ (++ "bc") . (`replicate` testChar) . (2 ^) &&& const (+ (Nothing, Nothing) <~> RegEx.captureGroup (+ map (+ ((Nothing, Nothing) <~>) . return {-to List-monad-} . (testRequirement ^#) . (2 ^)+ ) [0 :: Int .. 1]+ ) *: RegEx.Require (Meta.Literal 'c') -: []+ )+ ),+{-+ "^(<testChar>?|<testChar>{2}|<testChar>{4}|<testChar>{8}| ... |<testChar>{2^<complexity>})*$".+ Explanation: all but one of the 'Alternatives' must be used, one on each successive repetition, in order to consume all the 'InputData'.+ A correct implementation will match against the longest possible Alternative on each successive repetition, just as if the repetition were unrolled; see 'REPEATED_ALTERNATIVES'.+ Performance: vastly worse Boolean-result performance (even with 'ExecutionOptions.permitReorderingOfAlternatives'),+ & worse data-capture performance (even with 'ExecutionOptions.useFirstMatchAmongAlternatives'), than "Text.Regex.Posix".+-}+ (+ REPEATABLE_ALTERNATIVES,+ (`replicate` testChar) . pred {-requires several different Alternatives-} . (2 ^) &&& (+ \i -> ((Just Anchor.Bow, Just Anchor.Stern) <~>) . return {-to List-monad-} . Repeatable.zeroOrMore . RegEx.captureGroup . map ((Nothing, Nothing) <~>) $ [Repeatable.zeroOrOne testRequirement] : map (+ return {-to List-monad-} . (testRequirement ^#) . (2 ^)+ ) [1 .. i]+ )+ ),+{-+ "^(<testChar>?|<testChar>{2}|<testChar>{4}| ... |<testChar>{2^<complexity>})(<testChar>?|<testChar>{2}|<testChar>{4}| ... |<testChar>{2^<complexity>})(<testChar>?|<testChar>{2}|<testChar>{4}| ... |<testChar>{2^<complexity>})$".+ Explanation: 'REPEATABLE_ALTERNATIVES' with the repetition unrolled, to permit inspection of individual capture-groups,+ revealing the use by "Text.Regex.Posix", of Perl-style "1st-past-the-post" selection between Alternatives.+ Performance: much faster than 'REPEATABLE_ALTERNATIVES'. Vastly better Boolean-result & data-capture performance than both 'REPEATABLE_ALTERNATIVES' & "Text.Regex.Posix".+-}+ (+ REPEATED_ALTERNATIVES,+ (`replicate` testChar) . pred {-requires several different Alternatives-} . (2 ^) &&& (+ \i -> ((Just Anchor.Bow, Just Anchor.Stern) <~>) . replicate i . Repeatable.one . RegEx.captureGroup . map ((Nothing, Nothing) <~>) $ [Repeatable.zeroOrOne testRequirement] : map (+ return {-to List-monad-} . (testRequirement ^#) . (2 ^)+ ) [1 .. i]+ )+ ),+{-+ "^((((<testChar>{0,2}){0,2}){0,2}){0,2}){0,2} ... $".+ Explanation:+ Performance: vastly better Boolean-result performance than "Text.Regex.Posix", which exhausts memory at n==10. Better data-capture performance too.+ Stack-overflow at 'Test.Performance.ExtendedRegExTest.TestComplexity'=14.+-}+ (+ LINEAR_FRACTAL_RANGES,+ let+ base :: Repeatable.Repetitions+ base = 2 --Arbitrarily.++ bounds :: Repeatable.RepetitionBounds+ bounds = (0, Just base)+ in (`replicate` testChar) . (base ^) &&& RegEx.dock . (+ (+ mkExtendedRegExFromRepeatableSingletonAlternative (^#-># bounds) `iterate` ((Nothing, Nothing) <~>) [testRequirement ^#-># bounds] {-initial value-}+ ) !!+ ) . pred+ ),+{-+ "^((((((((((((((<testChar>*?)*?)*?)*?)*?)*?)*?)*?)*?)*?)*?)*?)*?)*?)*? ... $".+ Explanation: interesting because all 'RegEx.Alternatives' are expanded before the first comparison against input data, & the number possible repetitions of each expansion is very vague.+ Performance: vastly better Boolean-result performance than "Text.Regex.Posix", but inferior data-capture performance.+-}+ (+ LINEAR_FRACTAL_NONGREEDY_STAR,+ const (replicate 8 {-arbitrary-} testChar) &&& RegEx.dock . (+ (+ mkExtendedRegExFromRepeatableSingletonAlternative Repeatable.zeroOrMore' `iterate` ((Nothing, Nothing) <~>) [Repeatable.zeroOrMore' testRequirement] {-initial value-}+ ) !!+ )+ ),+{-+ "^((([^<testChar>]|<testChar>)|(<testChar>|[^<testChar>]))|((<testChar>|[^<testChar>])|([^<testChar>]|<testChar>)))$".+ Explanation: exhibits symmetry in the left & right branches of the binary tree of RegEx.Alternatives, & so the exponentially expanding set of leaves are equivalent & all match the data.+ Performance: vastly better Boolean-result & comparable data.capture performance to "Text.Regex.Posix", which exhausts memory at complexity 5 to 6, & to egrep which lasts until complexity==6.+-}+ (+ BINARY_TREE_FRACTAL,+ let+ base :: Repeatable.Repetitions+ base = 2 --Arbitrarily.+ in (`replicate` testChar) . (base ^) &&& RegEx.dock . head . (+ (+ iterate (+ \extendedRegExList -> (+ ((Nothing, Nothing) <~>) . return {-to List-monad-} . (^# base) . RegEx.captureGroup+ ) `map` [extendedRegExList, reverse extendedRegExList]+ ) $ map (+ ((Nothing, Nothing) <~>) . return {-to List-monad-} . ($ RegEx.Require $ Meta.Literal testChar)+ ) [Repeatable.one, Repeatable.zeroOrOne]+ ) !!+ )+ ),+{-+ "^(<testChar>1|<testChar>{2}2|<testChar>{3}3| ... |<testChar>{<complexity>}<complexity>)*<testChar>*$".+ Explanation: none of the Alternatives can match, & consequently the group must match zero times, presenting the opportunity for many wasted trial repetitions.+ Performance: comparable Boolean-result performance to "Text.Regex.Posix", & marginally superior data-capture performance.+-}+ (+ FUTILE_REPEATABLE_ALTERNATIVES,+ \alternatives -> (+ replicate 1024 {-arbitrarily-} testChar,+ ((Just Anchor.Bow, Just Anchor.Stern) <~>) $ map Repeatable.zeroOrMore [+ RegEx.captureGroup . map ((Nothing, Nothing) <~>) $ (+ \i -> ($ i) `map` [+ (testRequirement ^#),+ RegEx.simply . Meta.Literal . Data.Char.chr . (Data.Char.ord '0' +) . (`mod` 10)+ ]+ ) `map` [1 .. alternatives],+ testRequirement+ ]+ )+ ),+{-+ "^a?((ab?)*|(bc?)*|(cd?)*| ...)*$".+ Explanation: the zeroeth term matches greedily, consuming the datum mandated by the 1st Alternative, so the 2nd Alternative greedily consumes two data, including the datum mandated by the 3rd, ...+ If there're an even number of Alternatives, then n/2 repetitions (one for each), will consume all the input data, but otherwise the final input datum isn't consumed.+ The consumption back-tracks rapidly until the zeroeth term yields-up the initial datum, after which the odd Alternatives match consuming all the input data, in (n/2 + 1) repetitions.+ This suggests a very tractable O(n) time-complexity, but Posix requires that we select the combination of Alternatives which consume most data,+ & since there are two terms in the regex that can consume each input datum, it looks like there should be 2^n candidate solutions; in fact there's just one.+ So, all O(Alternatives ^ repetitions) permutations may be searched in vain for a superior solution.+ Performance: vastly worse Boolean-result performance than "Text.Regex.Posix", but the latter then stalls indefinitely (CPU-bound) at complexity=[13 (not verbose), 4 (verbose)].+-}+ (+ OVERLAPPING_ALTERNATIVES,+ let+ bracket :: a -> [a] -> [a]+ bracket terminal = (terminal :) . (++ [terminal])++ zeroOrMoreAlternatives :: [RegEx.Concatenation Char] -> RegEx.Concatenation Char+ zeroOrMoreAlternatives = return {-to List-monad-} . Repeatable.zeroOrMore . RegEx.captureGroup . map ((Nothing, Nothing) <~>)+ in (+ id &&& (+ ((Just Anchor.Bow, Just Anchor.Stern) <~>) . (RegEx.Require (Meta.Literal testChar) ?:) . zeroOrMoreAlternatives . map (+ \(a, b) -> zeroOrMoreAlternatives $ return {-to List-monad-} [a, b]+ ) . init . tail . uncurry zip . (+ map Repeatable.one . init &&& map Repeatable.zeroOrOne . tail+ ) . bracket undefined . map (+ RegEx.Require . Meta.Literal+ )+ )+ ) . (`take` (filter Data.Char.isAlphaNum $ filter Data.Char.isAscii [minBound .. maxBound])) . succ+ ),+{-+ "^(a|b|c)*(a.*?c){3}$".+ Explanation: zero repetitions of the set of Alternatives on the LHS is the only solution, but because the greedy star, within which they're grouped, permits many more,+ & because the RHS's requirement for all the input-data, isn't explicit, there's a temptation to over-consume.+ Performance: empirically O(2^n) time-complexity, but linearised by 'ExecutionOptions.moderateGreed'.+ Better Boolean-result & data-capture performance than "Text.Regex.Posix".+-}+ (+ UNBRIDLED_GREED,+ \complexity -> let+ complexity' :: Test.Performance.ExtendedRegExTest.TestComplexity+ complexity' = 1 + complexity++ testSet :: String+ testSet = take complexity' . filter Data.Char.isAlphaNum $ filter Data.Char.isAscii [minBound .. maxBound]+ in (+ concat $ replicate complexity' testSet,+ ((Just Anchor.Bow, Just Anchor.Stern) <~>) $ (+ RegEx.captureGroup . map ((Nothing, Nothing) <~>) $ map (+ return {-to List-monad-} . RegEx.simply . Meta.Literal+ ) testSet+ ) *: [+ (+ RegEx.captureGroup . map ((Nothing, Nothing) <~>) . return {-to List-monad-} $ RegEx.Require (Meta.Literal $ head testSet) -: (.*?) : RegEx.Require (Meta.Literal $ last testSet) -: []+ ) ^# complexity'+ ]+ )+ ),+{-+ "^(a*[^a]|[^b]|[^c]| ... |[^z])*$"+ Explanation: The first alternative greedily consumes all the input data until finding a mismatch on the last, whilst all the remaining alternatives successfully consume a single input datum.+ The same occurs on each successive repetition of the alternatives, of which there are as many as input data,+ leading to a lot of wasted time with O(<alternatives> ^ <input data>) time-complexity.+ Performance: poor Boolean-result & worse data-capture performance (benefiting from 'ExecutionOptions.useFirstMatchAmongAlternatives'), compared to "Text.Regex.Posix".+-}+ (+ MULTIPLE_CHOICE,+ const (replicate 8 {-arbitrary-} testChar) &&& (+ ((Just Anchor.Bow, Just Anchor.Stern) <~>) . return {-to List-monad-} . Repeatable.zeroOrMore . RegEx.captureGroup . map ((Nothing, Nothing) <~>) . (+ [+ Repeatable.zeroOrMore testRequirement,+ RegEx.simply . Meta.NoneOf $ map BracketExpressionMember.Literal [testChar]+ ] :+ ) . map (+ return {-to List-monad-} . RegEx.simply . Meta.NoneOf . return {-to List-monad-} . BracketExpressionMember.Literal+ ) . (+ `take` tail [testChar ..]+ )+ )+ )+ ]+ ) where+ testChar :: Char+ testChar = 'a'++ testRequirement :: RegEx.Pattern Char+ testRequirement = RegEx.Require $ Meta.Literal testChar++ mkExtendedRegExFromRepeatableSingletonAlternative :: (RegEx.Pattern Char -> RegEx.RepeatablePattern Char) -> RegEx.ExtendedRegEx Char -> RegEx.ExtendedRegEx Char+ mkExtendedRegExFromRepeatableSingletonAlternative makeRepetitions = ((Nothing, Nothing) <~>) . return {-to List-monad-} . makeRepetitions . RegEx.captureGroup . return {-to List-monad-}++{- |+ * The sum of the sequence descending from the specified integer; @n + (n - 1) + (n - 2) + ... + 2 + 1 + 0@.++ * <http://en.wikipedia.org/wiki/Triangular_number>.++ * <http://mathworld.wolfram.com/TriangularNumber.html>.+-}+triangularSeries :: Integral i => i -> i+triangularSeries i = i * (i + 1) `div` 2
+ src/Grecce/Test/QC/ExtendedRegExChar.hs view
@@ -0,0 +1,205 @@+{-# LANGUAGE CPP #-}+{-# 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@] Implements 'Test.QuickCheck.Arbitrary' & defines tests based on it.+-}++module Grecce.Test.QC.ExtendedRegExChar(+-- * Types+-- ** Data-types+-- AlternativesChar(..),+-- PatternChar(..),+-- RepeatablePatternChar(..),+-- ** Type-synonyms+-- Testable,+-- * Functions+-- deconstructAlternativesChar,+-- deconstructPatternChar,+-- deconstructRepeatablePatternChar,+ quickChecks+) where++import Control.Applicative((<$>))+import Grecce.Test.QC.MetaChar()+import qualified RegExChar.ExtendedRegExChar as ExtendedRegExChar --CAVEAT: beware of the similar name.+import qualified RegExChar.MetaChar as MetaChar+import qualified RegExDot.Anchor as Anchor+import qualified RegExDot.BracketExpressionMember as BracketExpressionMember+import qualified RegExDot.Consumer as Consumer+import qualified RegExDot.ConsumptionBounds as ConsumptionBounds+import qualified RegExDot.ConsumptionProfile as ConsumptionProfile+import qualified RegExDot.Meta as Meta+import qualified RegExDot.RegEx as RegEx+import qualified RegExDot.RegExOpts as RegExOpts+import qualified RegExDot.Repeatable as Repeatable+import qualified RegExDot.Result as Result+import qualified Test.QuickCheck+import qualified ToolShed.SelfValidate as SelfValidate+import RegExDot.DSL((-:), (?:), (+:), (<~>))+import RegExDot.RegEx((+~))+import Test.QuickCheck((==>))++-- | A specialised instance, required to instantiate 'Test.QuickCheck.Arbitrary'.+newtype AlternativesChar = MkAlternativesChar (RegEx.Alternatives Char) deriving (Eq, Read, Show)++-- | Accessor.+deconstructAlternativesChar :: AlternativesChar -> RegEx.Alternatives Char+deconstructAlternativesChar (MkAlternativesChar a) = a++instance SelfValidate.SelfValidator AlternativesChar where+ isValid = SelfValidate.isValid . deconstructAlternativesChar++instance Consumer.Consumer AlternativesChar where+ consumptionProfile = Consumer.consumptionProfile . deconstructAlternativesChar+ starHeight = Consumer.starHeight . deconstructAlternativesChar++instance Test.QuickCheck.Arbitrary AlternativesChar where+ arbitrary = MkAlternativesChar . RegEx.MkAlternatives . map ExtendedRegExChar.extendedRegEx <$> (+ Test.QuickCheck.elements [1, 2] >>= Test.QuickCheck.vector {-[ExtendedRegExChar]-}+ ) --TODO: permit zero alternatives.+#if !(MIN_VERSION_QuickCheck(2,1,0))+ coarbitrary = undefined --CAVEAT: stops warnings from ghc.+#endif++-- | A specialised instance, required to instantiate 'Test.QuickCheck.Arbitrary'.+newtype PatternChar = MkPatternChar (RegEx.Pattern Char) deriving (Eq, Read, Show)++-- | Accessor.+deconstructPatternChar :: PatternChar -> RegEx.Pattern Char+deconstructPatternChar (MkPatternChar pattern) = pattern++instance SelfValidate.SelfValidator PatternChar where+ isValid = SelfValidate.isValid . deconstructPatternChar++instance Consumer.Consumer PatternChar where+ consumptionProfile = Consumer.consumptionProfile . deconstructPatternChar+ starHeight = Consumer.starHeight . deconstructPatternChar++instance Test.QuickCheck.Arbitrary PatternChar where+ arbitrary = MkPatternChar <$> Test.QuickCheck.frequency [+ (4, RegEx.Require . MetaChar.deconstruct <$> Test.QuickCheck.arbitrary {-MetaChar-}),+ (1, RegEx.CaptureGroup . deconstructAlternativesChar <$> Test.QuickCheck.arbitrary {-AlternativesChar-})+ ] --CAVEAT: may recurse forever if 'RegEx.CaptureGroup / RegEx.Require' is too high.+#if ! (MIN_VERSION_QuickCheck(2,1,0))+ coarbitrary = undefined --CAVEAT: stops warnings from ghc.+#endif++-- | A specialised instance, required to instantiate 'Test.QuickCheck.Arbitrary'.+newtype RepeatablePatternChar = MkRepeatablePatternChar (Repeatable.Repeatable PatternChar) deriving (Eq, Read, Show)++-- | Accessor.+deconstructRepeatablePatternChar :: RepeatablePatternChar -> Repeatable.Repeatable PatternChar+deconstructRepeatablePatternChar (MkRepeatablePatternChar repeatable) = repeatable++instance SelfValidate.SelfValidator RepeatablePatternChar where+ isValid = SelfValidate.isValid . deconstructRepeatablePatternChar++instance Consumer.Consumer RepeatablePatternChar where+ consumptionProfile = Consumer.consumptionProfile . deconstructRepeatablePatternChar+ starHeight = Consumer.starHeight . deconstructRepeatablePatternChar++instance Test.QuickCheck.Arbitrary RepeatablePatternChar where+ arbitrary = do+ patternChar <- Test.QuickCheck.arbitrary+ fewest <- Test.QuickCheck.elements [0 .. 9] --Could be more, but this is an adequate test.+ most <- Test.QuickCheck.oneof [return {-to Gen-monad-} Nothing, Just <$> Test.QuickCheck.elements [max fewest 1 .. 9]]+ isGreedy <- Test.QuickCheck.arbitrary {-Bool-}++ let+ repetitionBounds :: Repeatable.RepetitionBounds+ repetitionBounds = (fewest, most)++ return {-to Gen-monad-} $ MkRepeatablePatternChar Repeatable.MkRepeatable {+ Repeatable.base = patternChar,+ Repeatable.repetitionBounds = repetitionBounds,+ Repeatable.isGreedy = Repeatable.hasPreciseBounds repetitionBounds || isGreedy --Only specify non-greedy where space exists.+ }+#if ! (MIN_VERSION_QuickCheck(2,1,0))+ coarbitrary = undefined --CAVEAT: stops warnings from ghc.+#endif++instance Test.QuickCheck.Arbitrary ExtendedRegExChar.ExtendedRegExChar where+ arbitrary = do+ hasBowAnchor <- Test.QuickCheck.arbitrary {-Bool-}+ concatenation <- map ((deconstructPatternChar <$> {-replace base-}) . deconstructRepeatablePatternChar {-Repeatable.Repeatable PatternChar-}) <$> Test.QuickCheck.vector 2 {-[RepeatablePatternChar]-}+ hasSternAnchor <- Test.QuickCheck.arbitrary {-Bool-}++ return {-to Gen-monad-} $ ExtendedRegExChar.MkExtendedRegExChar False RegEx.MkExtendedRegEx {+ RegEx.bowAnchor = if hasBowAnchor then Just Anchor.Bow else Nothing,+ RegEx.concatenation = concatenation,+ RegEx.sternAnchor = if hasSternAnchor then Just Anchor.Stern else Nothing+ }++#if ! (MIN_VERSION_QuickCheck(2,1,0))+ coarbitrary = undefined --CAVEAT: stops warnings from ghc.+#endif++type Testable = ExtendedRegExChar.ExtendedRegExChar -> Test.QuickCheck.Property++-- | Defines invariant properties, which must hold for any 'ExtendedRegExChar.ExtendedRegExChar'.+quickChecks :: (Testable -> IO ()) -> IO ()+quickChecks checker = quickChecks1 >> quickChecks2 where+ quickChecks1 = checker `mapM_` [prop_consumptionProfile, prop_consumptionProfile2, prop_io, prop_io', prop_isValid, prop_starHeight] where+ prop_consumptionProfile, prop_consumptionProfile2, prop_io, prop_io', prop_isValid, prop_starHeight :: Testable+ prop_consumptionProfile r = Test.QuickCheck.label "prop_consumptionProfile" $ (+ case maybeMaxData of+ Nothing -> True+ Just m -> m >= minData+ ) && (+ not b == all (not . Consumer.getHasSpecificRequirement) (RegEx.concatenation $ ExtendedRegExChar.extendedRegEx r)+ )+ where+ ConsumptionProfile.MkConsumptionProfile {+ ConsumptionProfile.consumptionBounds = (minData, maybeMaxData),+ ConsumptionProfile.hasSpecificRequirement = b+ } = Consumer.consumptionProfile r++ prop_consumptionProfile2 r = RegEx.isDefined (ExtendedRegExChar.extendedRegEx r) ==> Test.QuickCheck.label "prop_consumptionProfile2" $ or [hasSpecificRequirement, canConsumeAnything] --There's either; a requirement for at least one specific, or we can consume at least one arbitrary; input datum.+ where+ ConsumptionProfile.MkConsumptionProfile {+ ConsumptionProfile.hasSpecificRequirement = hasSpecificRequirement,+ ConsumptionProfile.canConsumeAnything = canConsumeAnything+ } = Consumer.consumptionProfile r++ prop_io r = Test.QuickCheck.label "prop_io" $ read (show r) == r+ prop_io' r = Test.QuickCheck.label "prop_io'" $ read (show r') == r' where r' = ExtendedRegExChar.extendedRegEx r --Check "RegEx.ExtendedRegEx Char" too.+ prop_isValid r = Test.QuickCheck.label "prop_isValid" $ SelfValidate.isValid r+ prop_starHeight r = Test.QuickCheck.label "prop_starHeight" $ (Consumer.starHeight r == 0) == ConsumptionBounds.isPrecise (Consumer.getConsumptionBounds r)++ quickChecks2 = Test.QuickCheck.quickCheck {-'checker' is technically the wrong type-} `mapM_` [prop_double] where+ prop_double :: Double -> Test.QuickCheck.Property+ prop_double d = Test.QuickCheck.label "prop_double" $ RegEx.extractDataFromMatchList `fmap` Result.getMatchList result == Just s where+ s :: String+ s = show $ d * 1e6 --Implementation of show, uses exponential notation, when the number is large.++ result :: RegEx.Result Char+ result = s +~ RegExOpts.mkRegEx (+ (Just Anchor.Bow, Just Anchor.Stern) <~> sign ?: digits +: map (+ Repeatable.zeroOrOne . RegEx.captureGroup . map ((Nothing, Nothing) <~>) . return {-to List-monad-}+ ) [+ RegEx.Require (Meta.Literal '.') -: digits +: [],+ RegEx.Require (Meta.AnyOf $ map BracketExpressionMember.Literal "eE") -: sign ?: digits +: []+ ]+ ) where+ sign, digits :: RegEx.Pattern Char+ sign = RegEx.Require . Meta.AnyOf $ map BracketExpressionMember.Literal "+-"+ digits = RegEx.Require . Meta.AnyOf $ map BracketExpressionMember.Literal ['0' .. '9']+
+ src/Grecce/Test/QC/MetaChar.hs view
@@ -0,0 +1,78 @@+{-# LANGUAGE CPP #-}+{-# 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@] Implements 'Test.QuickCheck.Arbitrary' & defines tests based on it.++ [@TODO@] Test /Perl-style shortcuts/ & /Posix Character-classes/.+-}++module Grecce.Test.QC.MetaChar(+-- * Types+-- ** Type-synonyms+-- Testable,+-- * Constants+-- testCharacters,+-- * Functions+ quickChecks+) where++import Control.Applicative((<$>))+import qualified Data.List+import qualified RegExChar.MetaChar as MetaChar --CAVEAT: beware of the similar name.+import qualified RegExDot.BracketExpression as BracketExpression+import qualified RegExDot.BracketExpressionMember as BracketExpressionMember+import qualified RegExDot.Meta as Meta+import qualified Test.QuickCheck++-- CAVEAT: module "Test.QuickCheck.Arbitrary" defines an instance of this from package "Quickcheck-2.1.0.1".+#if !(MIN_VERSION_QuickCheck(2,1,0))+instance Test.QuickCheck.Arbitrary Char where+ arbitrary = Test.QuickCheck.elements testCharacters+ coarbitrary = undefined --CAVEAT: stops warnings from ghc.+#endif++testCharacters :: String+testCharacters = filter (`notElem` [Meta.shortcutToken, BracketExpression.negationToken]) [' ' .. '~'] --TODO: permit all characters.++instance Test.QuickCheck.Arbitrary MetaChar.MetaChar where+ arbitrary = Test.QuickCheck.oneof $ map (MetaChar.MkMetaChar <$>) [+ return {-to Gen-monad-} Meta.Any,+ Meta.Literal <$> Test.QuickCheck.elements testCharacters,+ Meta.AnyOf . map BracketExpressionMember.Literal . Data.List.nub <$> arbitraryCharList 1 16, --Test.QuickCheck.vector 16 {-Char-},+ Meta.NoneOf . map BracketExpressionMember.Literal . Data.List.nub <$> arbitraryCharList 1 4+ ] where+ arbitraryCharList :: Int -> Int -> Test.QuickCheck.Gen String+ arbitraryCharList i j = do+ l <- Test.QuickCheck.arbitrary {-Int-}++ take (min j $ i `max` l) . Data.List.nub . Data.List.filter (`elem` testCharacters) <$> Test.QuickCheck.vector maxBound {-Char-}+#if ! (MIN_VERSION_QuickCheck(2,1,0))+ coarbitrary = undefined --CAVEAT: stops warnings from ghc.+#endif++type Testable = MetaChar.MetaChar -> Test.QuickCheck.Property++-- | Defines invariant properties, which must hold for any 'MetaChar.MetaChar'.+quickChecks :: (Testable -> IO ()) -> IO ()+quickChecks checker = checker `mapM_` [prop_io] where+ prop_io :: Testable+ prop_io m = Test.QuickCheck.label "prop_io" $ read (show m) == m+
+ src/Grecce/Test/QC/QuickChecks.hs view
@@ -0,0 +1,52 @@+{-# LANGUAGE CPP #-}+{-+ 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@] Calls the /quickChecks/-functions for modules supporting this feature.+-}++module Grecce.Test.QC.QuickChecks(+-- * Functions+ run+) where++import qualified Grecce.Test.QC.ExtendedRegExChar as Test.QC.ExtendedRegExChar+import qualified Grecce.Test.QC.MetaChar as Test.QC.MetaChar+import qualified Grecce.Test.QC.RepeatableMetaChar as Test.QC.RepeatableMetaChar+import qualified Test.QuickCheck++{- | Run the /quickChecks/-functions for modules supporting this feature.+ The single Boolean flag enables verbose output.+-}+run :: Bool -> IO ()+run isVerbose =+ putStrLn "MetaChar" >> Test.QC.MetaChar.quickChecks checker+ >> putStrLn "RepeatableMetaChar" >> Test.QC.RepeatableMetaChar.quickChecks checker+ >> putStrLn "ExtendedRegExChar" >> Test.QC.ExtendedRegExChar.quickChecks checker+ where+ checker :: Test.QuickCheck.Testable t => t -> IO ()+ checker+ | isVerbose =+#if MIN_VERSION_QuickCheck(2,4,0)+ Test.QuickCheck.verboseCheck+#else+ error "Grecce.Test.QC.QuickChecks:\t'Test.QuickCheck.verboseCheck' is only available as of 'QuickCheck-2.4'."+#endif+ | otherwise = Test.QuickCheck.quickCheck+
+ src/Grecce/Test/QC/RepeatableMetaChar.hs view
@@ -0,0 +1,98 @@+{-# LANGUAGE CPP #-}+{-+ 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@] Implements 'Test.QuickCheck.Arbitrary' & defines tests based on it.+-}++module Grecce.Test.QC.RepeatableMetaChar(+-- * Types+-- ** Data-types+-- RepeatableMetaChar,+-- ** Type-synonyms+-- Testable,+-- * Functions+-- deconstruct,+ quickChecks+) where++import Control.Applicative((<$>))+import Grecce.Test.QC.MetaChar()+import qualified RegExChar.MetaChar as MetaChar+import qualified RegExDot.Consumer as Consumer+import qualified RegExDot.ConsumptionBounds as ConsumptionBounds+import qualified RegExDot.ConsumptionProfile as ConsumptionProfile+import qualified RegExDot.Repeatable as Repeatable+import qualified Test.QuickCheck+import qualified ToolShed.SelfValidate as SelfValidate++-- | A specialised instance, required to instantiate 'Test.QuickCheck.Arbitrary'.+newtype RepeatableMetaChar = MkRepeatableMetaChar (Repeatable.Repeatable MetaChar.MetaChar) deriving (Eq, Read, Show)++-- | Accessor.+deconstruct :: RepeatableMetaChar -> Repeatable.Repeatable MetaChar.MetaChar+deconstruct (MkRepeatableMetaChar repeatableMetaChar) = repeatableMetaChar++instance SelfValidate.SelfValidator RepeatableMetaChar where+ isValid = SelfValidate.isValid . deconstruct++instance Consumer.Consumer RepeatableMetaChar where+ consumptionProfile = Consumer.consumptionProfile . deconstruct+ starHeight = Consumer.starHeight . deconstruct++instance Test.QuickCheck.Arbitrary RepeatableMetaChar where+ arbitrary = do+ metaChar <- Test.QuickCheck.arbitrary+ fewest <- Test.QuickCheck.elements [0 .. 9] --Could be more, but this is an adequate test.+ most <- Test.QuickCheck.oneof [return Nothing, Just <$> Test.QuickCheck.elements [max fewest 1 .. 9]]+ isGreedy <- Test.QuickCheck.arbitrary++ let+ repetitionBounds :: Repeatable.RepetitionBounds+ repetitionBounds = (fewest, most)++ return {-to Gen-monad-} . MkRepeatableMetaChar $ Repeatable.MkRepeatable {+ Repeatable.base = metaChar,+ Repeatable.repetitionBounds = repetitionBounds,+ Repeatable.isGreedy = Repeatable.hasPreciseBounds repetitionBounds || isGreedy --Only specify 'non-greedy' where space exists.+ }+#if !(MIN_VERSION_QuickCheck(2,1,0))+ coarbitrary = undefined --CAVEAT: stops warnings from ghc.+#endif++type Testable = RepeatableMetaChar -> Test.QuickCheck.Property++-- | Defines invariant properties, which must hold for any 'RepeatableMetaChar'.+quickChecks :: (Testable -> IO ()) -> IO ()+quickChecks checker = checker `mapM_` [prop_consumptionProfile, prop_io, prop_isValid, prop_starHeight] where+ prop_consumptionProfile, prop_io, prop_isValid, prop_starHeight :: Testable+ prop_consumptionProfile r = Test.QuickCheck.label "prop_consumptionProfile" $ (+ case maybeMaxData of+ Nothing -> True+ Just m -> m >= minData+ ) && (+ b == Consumer.getHasSpecificRequirement (Repeatable.base $ deconstruct r)+ ) where ConsumptionProfile.MkConsumptionProfile {+ ConsumptionProfile.consumptionBounds = (minData, maybeMaxData),+ ConsumptionProfile.hasSpecificRequirement = b+ } = Consumer.consumptionProfile r+ prop_io (MkRepeatableMetaChar r) = Test.QuickCheck.label "prop_io" $ read (show r) == r+ prop_isValid r = Test.QuickCheck.label "prop_isValid" $ SelfValidate.isValid r+ prop_starHeight (MkRepeatableMetaChar r) = Test.QuickCheck.label "prop_starHeight" $ Consumer.starHeight r == if ConsumptionBounds.isPrecise (Consumer.getConsumptionBounds r) then 0 else 1+
src/Main.hs view
@@ -41,20 +41,20 @@ import qualified Distribution.Package import qualified Distribution.Text import qualified Distribution.Version-import qualified Grecce.Assert.RegExOptsChar as Assert.RegExOptsChar-import qualified Grecce.CommandOptions as CommandOptions-import qualified Grecce.Grep as Grep-import qualified Grecce.Performance.ExtendedRegEx as Performance.ExtendedRegEx-import qualified Grecce.Performance.ExtendedRegExTestsNegative as Performance.ExtendedRegExTestsNegative-import qualified Grecce.Performance.ExtendedRegExTestsPositive as Performance.ExtendedRegExTestsPositive-import qualified Grecce.QC.QuickChecks as QC.QuickChecks-import qualified RegExDot.CompilationOptions as CompilationOptions-import qualified RegExDot.ExecutionOptions as ExecutionOptions+import qualified Grecce.CommandOptions as CommandOptions+import qualified Grecce.Grep as Grep+import qualified Grecce.Test.Assert.RegExOptsChar as Test.Assert.RegExOptsChar+import qualified Grecce.Test.Performance.ExtendedRegEx as Test.Performance.ExtendedRegEx+import qualified Grecce.Test.Performance.ExtendedRegExTestsNegative as Test.Performance.ExtendedRegExTestsNegative+import qualified Grecce.Test.Performance.ExtendedRegExTestsPositive as Test.Performance.ExtendedRegExTestsPositive+import qualified Grecce.Test.QC.QuickChecks as Test.QC.QuickChecks+import qualified RegExDot.CompilationOptions as CompilationOptions+import qualified RegExDot.ExecutionOptions as ExecutionOptions import qualified System-import qualified System.Console.GetOpt as G+import qualified System.Console.GetOpt as G import qualified System.IO import qualified System.IO.Error-import qualified ToolShed.Defaultable as Defaultable+import qualified ToolShed.Defaultable as Defaultable -- | Used to thread user-defined command-line options, though the list of functions which implement them. type CommandLineAction = CommandOptions.CommandOptions -> IO CommandOptions.CommandOptions --Supplied as the type-argument to 'G.OptDescr'.@@ -92,9 +92,9 @@ extendedRegExCharPerformance `G.ReqArg` "(Int,Either [TestName])" ) ( "Test:\tcompare performance with other RegEx-engines, using the specified pair (<Maximum complexity>, Left " ++ show [- minBound :: Performance.ExtendedRegExTestsNegative.TestName .. maxBound+ minBound :: Test.Performance.ExtendedRegExTestsNegative.TestName .. maxBound ] ++ " or Right " ++ show [- minBound :: Performance.ExtendedRegExTestsPositive.TestName .. maxBound+ minBound :: Test.Performance.ExtendedRegExTestsPositive.TestName .. maxBound ] ++ "). NB: specify this option last." ), G.Option "" ["runQuickChecks"] (G.NoArg runQuickChecks) "Test:\trun Quick-checks using arbitrary data & then exit. Specify 'verbose' before this option, for detailed output.",@@ -122,24 +122,24 @@ packageIdentifier :: Distribution.Package.PackageIdentifier packageIdentifier = Distribution.Package.PackageIdentifier { Distribution.Package.pkgName = Distribution.Package.PackageName "grecce",- Distribution.Package.pkgVersion = Distribution.Version.Version [0, 9, 0, 7] []+ Distribution.Package.pkgVersion = Distribution.Version.Version [0, 9, 0, 8] [] } printUsage = System.IO.hPutStrLn System.IO.stderr usage >> System.exitWith System.ExitSuccess printCurrentSettings, runQuickChecks :: CommandLineAction printCurrentSettings commandOptions = print commandOptions >> System.exitWith System.ExitSuccess- runQuickChecks commandOptions = QC.QuickChecks.run (CommandOptions.verbose commandOptions) >> System.exitWith System.ExitSuccess+ runQuickChecks commandOptions = Test.QC.QuickChecks.run (CommandOptions.verbose commandOptions) >> System.exitWith System.ExitSuccess extendedRegExCharPerformance, readExtendedRegExCharFromFile, assertFromFile, assertPosixFromFile, setExtendedRegExChar :: String -> CommandLineAction extendedRegExCharPerformance s commandOptions = (- uncurry $ Performance.ExtendedRegEx.run (CommandOptions.executionOptions commandOptions)+ uncurry $ Test.Performance.ExtendedRegEx.run (CommandOptions.executionOptions commandOptions) ) (read s) >> System.exitWith System.ExitSuccess readExtendedRegExCharFromFile s commandOptions = (head . dropWhile null . lines <$> readFile s) >>= (`setExtendedRegExChar` commandOptions) assertFromFile fileName commandOptions = do- failedTests <- Assert.RegExOptsChar.findFailures (CommandOptions.compilationOptions commandOptions) (CommandOptions.executionOptions commandOptions) <$> Assert.RegExOptsChar.readTests fileName+ failedTests <- Test.Assert.RegExOptsChar.findFailures (CommandOptions.compilationOptions commandOptions) (CommandOptions.executionOptions commandOptions) <$> Test.Assert.RegExOptsChar.readTests fileName if null failedTests then System.exitWith System.ExitSuccess@@ -149,7 +149,7 @@ System.exitWith $ System.ExitFailure 2 assertPosixFromFile fileName _ = do- failedTests <- Assert.RegExOptsChar.findFailuresPosix <$> Assert.RegExOptsChar.readTests fileName+ failedTests <- Test.Assert.RegExOptsChar.findFailuresPosix <$> Test.Assert.RegExOptsChar.readTests fileName if null failedTests then System.exitWith System.ExitSuccess
src/RegExChar/ExtendedRegExChar.hs view
@@ -34,7 +34,6 @@ ) where import Control.Applicative((<$>))-import Control.Arrow((***)) import RegExDot.DSL((<~>), (-:)) import Text.ParserCombinators.Parsec((<?>), (<|>)) import qualified Control.Monad@@ -46,6 +45,7 @@ import qualified RegExDot.RegExOpts as RegExOpts import qualified RegExDot.Repeatable as Repeatable import qualified Text.ParserCombinators.Parsec as Parsec+import qualified ToolShed.Pair as Pair import qualified ToolShed.SelfValidate as SelfValidate infix 4 +~, =~, /~ --Same as (==) & (/=).@@ -101,7 +101,7 @@ ( do repeatableCaptureGroup <- Repeatable.repeatableParser . RegEx.CaptureGroup =<< uncurry Parsec.between (- Parsec.char *** Parsec.char $ RegEx.captureGroupDelimiters+ Pair.mirror Parsec.char RegEx.captureGroupDelimiters ) alternativesParser {-recurse-} <?> "RegEx.captureGroupDelimiters " ++ show RegEx.captureGroupDelimiters extendedRegEx' <- extendedRegExParser --Recurse.
src/RegExChar/MetaChar.hs view
@@ -74,6 +74,7 @@ import qualified RegExDot.Repeatable as Repeatable import qualified RegExDot.ShowablePredicate as ShowablePredicate import qualified Text.ParserCombinators.Parsec as Parsec+import qualified ToolShed.Pair as Pair import qualified ToolShed.SelfValidate as SelfValidate -- | Holds a mapping from an escape-sequence & the predicate via which it is implemented.@@ -120,7 +121,7 @@ metaCharParser = MkMetaChar <$> Parsec.choice [ (Parsec.char Meta.anyToken <?> "Meta.anyToken " ++ show Meta.anyToken) >> return {-to GenParser-monad-} Meta.Any, (Parsec.char Meta.shortcutToken <?> "Meta.shortcutToken " ++ show Meta.shortcutToken) >> Meta.expand <$> Parsec.anyChar,- uncurry Parsec.between (Parsec.char *** Parsec.char $ BracketExpression.delimiterTokens) (+ uncurry Parsec.between (Pair.mirror Parsec.char BracketExpression.delimiterTokens) ( do let implementPosixCharacterClass :: String -> Maybe (ShowablePredicate.ShowablePredicate Char)@@ -168,7 +169,7 @@ _ -> BracketExpressionMember.Literal c --Escaped literal. ) <?> "Perl-style shortcut", Parsec.try (- uncurry Parsec.between (Parsec.string *** Parsec.string $ posixCharacterClassDelimiters) (+ uncurry Parsec.between (Pair.mirror Parsec.string posixCharacterClassDelimiters) ( do identifier <- Parsec.many1 $ Parsec.noneOf [head $ snd posixCharacterClassDelimiters]