regexchar 0.9.0.12 → 0.9.0.13
raw patch · 39 files changed
+2595/−2587 lines, 39 filesdep +regexchardep ~Cabaldep ~toolshedPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: regexchar
Dependency ranges changed: Cabal, toolshed
API changes (from Hackage documentation)
- RegExChar.ExtendedRegExChar: extendedRegEx :: ExtendedRegExChar -> ExtendedRegEx Char
- RegExChar.ExtendedRegExChar: hasNonCapturingTopLevelAlternatives :: ExtendedRegExChar -> Bool
- RegExChar.ExtendedRegExChar: instance Consumer ExtendedRegExChar
- RegExChar.ExtendedRegExChar: instance Eq ExtendedRegExChar
- RegExChar.ExtendedRegExChar: instance Read ExtendedRegExChar
- RegExChar.ExtendedRegExChar: instance SelfValidator ExtendedRegExChar
- RegExChar.ExtendedRegExChar: instance ShortcutExpander Char
- RegExChar.ExtendedRegExChar: instance Show ExtendedRegExChar
- RegExChar.MetaChar: instance Consumer MetaChar
- RegExChar.MetaChar: instance Eq MetaChar
- RegExChar.MetaChar: instance Read MetaChar
- RegExChar.MetaChar: instance SelfValidator MetaChar
- RegExChar.MetaChar: instance ShortcutExpander Char
- RegExChar.MetaChar: instance Show MetaChar
- RegExChar.RegExOptsChar: instance RegexLike RegExOptsChar InputData
- RegExChar.RegExOptsChar: instance RegexMaker RegExOptsChar CompilationOptions ExecutionOptions String
- RegExChar.RegExOptsChar: instance RegexOptions RegExOptsChar CompilationOptions ExecutionOptions
+ RegExChar.ExtendedRegExChar: [extendedRegEx] :: ExtendedRegExChar -> ExtendedRegEx Char
+ RegExChar.ExtendedRegExChar: [hasNonCapturingTopLevelAlternatives] :: ExtendedRegExChar -> Bool
+ RegExChar.ExtendedRegExChar: instance GHC.Classes.Eq RegExChar.ExtendedRegExChar.ExtendedRegExChar
+ RegExChar.ExtendedRegExChar: instance GHC.Read.Read RegExChar.ExtendedRegExChar.ExtendedRegExChar
+ RegExChar.ExtendedRegExChar: instance GHC.Show.Show RegExChar.ExtendedRegExChar.ExtendedRegExChar
+ RegExChar.ExtendedRegExChar: instance RegExDot.Consumer.Consumer RegExChar.ExtendedRegExChar.ExtendedRegExChar
+ RegExChar.ExtendedRegExChar: instance RegExDot.RegEx.ShortcutExpander GHC.Types.Char
+ RegExChar.ExtendedRegExChar: instance ToolShed.SelfValidate.SelfValidator RegExChar.ExtendedRegExChar.ExtendedRegExChar
+ RegExChar.MetaChar: instance GHC.Classes.Eq RegExChar.MetaChar.MetaChar
+ RegExChar.MetaChar: instance GHC.Read.Read RegExChar.MetaChar.MetaChar
+ RegExChar.MetaChar: instance GHC.Show.Show RegExChar.MetaChar.MetaChar
+ RegExChar.MetaChar: instance RegExDot.BracketExpressionMember.ShortcutExpander GHC.Types.Char
+ RegExChar.MetaChar: instance RegExDot.Consumer.Consumer RegExChar.MetaChar.MetaChar
+ RegExChar.MetaChar: instance RegExDot.Meta.ShortcutExpander GHC.Types.Char
+ RegExChar.MetaChar: instance ToolShed.SelfValidate.SelfValidator RegExChar.MetaChar.MetaChar
+ RegExChar.RegExOptsChar: instance Text.Regex.Base.RegexLike.RegexLike RegExChar.RegExOptsChar.RegExOptsChar RegExChar.ExtendedRegExChar.InputData
+ RegExChar.RegExOptsChar: instance Text.Regex.Base.RegexLike.RegexMaker RegExChar.RegExOptsChar.RegExOptsChar RegExDot.CompilationOptions.CompilationOptions RegExDot.ExecutionOptions.ExecutionOptions GHC.Base.String
+ RegExChar.RegExOptsChar: instance Text.Regex.Base.RegexLike.RegexOptions RegExChar.RegExOptsChar.RegExOptsChar RegExDot.CompilationOptions.CompilationOptions RegExDot.ExecutionOptions.ExecutionOptions
Files
- README.markdown +24/−0
- changelog +0/−66
- changelog.markdown +77/−0
- copyright +1/−1
- debian/DEBIAN/control +3/−4
- makefile +0/−81
- man/man1/grecce.1 +3/−6
- regexchar.cabal +92/−42
- regexchar.spec +22/−24
- src-exe/Grecce/CommandOptions.hs +172/−0
- src-exe/Grecce/Grep.hs +206/−0
- src-exe/Grecce/Test/Assert/RegExOptsChar.hs +93/−0
- src-exe/Grecce/Test/Performance/ExtendedRegEx.hs +148/−0
- src-exe/Grecce/Test/Performance/ExtendedRegExTest.hs +51/−0
- src-exe/Grecce/Test/Performance/ExtendedRegExTestsNegative.hs +85/−0
- src-exe/Grecce/Test/Performance/ExtendedRegExTestsPositive.hs +329/−0
- src-exe/Main.hs +208/−0
- src-lib/RegExChar/ExtendedRegExChar.hs +210/−0
- src-lib/RegExChar/MetaChar.hs +256/−0
- src-lib/RegExChar/RegExOptsChar.hs +219/−0
- src-test/Grecce/Test/QC/ExtendedRegExChar.hs +192/−0
- src-test/Grecce/Test/QC/MetaChar.hs +66/−0
- src-test/Grecce/Test/QC/RepeatableMetaChar.hs +92/−0
- src-test/Main.hs +46/−0
- src/Grecce/CommandOptions.hs +0/−172
- src/Grecce/Grep.hs +0/−206
- src/Grecce/Test/Assert/RegExOptsChar.hs +0/−93
- src/Grecce/Test/Performance/ExtendedRegEx.hs +0/−145
- src/Grecce/Test/Performance/ExtendedRegExTest.hs +0/−51
- src/Grecce/Test/Performance/ExtendedRegExTestsNegative.hs +0/−85
- src/Grecce/Test/Performance/ExtendedRegExTestsPositive.hs +0/−329
- src/Grecce/Test/QC/ExtendedRegExChar.hs +0/−188
- src/Grecce/Test/QC/MetaChar.hs +0/−67
- src/Grecce/Test/QC/QuickChecks.hs +0/−49
- src/Grecce/Test/QC/RepeatableMetaChar.hs +0/−92
- src/Main.hs +0/−207
- src/RegExChar/ExtendedRegExChar.hs +0/−207
- src/RegExChar/MetaChar.hs +0/−253
- src/RegExChar/RegExOptsChar.hs +0/−219
+ README.markdown view
@@ -0,0 +1,24 @@+# **RegExChar**.++This is **RegExChar**, a regex-engine built from the polymorphic regex-engine 'RegExDot' as a proof of concept.++## Installation++It can be built and installed using [Cabal](https://www.haskell.org/cabal/users-guide/installing-packages.html).++## Documentation++* The directory **man/** contains the documentation.+* More information about this application can be found at [RegExChar](http://functionalley.eu/RegExChar/regExChar.html).++## License++For information on copying and distributing this package, see the file **LICENSE** in this directory.++## Bug-reporting++Bug-reports should be emailed to <regexchar *at* functionalley *dot* eu>.++## Author++This application is written and maintained by Dr. Alistair Ward.
− changelog
@@ -1,66 +0,0 @@-2010-11-18 Dr. Alistair Ward <regexchar at functionalley dot eu>--0.9.0.0- * First version of the package.-0.9.0.1- * Reacted to major-number changes in the packages 'ToolShed-0.10.0.0' & 'RegExDot-0.10.0.0'.- * Created "src/RegExChar/" (& then modified module-names accordingly) & "data/" sub-directories.- * Pacified 'hlint'.- * Added a makefile.- * Qualified identifiers used in error-messages.-0.9.0.2- * Ported to ghc-7.0.1:- Replaced the deprecated module "Test.QuickCheck.Batch", & labelled properties.- * Added the cabal-flag 'threaded', to account for the requirement to link with the threaded runtime when using a threaded version of the library package "RegExDot".-0.9.0.3- * Parallelised processing where @grecce@ is requested to read multiple input-data files.- * Extracted matching-operation from the "Main" module, into new module "Grecce.Grep".- * Migrated modules required only by the executable 'grecce', from the directory "RegExChar" into the new directory "Grecce".- * Replaced use of @threaded@-flag in source-code, with CABAL CPP-macro.- * Added RPM-spec file.-0.9.0.4- * Added default values of command-line flags, to usage-message.- * Reacted to migration of "RegExDot.Options" to "ToolShed.Options".- * Used new "ToolShed.Package" module.-0.9.0.5- * Fixed negated reference to unparenthesised CPP-macro 'MIN_VERSION'.- * Protected reference in "Grecce/QC/QuickChecks.hs", to 'Test.QuickCheck.verboseCheck', using CPP-statement.-0.9.0.6- * Amended RPM ".spec" file, to install under "/usr", rather than "/usr/local".- * Renamed package from 'RegExChar' to 'regexchar', for compatibility with Debian's ".deb"-format.- * Added directory-structure required to build ".deb" package.-0.9.0.7- * Refined man-page to be an 'extra-source-file' in .cabal & .spec files.- * Added manually controlled "llvm" flag to "regexchar.cabal".- * Replaced references to the module "ToolShed.Package" with "Distribution.Package".- * 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.-0.9.0.9- * Recoded "RegExChar.RegExOptsChar" to the interface "Data.Array.IArray.IArray", rather than the data-type "Data.Array.Array".- * Replaced '(+ 1)' and '(- 1)' with the faster calls 'succ' and 'pred', in "RegExChar.RegExOptsChar", "Grecce.Test.Performance.ExtendedRegExTestsPositive" and "Grecce.Grep".- * Used 'Paths_regexchar.version' in 'Main', rather than hard-coding it.- * Reacted to new module-hierarchy and addition of method 'ToolShed.SelfValidate.getErrors', in toolshed-0.13.0.0, and used it to improved error-reporting in @instance Read RegExChar.ExtendedRegExChar.ExtendedRegExChar@.- * Replaced use of "Control.Monad" in "RegExChar.MetaChar" and "RegExChar.ExtendedRegExChar", with "Control.Applicative".- * Replaced "System" with "System.Environment" and "System.Exit".- * Removed dependency on "haskell98".-0.9.0.10- * Fully qualified symbols imported from package "regexdot".- * Used new constant 'RegExDot.Anchor.unanchored'.- * Added details to any failure to parse the command-line arguments.- * Used new test-modules from 'toolshed-0.14.0.0'.-0.9.0.11- * Refactored 'Grecce.Test.QC.QuickChecks'.- * Replaced calls to 'error' from inside the IO-monad, with 'Control.Monad.fail'.- * Trapped command-line arguments to which garbage has been appended.- * Added files to build .deb to 'regexchar.cabal'.- * Tested with 'haskell-platform-2013.2.0.0'.- * Replaced preprocessor-directives with 'build-depends' constraints in 'regexchar.cabal'.-0.9.0.12- * Either replaced instances of '(<$>)' with 'fmap' to avoid ambiguity between "Control.Applicative" & "Prelude" which (from 'base-4.8') also exports this symbol, or hid the symbol when importing the "Prelude"..
+ changelog.markdown view
@@ -0,0 +1,77 @@+# 2010-11-18 Dr. Alistair Ward <regexchar at functionalley dot eu>++## 0.9.0.0+ * First version of the package.+## 0.9.0.1+ * Reacted to major-number changes in the packages **ToolShed-0.10.0.0** & **RegExDot-0.10.0.0**.+ * Created **src/RegExChar/** (& then modified module-names accordingly) & **data/** sub-directories.+ * Pacified **hlint**.+ * Added a makefile.+ * Qualified identifiers used in error-messages.+## 0.9.0.2+ * Ported to ghc-7.0.1:+ Replaced the deprecated module **Test.QuickCheck.Batch**, & labelled properties.+ * Added the cabal-flag **threaded**, to account for the requirement to link with the threaded runtime when using a threaded version of the library package **RegExDot**.+## 0.9.0.3+ * Parallelised processing where **grecce** is requested to read multiple input-data files.+ * Extracted matching-operation from the **Main** module, into new module **Grecce.Grep**.+ * Migrated modules required only by the executable **grecce**, from the directory **RegExChar** into the new directory **Grecce**.+ * Replaced use of @threaded@-flag in source-code, with CABAL CPP-macro.+ * Added *.spec*-file.+## 0.9.0.4+ * Added default values of command-line flags, to usage-message.+ * Reacted to migration of **RegExDot.Options** to **ToolShed.Options**.+ * Used new **ToolShed.Package** module.+## 0.9.0.5+ * Fixed negated reference to unparenthesised CPP-macro **MIN_VERSION**.+ * Protected reference in **Grecce/QC/QuickChecks.hs**, to `Test.QuickCheck.verboseCheck`, using CPP-statement.+## 0.9.0.6+ * Amended RPM *.spec*-file, to install under **/usr**, rather than **/usr/local**.+ * Renamed package from **RegExChar** to **regexchar**, for compatibility with Debian's *.deb*-format.+ * Added directory-structure required to build *.deb*-package.+## 0.9.0.7+ * Redefined man-page to be an **extra-source-file** in the *.cabal*-file & the *.spec*-file.+ * Added manually controlled **llvm** flag to the *.cabal*-file.+ * Replaced references to the module **ToolShed.Package** with **Distribution.Package**.+ * 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 [Hackage](http://hackage.haskell.org/packages/hackage.html).+## 0.9.0.8+ * Amended the *.cabal*-file 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 the *cabal*-file.+## 0.9.0.9+ * Recoded **RegExChar.RegExOptsChar** to the interface `Data.Array.IArray.IArray`, rather than the data-type `Data.Array.Array`.+ * Replaced `(+ 1)` and `(- 1)` with the faster calls `succ` and `pred`, in **RegExChar.RegExOptsChar**, **Grecce.Test.Performance.ExtendedRegExTestsPositive** and **Grecce.Grep**.+ * Used `Paths_regexchar.version` in **Main**, rather than hard-coding it.+ * Reacted to new module-hierarchy and addition of method `ToolShed.SelfValidate.getErrors`, in **toolshed-0.13.0.0**, and used it to improved error-reporting in @instance Read RegExChar.ExtendedRegExChar.ExtendedRegExChar@.+ * Replaced use of `Control.Monad` in **RegExChar.MetaChar** and **RegExChar.ExtendedRegExChar**, with `Control.Applicative`.+ * Replaced `System` with `System.Environment` and `System.Exit`.+ * Removed dependency on **haskell98**.+## 0.9.0.10+ * Fully qualified symbols imported from package **regexdot**.+ * Used new constant `RegExDot.Anchor.unanchored`.+ * Added details to any failure to parse the command-line arguments.+ * Used new test-modules from **toolshed-0.14.0.0**.+## 0.9.0.11+ * Refactored **Grecce.Test.QC.QuickChecks**.+ * Replaced calls to `error` from inside the IO-monad, with `Control.Monad.fail`.+ * Trapped command-line arguments to which garbage has been appended.+ * Added files to build *.deb* to the *.cabal*-file.+ * Tested with **haskell-platform-2013.2.0.0**.+ * Replaced preprocessor-directives with **build-depends** constraints in the *.cabal*-file.+## 0.9.0.12+ * Either replaced instances of `(<$>)` with `fmap` to avoid ambiguity between **Control.Applicative** & **Prelude** which (from **base-4.8**) also exports this symbol, or hid the symbol when importing the **Prelude**..+## 0.9.0.13+ * Added the compiler to the output returned for the command-line option **version**.+ * Changed flag **threaded** in the *.cabal*-file to **manual**.+ * Added **Default-language**-specification to the *.cabal*-file.+ * Added file **README.markdown**.+ * Converted this file to markdown-format.+ * Replaced `System.Exit.exitWith System.Exit.ExitSuccess` with `System.Exit.exitSuccess`.+ * Moved the entry-point to the test-suite from **Main.hs** to **Test.hs**, both to integrate with **cabal** & to minimise the dependencies of the executable.+ * Partitioned the source-files into **src-lib**, **src-exe**, & **src-test** directories, & referenced them individually from the *.cabal*-file to avoid repeated compilation.+ * Used **CPP** to control the import of symbols from **Control.Applicative**.+
copyright view
@@ -5,7 +5,7 @@ Copyright (C) 2010-2011 Dr. Alistair Ward. All Rights Reserved. Home-page:- http://functionalley.eu+ http://functionalley.eu/RegExChar/regExChar.html License: GNU GENERAL PUBLIC LICENSE Version 3; see '/usr/share/common-licenses/GPL-3' or '/usr/share/doc/licenses/gpl-3.0.txt' where available, or the local packaged file 'LICENSE'.
debian/DEBIAN/control view
@@ -1,14 +1,13 @@ Package: regexchar-Version: 0.9.0.12-1+Version: 0.9.0.13-1 Section: text Priority: optional-#Architecture: i386 Architecture: amd64 Depends: libc6-Installed-size: 3496+Installed-size: 3222 Maintainer: Dr. Alistair Ward <regexchar at functionalley dot eu> Provides:-Homepage: http://functionalley.eu+Homepage: http://functionalley.eu/RegExChar/regExChar.html Description: POSIX extended regular-expression engine. POSIX extended regex-engine, specialised from the underlying polymorphic engine regexdot, to merely process character-lists, as is typically required.
− makefile
@@ -1,81 +0,0 @@-# Copyright (C) 2010-2014 Dr. Alistair Ward-#-# This program is free software: you can redistribute it and/or modify-# it under the terms of the GNU General Public License as published by-# the Free Software Foundation, either version 3 of the License, or-# (at your option) any later version.-#-# This program is distributed in the hope that it will be useful,-# but WITHOUT ANY WARRANTY; without even the implied warranty of-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the-# GNU General Public License for more details.-#-# You should have received a copy of the GNU General Public License-# along with this program. If not, see <http://www.gnu.org/licenses/>.--.PHONY: all build check clean clobber configure copy deb haddock help hlint install lintian prof rpm rpmlint sdist--PACKAGE_NAME=regexchar--all: install--install: build haddock- @[ -z "$$CABAL_INSTALL_OPTIONS" ] || echo "INFO: CABAL_INSTALL_OPTIONS='$$CABAL_INSTALL_OPTIONS'"- runhaskell Setup $@ $$CABAL_INSTALL_OPTIONS--prof:- CABAL_CONFIGURE_OPTIONS="--enable-library-profiling --enable-executable-profiling $$CABAL_CONFIGURE_OPTIONS" make build--copy: build- @[ -z "$$CABAL_COPY_OPTIONS" ] || echo "INFO: CABAL_COPY_OPTIONS='$$CABAL_COPY_OPTIONS'"- runhaskell Setup $@ $$CABAL_COPY_OPTIONS--build: configure- @[ -z "$$CABAL_BUILD_OPTIONS" ] || echo "INFO: CABAL_BUILD_OPTIONS='$$CABAL_BUILD_OPTIONS'"- runhaskell Setup $@ $$CABAL_BUILD_OPTIONS--configure: $(PACKAGE_NAME).cabal Setup.hs- @[ -z "$$CABAL_CONFIGURE_OPTIONS" ] || echo "INFO: CABAL_CONFIGURE_OPTIONS='$$CABAL_CONFIGURE_OPTIONS'"- runhaskell Setup $@ $$CABAL_CONFIGURE_OPTIONS #--user--haddock: configure- PATH=~/.cabal/bin:$$PATH runhaskell Setup $@ --hyperlink-source --executables #Amend path to find 'HsColour', as required for 'hyperlink-source'.--hlint:- @$@ -i 'Use ||' src/ +RTS -N--sdist:- TAR_OPTIONS='--format=ustar' runhaskell Setup $@--check:- cabal upload --check --verbose=3 dist/*.tar.gz;--rpm: $(PACKAGE_NAME).spec- rpmbuild -bb --nodeps -- $^- mv -- `find ~/rpmbuild -type f -name '$(PACKAGE_NAME)*.rpm' -mtime 0 -print` ./--rpmlint: $(PACKAGE_NAME).spec- @$@ $^--deb: build- mkdir -p debian/usr/bin/ && strip --strip-all dist/build/grecce/grecce && mv dist/build/grecce/grecce debian/usr/bin/- mkdir -p 'debian/usr/share/doc/$(PACKAGE_NAME)/' && cp changelog changelog.Debian copyright LICENSE 'debian/usr/share/doc/$(PACKAGE_NAME)/' && cd 'debian/usr/share/doc/$(PACKAGE_NAME)/' && gzip --best -f changelog changelog.Debian LICENSE- cp 'copyright' 'debian/usr/share/doc/$(PACKAGE_NAME)/'- mkdir -p 'debian/usr/share/$(PACKAGE_NAME)/' && cp -R data/ 'debian/usr/share/$(PACKAGE_NAME)/'- mkdir -p debian/usr/share/ && cp -R man/ debian/usr/share/ && gzip --best -f debian/usr/share/man/man1/*.1- cd 'debian' && md5sum `find usr -type f -print` >'DEBIAN/md5sums'- fakeroot dpkg-deb --build debian .--lintian:- lintian -v *.deb--clean:- runhaskell Setup $@- rm -rf 'debian/usr/' 'debian/DEBIAN/md5sums'- find src -type f \( -name '*.hc' -o -name '*.hcr' -o -name '*.hi' -o -name '*.o' \) -delete--clobber: clean- rm -f -- *.rpm *.deb--help:- @grep '^[a-zA-Z].*:' makefile | sed -e 's/:.*//'
man/man1/grecce.1 view
@@ -131,9 +131,6 @@ Display the currently defined options & then exit. .SS Test .TP-\fB--runQuickChecks\fR-Uses \fBQuickCheck\fR to validate invariant properties, using arbitrary data; then exits.-.TP \fB--assert=\fR\fIfile\fR Confirms the assertions in the specified file from the \fBdata\fR-directory of the distribution; then exits. .SH EXIT-STATUS@@ -181,7 +178,7 @@ Written by Dr. Alistair Ward. .SH BUGS .SS "REPORTING BUGS"-Report bugs to \fBgrecce\fR \fIat\fR \fBfunctionalley\fR \fIdot\fR \fBeu\fR+Report bugs to <\fBgrecce\fR \fIat\fR \fBfunctionalley\fR \fIdot\fR \fBeu\fR>. .SS Known Bugs There is no support for either \fIBack-references\fR or \fICase-insensitivity\fR. .PP@@ -189,7 +186,7 @@ .PP File-reading is rather slow, due to the inability to optimise the underlying polymorphic regex-engine for Characters. .SH COPYRIGHT-Copyright \(co 2010 Dr. Alistair Ward+Copyright \(co 2010-2015 Dr. Alistair Ward .PP 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. .PP@@ -198,7 +195,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see \fB<http://www.gnu.org/licenses/>\fR. .SH "SEE ALSO" .IP \(bu-Home-page: \fBhttp://functionalley.eu\fR+Home-page: \fBhttp://functionalley.eu/RegExChar/regExChar\fR .IP \(bu Source-documentation is generated by "\fBHaddock\fR", & is available in the distribution. .IP \(bu
regexchar.cabal view
@@ -1,35 +1,65 @@--- Package-properties-Name: regexchar-Version: 0.9.0.12-Cabal-Version: >= 1.6-Copyright: (C) 2010-2015 Dr. Alistair Ward-License: GPL-License-file: LICENSE-Author: Dr. Alistair Ward-Stability: Stable interface, incomplete features.-Synopsis: A POSIX, extended regex-engine.-Build-Type: Simple-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, GHC == 7.4, GHC == 7.6, GHC == 7.10-Homepage: http://functionalley.eu-Maintainer: regexchar <at> functionalley <dot> eu-Bug-reports: regexchar <at> functionalley <dot> eu-Data-files: data/*.txt-Extra-Source-Files: changelog copyright changelog.Debian debian/DEBIAN/control makefile man/man1/grecce.1 regexchar.spec+-- This file is part of RegExChar.+--+-- RegExChar 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.+--+-- RegExChar 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 RegExChar. If not, see <http://www.gnu.org/licenses/>. -flag threaded- Description: Link with the threaded runtime.- default: True+Name: regexchar+Version: 0.9.0.13+Cabal-version: >= 1.10+Copyright: (C) 2010-2015 Dr. Alistair Ward+License: GPL+License-file: LICENSE+Author: Dr. Alistair Ward+Stability: stable+Synopsis: A POSIX, extended regex-engine.+Build-type: Simple+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, GHC == 7.4, GHC == 7.6, GHC == 7.8, GHC == 7.10+Homepage: http://functionalley.eu/RegExChar/regExChar.html+Maintainer: mailto <colon> regexchar <at> functionalley <dot> eu+Bug-reports: mailto <colon> regexchar <at> functionalley <dot> eu --- Turn on using: 'runhaskell ./Setup.hs configure -f llvm'.+-- These files can be read by the application from 'Paths_weekdaze.datadir'.+Data-files:+ data/*.txt++-- None of these files are needed at run-time.+Extra-source-files:+ changelog.Debian+ changelog.markdown+ copyright+ debian/DEBIAN/control+ man/man1/grecce.1+ README.markdown+ regexchar.spec++-- Enable using: 'cabal configure -f llvm'. flag llvm Description: Whether the 'llvm' compiler-backend has been installed and is required for code-generation.- manual: True- default: False+ Manual: True+ Default: False +flag threaded+ Description: Link with the threaded runtime.+ Manual: True+ Default: True+ Library- hs-source-dirs: src+ Default-language: Haskell2010+ GHC-options: -Wall -O2 -fno-warn-tabs+ GHC-prof-options: -prof -auto-all -caf-all+ Hs-source-dirs: src-lib Exposed-modules: RegExChar.ExtendedRegExChar@@ -37,13 +67,13 @@ RegExChar.RegExOptsChar Build-depends:+ array, base == 4.*, containers, parsec == 3.*,+ regex-base, regexdot >= 0.11.1.0,- toolshed >= 0.14-- GHC-options: -Wall -fno-warn-tabs+ toolshed >= 0.16 if impl(ghc >= 7.4.1) GHC-prof-options: -prof -fprof-auto -fprof-cafs@@ -54,8 +84,13 @@ GHC-options: -fllvm Executable grecce- hs-source-dirs: src- Main-Is: Main.hs+ Default-language: Haskell2010+ GHC-options: -Wall -O2 -fno-warn-tabs+ GHC-prof-options: -prof -auto-all -caf-all+ Hs-source-dirs: src-exe+ Main-is: Main.hs++-- Required for 'cabal sdist'. Other-modules: Grecce.CommandOptions Grecce.Grep@@ -64,21 +99,16 @@ 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,- Cabal >= 1.6 && < 2,+ base == 4.*,+ Cabal >= 1.10, parallel >= 3.0,- QuickCheck >= 2.4,+ regexchar, regex-base,- regex-posix-- GHC-options: -Wall -O2 -fno-warn-tabs- GHC-prof-options: -prof -auto-all -caf-all+ regex-posix,+ regexdot >= 0.11.1.0,+ toolshed >= 0.16 if flag(threaded) GHC-options: -threaded@@ -88,4 +118,24 @@ if flag(llvm) GHC-options: -fllvm++Test-Suite quickCheck+ Default-language: Haskell2010+ GHC-options: -Wall -fno-warn-tabs+ Hs-source-dirs: src-test+ Main-is: Main.hs+ Type: exitcode-stdio-1.0++-- Required for 'cabal sdist'.+ Other-modules:+ Grecce.Test.QC.ExtendedRegExChar+ Grecce.Test.QC.MetaChar+ Grecce.Test.QC.RepeatableMetaChar++ Build-depends:+ base == 4.*,+ QuickCheck >= 2.4,+ regexchar,+ regexdot >= 0.11.1.0,+ toolshed >= 0.16
regexchar.spec view
@@ -1,4 +1,4 @@-# Copyright (C) 2012-2014 Dr. Alistair Ward+# Copyright (C) 2012-2015 Dr. Alistair Ward # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by@@ -15,57 +15,55 @@ %define package %name-%version %define tarBall %package.tar.gz-%define _bindir %prefix/bin-%define _libdir %prefix/lib/%package %define _sharedir %prefix/share-%define _datadir %_sharedir/%package-%define _docdir %_sharedir/doc/%package+%define _datadir %_sharedir/%name+%define _docdir %_sharedir/doc/%name %define _mandir %_sharedir/man Summary: A re-implementation of egrep, using a specialisation of a polymorphic regex-engine Name: regexchar-Version: 0.9.0.12+Version: 0.9.0.13 Release: 1 License: GPLv3-#From '/usr/share/doc/packages/rpm/GROUPS'.+# From '/usr/share/doc/packages/rpm/GROUPS'. Group: Applications/Text Source0: http://functionalley.eu/Downloads/sdist/%tarBall-URL: http://functionalley.eu+URL: http://functionalley.eu/RegExChar/regExChar.html Prefix: /usr-BuildRequires: ghc+BuildRequires: haskell-platform %description-Provides a POSIX extended regex-engine, specialised from an underlying polymorphic engine, to process merely character-lists, as is typically required.+Provides a POSIX extended regex-engine, specialised from an underlying polymorphic engine "regexdot", to process merely character-lists, as is typically required. %prep-#NB: we've changed directory to %_builddir-(cd $OLDPWD && runhaskell Setup sdist) && tar -zxf $OLDPWD/dist/%tarBall #Make a source-distribution & unpack it into the build-directory.-cd '%package/' && runhaskell Setup configure --user --prefix=%prefix #Configure to use the user's personal package-database & to generate an appropriate Paths module.+# NB: CWD has changed to %_builddir+echo 'package="%package", prefix="%prefix", _builddir="%_builddir", buildroot="%buildroot"'+(cd $OLDPWD && cabal sdist) && tar -zxf $OLDPWD/dist/%tarBall # Make a source-distribution & unpack it into the build-directory.+cd '%package/' && cabal configure --user --prefix='%prefix' --datadir='%_datadir' --datasubdir='' --docdir='%_docdir' # Tell cabal to use the user's personal package-database, to generate an appropriate "Paths" module, & where to place the documentation. %build-cd '%package/' && runhaskell Setup build #Descend into the source-distribution and build according to the previously established configuration.+cd '%package/' && cabal build # Descend into the source-distribution and build according to the previously established configuration. %install-cd '%package/' #Descend into the build-directory.-runhaskell Setup copy --destdir=%buildroot #Install the package in the target-directory.-rm -rf '%buildroot%_libdir/' #Remove unrequired libraries.-mkdir -p -- '%buildroot%_docdir' && mv changelog copyright LICENSE '%buildroot%_docdir/'+cd '%package/' # Descend into the build-directory.+cabal copy --destdir='%buildroot' # Install the built package in the target-directory.+mkdir -p -- '%buildroot%_docdir' && mv 'changelog.markdown' 'copyright' 'README.markdown' '%buildroot%_docdir/' # 'LICENSE' has already been copied by cabal. mkdir -p -- '%buildroot%_mandir' && mv 'man/man1' '%buildroot%_mandir/'+rm -rf -- '%buildroot%prefix/lib/' # The library isn't a deliverable. %clean-make --directory='%package' clobber-rm -rf -- '%_builddir/%package/' '%buildroot'+rm -rf -- '%_builddir/%package/' '%buildroot/' # Only the '.rpm' is required. %files-%attr(0755, root, root) %_bindir/grecce+%attr(0755, root, root) %prefix/bin/grecce %attr(0644, root, root) %_datadir/data/*.txt-%attr(0644, root, root) %doc %_docdir/changelog+%attr(0644, root, root) %doc %_docdir/changelog.markdown %attr(0644, root, root) %doc %_docdir/copyright %attr(0644, root, root) %doc %_docdir/LICENSE+%attr(0644, root, root) %doc %_docdir/README.markdown %attr(0644, root, root) %doc %_mandir/man1/grecce.1.gz %changelog * Wed May 09 2012 Alistair Ward <regexchar at functionalley dot eu> 0.9.0.10-1-Removed "rm -rf -- '%buildroot'" from build, for security.-Corrected "Paths" module, even though it's unused.+First cut.
+ src-exe/Grecce/CommandOptions.hs view
@@ -0,0 +1,172 @@+{-+ 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 options, read from the command-line,+ & as such assumes that the type-parameter of the underlying polymorphic /regex/-engine is 'Char'.++ * Supplies mutators, which ensure the consistency of the chosen set.+-}++module Grecce.CommandOptions(+-- * Types+-- ** Type-synonyms+ CompilationOptionsMutator,+ ExecutionOptionsMutator,+-- Transformer,+-- ** Data-types+ CommandOptions(..),+-- * Functions+-- ** Mutators+ setCompilationOption,+ setCountMatches,+ setExecutionOption,+ setInvertMatch,+ setPrependLineNumbers,+ setListFilesWithMatches,+ setListFilesWithoutMatch,+ setVerbose+) where++import qualified RegExChar.ExtendedRegExChar as ExtendedRegExChar+import qualified RegExDot.CompilationOptions+import qualified RegExDot.ExecutionOptions+import qualified ToolShed.Defaultable+import qualified ToolShed.Options++-- | The set of command-line options; based on those provided by /egrep/.+data CommandOptions = MkCommandOptions {+ countMatches :: Bool, -- ^ Print only a count of matching lines per file.+ extendedRegExChar :: Maybe ExtendedRegExChar.ExtendedRegExChar, -- ^ The /regex/.+ invertMatch :: Bool, -- ^ Select non-matching lines.+ prependLineNumbers :: Bool, -- ^ Prepend the line-number of the matched input-data, to the output.+ listFilesWithMatches :: Bool, -- ^ List the names of any files containing at least one match.+ listFilesWithoutMatch :: Bool, -- ^ List the names of any files containing zero matches.+ verbose :: Bool, -- ^ Show the captured data.+ compilationOptions :: RegExDot.CompilationOptions.CompilationOptions, -- ^ Used to define the regex-request.+ executionOptions :: RegExDot.ExecutionOptions.ExecutionOptions -- ^ Used to control the regex-runtime.+} deriving Show++-- | An arbitrary function which transforms the supplied 'RegExDot.ExecutionOptions.ExecutionOptions'.+type Transformer = CommandOptions -> CommandOptions++instance ToolShed.Defaultable.Defaultable CommandOptions where+ defaultValue = ToolShed.Options.blankValue {+ countMatches = False,+ extendedRegExChar = Nothing,+ invertMatch = False,+ prependLineNumbers = False,+ listFilesWithMatches = False,+ listFilesWithoutMatch = False,+ verbose = False,+ compilationOptions = ToolShed.Defaultable.defaultValue,+ executionOptions = ToolShed.Defaultable.defaultValue+ }++instance ToolShed.Options.Options CommandOptions where+ blankValue = MkCommandOptions {+ countMatches = undefined,+ extendedRegExChar = undefined,+ invertMatch = undefined,+ prependLineNumbers = undefined,+ listFilesWithMatches = undefined,+ listFilesWithoutMatch = undefined,+ verbose = undefined,+ compilationOptions = undefined,+ executionOptions = undefined+ }++-- | Mutator.+setCountMatches :: Transformer+setCountMatches commandOptions+ | ($ commandOptions) `any` [+ prependLineNumbers,+ listFilesWithoutMatch,+ listFilesWithMatches,+ verbose+ ] = error "Grecce.CommandOptions.setCountMatches:\t'countMatches' is incompatible with; 'prependLineNumbers', 'listFilesWithoutMatch', 'listFilesWithMatches' & 'verbose'."+ | otherwise = commandOptions { countMatches = True }++-- | Mutator.+setInvertMatch :: Transformer+setInvertMatch commandOptions+ | verbose commandOptions = error "Grecce.CommandOptions.setInvertMatch:\t'invertMatch' is incompatible with 'verbose'."+ | otherwise = commandOptions { invertMatch = True }++-- | Mutator.+setPrependLineNumbers :: Transformer+setPrependLineNumbers commandOptions+ | ($ commandOptions) `any` [+ countMatches,+ listFilesWithoutMatch,+ listFilesWithMatches+ ] = error "Grecce.CommandOptions.setPrependLineNumbers:\t'prependLineNumbers' is incompatible with 'countMatches', 'listFilesWithoutMatch' & 'listFilesWithMatches'."+ | otherwise = commandOptions { prependLineNumbers = True }++-- | Mutator.+setListFilesWithMatches :: Transformer+setListFilesWithMatches commandOptions+ | ($ commandOptions) `any` [+ countMatches,+ prependLineNumbers,+ listFilesWithoutMatch,+ verbose+ ] = error "Grecce.CommandOptions.setListFilesWithMatches:\t'listFilesWithMatches' is incompatible with; 'countMatches', 'prependLineNumbers', 'listFilesWithoutMatch' & 'verbose'."+ | otherwise = commandOptions { listFilesWithMatches = True }++-- | Mutator.+setListFilesWithoutMatch :: Transformer+setListFilesWithoutMatch commandOptions+ | ($ commandOptions) `any` [+ countMatches,+ prependLineNumbers,+ listFilesWithMatches,+ verbose+ ] = error "Grecce.CommandOptions.setListFilesWithoutMatch:\t'listFilesWithoutMatch' is incompatible with; 'countMatches', 'prependLineNumbers', 'listFilesWithMatches' & 'verbose'."+ | otherwise = commandOptions { listFilesWithoutMatch = True }++-- | Mutator, which additionally sets the corresponding 'RegExDot.ExecutionOptions.ExecutionOptions'.+setVerbose :: Transformer+setVerbose commandOptions+ | ($ commandOptions) `any` [+ countMatches,+ invertMatch,+ listFilesWithoutMatch,+ listFilesWithMatches+ ] = error "Grecce.CommandOptions.setVerbose:\t'verbose' is incompatible with; 'countMatches', 'invertMatch', 'listFilesWithoutMatch' & 'listFilesWithMatches'."+ | otherwise = commandOptions {+ verbose = True,+ executionOptions = RegExDot.ExecutionOptions.setVerbose True $ executionOptions commandOptions+ }++-- | A function which accepts a 'String' & the 'RegExDot.CompilationOptions.CompilationOptions' to transform, using its value.+type CompilationOptionsMutator = String -> RegExDot.CompilationOptions.CompilationOptions -> RegExDot.CompilationOptions.CompilationOptions++-- | Generic mutator, which accepts a function & the parameter-string it requires, to amend an unspecified field of 'RegExDot.CompilationOptions.CompilationOptions'.+setCompilationOption :: CompilationOptionsMutator -> String -> Transformer+setCompilationOption compilationOptionsMutator s commandOptions = commandOptions { compilationOptions = compilationOptionsMutator s $ compilationOptions commandOptions }++-- | A function which accepts a 'String' & the 'RegExDot.ExecutionOptions.ExecutionOptions' to transform, using its value.+type ExecutionOptionsMutator = String -> RegExDot.ExecutionOptions.ExecutionOptions -> RegExDot.ExecutionOptions.ExecutionOptions++-- | Generic mutator, which accepts a function & the parameter-string it requires, to amend an unspecified field of 'RegExDot.ExecutionOptions.ExecutionOptions'.+setExecutionOption :: ExecutionOptionsMutator -> String -> Transformer+setExecutionOption executionOptionsMutator s commandOptions = commandOptions { executionOptions = executionOptionsMutator s $ executionOptions commandOptions }+
+ src-exe/Grecce/Grep.hs view
@@ -0,0 +1,206 @@+{-+ Copyright (C) 2010-2015 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@] Performs the match-operation on the specified file-list, using the user's command-line options.+-}++module Grecce.Grep(+-- * Types+-- LineNumber,+-- Matches,+-- ** Type-synonyms+-- * Functions+ grep+) where++import Control.Arrow((&&&))+import qualified Control.Arrow+import qualified Control.Monad+import qualified Control.Parallel.Strategies+import qualified Data.List+import qualified Grecce.CommandOptions as CommandOptions+import qualified RegExChar.ExtendedRegExChar as ExtendedRegExChar+import RegExChar.ExtendedRegExChar((+~), (=~))+import qualified RegExDot.RegEx+import qualified RegExDot.RegExOpts+import qualified RegExDot.Result+import qualified System.Exit+import qualified System.IO++type LineNumber = Int+type Matches = Int++-- | Creates a 'RegExDot.RegExOpts.RegExOpts' record from 'CommandOptions.CommandOptions', and uses it to filter the lines read from the list of input-data files, the result of which is then printed.+grep+ :: CommandOptions.CommandOptions -- ^ The match-criteria, which may include the name of the file on which to operate.+ -> [String] -- ^ A supplementary list of files on which to operate+ -> IO () -- ^ Nothing is returned, since the result is printed.+grep commandOptions nonOptions+{-+ The algorithm depends fundamentally on whether the data comes from 'stdin' or from file.+ This is because of the requirement, in the former case, to deliver instantaneous feedback.+ This forces us to print matches as they're found, rather than batching them for return to the caller to print.+ If we COULD return 'IO [String]', then 'matchFilterStdIn' could be called instead of 'grep' when (fileName == "-"); annoyingly, it just doesn't work.+-}+ | any ($ fileNames) [null, all (== "-")] = let+ matchFilterStdIn :: LineNumber -> Matches -> IO System.Exit.ExitCode+ matchFilterStdIn lineNumber matches = {-#SCC "matchFilterStdIn" #-} do+ isEOF <- System.IO.isEOF++ if isEOF+ then if listFilesWithMatches+ then return {-to IO-monad-} $ System.Exit.ExitFailure 1 -- No match has been found, otherwise we'd have exited earlier.+ else if listFilesWithoutMatch+ then putStrLn dummyFileName >> return {-to IO-monad-} System.Exit.ExitSuccess+ else do+ Control.Monad.when countMatches $ print matches++ return {-to IO-monad-} $ if matches == 0+ then System.Exit.ExitFailure 1+ else System.Exit.ExitSuccess+ else {-not EOF-} do+ line <- getLine -- Process line-at-a-time, to provide instant feedback.++ let+ lineNumber' :: LineNumber+ lineNumber' = succ lineNumber++ onSuccess :: String -> IO System.Exit.ExitCode+ onSuccess line'+ | listFilesWithMatches = putStrLn dummyFileName >> return {-to IO-monad-} System.Exit.ExitSuccess+ | listFilesWithoutMatch = return {-to IO-monad-} $ System.Exit.ExitFailure 1+ | otherwise = do+ Control.Monad.unless countMatches . putStrLn $ (+ if prependLineNumbers+ then showLineNumber lineNumber'+ else id+ ) line'++ matchFilterStdIn lineNumber' $ succ matches -- Recurse.++ onFailure, onMismatch :: IO System.Exit.ExitCode+ onFailure = matchFilterStdIn lineNumber' matches -- Recurse+ onMismatch+ | invertMatch = onSuccess line+ | otherwise = onFailure++ if verbose+ then let+ result :: RegExDot.RegEx.Result Char+ result = line +~ regExOpts+ in if RegExDot.Result.isMatch result+ then if invertMatch+ then onFailure+ else onSuccess $ show result+ else onMismatch+ else {-terse-} if line =~ regExOpts+ then if invertMatch+ then onFailure+ else onSuccess line+ else onMismatch+ where+ dummyFileName :: String+ dummyFileName = "<standard input>"+ in matchFilterStdIn 0 0 >>= System.Exit.exitWith+ | otherwise {-file-names provided-} = let+ findMatch :: String -> RegExDot.RegEx.Result Char+ findMatch = (+~ regExOpts)++ matchFilter :: [String] -> [String]+ matchFilter = {-#SCC "matchFilter" #-} (+ if countMatches+ then return {-to List-monad-} . show . length -- Substitute the list of matches, with its length.+ else id+ ) . (+ let+ isMatch :: String -> Bool+ isMatch = {-#SCC "isMatch" #-} (if invertMatch then not else id) . (=~ regExOpts)+ in if prependLineNumbers+ then map (uncurry showLineNumber) . (+ if verbose+ then map (+ Control.Arrow.second show+ ) . filter (+ RegExDot.Result.isMatch . snd+ ) . map (+ Control.Arrow.second findMatch+ )+ else {-terse-} filter (isMatch . snd)+ ) . zip [1 :: LineNumber ..]+ else {-unnumbered-} if verbose+ then map show . filter RegExDot.Result.isMatch . map findMatch+ else {-terse-} filter isMatch+ )++ identifySource :: String -> [String] -> [String]+ identifySource fileName results+ | listFilesWithMatches = [fileName | not isEmpty] {-list-comprehension-}+ | listFilesWithoutMatch = [fileName | isEmpty] {-list-comprehension-}+ | length (Data.List.nub fileNames) > 1 = (showString fileName . separate) `map` results+ | otherwise = results+ where+ isEmpty :: Bool+ isEmpty = null results+ in do+ results <- (+ concat . Control.Parallel.Strategies.parMap Control.Parallel.Strategies.rdeepseq (+ uncurry identifySource . Control.Arrow.second (matchFilter . {-#SCC "lines" #-} lines {-chops '\n'-})+ ) . zip fileNames+ ) `fmap` mapM readFile fileNames++ mapM_ putStrLn results++ System.Exit.exitWith $ if null results+ then System.Exit.ExitFailure 1+ else System.Exit.ExitSuccess+ where+ countMatches, invertMatch, prependLineNumbers, listFilesWithMatches, listFilesWithoutMatch, verbose :: Bool+ [countMatches, invertMatch, prependLineNumbers, listFilesWithMatches, listFilesWithoutMatch, verbose] = ($ commandOptions) `map` [+ CommandOptions.countMatches,+ CommandOptions.invertMatch,+ CommandOptions.prependLineNumbers,+ CommandOptions.listFilesWithMatches,+ CommandOptions.listFilesWithoutMatch,+ CommandOptions.verbose+ ] -- Divvy-up.++-- Separate the regEx & the names of the input-data files to feed it.+ extendedRegExChar :: ExtendedRegExChar.ExtendedRegExChar+ fileNames :: [String]+ (extendedRegExChar, fileNames) = case CommandOptions.extendedRegExChar commandOptions of+ Just c -> (c, nonOptions)+ _+ | null nonOptions -> error "Undefined regex."+ | otherwise -> read . head &&& tail $ nonOptions++-- Combine the regex & options, to create a complete task-description.+ regExOpts :: RegExDot.RegExOpts.RegExOpts ExtendedRegExChar.ExtendedRegExChar+ regExOpts = RegExDot.RegExOpts.MkRegExOpts {+ RegExDot.RegExOpts.compilationOptions = CommandOptions.compilationOptions commandOptions,+ RegExDot.RegExOpts.executionOptions = CommandOptions.executionOptions commandOptions,+ RegExDot.RegExOpts.regEx = extendedRegExChar+ }++-- Define functions used to format the output.+ separate :: ShowS+ separate = showChar ':'++ showLineNumber :: LineNumber -> ShowS+ showLineNumber n = shows n . separate+
+ src-exe/Grecce/Test/Assert/RegExOptsChar.hs view
@@ -0,0 +1,93 @@+{-# LANGUAGE FlexibleContexts #-}+{-+ Copyright (C) 2010-2015 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@]++ * 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 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+import qualified RegExDot.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 :: RegExDot.CompilationOptions.CompilationOptions -> RegExDot.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 parse " ++ show fileName ++ "; " ++ show line ++ "."+ ) . filter (+ (/= '#') . head -- Remove comments.+ ) . filter (+ not . null -- Remove blank lines.+ ) . lines+ ) `fmap` readFile fileName+
+ src-exe/Grecce/Test/Performance/ExtendedRegEx.hs view
@@ -0,0 +1,148 @@+{-# LANGUAGE CPP #-}+{-+ Copyright (C) 2010-2015 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@]++ * 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 'RegExDot.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 qualified Control.Arrow+import 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+import qualified RegExDot.RegEx+import qualified RegExDot.RegExOpts+import qualified Text.Regex.Posix+import qualified ToolShed.Defaultable+import qualified ToolShed.System.TimePure++#if !MIN_VERSION_base(4,8,0)+import Control.Applicative((<$>), (<*>))+#endif++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 :: RegExDot.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 (+ ToolShed.System.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 -> (+ RegExDot.RegEx./~ RegExDot.RegExOpts.MkRegExOpts { -- CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.+ RegExDot.RegExOpts.compilationOptions = ToolShed.Defaultable.defaultValue,+ RegExDot.RegExOpts.executionOptions = executionOptions,+ RegExDot.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 <$> (+ (ToolShed.System.TimePure.getCPUSeconds . ($ inputData)) `mapM` [+ showMatch,+ if RegExDot.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 :: RegExDot.RegExOpts.RegExOpts (RegExDot.RegEx.ExtendedRegEx Char)+ regExOpts = RegExDot.RegExOpts.MkRegExOpts {+ RegExDot.RegExOpts.compilationOptions = ToolShed.Defaultable.defaultValue,+ RegExDot.RegExOpts.executionOptions = executionOptions,+ RegExDot.RegExOpts.regEx = extendedRegEx+ } -- Similar to 'RegexLike.makeRegexOpts', but without the requirement to read the regex.+ in if RegExDot.ExecutionOptions.requireMatchList executionOptions+ then show . (RegExDot.RegEx.+~ regExOpts) -- CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.+ else show . (RegExDot.RegEx.=~ regExOpts) -- CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.+ ) &&& show . ExtendedRegExChar.MkExtendedRegExChar False {-hasNonCapturingTopLevelAlternatives-}+ ) . testGenerator+
+ src-exe/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++-- | 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 = (RegExDot.RegEx.InputData a, RegExDot.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-exe/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-exe/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+import qualified RegExDot.BracketExpressionMember+import RegExDot.DSL((-:), (?:), (*:), (<~>))+import qualified RegExDot.Meta+import qualified RegExDot.RegEx+import RegExDot.RegEx((.*), (.*?))+import qualified RegExDot.Repeatable+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 -> (RegExDot.RegEx.Pattern Char -> RegExDot.RegEx.RepeatablePattern Char) -> RegExDot.RegEx.RepeatablePattern Char+ exactly n f = (RegExDot.RegEx.captureGroup . map (RegExDot.Anchor.unanchored <~>) . return {-to List-monad-} . return {-to List-monad-} $ f testRequirement) ^# n+ in ((Just RegExDot.Anchor.Bow, Just RegExDot.Anchor.Stern) <~>) . (`map` [RegExDot.Repeatable.zeroOrOne, RegExDot.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")) &&& RegExDot.RegEx.dock . (+ `mkExtendedRegExFromRepeatableSingletonAlternative` (RegExDot.Anchor.unanchored <~>) [+ (.*),+ RegExDot.RegEx.simply $ RegExDot.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 RegExDot.Anchor.Bow, Just RegExDot.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 (+ RegExDot.Anchor.unanchored <~> RegExDot.RegEx.captureGroup (+ map (+ (RegExDot.Anchor.unanchored <~>) . return {-to List-monad-} . (testRequirement ^#) . (2 ^)+ ) [0 :: Int .. 1]+ ) *: RegExDot.RegEx.Require (RegExDot.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 RegExDot.Anchor.Bow, Just RegExDot.Anchor.Stern) <~>) . return {-to List-monad-} . RegExDot.Repeatable.zeroOrMore . RegExDot.RegEx.captureGroup . map (RegExDot.Anchor.unanchored <~>) $ [RegExDot.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 RegExDot.Anchor.Bow, Just RegExDot.Anchor.Stern) <~>) . replicate i . RegExDot.Repeatable.one . RegExDot.RegEx.captureGroup . map (RegExDot.Anchor.unanchored <~>) $ [RegExDot.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 :: RegExDot.Repeatable.Repetitions+ base = 2 -- Arbitrarily.++ bounds :: RegExDot.Repeatable.RepetitionBounds+ bounds = (0, Just base)+ in (`replicate` testChar) . (base ^) &&& RegExDot.RegEx.dock . (+ (+ mkExtendedRegExFromRepeatableSingletonAlternative (^#-># bounds) `iterate` (RegExDot.Anchor.unanchored <~>) [testRequirement ^#-># bounds] {-initial value-}+ ) !!+ ) . pred+ ),+{-+ "^((((((((((((((<testChar>*?)*?)*?)*?)*?)*?)*?)*?)*?)*?)*?)*?)*?)*?)*? ... $".+ Explanation: interesting because all 'RegExDot.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) &&& RegExDot.RegEx.dock . (+ (+ mkExtendedRegExFromRepeatableSingletonAlternative RegExDot.Repeatable.zeroOrMore' `iterate` (RegExDot.Anchor.unanchored <~>) [RegExDot.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 RegExDot.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 :: RegExDot.Repeatable.Repetitions+ base = 2 -- Arbitrarily.+ in (`replicate` testChar) . (base ^) &&& RegExDot.RegEx.dock . head . (+ (+ iterate (+ \extendedRegExList -> (+ (RegExDot.Anchor.unanchored <~>) . return {-to List-monad-} . (^# base) . RegExDot.RegEx.captureGroup+ ) `map` [extendedRegExList, reverse extendedRegExList]+ ) $ map (+ (RegExDot.Anchor.unanchored <~>) . return {-to List-monad-} . ($ RegExDot.RegEx.Require $ RegExDot.Meta.Literal testChar)+ ) [RegExDot.Repeatable.one, RegExDot.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 RegExDot.Anchor.Bow, Just RegExDot.Anchor.Stern) <~>) $ map RegExDot.Repeatable.zeroOrMore [+ RegExDot.RegEx.captureGroup . map (RegExDot.Anchor.unanchored <~>) $ (+ \i -> ($ i) `map` [+ (testRequirement ^#),+ RegExDot.RegEx.simply . RegExDot.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 :: [RegExDot.RegEx.Concatenation Char] -> RegExDot.RegEx.Concatenation Char+ zeroOrMoreAlternatives = return {-to List-monad-} . RegExDot.Repeatable.zeroOrMore . RegExDot.RegEx.captureGroup . map (RegExDot.Anchor.unanchored <~>)+ in (+ id &&& (+ ((Just RegExDot.Anchor.Bow, Just RegExDot.Anchor.Stern) <~>) . (RegExDot.RegEx.Require (RegExDot.Meta.Literal testChar) ?:) . zeroOrMoreAlternatives . map (+ \(a, b) -> zeroOrMoreAlternatives $ return {-to List-monad-} [a, b]+ ) . init . tail . uncurry zip . (+ map RegExDot.Repeatable.one . init &&& map RegExDot.Repeatable.zeroOrOne . tail+ ) . bracket undefined . map (+ RegExDot.RegEx.Require . RegExDot.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' = succ complexity++ testSet :: String+ testSet = take complexity' . filter Data.Char.isAlphaNum $ filter Data.Char.isAscii [minBound .. maxBound]+ in (+ concat $ replicate complexity' testSet,+ ((Just RegExDot.Anchor.Bow, Just RegExDot.Anchor.Stern) <~>) $ (+ RegExDot.RegEx.captureGroup . map (RegExDot.Anchor.unanchored <~>) $ map (+ return {-to List-monad-} . RegExDot.RegEx.simply . RegExDot.Meta.Literal+ ) testSet+ ) *: [+ (+ RegExDot.RegEx.captureGroup . map (RegExDot.Anchor.unanchored <~>) . return {-to List-monad-} $ RegExDot.RegEx.Require (RegExDot.Meta.Literal $ head testSet) -: (.*?) : RegExDot.RegEx.Require (RegExDot.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 RegExDot.Anchor.Bow, Just RegExDot.Anchor.Stern) <~>) . return {-to List-monad-} . RegExDot.Repeatable.zeroOrMore . RegExDot.RegEx.captureGroup . map (RegExDot.Anchor.unanchored <~>) . (+ [+ RegExDot.Repeatable.zeroOrMore testRequirement,+ RegExDot.RegEx.simply . RegExDot.Meta.NoneOf $ map RegExDot.BracketExpressionMember.Literal [testChar]+ ] :+ ) . map (+ return {-to List-monad-} . RegExDot.RegEx.simply . RegExDot.Meta.NoneOf . return {-to List-monad-} . RegExDot.BracketExpressionMember.Literal+ ) . (+ `take` tail [testChar ..]+ )+ )+ )+ ]+ ) where+ testChar :: Char+ testChar = 'a'++ testRequirement :: RegExDot.RegEx.Pattern Char+ testRequirement = RegExDot.RegEx.Require $ RegExDot.Meta.Literal testChar++ mkExtendedRegExFromRepeatableSingletonAlternative :: (RegExDot.RegEx.Pattern Char -> RegExDot.RegEx.RepeatablePattern Char) -> RegExDot.RegEx.ExtendedRegEx Char -> RegExDot.RegEx.ExtendedRegEx Char+ mkExtendedRegExFromRepeatableSingletonAlternative makeRepetitions = (RegExDot.Anchor.unanchored <~>) . return {-to List-monad-} . makeRepetitions . RegExDot.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 * succ i `div` 2
+ src-exe/Main.hs view
@@ -0,0 +1,208 @@+{-+ Copyright (C) 2010-2015 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@]++ * Contains the entry-point to the program.++ * Provides command-line access to "ExtendedRegExChar".++ * Defines the permissible command-line arguments; aping /egrep/, but with the addition of /--verbose/, which enables one to see the details of the data-capture.++ [@TODO@] Coordinate the output of 'printVersion' with the definition in '.cabal'; this may not be feasible.+-}++module Main(main) where++import qualified Data.List+import qualified Data.Version+import qualified Distribution.Package+import qualified Distribution.Text+import qualified Distribution.Version+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 Paths_regexchar as Paths -- Either local stub, or package-instance autogenerated by 'Setup.hs build'.+import qualified RegExDot.CompilationOptions+import qualified RegExDot.ExecutionOptions+import qualified System.Console.GetOpt as G+import qualified System.Environment+import qualified System.Exit+import qualified System.Info+import qualified System.IO+import qualified System.IO.Error+import qualified ToolShed.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'.++-- | On failure to parse the specified string, returns an explanatory error.+read' :: Read a => String -> String -> a+read' errorMessage s = case reads s of+ [(x, "")] -> x+ _ -> error $ errorMessage ++ show s++-- | On failure to parse a command-line argument, returns an explanatory error.+readCommandArg :: Read a => String -> a+readCommandArg = read' "failed to parse command-line argument "++{- |+ * Parses the command-line arguments, to determine 'CommandOptions.CommandOptions'.++ * Arguments following 'CommandOptions.CommandOptions', are interpreted as a /regex/, followed by the names of the files to which it is applied.+-}+main :: IO ()+main = do+ progName <- System.Environment.getProgName++ let+ usageMessage :: String+ usageMessage = "Usage:\t" ++ G.usageInfo progName optDescrList ++ " <regex> [<file-name>, ...]"++-- Define the command-line options, & the 'CommandLineAction's used to handle them.+ optDescrList :: [G.OptDescr CommandLineAction]+ optDescrList = [+-- String [String] (G.ArgDescr CommandLineAction) String+ G.Option "?" ["help"] (G.NoArg $ const printUsage) "Output:\tdisplay this help-text & then exit.",+ G.Option "" ["verbose"] (G.NoArg $ return {-to IO-monad-} . CommandOptions.setVerbose) ("Output:\tsee how the input data was captured by the RegEx; default '" ++ show (CommandOptions.verbose ToolShed.Defaultable.defaultValue) ++ "'. CAVEAT: to be effective, it should precede other options."),+ G.Option "" ["version"] (G.NoArg $ const printVersion) "Output:\tprint version-information & then exit.",+ G.Option "c" ["count"] (G.NoArg $ return {-to IO-monad-} . CommandOptions.setCountMatches) ("Output:\tprint only a count of matching lines per file; default '" ++ show (CommandOptions.countMatches ToolShed.Defaultable.defaultValue) ++ "'."),+ G.Option "l" ["files-with-matches"] (G.NoArg $ return {-to IO-monad-} . CommandOptions.setListFilesWithMatches) ("Output:\tlist names of files containing at least one match; default '" ++ show (CommandOptions.listFilesWithMatches ToolShed.Defaultable.defaultValue) ++ "'."),+ G.Option "L" ["files-without-match"] (G.NoArg $ return {-to IO-monad-} . CommandOptions.setListFilesWithoutMatch) ("Output:\tlist names of files not containing any matches; default '" ++ show (CommandOptions.listFilesWithoutMatch ToolShed.Defaultable.defaultValue) ++ "'."),+ G.Option "n" ["number"] (G.NoArg $ return {-to IO-monad-} . CommandOptions.setPrependLineNumbers) ("Output:\tprepend the line-number of the input, to the output; default '" ++ show (CommandOptions.prependLineNumbers ToolShed.Defaultable.defaultValue) ++ "'."),+ G.Option "v" ["invert-match"] (G.NoArg $ return {-to IO-monad-} . CommandOptions.setInvertMatch) ("Output:\tselect non-matching lines; default '" ++ show (CommandOptions.invertMatch ToolShed.Defaultable.defaultValue) ++ "'."),+ G.Option "" ["printCurrentSettings"] (G.NoArg printCurrentSettings) "Output:\tdisplay the currently defined options & then exit.",+ G.Option "e" ["regex"] (setExtendedRegExChar `G.ReqArg` "<regex>") "Input:\tdefine the regex.",+ G.Option "f" ["file"] (readExtendedRegExCharFromFile `G.ReqArg` "<file-name>") "Input:\tread the regex from file.",+ G.Option "" ["performance"] (+ extendedRegExCharPerformance `G.ReqArg` "(Int,Either [TestName])"+ ) (+ "Test:\tcompare performance with other RegEx-engines, using the specified pair (<Maximum complexity>, Left " ++ show [+ minBound :: Test.Performance.ExtendedRegExTestsNegative.TestName .. maxBound+ ] ++ " or Right " ++ show [+ minBound :: Test.Performance.ExtendedRegExTestsPositive.TestName .. maxBound+ ] ++ "). NB: specify this option last."+ ),+ G.Option "" ["assert"] (assertFromFile `G.ReqArg` "<file-name>") "Test:\trun the assertions, defined in the specified file, & then exit. NB: specify this option last.",+ G.Option "" ["assertPosix"] (assertPosixFromFile `G.ReqArg` "<file-name>") "Test:\trun the assertions, defined in the specified file, against Text.Regex.Posix, & then exit. NB: specify this option last.",+ G.Option "" ["abortTrialRepetitionsOnInherentFailure"] (setExecutionOption abortTrialRepetitionsOnInherentFailure `G.ReqArg` "Bool") ("Performance:\tif an alternative can't match, irrespective of the subsequent concatenation, then terminate futile trial repetitions; default '" ++ show (RegExDot.ExecutionOptions.abortTrialRepetitionsOnInherentFailure ToolShed.Defaultable.defaultValue) ++ "'."),+-- G.Option "" ["abortTrialRepetitionsOnZeroConsumption"] (setExecutionOption abortTrialRepetitionsOnZeroConsumption `G.ReqArg` "Bool") ("Performance:\tcheck for zero data-consumption by the n-th repetition of an alternative, before considering (n + 1); default '" ++ show (RegExDot.ExecutionOptions.abortTrialRepetitionsOnZeroConsumption ToolShed.Defaultable.defaultValue) ++ "'."),+-- G.Option "" ["bypassInputDataForLiberalConsumer"] (setExecutionOption bypassInputDataForLiberalConsumer `G.ReqArg` "Bool") ("Performance:\tbypass reading of the unconsumed input data, if the remaining RegEx can consume a sufficient quantity of anything; default '" ++ show (RegExDot.ExecutionOptions.bypassInputDataForLiberalConsumer ToolShed.Defaultable.defaultValue) ++ "'."),+ G.Option "" ["catchIncompatibleAnchors"] (setExecutionOption catchIncompatibleAnchors `G.ReqArg` "Bool") ("Performance:\tavoid futile trial solutions, involving repetitions of anchored alternatives, which must consume data; default '" ++ show (RegExDot.ExecutionOptions.catchIncompatibleAnchors ToolShed.Defaultable.defaultValue) ++ "'."),+ G.Option "" ["checkExistenceOfInelasticTail"] (setExecutionOption checkExistenceOfInelasticTail `G.ReqArg` "Bool") ("Performance:\tif the regex ends in one or more Meta-data, whose repetition-range is precisely defined, check that this sequence exists at the end of the input data; default '" ++ show (RegExDot.ExecutionOptions.checkExistenceOfInelasticTail ToolShed.Defaultable.defaultValue) ++ "'."),+ G.Option "" ["checkForUnconsumableData"] (setExecutionOption checkForUnconsumableData `G.ReqArg` "Bool") ("Performance:\tcheck whether there's no possibility of consuming some of the input data; default '" ++ show (RegExDot.ExecutionOptions.checkForUnconsumableData ToolShed.Defaultable.defaultValue) ++ "'."),+ G.Option "" ["moderateGreed"] (setExecutionOption moderateGreed `G.ReqArg` "Bool") ("Performance:\tgreedily consume data, only up to the limit beyond which, future requirements would be compromised; default '" ++ show (RegExDot.ExecutionOptions.moderateGreed ToolShed.Defaultable.defaultValue) ++ "'."),+ G.Option "" ["permitReorderingOfAlternatives"] (setExecutionOption permitReorderingOfAlternatives `G.ReqArg` "Bool") ("Performance:\tpermit alternatives to be re-ordered, in an attempt to process the cheapest first; default '" ++ show (RegExDot.ExecutionOptions.permitReorderingOfAlternatives ToolShed.Defaultable.defaultValue) ++ "'."),+ G.Option "" ["preferAlternativesWhichFeedTheGreedy"] (setExecutionOption preferAlternativesWhichFeedTheGreedy `G.ReqArg` "Bool") ("Preference:\tprefer solutions in which the choice of alternatives directs data from non-greedy to greedy consumers; default '" ++ show (RegExDot.ExecutionOptions.preferAlternativesWhichFeedTheGreedy ToolShed.Defaultable.defaultValue) ++ "'."),+ G.Option "" ["preferAlternativesWhichMimickUnrolling"] (setExecutionOption preferAlternativesWhichMimickUnrolling `G.ReqArg` "Bool") ("Preference:\tprefer solutions in which the choice of alternatives consumes data like the unrolled repeatable group; default '" ++ show (RegExDot.ExecutionOptions.preferAlternativesWhichMimickUnrolling ToolShed.Defaultable.defaultValue) ++ "'."),+ G.Option "" ["preferFewerRepeatedAlternatives"] (setExecutionOption preferFewerRepeatedAlternatives `G.ReqArg` "Bool") ("Preference:\tprefer solutions employing fewer repetitions of alternatives, to discourage the capture of null lists; default '" ++ show (RegExDot.ExecutionOptions.preferFewerRepeatedAlternatives ToolShed.Defaultable.defaultValue) ++ "'."),+ G.Option "" ["unrollRepeatedSingletonAlternative"] (setExecutionOption unrollRepeatedSingletonAlternative `G.ReqArg` "Bool") ("Performance:\tunroll repetitions of singleton alternatives; this doesn't affect the result; default '" ++ show (RegExDot.ExecutionOptions.unrollRepeatedSingletonAlternative ToolShed.Defaultable.defaultValue) ++ "'."),+ G.Option "" ["useFirstMatchAmongAlternatives"] (setExecutionOption useFirstMatchAmongAlternatives `G.ReqArg` "Bool") ("Performance:\trather than performing an exhaustive search for the optimal choice amongst alternatives, merely select the first that matches; conform to Perl rather than Posix; default '" ++ show (RegExDot.ExecutionOptions.useFirstMatchAmongAlternatives ToolShed.Defaultable.defaultValue) ++ "'."),+ G.Option "" ["validateMinConsumptionOfAlternatives"] (setExecutionOption validateMinConsumptionOfAlternatives `G.ReqArg` "Bool") ("Performance:\twhen the number of repetitions of a group of alternatives is precisely specified, check the availability of the resulting minimum data-requirement; default '" ++ show (RegExDot.ExecutionOptions.validateMinConsumptionOfAlternatives ToolShed.Defaultable.defaultValue) ++ "'."),+ G.Option "" ["complyStrictlyWithPosix"] (setCompilationOption complyStrictlyWithPosix `G.ReqArg` "Bool") ("Output:\tdefine the offset of captured data, corresponding to a sub-expression which matched zero times, as the artificial value -1 specified by Posix; currently only affects results of 'assert'; default '" ++ show (RegExDot.CompilationOptions.complyStrictlyWithPosix ToolShed.Defaultable.defaultValue) ++ "'.")+ ] where+ printVersion, printUsage :: IO CommandOptions.CommandOptions++ printVersion = System.IO.hPutStrLn System.IO.stderr (+ Distribution.Text.display packageIdentifier ++ "\n\nCompiled by " ++ show compiler ++ ".\n\nCopyright (C) 2010-2015 " ++ author ++ ".\nThis program comes with ABSOLUTELY NO WARRANTY.\nThis is free software, and you are welcome to redistribute it under certain conditions.\n\nWritten by " ++ author ++ "."+ ) >> System.Exit.exitSuccess where+ packageIdentifier :: Distribution.Package.PackageIdentifier+ packageIdentifier = Distribution.Package.PackageIdentifier {+ Distribution.Package.pkgName = Distribution.Package.PackageName "grecce",+ Distribution.Package.pkgVersion = Distribution.Version.Version (Data.Version.versionBranch Paths.version) []+ }++ author, compiler :: String+ author = "Dr. Alistair Ward"+ compiler = System.Info.compilerName ++ "-" ++ Data.List.intercalate "." (map show $ Data.Version.versionBranch System.Info.compilerVersion)++ printUsage = System.IO.hPutStrLn System.IO.stderr usageMessage >> System.Exit.exitSuccess++ printCurrentSettings :: CommandLineAction+ printCurrentSettings commandOptions = print commandOptions >> System.Exit.exitSuccess++ extendedRegExCharPerformance, readExtendedRegExCharFromFile, assertFromFile, assertPosixFromFile, setExtendedRegExChar :: String -> CommandLineAction+ extendedRegExCharPerformance s commandOptions = (+ uncurry $ Test.Performance.ExtendedRegEx.run (CommandOptions.executionOptions commandOptions)+ ) (readCommandArg s) >> System.Exit.exitSuccess++ readExtendedRegExCharFromFile s commandOptions = ((head . dropWhile null . lines) `fmap` readFile s) >>= (`setExtendedRegExChar` commandOptions)++ assertFromFile fileName commandOptions = do+ failedTests <- Test.Assert.RegExOptsChar.findFailures (CommandOptions.compilationOptions commandOptions) (CommandOptions.executionOptions commandOptions) `fmap` Test.Assert.RegExOptsChar.readTests fileName++ if null failedTests+ then System.Exit.exitSuccess+ else do+ mapM_ print failedTests++ System.Exit.exitWith $ System.Exit.ExitFailure 2++ assertPosixFromFile fileName _ = do+ failedTests <- Test.Assert.RegExOptsChar.findFailuresPosix `fmap` Test.Assert.RegExOptsChar.readTests fileName++ if null failedTests+ then System.Exit.exitSuccess+ else do+ mapM_ print failedTests++ System.Exit.exitWith $ System.Exit.ExitFailure 2++ setExtendedRegExChar s commandOptions = case CommandOptions.extendedRegExChar commandOptions of+ Just extendedRegExChar -> fail $ "The regex has already been defined as /" ++ show extendedRegExChar ++ "/"+ _ -> return {-to IO-monad-} commandOptions { CommandOptions.extendedRegExChar = Just $ readCommandArg s }++ abortTrialRepetitionsOnInherentFailure, {-abortTrialRepetitionsOnZeroConsumption, bypassInputDataForLiberalConsumer,-} catchIncompatibleAnchors, checkExistenceOfInelasticTail, checkForUnconsumableData, moderateGreed, permitReorderingOfAlternatives, preferAlternativesWhichFeedTheGreedy, preferAlternativesWhichMimickUnrolling, preferFewerRepeatedAlternatives, unrollRepeatedSingletonAlternative, useFirstMatchAmongAlternatives, validateMinConsumptionOfAlternatives :: CommandOptions.ExecutionOptionsMutator+ abortTrialRepetitionsOnInherentFailure s e = e { RegExDot.ExecutionOptions.abortTrialRepetitionsOnInherentFailure = readCommandArg s }+-- abortTrialRepetitionsOnZeroConsumption s e = e { RegExDot.ExecutionOptions.abortTrialRepetitionsOnZeroConsumption = readCommandArg s }+-- bypassInputDataForLiberalConsumer s e = e { RegExDot.ExecutionOptions.bypassInputDataForLiberalConsumer = readCommandArg s }+ catchIncompatibleAnchors s e = e { RegExDot.ExecutionOptions.catchIncompatibleAnchors = readCommandArg s }+ checkExistenceOfInelasticTail s e = e { RegExDot.ExecutionOptions.checkExistenceOfInelasticTail = readCommandArg s }+ checkForUnconsumableData s e = e { RegExDot.ExecutionOptions.checkForUnconsumableData = readCommandArg s }+ moderateGreed s e = e { RegExDot.ExecutionOptions.moderateGreed = readCommandArg s }+ permitReorderingOfAlternatives s e = e { RegExDot.ExecutionOptions.permitReorderingOfAlternatives = readCommandArg s }+ preferAlternativesWhichFeedTheGreedy s e = e { RegExDot.ExecutionOptions.preferAlternativesWhichFeedTheGreedy = readCommandArg s }+ preferAlternativesWhichMimickUnrolling s e = e { RegExDot.ExecutionOptions.preferAlternativesWhichMimickUnrolling = readCommandArg s }+ preferFewerRepeatedAlternatives s e = e { RegExDot.ExecutionOptions.preferFewerRepeatedAlternatives = readCommandArg s }+ unrollRepeatedSingletonAlternative s e = e { RegExDot.ExecutionOptions.unrollRepeatedSingletonAlternative = readCommandArg s }+ useFirstMatchAmongAlternatives s e = e { RegExDot.ExecutionOptions.useFirstMatchAmongAlternatives = readCommandArg s }+ validateMinConsumptionOfAlternatives s e = e { RegExDot.ExecutionOptions.validateMinConsumptionOfAlternatives = readCommandArg s }++ complyStrictlyWithPosix :: CommandOptions.CompilationOptionsMutator+ complyStrictlyWithPosix s e = e { RegExDot.CompilationOptions.complyStrictlyWithPosix = readCommandArg s }++ setCompilationOption :: CommandOptions.CompilationOptionsMutator -> String -> CommandLineAction+ setCompilationOption compilationOptionsMutator s = return {-to IO-monad-} . CommandOptions.setCompilationOption compilationOptionsMutator s++ setExecutionOption :: CommandOptions.ExecutionOptionsMutator -> String -> CommandLineAction+ setExecutionOption executionOptionsMutator s = return {-to IO-monad-} . CommandOptions.setExecutionOption executionOptionsMutator s++ args <- System.Environment.getArgs++-- G.getOpt :: G.ArgOrder CommandLineAction -> [G.OptDescr Action] -> [String] -> ([Action], [String], [String])+ case G.getOpt G.RequireOrder optDescrList args of+ (commandLineActions, nonOptions, []) -> do+ commandOptions <- Data.List.foldl' (>>=) (return {-to IO-monad-} ToolShed.Defaultable.defaultValue) commandLineActions -- Sequentially transform the 'CommandOptions', using 'CommandLineAction's corresponding to the specified command-line flags.++ Grep.grep commandOptions nonOptions -- Perform the requested match, against the list of input-data files.+ (_, _, errors) -> System.IO.Error.ioError . System.IO.Error.userError $ concat errors ++ usageMessage -- Throw.+
+ src-lib/RegExChar/ExtendedRegExChar.hs view
@@ -0,0 +1,210 @@+{-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-+ Copyright (C) 2010-2015 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@] An 'RegExDot.RegEx.ExtendedRegEx', which has been specialised for 'Char', to create a tradition non-polymorphic /regex/.+-}++module RegExChar.ExtendedRegExChar(+-- * Types+-- ** Type-synonyms+ ExtendedRegExChar(..),+ InputData,+-- * Functions+-- ** Operators+ (+~),+ (=~),+ (/~)+) where++import qualified Data.List+import qualified RegExChar.MetaChar as MetaChar+import qualified RegExDot.Anchor+import qualified RegExDot.Consumer+import RegExDot.DSL((<~>), (-:))+import qualified RegExDot.RegEx+import qualified RegExDot.RegExOpts+import qualified RegExDot.Repeatable+import qualified Text.ParserCombinators.Parsec as Parsec+import Text.ParserCombinators.Parsec((<?>), (<|>))+import qualified ToolShed.Data.Pair+import qualified ToolShed.SelfValidate++#if !MIN_VERSION_base(4,8,0)+import Control.Applicative((<$>), (<*>))+#endif++infix 4 +~, =~, /~ -- Same as (==) & (/=).++-- | Specialise a 'RegExDot.RegEx.ExtendedRegEx' for 'Char', & encapsulate it to permit tailored instance-declarations.+data ExtendedRegExChar = MkExtendedRegExChar {+ hasNonCapturingTopLevelAlternatives :: Bool, -- ^ The string from which a 'RegExDot.RegEx.ExtendedRegEx' is read, may, if data-capture isn't required, omit explicit delimiters around top-level 'RegExDot.RegEx.Alternatives'.+ extendedRegEx :: RegExDot.RegEx.ExtendedRegEx Char+} deriving Eq++-- | Abbreviation.+type InputData = RegExDot.RegEx.InputData Char++instance ToolShed.SelfValidate.SelfValidator ExtendedRegExChar where+ getErrors MkExtendedRegExChar {+ hasNonCapturingTopLevelAlternatives = hasNonCapturingTopLevelAlternatives',+ extendedRegEx = extendedRegEx'+ }+ | not $ ToolShed.SelfValidate.isValid extendedRegEx' = ToolShed.SelfValidate.getErrors extendedRegEx'+ | otherwise = ToolShed.SelfValidate.extractErrors [+ (hasNonCapturingTopLevelAlternatives' && any (not . RegExDot.RegEx.isCaptureGroup . RegExDot.Repeatable.base) (RegExDot.RegEx.concatenation extendedRegEx'), "Invalid NonCapturingTopLevelAlternatives.")+ ]++instance RegExDot.Consumer.Consumer ExtendedRegExChar where+ consumptionProfile = RegExDot.Consumer.consumptionProfile . extendedRegEx+ starHeight = RegExDot.Consumer.starHeight . extendedRegEx++instance RegExDot.RegEx.ShortcutExpander Char where+ expand c = error $ "RegExDot.RegEx.ShortcutExpander.expand RegExChar.ExtendedRegExChar:\tunrecognised shortcut '" ++ show c ++ "'."++instance Read ExtendedRegExChar where+ readsPrec _ s+ | s == reverse RegExDot.Anchor.tokens = [+ (+ MkExtendedRegExChar {+ hasNonCapturingTopLevelAlternatives = False,+ extendedRegEx = (Just RegExDot.Anchor.Bow, Just RegExDot.Anchor.Stern) <~> []+ },+ ""+ )+ ] -- The order of adjacent zero-width assertions is irrelevant.+ | otherwise = let+ extendedRegExCharParser :: Parsec.Parser ExtendedRegExChar+ extendedRegExCharParser = reduce {-correct prior assumption-} <$> alternativesParser {-assume non-capturing top-level Alternatives-} where+ reduce :: RegExDot.RegEx.Alternatives Char -> ExtendedRegExChar+ reduce alternatives+ | RegExDot.RegEx.isSingletonAlternatives alternatives = MkExtendedRegExChar False . head $ RegExDot.RegEx.deconstructAlternatives alternatives+ | otherwise = MkExtendedRegExChar True $ RegExDot.Anchor.unanchored <~> RegExDot.RegEx.CaptureGroup alternatives -: [] -- Infer non-capturing top-level 'RegExDot.RegEx.Alternatives' from the presence of 'RegExDot.RegEx.alternativeExtendedRegExSeparatorToken's.++ alternativesParser :: Parsec.Parser (RegExDot.RegEx.Alternatives Char)+ alternativesParser = RegExDot.RegEx.MkAlternatives <$> extendedRegExParser `Parsec.sepBy1` (Parsec.char RegExDot.RegEx.alternativeExtendedRegExSeparatorToken <?> "RegExDot.RegEx.alternativeExtendedRegExSeparatorToken " ++ show RegExDot.RegEx.alternativeExtendedRegExSeparatorToken) where+ extendedRegExParser :: Parsec.Parser (RegExDot.RegEx.ExtendedRegEx Char)+ extendedRegExParser = do+ maybeBowAnchor <- Parsec.option Nothing {-default-} $ (Parsec.char RegExDot.Anchor.bowToken <?> "RegExDot.Anchor.bowToken " ++ show RegExDot.Anchor.bowToken) >> return {-to ParsecT-monad-} (Just RegExDot.Anchor.Bow)+ repeatableRequirementList <- repeatableRequirementListParser++ (+ do+ repeatableCaptureGroup <- RegExDot.Repeatable.repeatableParser . RegExDot.RegEx.CaptureGroup =<< uncurry Parsec.between (+ ToolShed.Data.Pair.mirror Parsec.char RegExDot.RegEx.captureGroupDelimiters+ ) alternativesParser {-recurse-} <?> "RegExDot.RegEx.captureGroupDelimiters " ++ show RegExDot.RegEx.captureGroupDelimiters++ extendedRegEx' <- extendedRegExParser -- Recurse.++ return {-to ParsecT-monad-} $ RegExDot.RegEx.transformExtendedRegEx ((repeatableRequirementList ++) . (repeatableCaptureGroup :)) extendedRegEx' { RegExDot.RegEx.bowAnchor = maybeBowAnchor }+ ) <|> (+ do+ maybeSternAnchor <- Parsec.option Nothing {-default-} $ (Parsec.char RegExDot.Anchor.sternToken <?> "RegExDot.Anchor.sternToken " ++ show RegExDot.Anchor.sternToken) >> return {-to ParsecT-monad-} (Just RegExDot.Anchor.Stern)++ return {-to ParsecT-monad-} RegExDot.RegEx.MkExtendedRegEx {+ RegExDot.RegEx.bowAnchor = maybeBowAnchor,+ RegExDot.RegEx.concatenation = repeatableRequirementList,+ RegExDot.RegEx.sternAnchor = maybeSternAnchor+ }+ )+ where+ repeatableRequirementListParser :: Parsec.Parser (RegExDot.RegEx.Concatenation Char)+ repeatableRequirementListParser = Parsec.choice [+ Parsec.try . Parsec.lookAhead $ (+ Parsec.char RegExDot.Anchor.sternToken <?> "RegExDot.Anchor.sternToken " ++ show RegExDot.Anchor.sternToken+ ) >> (+ (+ Parsec.eof >> return {-to ParsecT-monad-} []+ ) <|> (+ Parsec.oneOf [RegExDot.RegEx.alternativeExtendedRegExSeparatorToken, snd RegExDot.RegEx.captureGroupDelimiters] >> return {-to ParsecT-monad-} []+ )+ ),+ (:) <$> (+ MetaChar.metaCharParser >>= RegExDot.Repeatable.repeatableParser . RegExDot.RegEx.Require . MetaChar.deconstruct+ ) <*> repeatableRequirementListParser, {-recurse-}+ return {-to ParsecT-monad-} []+ ]++ in (+ error . ("readsPrec RegExChar.ExtendedRegExChar:\tparse-error; " ++) . show -- Failure to parse.+ ) `either` (+ \pair@(extendedRegExChar, _) -> if ToolShed.SelfValidate.isValid extendedRegExChar+ then [pair]+ else error $ ToolShed.SelfValidate.getFirstError extendedRegExChar -- Parsed OK, but invalid.+ ) $ Parsec.parse (+ (,) <$> extendedRegExCharParser <*> Parsec.getInput+ ) "ExtendedRegExChar" s++instance Show ExtendedRegExChar where+ showsPrec _ MkExtendedRegExChar {+ hasNonCapturingTopLevelAlternatives = hasNonCapturingTopLevelAlternatives',+ extendedRegEx = RegExDot.RegEx.MkExtendedRegEx {+ RegExDot.RegEx.bowAnchor = maybeBowAnchor,+ RegExDot.RegEx.concatenation = concatenation',+ RegExDot.RegEx.sternAnchor = maybeSternAnchor+ }+ } = RegExDot.RegEx.showsMaybeAnchor maybeBowAnchor . foldl (.) (showString "") (+ (+ let+ showAlternatives :: RegExDot.RegEx.Alternatives Char -> [ShowS]+ showAlternatives = Data.List.intersperse (showChar RegExDot.RegEx.alternativeExtendedRegExSeparatorToken) . map (shows . MkExtendedRegExChar False) . RegExDot.RegEx.deconstructAlternatives+ in if hasNonCapturingTopLevelAlternatives'+ then map (+ \repeatablePattern -> case RegExDot.Repeatable.base repeatablePattern of+ RegExDot.RegEx.CaptureGroup alternatives -> foldr (.) (showString "") $ showAlternatives alternatives+ _ -> error $ "Show RegExChar.ExtendedRegExChar: unexpected " ++ show repeatablePattern+ )+ else map (+ \repeatablePattern -> (+ case RegExDot.Repeatable.base repeatablePattern of+ RegExDot.RegEx.Require meta -> shows $ MetaChar.MkMetaChar meta+ RegExDot.RegEx.CaptureGroup alternatives -> showChar (+ fst RegExDot.RegEx.captureGroupDelimiters+ ) . foldr (.) (+ showChar $ snd RegExDot.RegEx.captureGroupDelimiters -- Initial value.+ ) (+ showAlternatives alternatives+ )+ ) . RegExDot.Repeatable.showSuffix repeatablePattern+ )+ ) concatenation'+ ) . RegExDot.RegEx.showsMaybeAnchor maybeSternAnchor++-- | A veneer over the underlying polymorphic operator, 'RegExDot.RegEx.+~'.+(+~)+ :: InputData -- ^ The input-data string.+ -> RegExDot.RegExOpts.RegExOpts ExtendedRegExChar -- ^ The match-options, parameterised by the regex-specification.+ -> RegExDot.RegEx.Result Char+inputData +~ regExOpts = inputData RegExDot.RegEx.+~ fmap extendedRegEx regExOpts -- CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.++-- | A veneer over the underlying polymorphic operator, 'RegExDot.RegEx.=~'.+(=~)+ :: InputData -- ^ The input-data string.+ -> RegExDot.RegExOpts.RegExOpts ExtendedRegExChar -- ^ The match-options, parameterised by the regex-specification.+ -> Bool+inputData =~ regExOpts = inputData RegExDot.RegEx.=~ fmap extendedRegEx regExOpts -- CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.++-- | Pattern-mismatch operator.+(/~)+ :: InputData -- ^ The input-data string.+ -> RegExDot.RegExOpts.RegExOpts ExtendedRegExChar -- ^ The match-options, parameterised by the regex-specification.+ -> Bool+(/~) inputData = not . (inputData =~)+
+ src-lib/RegExChar/MetaChar.hs view
@@ -0,0 +1,256 @@+{-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-+ Copyright (C) 2010-2015 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@]++ * Implements 'RegExDot.Meta.ShortcutExpander' 'Char', to implement some of the traditional /Perl-style shortcuts/:++ [@\\d@] => Any 'Data.Char.isDigit'-character.++ [@\\D@] => Any non-'Data.Char.isDigit' character.++ [@\\s@] => Any 'Data.Char.isSpace'-character.++ [@\\S@] => Any non-'Data.Char.isSpace' character.++ [@\\w@] => Any 'Data.Char.isWord' letter.++ [@\\W@] => Any non-'Data.Char.isWord' letter.++ * Enables one to compose concise regexen, containing any 'Char' that's a member of one of these predefined sets.++ * Though 'RegExDot.Meta.Meta' is polymorphic, & the type-parameter can't be assumed to implement either 'Enum' or 'Ord',+ 'Char' actually does, so this module is able to implement ranges within a /Bracket-expression/.++ * Defines specialised instances of 'Read' & 'Show', to cope with /Perl-style shortcuts/, /Posix Character-classes/ & /Bracket-expression/ range-specifications.++ [@TODO@] Use @Environment.getLocale@. Regrettably, this returns in the "IO"-monad, & even it didn't, how does one pass that information to 'Read' ?!+-}++module RegExChar.MetaChar(+-- * Types+-- AssociationList,+-- Dictionary,+-- ** Type-synonyms+ MetaChar(..),+-- * Constants+-- bracketExpressionRangeToken,+-- posixCharacterClassDelimiters,+-- * Functions+-- implementPerlShortcut,+ metaCharParser,+-- ** Accessors (Deconstructors)+ deconstruct+) where++import Control.Arrow((***))+import qualified Data.Char+import qualified Data.Map+import qualified RegExDot.BracketExpression+import qualified RegExDot.BracketExpressionMember+import qualified RegExDot.Consumer+import qualified RegExDot.Meta+import qualified RegExDot.RegEx+import qualified RegExDot.Repeatable+import qualified RegExDot.ShowablePredicate+import qualified Text.ParserCombinators.Parsec as Parsec+import Text.ParserCombinators.Parsec((<?>))+import qualified ToolShed.Data.Pair+import qualified ToolShed.SelfValidate++#if !MIN_VERSION_base(4,8,0)+import Control.Applicative((<$>), (<*>))+#endif++-- | Holds a mapping from an escape-sequence & the predicate via which it is implemented.+type AssociationList key = [(key, RegExDot.ShowablePredicate.Predicate Char)]++-- | Holds a mapping from an escape-sequence & the predicate via which it is implemented.+type Dictionary key = Data.Map.Map key (RegExDot.ShowablePredicate.Predicate Char)++instance RegExDot.BracketExpressionMember.ShortcutExpander Char where+ findPredicate shortcut = RegExDot.ShowablePredicate.MkShowablePredicate [RegExDot.Meta.shortcutToken, shortcut] <$> shortcut `Data.Map.lookup` dictionary where+ dictionary :: Dictionary Char+ dictionary = Data.Map.fromList $ perlShortcuts ++ invert perlShortcuts where+ perlShortcuts :: AssociationList Char+ perlShortcuts = [+ ('d', Data.Char.isDigit),+ ('s', Data.Char.isSpace),+ ('w', \c -> ($ c) `any` [Data.Char.isAlphaNum, (== '_')])+ ]++ invert :: AssociationList Char -> AssociationList Char+ invert = map (Data.Char.toUpper *** (not .))++instance RegExDot.Meta.ShortcutExpander Char where+ expand c = case RegExDot.BracketExpressionMember.findPredicate c of+ Just showablePredicate -> RegExDot.Meta.Predicate showablePredicate+ _ -> RegExDot.Meta.Literal c++-- | A specialised instance, encapsulated to permit tailored instance-declarations.+newtype MetaChar = MkMetaChar (RegExDot.Meta.Meta Char) deriving Eq++-- | Accessor.+deconstruct :: MetaChar -> RegExDot.Meta.Meta Char+deconstruct (MkMetaChar metaChar) = metaChar++instance ToolShed.SelfValidate.SelfValidator MetaChar where+ getErrors = ToolShed.SelfValidate.getErrors . deconstruct++instance RegExDot.Consumer.Consumer MetaChar where+ consumptionProfile = RegExDot.Consumer.consumptionProfile . deconstruct+ starHeight = RegExDot.Consumer.starHeight . deconstruct++-- | Builds a parser of traditional regex-syntax, which understands 'Char'-specific concepts like /Perl-style shortcuts/ & /Posix Character-classes/.+metaCharParser :: Parsec.Parser MetaChar+metaCharParser = MkMetaChar <$> Parsec.choice [+ (Parsec.char RegExDot.Meta.anyToken <?> "RegExDot.Meta.anyToken " ++ show RegExDot.Meta.anyToken) >> return {-to ParsecT-monad-} RegExDot.Meta.Any,+ (Parsec.char RegExDot.Meta.shortcutToken <?> "RegExDot.Meta.shortcutToken " ++ show RegExDot.Meta.shortcutToken) >> RegExDot.Meta.expand <$> Parsec.anyChar,+ uncurry Parsec.between (ToolShed.Data.Pair.mirror Parsec.char RegExDot.BracketExpression.delimiterTokens) (+ do+ let+ implementPosixCharacterClass :: String -> Maybe (RegExDot.ShowablePredicate.ShowablePredicate Char)+ implementPosixCharacterClass identifier = RegExDot.ShowablePredicate.MkShowablePredicate (+ fst posixCharacterClassDelimiters ++ identifier ++ snd posixCharacterClassDelimiters+ ) <$> identifier `Data.Map.lookup` dictionary where+ dictionary :: Dictionary String+ dictionary = Data.Map.fromList $ posixCharacterClasses ++ invert posixCharacterClasses where+ posixCharacterClasses :: AssociationList String+ posixCharacterClasses = [+ ("alnum", Data.Char.isAlphaNum),+ ("alpha", Data.Char.isAlpha),+ ("ascii", Data.Char.isAscii),+ ("blank", (`elem` " \t")),+ ("cntrl", Data.Char.isControl),+ ("digit", Data.Char.isDigit),+ ("graph", \c -> not $ ($ c) `any` [Data.Char.isSpace, Data.Char.isControl]),+ ("lower", Data.Char.isLower),+ ("print", Data.Char.isPrint),+ ("punct", Data.Char.isPunctuation),+ ("space", Data.Char.isSpace),+ ("upper", Data.Char.isUpper),+ ("word", \c -> ($ c) `any` [Data.Char.isAlphaNum, (== '_')]),+ ("xdigit", Data.Char.isHexDigit)+ ]++ invert :: AssociationList String -> AssociationList String+ invert = map $ (RegExDot.BracketExpression.negationToken :) *** (not .)++ cTor <- Parsec.option RegExDot.Meta.AnyOf {-default-} $ (Parsec.char RegExDot.BracketExpression.negationToken <?> "RegExDot.BracketExpression.negationToken " ++ show RegExDot.BracketExpression.negationToken) >> return {-to ParsecT-monad-} RegExDot.Meta.NoneOf++ literalBracketExpressionTerminator <- Parsec.option [] {-default-} $ return {-to List-monad-} . RegExDot.BracketExpressionMember.Literal <$> (+ Parsec.char (snd RegExDot.BracketExpression.delimiterTokens) <?> "Literal Bracket-expression terminator " ++ show (snd RegExDot.BracketExpression.delimiterTokens)+ ) -- If the first item in a BracketExpression (or negated BracketExpression) is the terminator-token, then it is treated as a 'RegExDot.BracketExpressionMember.Literal'.++ cTor . (literalBracketExpressionTerminator ++) <$> Parsec.many {-potentially zero-} (+ Parsec.choice [+ (+ do+ _ <- Parsec.char RegExDot.Meta.shortcutToken <?> "RegExDot.Meta.shortcutToken " ++ show RegExDot.Meta.shortcutToken+ c <- Parsec.anyChar++ return {-to ParsecT-monad-} $ case RegExDot.BracketExpressionMember.findPredicate c of+ Just showablePredicate -> RegExDot.BracketExpressionMember.Predicate showablePredicate+ _ -> RegExDot.BracketExpressionMember.Literal c -- Escaped literal.+ ) <?> "Perl-style shortcut",+ Parsec.try (+ uncurry Parsec.between (ToolShed.Data.Pair.mirror Parsec.string posixCharacterClassDelimiters) (+ do+ identifier <- Parsec.many1 $ Parsec.noneOf [head $ snd posixCharacterClassDelimiters]++ case implementPosixCharacterClass identifier of+ Just showablePredicate -> return {-to ParsecT-monad-} $ RegExDot.BracketExpressionMember.Predicate showablePredicate+ _ -> Parsec.unexpected $ "MetaChar.metaCharParser:\tunrecognised Posix Character-class; " ++ show identifier+ ) <?> "Posix Character-class " ++ show posixCharacterClassDelimiters+ ), -- Regurgitate erroneously consumed input.+ Parsec.try (+ (+ do+ rangeStart <- Parsec.noneOf [snd RegExDot.BracketExpression.delimiterTokens]+ _ <- Parsec.char bracketExpressionRangeToken <?> "bracketExpressionRangeToken " ++ show bracketExpressionRangeToken+ rangeEnd <- Parsec.noneOf [snd RegExDot.BracketExpression.delimiterTokens]++ return {-to ParsecT-monad-} . RegExDot.BracketExpressionMember.Predicate . RegExDot.ShowablePredicate.MkShowablePredicate [+ rangeStart,+ bracketExpressionRangeToken,+ rangeEnd+ ] $ \c -> rangeStart <= c && c <= rangeEnd -- Create custom predicate, utilising "Ord Char".+ ) <?> "Bracket-expression range"+ ), -- Regurgitate erroneously consumed input.+ RegExDot.BracketExpressionMember.Literal <$> Parsec.noneOf [snd RegExDot.BracketExpression.delimiterTokens] <?> "RegExDot.BracketExpressionMember.Literal" -- TODO: the first Char-member can be a literal ']'.+ ] <?> "Bracket-expression member"+ ) <?> "Bracket-expression member-list"+ ) <?> "RegExDot.BracketExpression.delimiterTokens " ++ show RegExDot.BracketExpression.delimiterTokens,+ RegExDot.Meta.Literal <$> Parsec.noneOf RegExDot.RegEx.tokens+ ]++instance Read MetaChar where+ readsPrec _ = (+ (fail . ("readsPrec RegExChar.MetaChar:\tparse-error; " ++) . show) `either` return+ ) . Parsec.parse (+ (,) <$> metaCharParser <*> Parsec.getInput+ ) "MetaChar"++-- | The token used to signify an ordered range of members in a /Bracket-expression/.+bracketExpressionRangeToken :: Char+bracketExpressionRangeToken = '-'++-- | The delimiters of a /Posix Character-class/.+posixCharacterClassDelimiters :: (String, String)+posixCharacterClassDelimiters = ("[:", ":]")++instance Show MetaChar where+ showsPrec _ (MkMetaChar RegExDot.Meta.Any) = showChar RegExDot.Meta.anyToken+ showsPrec _ (MkMetaChar (RegExDot.Meta.Literal c)) = (+ if c `elem` [+ fst RegExDot.BracketExpression.delimiterTokens,+ fst RegExDot.Repeatable.rangeDelimiters,+ RegExDot.Repeatable.oneOrMoreToken,+ RegExDot.Repeatable.zeroOrMoreToken,+ RegExDot.Repeatable.zeroOrOneToken,+ RegExDot.Meta.anyToken,+ RegExDot.Meta.shortcutToken+ ] ++ RegExDot.RegEx.tokens+ then showChar RegExDot.Meta.shortcutToken+ else id+ ) . showChar c+ showsPrec _ (MkMetaChar (RegExDot.Meta.AnyOf bracketExpression)) = showChar (+ fst RegExDot.BracketExpression.delimiterTokens+ ) . showString (+ foldr (+ \e -> case e of+ RegExDot.BracketExpressionMember.Predicate showablePredicate -> shows showablePredicate+ RegExDot.BracketExpressionMember.Literal literal -> (+ if literal `elem` [+ bracketExpressionRangeToken, -- CAVEAT: only unambiguously literal when at the start or end of a "BracketExpression".+ RegExDot.Meta.shortcutToken,+ snd RegExDot.BracketExpression.delimiterTokens -- CAVEAT: only unambiguously literal when at the start of a "BracketExpression".+ ]+ then showChar RegExDot.Meta.shortcutToken+ else id+ ) . showChar literal+ ) (+ showChar (snd RegExDot.BracketExpression.delimiterTokens) "" -- Initial value.+ ) bracketExpression+ )+ showsPrec _ (MkMetaChar (RegExDot.Meta.NoneOf bracketExpression)) = showChar x . showChar RegExDot.BracketExpression.negationToken . showString xs where (x : xs) = show . MkMetaChar $ RegExDot.Meta.AnyOf bracketExpression+ showsPrec _ (MkMetaChar m) = shows m+
+ src-lib/RegExChar/RegExOptsChar.hs view
@@ -0,0 +1,219 @@+{-# LANGUAGE MultiParamTypeClasses, TypeSynonymInstances, FlexibleInstances, FlexibleContexts #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++{-+ Copyright (C) 2010-2015 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@] Creates a /back-end/ implementation, conforming to <http://hackage.haskell.org/packages/archive/regex-base/latest/doc/html/Text-Regex-Base-RegexLike.html>.++ [@CAVEATS@]++ * The underlying polymorphic (in terms of the base-type of the list of input data) 'RegExDot.RegEx.ExtendedRegEx'-engine is never going to be a drop-in replacement for other /regex/-engines,+ so this standard interface has only been implemented for traditional input data-type ['Char'].++ * The standard interface requires many Haskell-extensions, but since this implementation is just a thin layer over the underlying portable polymorphic 'RegExDot.RegEx.ExtendedRegEx'-engine,+ the latter can still be used directly, where any of these extensions are unavailable.+-}++module RegExChar.RegExOptsChar(+-- * Types+-- ** Type-synonyms+ MatchSpan,+-- MatchDataSpan,+ RegExOptsChar,+-- * Functions+-- exciseNonCapturingTopLevelAlternatives,+-- matchUntilFailure,+-- toMatchDataSpanList,+ toZeroIndexedArray,+-- ** Operators+ (=~)+-- ** Predicates+-- complyStrictlyWithPosix,+-- hasNonCapturingTopLevelAlternatives+) where++import qualified Data.Array.IArray+import qualified RegExChar.ExtendedRegExChar as ExtendedRegExChar+import qualified RegExDot.CompilationOptions+import qualified RegExDot.ConsumptionBounds+import qualified RegExDot.DataSpan+import qualified RegExDot.DataSpanTree+import qualified RegExDot.ExecutionOptions+import qualified RegExDot.RegEx+import qualified RegExDot.RegExOpts+import qualified RegExDot.Result+import qualified RegExDot.Tree+import qualified Text.Regex.Base.RegexLike as RegexLike+import Text.Regex.Base.Context() -- Instance-declarations.+import qualified ToolShed.Data.List+import qualified ToolShed.Defaultable+import qualified ToolShed.Options++infix 4 =~ -- Same as (==).++-- | Defines a specific instance of the polymorphic base-type.+type RegExOptsChar = RegExDot.RegExOpts.RegExOpts ExtendedRegExChar.ExtendedRegExChar++-- | Convenience accessor-function.+hasNonCapturingTopLevelAlternatives :: RegExOptsChar -> Bool+hasNonCapturingTopLevelAlternatives = ExtendedRegExChar.hasNonCapturingTopLevelAlternatives . RegExDot.RegExOpts.regEx++-- | Convenience accessor-function.+complyStrictlyWithPosix :: RegExOptsChar -> Bool+complyStrictlyWithPosix = RegExDot.CompilationOptions.complyStrictlyWithPosix . RegExDot.RegExOpts.compilationOptions++instance RegexLike.RegexOptions RegExOptsChar RegExDot.CompilationOptions.CompilationOptions RegExDot.ExecutionOptions.ExecutionOptions where+ blankCompOpt = ToolShed.Options.blankValue+ blankExecOpt = ToolShed.Options.blankValue+ defaultCompOpt = ToolShed.Defaultable.defaultValue+ defaultExecOpt = ToolShed.Defaultable.defaultValue+ setExecOpts e r = r { RegExDot.RegExOpts.executionOptions = e }+ getExecOpts = RegExDot.RegExOpts.executionOptions++-- Newer versions of this 'Text.Regex.Base.RegexLike.RegexMaker' have additional monadic methods, which can fail on parse-errors.+instance RegexLike.RegexMaker RegExOptsChar RegExDot.CompilationOptions.CompilationOptions RegExDot.ExecutionOptions.ExecutionOptions String where+ makeRegexOpts c e source = RegExDot.RegExOpts.MkRegExOpts {+ RegExDot.RegExOpts.compilationOptions = c,+ RegExDot.RegExOpts.executionOptions = e,+ RegExDot.RegExOpts.regEx = read source+ }++-- | The offset and length of the 'RegExDot.RegEx.InputData' consumed in one 'RegExDot.RegEx.Match'.+type MatchSpan = (RegexLike.MatchOffset, RegexLike.MatchLength)++{- |+ * The 'MatchSpan' augmented by the consumed 'ExtendedRegExChar.InputData' to which it refers.++ * Similar to 'RegExDot.DataSpan.DataSpan'.+-}+type MatchDataSpan = (ExtendedRegExChar.InputData, MatchSpan)++{- |+ * Convert the 'RegExDot.RegEx.MatchList', into the 'MatchDataSpan's required by "Text.Regex.Base.RegexLike".++ * Treat the whole 'ExtendedRegExChar.ExtendedRegExChar' as an additional implicit capture-group.++ * CAVEAT: @RegExDot.DataSpanTree.extractCaptureGroups True@ may return some 'RegExDot.DataSpan.DataSpan's containing the artificial offset @-1@ specified by Posix => don't use this as the basis for any arithmetic.+-}+toMatchDataSpanList+ :: Bool+ -> RegExDot.ConsumptionBounds.DataLength -- ^ The offset to use for zero-length match.+ -> RegExDot.RegEx.MatchList Char -- ^ The list of matches.+ -> [MatchDataSpan]+toMatchDataSpanList _ offset [] = [RegExDot.DataSpan.empty offset] -- The whole regex matched, but consumed nothing.+toMatchDataSpanList strictPosixCompliance _ matchList = RegExDot.DataSpanTree.extractCaptureGroups strictPosixCompliance . return {-to List-monad-} . RegExDot.Tree.Node . return {-to List-monad-} $ RegExDot.DataSpanTree.toTreeList matchList++-- | Optionally remove the 2nd element from the list, where it represents the data captured by a groups of top-level 'Alternatives', which weren't explicitly delimited & therefore are non-capturing.+exciseNonCapturingTopLevelAlternatives :: RegExOptsChar -> [MatchDataSpan] -> [MatchDataSpan]+exciseNonCapturingTopLevelAlternatives regExOptsChar+ | hasNonCapturingTopLevelAlternatives regExOptsChar = ToolShed.Data.List.excise 1 -- The zeroeth represents the whole regex, the 1st represents the top-level non-capturing group of Alternatives, & the remainder represent explicitly delimited groups.+ | otherwise = id++-- | Convert the specified list, into a zero-indexed array.+toZeroIndexedArray :: [e] -> Data.Array.IArray.Array Int e+toZeroIndexedArray l = Data.Array.IArray.listArray (0, pred $ length l) l++{- |+ * Repeatedly apply the 'RegExOptsChar' to the 'ExtendedRegExChar.InputData', forwarding unmatched input data to the next match-attempt, until it's all been consumed.++ * The offsets, from the start of the input data, of all matches after the first, are shifted to account for input data already consumed by previous matches.+-}+matchUntilFailure+ :: RegExOptsChar -- ^ The match-criteria.+ -> ExtendedRegExChar.InputData -- ^ The input-data, to be consumed by repeated matching.+ -> [RegExDot.RegEx.MatchList Char]+matchUntilFailure regExOptsChar inputData = shiftOffsets 0 $ matchUntilFailure' inputData where+ matchUntilFailure' :: ExtendedRegExChar.InputData -> [RegExDot.RegEx.MatchList Char]+ matchUntilFailure' unmatchedInputData = case unmatchedInputData ExtendedRegExChar.+~ regExOptsChar of -- CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.+ (_, Just matchList, maybeSternAnchorResult) -> matchList : if null (RegExDot.RegEx.extractDataFromMatchList matchList) then [] else matchUntilFailure' (RegExDot.RegEx.extractDataFromMatch' maybeSternAnchorResult)+ _ -> []++ shiftOffsets :: RegExDot.ConsumptionBounds.DataLength -> [RegExDot.RegEx.MatchList Char] -> [RegExDot.RegEx.MatchList Char]+ shiftOffsets offset (matchList : matchLists) = let+ shiftedMatchList :: RegExDot.RegEx.MatchList Char+ shiftedMatchList = RegExDot.RegEx.shiftMatchList offset matchList+ in shiftedMatchList : shiftOffsets (RegExDot.DataSpan.after . last . RegExDot.DataSpanTree.flattenTreeList 0 $ RegExDot.DataSpanTree.toTreeList shiftedMatchList) matchLists {-recurse-}+ shiftOffsets _ _ = []++instance RegexLike.RegexLike RegExOptsChar ExtendedRegExChar.InputData where+{- E.g.:+ ("xabcdxabdxxadx" RegExOptsChar.=~ "a(b(c?))*d") :: [Text.Regex.Base.RegexLike.MatchArray]+ [array (0,2) [(0,(1,4)),(1,(2,2)),(2,(3,1))],array (0,2) [(0,(6,3)),(1,(7,1)),(2,(8,0))],array (0,1) [(0,(11,2)),(1,(-1,0))]]+-}+-- matchAll :: RegExOptsChar -> ExtendedRegExChar.InputData -> [RegexLike.MatchArray]+ matchAll regExOptsChar = map (toZeroIndexedArray . map snd {-span-} . exciseNonCapturingTopLevelAlternatives regExOptsChar . toMatchDataSpanList (complyStrictlyWithPosix regExOptsChar) 0) . matchUntilFailure regExOptsChar++{- E.g.:+ ("xabcdxabdxxadx" RegExOptsChar.=~ "a(b(c?))*d") :: [Text.Regex.Base.RegexLike.MatchText String]+ [array (0,2) [(0,("abcd",(1,4))),(1,("bc",(2,2))),(2,("c",(3,1)))],array (0,2) [(0,("abd",(6,3))),(1,("b",(7,1))),(2,("",(8,0)))],array (0,1) [(0,("ad",(11,2))),(1,("",(-1,0)))]]+-}+-- matchAllText :: RegExOptsChar -> ExtendedRegExChar.InputData -> [RegexLike.MatchText ExtendedRegExChar.InputData]+ matchAllText regExOptsChar = map (toZeroIndexedArray . exciseNonCapturingTopLevelAlternatives regExOptsChar . toMatchDataSpanList (complyStrictlyWithPosix regExOptsChar) 0) . matchUntilFailure regExOptsChar++{- E.g.:+ ("xabcdx" RegExOptsChar.=~ "a(b(c?))*d") :: Text.Regex.Base.RegexLike.MatchArray+ array (0,2) [(0,(1,4)),(1,(2,2)),(2,(3,1))]+-}+-- matchOnce :: RegExOptsChar -> ExtendedRegExChar.InputData -> Maybe RegexLike.MatchArray+ matchOnce regExOptsChar inputData = (+ toZeroIndexedArray . map snd {-span-} . exciseNonCapturingTopLevelAlternatives regExOptsChar . toMatchDataSpanList (+ complyStrictlyWithPosix regExOptsChar+ ) (+ RegExDot.RegEx.externalMatchLength $ RegExDot.Result.getPreMatch extendedRegExResult+ )+ ) `fmap` RegExDot.Result.getMatchList extendedRegExResult where+ extendedRegExResult :: RegExDot.RegEx.Result Char+ extendedRegExResult = inputData ExtendedRegExChar.+~ regExOptsChar -- CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.++{- E.g.:+ ("xabcdx" RegExOptsChar.=~ "a(b(c?))*d") :: (String, Text.Regex.Base.RegexLike.MatchText String, String)+ ("x",array (0,2) [(0,("abcd",(1,4))),(1,("bc",(2,2))),(2,("c",(3,1)))],"x")+-}+-- matchOnceText :: RegExOptsChar -> ExtendedRegExChar.InputData -> Maybe (ExtendedRegExChar.InputData, MatchText ExtendedRegExChar.InputData, ExtendedRegExChar.InputData)+ matchOnceText regExOptsChar inputData = case inputData ExtendedRegExChar.+~ regExOptsChar of -- CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.+ (maybeBowAnchorResult, Just matchList, maybeSternAnchorResult) -> Just (+ RegExDot.RegEx.extractDataFromMatch' maybeBowAnchorResult,+ toZeroIndexedArray . exciseNonCapturingTopLevelAlternatives regExOptsChar $ toMatchDataSpanList (+ complyStrictlyWithPosix regExOptsChar+ ) (+ RegExDot.RegEx.externalMatchLength maybeBowAnchorResult+ ) matchList,+ RegExDot.RegEx.extractDataFromMatch' maybeSternAnchorResult+ )+ _ -> Nothing++{- E.g.:+ ("xabcdx" RegExOptsChar.=~ "a(b(c?))*d") :: Bool+ True+-}+-- matchTest :: RegExOptsChar -> ExtendedRegExChar.InputData -> Bool+ matchTest = flip (ExtendedRegExChar.=~) -- CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.++{- |+ * Match-operator.++ * The polymorphic return-type is resolved by the caller's "RegexLike.RegexContext".+-}+(=~) :: RegexLike.RegexContext RegExOptsChar ExtendedRegExChar.InputData target+ => ExtendedRegExChar.InputData -- ^ The input data.+ -> String -- ^ The string from which to read the regex-specification.+ -> target -- ^ The polymorphic return-type.+inputData =~ s = (RegexLike.makeRegex s :: RegExOptsChar) `RegexLike.match` inputData+
+ src-test/Grecce/Test/QC/ExtendedRegExChar.hs view
@@ -0,0 +1,192 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-+ Copyright (C) 2010-2015 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@] Implements 'Test.QuickCheck.Arbitrary' & defines tests based on it.+-}++module Grecce.Test.QC.ExtendedRegExChar(+-- * Constants+ results,+-- * Types+-- ** Data-types+-- AlternativesChar(..),+-- PatternChar(..),+-- RepeatablePatternChar(..),+-- * Functions+-- ** Accessors+-- deconstructAlternativesChar,+-- deconstructPatternChar,+-- deconstructRepeatablePatternChar+) where++import qualified Data.Maybe+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+import qualified RegExDot.BracketExpressionMember+import qualified RegExDot.Consumer+import qualified RegExDot.ConsumptionBounds+import qualified RegExDot.ConsumptionProfile+import RegExDot.DSL((-:), (?:), (+:), (<~>))+import qualified RegExDot.Meta+import qualified RegExDot.RegEx+import RegExDot.RegEx((+~))+import qualified RegExDot.RegExOpts+import qualified RegExDot.Repeatable+import qualified RegExDot.Result+import qualified Test.QuickCheck+import Test.QuickCheck((==>))+import qualified ToolShed.SelfValidate+import qualified ToolShed.Test.ReversibleIO++-- | A specialised instance, required to instantiate 'Test.QuickCheck.Arbitrary'.+newtype AlternativesChar = MkAlternativesChar (RegExDot.RegEx.Alternatives Char) deriving (Eq, Read, Show)++-- | Accessor.+deconstructAlternativesChar :: AlternativesChar -> RegExDot.RegEx.Alternatives Char+deconstructAlternativesChar (MkAlternativesChar a) = a++instance ToolShed.SelfValidate.SelfValidator AlternativesChar where+ getErrors = ToolShed.SelfValidate.getErrors . deconstructAlternativesChar++instance RegExDot.Consumer.Consumer AlternativesChar where+ consumptionProfile = RegExDot.Consumer.consumptionProfile . deconstructAlternativesChar+ starHeight = RegExDot.Consumer.starHeight . deconstructAlternativesChar++instance Test.QuickCheck.Arbitrary AlternativesChar where+ arbitrary = (MkAlternativesChar . RegExDot.RegEx.MkAlternatives . map ExtendedRegExChar.extendedRegEx) `fmap` (+ Test.QuickCheck.elements [1, 2] >>= Test.QuickCheck.vector {-[ExtendedRegExChar]-}+ ) -- TODO: permit zero alternatives.++-- | A specialised instance, required to instantiate 'Test.QuickCheck.Arbitrary'.+newtype PatternChar = MkPatternChar (RegExDot.RegEx.Pattern Char) deriving (Eq, Read, Show)++-- | Accessor.+deconstructPatternChar :: PatternChar -> RegExDot.RegEx.Pattern Char+deconstructPatternChar (MkPatternChar pattern) = pattern++instance ToolShed.SelfValidate.SelfValidator PatternChar where+ getErrors = ToolShed.SelfValidate.getErrors . deconstructPatternChar++instance RegExDot.Consumer.Consumer PatternChar where+ consumptionProfile = RegExDot.Consumer.consumptionProfile . deconstructPatternChar+ starHeight = RegExDot.Consumer.starHeight . deconstructPatternChar++instance Test.QuickCheck.Arbitrary PatternChar where+ arbitrary = MkPatternChar `fmap` Test.QuickCheck.frequency [+ (4, (RegExDot.RegEx.Require . MetaChar.deconstruct) `fmap` Test.QuickCheck.arbitrary {-MetaChar-}),+ (1, (RegExDot.RegEx.CaptureGroup . deconstructAlternativesChar) `fmap` Test.QuickCheck.arbitrary {-AlternativesChar-})+ ] -- CAVEAT: may recurse forever if 'RegExDot.RegEx.CaptureGroup / RegExDot.RegEx.Require' is too high.++-- | A specialised instance, required to instantiate 'Test.QuickCheck.Arbitrary'.+newtype RepeatablePatternChar = MkRepeatablePatternChar (RegExDot.Repeatable.Repeatable PatternChar) deriving (Eq, Read, Show)++-- | Accessor.+deconstructRepeatablePatternChar :: RepeatablePatternChar -> RegExDot.Repeatable.Repeatable PatternChar+deconstructRepeatablePatternChar (MkRepeatablePatternChar repeatable) = repeatable++instance ToolShed.SelfValidate.SelfValidator RepeatablePatternChar where+ getErrors = ToolShed.SelfValidate.getErrors . deconstructRepeatablePatternChar++instance RegExDot.Consumer.Consumer RepeatablePatternChar where+ consumptionProfile = RegExDot.Consumer.consumptionProfile . deconstructRepeatablePatternChar+ starHeight = RegExDot.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 `fmap` Test.QuickCheck.elements [max fewest 1 .. 9]]+ isGreedy <- Test.QuickCheck.arbitrary {-Bool-}++ let+ repetitionBounds :: RegExDot.Repeatable.RepetitionBounds+ repetitionBounds = (fewest, most)++ return {-to Gen-monad-} $ MkRepeatablePatternChar RegExDot.Repeatable.MkRepeatable {+ RegExDot.Repeatable.base = patternChar,+ RegExDot.Repeatable.repetitionBounds = repetitionBounds,+ RegExDot.Repeatable.isGreedy = RegExDot.Repeatable.hasPreciseBounds repetitionBounds || isGreedy -- Only specify non-greedy where space exists.+ }++instance Test.QuickCheck.Arbitrary ExtendedRegExChar.ExtendedRegExChar where+ arbitrary = do+ hasBowAnchor <- Test.QuickCheck.arbitrary {-Bool-}+ concatenation <- map (fmap deconstructPatternChar {-replace base-} . deconstructRepeatablePatternChar {-RegExDot.Repeatable.Repeatable PatternChar-}) `fmap` Test.QuickCheck.vector 2 {-[RepeatablePatternChar]-}+ hasSternAnchor <- Test.QuickCheck.arbitrary {-Bool-}++ return {-to Gen-monad-} $ ExtendedRegExChar.MkExtendedRegExChar False RegExDot.RegEx.MkExtendedRegEx {+ RegExDot.RegEx.bowAnchor = if hasBowAnchor then Just RegExDot.Anchor.Bow else Nothing,+ RegExDot.RegEx.concatenation = concatenation,+ RegExDot.RegEx.sternAnchor = if hasSternAnchor then Just RegExDot.Anchor.Stern else Nothing+ }++-- | The constant test-results for this data-type.+results :: IO [Test.QuickCheck.Result]+results = sequence [+ Test.QuickCheck.quickCheckResult prop_consumptionProfile,+ Test.QuickCheck.quickCheckResult prop_consumptionProfile2,+ Test.QuickCheck.quickCheckResult prop_io,+ Test.QuickCheck.quickCheckResult prop_io',+ Test.QuickCheck.quickCheckResult prop_isValid,+ Test.QuickCheck.quickCheckResult prop_starHeight,+ Test.QuickCheck.quickCheckResult prop_double+ ] where+ prop_consumptionProfile, prop_consumptionProfile2, prop_io, prop_io', prop_isValid, prop_starHeight :: ExtendedRegExChar.ExtendedRegExChar -> Test.QuickCheck.Property+ prop_consumptionProfile r = Test.QuickCheck.label "prop_consumptionProfile" $ Data.Maybe.maybe True (>= minData) maybeMaxData && (+ not b == all (not . RegExDot.Consumer.getHasSpecificRequirement) (RegExDot.RegEx.concatenation $ ExtendedRegExChar.extendedRegEx r)+ )+ where+ RegExDot.ConsumptionProfile.MkConsumptionProfile {+ RegExDot.ConsumptionProfile.consumptionBounds = (minData, maybeMaxData),+ RegExDot.ConsumptionProfile.hasSpecificRequirement = b+ } = RegExDot.Consumer.consumptionProfile r++ prop_consumptionProfile2 r = RegExDot.RegEx.isDefined (ExtendedRegExChar.extendedRegEx r) ==> Test.QuickCheck.label "prop_consumptionProfile2" $ hasSpecificRequirement || canConsumeAnything -- There's either; a requirement for at least one specific, or we can consume at least one arbitrary; input datum.+ where+ RegExDot.ConsumptionProfile.MkConsumptionProfile {+ RegExDot.ConsumptionProfile.hasSpecificRequirement = hasSpecificRequirement,+ RegExDot.ConsumptionProfile.canConsumeAnything = canConsumeAnything+ } = RegExDot.Consumer.consumptionProfile r++ prop_io = Test.QuickCheck.label "prop_io" . ToolShed.Test.ReversibleIO.isReversible+ prop_io' r = Test.QuickCheck.label "prop_io'" $ ToolShed.Test.ReversibleIO.isReversible r' where r' = ExtendedRegExChar.extendedRegEx r -- Check "RegExDot.RegEx.ExtendedRegEx Char" too.+ prop_isValid r = Test.QuickCheck.label "prop_isValid" $ ToolShed.SelfValidate.isValid r+ prop_starHeight r = Test.QuickCheck.label "prop_starHeight" $ (RegExDot.Consumer.starHeight r == 0) == RegExDot.ConsumptionBounds.isPrecise (RegExDot.Consumer.getConsumptionBounds r)++ prop_double :: Double -> Test.QuickCheck.Property+ prop_double d = Test.QuickCheck.label "prop_double" $ RegExDot.RegEx.extractDataFromMatchList `fmap` RegExDot.Result.getMatchList result == Just s where+ s :: String+ s = show $ d * 1e6 -- Implementation of show, uses exponential notation, when the number is large.++ result :: RegExDot.RegEx.Result Char+ result = s +~ RegExDot.RegExOpts.mkRegEx (+ (Just RegExDot.Anchor.Bow, Just RegExDot.Anchor.Stern) <~> sign ?: digits +: map (+ RegExDot.Repeatable.zeroOrOne . RegExDot.RegEx.captureGroup . map (RegExDot.Anchor.unanchored <~>) . return {-to List-monad-}+ ) [+ RegExDot.RegEx.Require (RegExDot.Meta.Literal '.') -: digits +: [],+ RegExDot.RegEx.Require (RegExDot.Meta.AnyOf $ map RegExDot.BracketExpressionMember.Literal "eE") -: sign ?: digits +: []+ ]+ ) where+ sign, digits :: RegExDot.RegEx.Pattern Char+ sign = RegExDot.RegEx.Require . RegExDot.Meta.AnyOf $ map RegExDot.BracketExpressionMember.Literal "+-"+ digits = RegExDot.RegEx.Require . RegExDot.Meta.AnyOf $ map RegExDot.BracketExpressionMember.Literal ['0' .. '9']+
+ src-test/Grecce/Test/QC/MetaChar.hs view
@@ -0,0 +1,66 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-+ Copyright (C) 2010-2015 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@] Implements 'Test.QuickCheck.Arbitrary' & defines tests based on it.++ [@TODO@] Test /Perl-style shortcuts/ & /Posix Character-classes/.+-}++module Grecce.Test.QC.MetaChar(+-- * Constants+ results,+-- testCharacters,+-- * Types+-- ** Type-synonyms+-- Testable+) where++import qualified Data.List+import qualified RegExChar.MetaChar as MetaChar -- CAVEAT: beware of the similar name.+import qualified RegExDot.BracketExpression+import qualified RegExDot.BracketExpressionMember+import qualified RegExDot.Meta+import qualified Test.QuickCheck+import qualified ToolShed.Test.ReversibleIO++testCharacters :: String+testCharacters = filter (`notElem` [RegExDot.Meta.shortcutToken, RegExDot.BracketExpression.negationToken]) [' ' .. '~'] -- TODO: permit all characters.++instance Test.QuickCheck.Arbitrary MetaChar.MetaChar where+ arbitrary = Test.QuickCheck.oneof $ map (fmap MetaChar.MkMetaChar) [+ return {-to Gen-monad-} RegExDot.Meta.Any,+ RegExDot.Meta.Literal `fmap` Test.QuickCheck.elements testCharacters,+ (RegExDot.Meta.AnyOf . map RegExDot.BracketExpressionMember.Literal . Data.List.nub) `fmap` arbitraryCharList 1 16, -- Test.QuickCheck.vector 16 {-Char-},+ (RegExDot.Meta.NoneOf . map RegExDot.BracketExpressionMember.Literal . Data.List.nub) `fmap` 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)) `fmap` Test.QuickCheck.vector maxBound {-Char-}++type Testable = MetaChar.MetaChar -> Test.QuickCheck.Property++-- | The constant test-results for this data-type.+results :: IO [Test.QuickCheck.Result]+results = mapM Test.QuickCheck.quickCheckResult [prop_io] where+ prop_io :: Testable+ prop_io = Test.QuickCheck.label "prop_io" . ToolShed.Test.ReversibleIO.isReversible+
+ src-test/Grecce/Test/QC/RepeatableMetaChar.hs view
@@ -0,0 +1,92 @@+{-+ Copyright (C) 2010-2015 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@] Implements 'Test.QuickCheck.Arbitrary' & defines tests based on it.+-}++module Grecce.Test.QC.RepeatableMetaChar(+-- * Constants+ results,+-- * Types+-- ** Data-types+-- RepeatableMetaChar,+-- ** Type-synonyms+-- Testable,+-- * Functions+-- deconstruct+) where++import qualified Data.Maybe+import Grecce.Test.QC.MetaChar()+import qualified RegExChar.MetaChar as MetaChar+import qualified RegExDot.Consumer+import qualified RegExDot.ConsumptionBounds+import qualified RegExDot.ConsumptionProfile+import qualified RegExDot.Repeatable+import qualified Test.QuickCheck+import qualified ToolShed.SelfValidate+import qualified ToolShed.Test.ReversibleIO++-- | A specialised instance, required to instantiate 'Test.QuickCheck.Arbitrary'.+newtype RepeatableMetaChar = MkRepeatableMetaChar (RegExDot.Repeatable.Repeatable MetaChar.MetaChar) deriving (Eq, Read, Show)++-- | Accessor.+deconstruct :: RepeatableMetaChar -> RegExDot.Repeatable.Repeatable MetaChar.MetaChar+deconstruct (MkRepeatableMetaChar repeatableMetaChar) = repeatableMetaChar++instance ToolShed.SelfValidate.SelfValidator RepeatableMetaChar where+ getErrors = ToolShed.SelfValidate.getErrors . deconstruct++instance RegExDot.Consumer.Consumer RepeatableMetaChar where+ consumptionProfile = RegExDot.Consumer.consumptionProfile . deconstruct+ starHeight = RegExDot.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 `fmap` Test.QuickCheck.elements [max fewest 1 .. 9]]+ isGreedy <- Test.QuickCheck.arbitrary++ let+ repetitionBounds :: RegExDot.Repeatable.RepetitionBounds+ repetitionBounds = (fewest, most)++ return {-to Gen-monad-} . MkRepeatableMetaChar $ RegExDot.Repeatable.MkRepeatable {+ RegExDot.Repeatable.base = metaChar,+ RegExDot.Repeatable.repetitionBounds = repetitionBounds,+ RegExDot.Repeatable.isGreedy = RegExDot.Repeatable.hasPreciseBounds repetitionBounds || isGreedy -- Only specify 'non-greedy' where space exists.+ }++type Testable = RepeatableMetaChar -> Test.QuickCheck.Property++-- | The constant test-results for this data-type.+results :: IO [Test.QuickCheck.Result]+results = mapM Test.QuickCheck.quickCheckResult [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" $ Data.Maybe.maybe True (>= minData) maybeMaxData && b == RegExDot.Consumer.getHasSpecificRequirement (RegExDot.Repeatable.base $ deconstruct r) where+ RegExDot.ConsumptionProfile.MkConsumptionProfile {+ RegExDot.ConsumptionProfile.consumptionBounds = (minData, maybeMaxData),+ RegExDot.ConsumptionProfile.hasSpecificRequirement = b+ } = RegExDot.Consumer.consumptionProfile r++ prop_io (MkRepeatableMetaChar r) = Test.QuickCheck.label "prop_io" $ ToolShed.Test.ReversibleIO.isReversible r+ prop_isValid r = Test.QuickCheck.label "prop_isValid" $ ToolShed.SelfValidate.isValid r+ prop_starHeight (MkRepeatableMetaChar r) = Test.QuickCheck.label "prop_starHeight" $ RegExDot.Consumer.starHeight r == if RegExDot.ConsumptionBounds.isPrecise (RegExDot.Consumer.getConsumptionBounds r) then 0 else 1+
+ src-test/Main.hs view
@@ -0,0 +1,46 @@+{-+ Copyright (C) 2015 Dr. Alistair Ward++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+-}+{- |+ [@AUTHOR@] Dr. Alistair Ward++ [@DESCRIPTION@] Calls the /quickChecks/-functions for modules supporting this feature.+-}++module Main(main) where++import Control.Arrow((***))+import qualified Control.Monad+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 System.Exit+import qualified ToolShed.Test.QuickCheck.Result++-- | Entry-point.+main :: IO ()+main = mapM_ (+ snd {-exit-status-} . (+ putStrLn . (++ ":") *** (+ >>= (`Control.Monad.unless` System.Exit.exitFailure) . all ToolShed.Test.QuickCheck.Result.isSuccessful+ )+ )+ ) [+ ("MetaChar", Test.QC.MetaChar.results),+ ("RepeatableMetaChar", Test.QC.RepeatableMetaChar.results),+ ("ExtendedRegExChar", Test.QC.ExtendedRegExChar.results)+ ]+
− src/Grecce/CommandOptions.hs
@@ -1,172 +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 options, read from the command-line,- & as such assumes that the type-parameter of the underlying polymorphic /regex/-engine is 'Char'.-- * Supplies mutators, which ensure the consistency of the chosen set.--}--module Grecce.CommandOptions(--- * Types--- ** Type-synonyms- CompilationOptionsMutator,- ExecutionOptionsMutator,--- Transformer,--- ** Data-types- CommandOptions(..),--- * Functions--- ** Mutators- setCompilationOption,- setCountMatches,- setExecutionOption,- setInvertMatch,- setPrependLineNumbers,- setListFilesWithMatches,- setListFilesWithoutMatch,- setVerbose-) where--import qualified RegExChar.ExtendedRegExChar as ExtendedRegExChar-import qualified RegExDot.CompilationOptions-import qualified RegExDot.ExecutionOptions-import qualified ToolShed.Defaultable-import qualified ToolShed.Options---- | The set of command-line options; based on those provided by /egrep/.-data CommandOptions = MkCommandOptions {- countMatches :: Bool, -- ^ Print only a count of matching lines per file.- extendedRegExChar :: Maybe ExtendedRegExChar.ExtendedRegExChar, -- ^ The /regex/.- invertMatch :: Bool, -- ^ Select non-matching lines.- prependLineNumbers :: Bool, -- ^ Prepend the line-number of the matched input-data, to the output.- listFilesWithMatches :: Bool, -- ^ List the names of any files containing at least one match.- listFilesWithoutMatch :: Bool, -- ^ List the names of any files containing zero matches.- verbose :: Bool, -- ^ Show the captured data.- compilationOptions :: RegExDot.CompilationOptions.CompilationOptions, -- ^ Used to define the regex-request.- executionOptions :: RegExDot.ExecutionOptions.ExecutionOptions -- ^ Used to control the regex-runtime.-} deriving Show---- | An arbitrary function which transforms the supplied 'RegExDot.ExecutionOptions.ExecutionOptions'.-type Transformer = CommandOptions -> CommandOptions--instance ToolShed.Defaultable.Defaultable CommandOptions where- defaultValue = ToolShed.Options.blankValue {- countMatches = False,- extendedRegExChar = Nothing,- invertMatch = False,- prependLineNumbers = False,- listFilesWithMatches = False,- listFilesWithoutMatch = False,- verbose = False,- compilationOptions = ToolShed.Defaultable.defaultValue,- executionOptions = ToolShed.Defaultable.defaultValue- }--instance ToolShed.Options.Options CommandOptions where- blankValue = MkCommandOptions {- countMatches = undefined,- extendedRegExChar = undefined,- invertMatch = undefined,- prependLineNumbers = undefined,- listFilesWithMatches = undefined,- listFilesWithoutMatch = undefined,- verbose = undefined,- compilationOptions = undefined,- executionOptions = undefined- }---- | Mutator.-setCountMatches :: Transformer-setCountMatches commandOptions- | ($ commandOptions) `any` [- prependLineNumbers,- listFilesWithoutMatch,- listFilesWithMatches,- verbose- ] = error "Grecce.CommandOptions.setCountMatches:\t'countMatches' is incompatible with; 'prependLineNumbers', 'listFilesWithoutMatch', 'listFilesWithMatches' & 'verbose'."- | otherwise = commandOptions { countMatches = True }---- | Mutator.-setInvertMatch :: Transformer-setInvertMatch commandOptions- | verbose commandOptions = error "Grecce.CommandOptions.setInvertMatch:\t'invertMatch' is incompatible with 'verbose'."- | otherwise = commandOptions { invertMatch = True }---- | Mutator.-setPrependLineNumbers :: Transformer-setPrependLineNumbers commandOptions- | ($ commandOptions) `any` [- countMatches,- listFilesWithoutMatch,- listFilesWithMatches- ] = error "Grecce.CommandOptions.setPrependLineNumbers:\t'prependLineNumbers' is incompatible with 'countMatches', 'listFilesWithoutMatch' & 'listFilesWithMatches'."- | otherwise = commandOptions { prependLineNumbers = True }---- | Mutator.-setListFilesWithMatches :: Transformer-setListFilesWithMatches commandOptions- | ($ commandOptions) `any` [- countMatches,- prependLineNumbers,- listFilesWithoutMatch,- verbose- ] = error "Grecce.CommandOptions.setListFilesWithMatches:\t'listFilesWithMatches' is incompatible with; 'countMatches', 'prependLineNumbers', 'listFilesWithoutMatch' & 'verbose'."- | otherwise = commandOptions { listFilesWithMatches = True }---- | Mutator.-setListFilesWithoutMatch :: Transformer-setListFilesWithoutMatch commandOptions- | ($ commandOptions) `any` [- countMatches,- prependLineNumbers,- listFilesWithMatches,- verbose- ] = error "Grecce.CommandOptions.setListFilesWithoutMatch:\t'listFilesWithoutMatch' is incompatible with; 'countMatches', 'prependLineNumbers', 'listFilesWithMatches' & 'verbose'."- | otherwise = commandOptions { listFilesWithoutMatch = True }---- | Mutator, which additionally sets the corresponding 'RegExDot.ExecutionOptions.ExecutionOptions'.-setVerbose :: Transformer-setVerbose commandOptions- | ($ commandOptions) `any` [- countMatches,- invertMatch,- listFilesWithoutMatch,- listFilesWithMatches- ] = error "Grecce.CommandOptions.setVerbose:\t'verbose' is incompatible with; 'countMatches', 'invertMatch', 'listFilesWithoutMatch' & 'listFilesWithMatches'."- | otherwise = commandOptions {- verbose = True,- executionOptions = RegExDot.ExecutionOptions.setVerbose True $ executionOptions commandOptions- }---- | A function which accepts a 'String' & the 'RegExDot.CompilationOptions.CompilationOptions' to transform, using its value.-type CompilationOptionsMutator = String -> RegExDot.CompilationOptions.CompilationOptions -> RegExDot.CompilationOptions.CompilationOptions---- | Generic mutator, which accepts a function & the parameter-string it requires, to amend an unspecified field of 'RegExDot.CompilationOptions.CompilationOptions'.-setCompilationOption :: CompilationOptionsMutator -> String -> Transformer-setCompilationOption compilationOptionsMutator s commandOptions = commandOptions { compilationOptions = compilationOptionsMutator s $ compilationOptions commandOptions }---- | A function which accepts a 'String' & the 'RegExDot.ExecutionOptions.ExecutionOptions' to transform, using its value.-type ExecutionOptionsMutator = String -> RegExDot.ExecutionOptions.ExecutionOptions -> RegExDot.ExecutionOptions.ExecutionOptions---- | Generic mutator, which accepts a function & the parameter-string it requires, to amend an unspecified field of 'RegExDot.ExecutionOptions.ExecutionOptions'.-setExecutionOption :: ExecutionOptionsMutator -> String -> Transformer-setExecutionOption executionOptionsMutator s commandOptions = commandOptions { executionOptions = executionOptionsMutator s $ executionOptions commandOptions }-
− src/Grecce/Grep.hs
@@ -1,206 +0,0 @@-{-- Copyright (C) 2010-2015 Dr. Alistair Ward-- This program is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This program is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this program. If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@] Dr. Alistair Ward-- [@DESCRIPTION@] Performs the match-operation on the specified file-list, using the user's command-line options.--}--module Grecce.Grep(--- * Types--- LineNumber,--- Matches,--- ** Type-synonyms--- * Functions- grep-) where--import Control.Arrow((&&&))-import qualified Control.Arrow-import qualified Control.Monad-import qualified Control.Parallel.Strategies-import qualified Data.List-import qualified Grecce.CommandOptions as CommandOptions-import qualified RegExChar.ExtendedRegExChar as ExtendedRegExChar-import RegExChar.ExtendedRegExChar((+~), (=~))-import qualified RegExDot.RegEx-import qualified RegExDot.RegExOpts-import qualified RegExDot.Result-import qualified System.Exit-import qualified System.IO--type LineNumber = Int-type Matches = Int---- | Creates a 'RegExDot.RegExOpts.RegExOpts' record from 'CommandOptions.CommandOptions', and uses it to filter the lines read from the list of input-data files, the result of which is then printed.-grep- :: CommandOptions.CommandOptions -- ^ The match-criteria, which may include the name of the file on which to operate.- -> [String] -- ^ A supplementary list of files on which to operate- -> IO () -- ^ Nothing is returned, since the result is printed.-grep commandOptions nonOptions-{-- The algorithm depends fundamentally on whether the data comes from 'stdin' or from file.- This is because of the requirement, in the former case, to deliver instantaneous feedback.- This forces us to print matches as they're found, rather than batching them for return to the caller to print.- If we COULD return 'IO [String]', then 'matchFilterStdIn' could be called instead of 'grep' when (fileName == "-"); annoyingly, it just doesn't work.--}- | any ($ fileNames) [null, all (== "-")] = let- matchFilterStdIn :: LineNumber -> Matches -> IO System.Exit.ExitCode- matchFilterStdIn lineNumber matches = {-#SCC "matchFilterStdIn" #-} do- isEOF <- System.IO.isEOF-- if isEOF- then if listFilesWithMatches- then return {-to IO-monad-} $ System.Exit.ExitFailure 1 -- No match has been found, otherwise we'd have exited earlier.- else if listFilesWithoutMatch- then putStrLn dummyFileName >> return {-to IO-monad-} System.Exit.ExitSuccess- else do- Control.Monad.when countMatches $ print matches-- return {-to IO-monad-} $ if matches == 0- then System.Exit.ExitFailure 1- else System.Exit.ExitSuccess- else {-not EOF-} do- line <- getLine -- Process line-at-a-time, to provide instant feedback.-- let- lineNumber' :: LineNumber- lineNumber' = succ lineNumber-- onSuccess :: String -> IO System.Exit.ExitCode- onSuccess line'- | listFilesWithMatches = putStrLn dummyFileName >> return {-to IO-monad-} System.Exit.ExitSuccess- | listFilesWithoutMatch = return {-to IO-monad-} $ System.Exit.ExitFailure 1- | otherwise = do- Control.Monad.unless countMatches . putStrLn $ (- if prependLineNumbers- then showLineNumber lineNumber'- else id- ) line'-- matchFilterStdIn lineNumber' $ succ matches -- Recurse.-- onFailure, onMismatch :: IO System.Exit.ExitCode- onFailure = matchFilterStdIn lineNumber' matches -- Recurse- onMismatch- | invertMatch = onSuccess line- | otherwise = onFailure-- if verbose- then let- result :: RegExDot.RegEx.Result Char- result = line +~ regExOpts- in if RegExDot.Result.isMatch result- then if invertMatch- then onFailure- else onSuccess $ show result- else onMismatch- else {-terse-} if line =~ regExOpts- then if invertMatch- then onFailure- else onSuccess line- else onMismatch- where- dummyFileName :: String- dummyFileName = "<standard input>"- in matchFilterStdIn 0 0 >>= System.Exit.exitWith- | otherwise {-file-names provided-} = let- findMatch :: String -> RegExDot.RegEx.Result Char- findMatch = (+~ regExOpts)-- matchFilter :: [String] -> [String]- matchFilter = {-#SCC "matchFilter" #-} (- if countMatches- then return {-to List-monad-} . show . length -- Substitute the list of matches, with its length.- else id- ) . (- let- isMatch :: String -> Bool- isMatch = {-#SCC "isMatch" #-} (if invertMatch then not else id) . (=~ regExOpts)- in if prependLineNumbers- then map (uncurry showLineNumber) . (- if verbose- then map (- Control.Arrow.second show- ) . filter (- RegExDot.Result.isMatch . snd- ) . map (- Control.Arrow.second findMatch- )- else {-terse-} filter (isMatch . snd)- ) . zip [1 :: LineNumber ..]- else {-unnumbered-} if verbose- then map show . filter RegExDot.Result.isMatch . map findMatch- else {-terse-} filter isMatch- )-- identifySource :: String -> [String] -> [String]- identifySource fileName results- | listFilesWithMatches = [fileName | not isEmpty] {-list-comprehension-}- | listFilesWithoutMatch = [fileName | isEmpty] {-list-comprehension-}- | length (Data.List.nub fileNames) > 1 = (showString fileName . separate) `map` results- | otherwise = results- where- isEmpty :: Bool- isEmpty = null results- in do- results <- (- concat . Control.Parallel.Strategies.parMap Control.Parallel.Strategies.rdeepseq (- uncurry identifySource . Control.Arrow.second (matchFilter . {-#SCC "lines" #-} lines {-chops '\n'-})- ) . zip fileNames- ) `fmap` mapM readFile fileNames-- mapM_ putStrLn results-- System.Exit.exitWith $ if null results- then System.Exit.ExitFailure 1- else System.Exit.ExitSuccess- where- countMatches, invertMatch, prependLineNumbers, listFilesWithMatches, listFilesWithoutMatch, verbose :: Bool- [countMatches, invertMatch, prependLineNumbers, listFilesWithMatches, listFilesWithoutMatch, verbose] = ($ commandOptions) `map` [- CommandOptions.countMatches,- CommandOptions.invertMatch,- CommandOptions.prependLineNumbers,- CommandOptions.listFilesWithMatches,- CommandOptions.listFilesWithoutMatch,- CommandOptions.verbose- ] -- Divvy-up.---- Separate the regEx & the names of the input-data files to feed it.- extendedRegExChar :: ExtendedRegExChar.ExtendedRegExChar- fileNames :: [String]- (extendedRegExChar, fileNames) = case CommandOptions.extendedRegExChar commandOptions of- Just c -> (c, nonOptions)- _- | null nonOptions -> error "Undefined regex."- | otherwise -> read . head &&& tail $ nonOptions---- Combine the regex & options, to create a complete task-description.- regExOpts :: RegExDot.RegExOpts.RegExOpts ExtendedRegExChar.ExtendedRegExChar- regExOpts = RegExDot.RegExOpts.MkRegExOpts {- RegExDot.RegExOpts.compilationOptions = CommandOptions.compilationOptions commandOptions,- RegExDot.RegExOpts.executionOptions = CommandOptions.executionOptions commandOptions,- RegExDot.RegExOpts.regEx = extendedRegExChar- }---- Define functions used to format the output.- separate :: ShowS- separate = showChar ':'-- showLineNumber :: LineNumber -> ShowS- showLineNumber n = shows n . separate-
− src/Grecce/Test/Assert/RegExOptsChar.hs
@@ -1,93 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}-{-- Copyright (C) 2010-2015 Dr. Alistair Ward-- This program is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This program is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this program. If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@] Dr. Alistair Ward-- [@DESCRIPTION@]-- * 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 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-import qualified RegExDot.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 :: RegExDot.CompilationOptions.CompilationOptions -> RegExDot.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 parse " ++ show fileName ++ "; " ++ show line ++ "."- ) . filter (- (/= '#') . head -- Remove comments.- ) . filter (- not . null -- Remove blank lines.- ) . lines- ) `fmap` readFile fileName-
− src/Grecce/Test/Performance/ExtendedRegEx.hs
@@ -1,145 +0,0 @@-{-- Copyright (C) 2010-2015 Dr. Alistair Ward-- This program is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This program is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this program. If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@] Dr. Alistair Ward-- [@DESCRIPTION@]-- * 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 'RegExDot.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 Prelude hiding ((<$>), (<*>)) -- The "Prelude" from 'base-4.8' exports this symbol.-import Control.Applicative((<$>), (<*>))-import qualified Control.Arrow-import 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-import qualified RegExDot.RegEx-import qualified RegExDot.RegExOpts-import qualified Text.Regex.Posix-import qualified ToolShed.Defaultable-import qualified ToolShed.System.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 :: RegExDot.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 (- ToolShed.System.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 -> (- RegExDot.RegEx./~ RegExDot.RegExOpts.MkRegExOpts { -- CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.- RegExDot.RegExOpts.compilationOptions = ToolShed.Defaultable.defaultValue,- RegExDot.RegExOpts.executionOptions = executionOptions,- RegExDot.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 <$> (- (ToolShed.System.TimePure.getCPUSeconds . ($ inputData)) `mapM` [- showMatch,- if RegExDot.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 :: RegExDot.RegExOpts.RegExOpts (RegExDot.RegEx.ExtendedRegEx Char)- regExOpts = RegExDot.RegExOpts.MkRegExOpts {- RegExDot.RegExOpts.compilationOptions = ToolShed.Defaultable.defaultValue,- RegExDot.RegExOpts.executionOptions = executionOptions,- RegExDot.RegExOpts.regEx = extendedRegEx- } -- Similar to 'RegexLike.makeRegexOpts', but without the requirement to read the regex.- in if RegExDot.ExecutionOptions.requireMatchList executionOptions- then show . (RegExDot.RegEx.+~ regExOpts) -- CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.- else show . (RegExDot.RegEx.=~ regExOpts) -- CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.- ) &&& show . ExtendedRegExChar.MkExtendedRegExChar False {-hasNonCapturingTopLevelAlternatives-}- ) . testGenerator-
− src/Grecce/Test/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.Test.Performance.ExtendedRegExTest(--- * Type-classes- TestGeneratorRepository(..),--- * Types--- ** Type-synonyms- TestComplexity,- Test,- TestGenerator-) where--import qualified RegExDot.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 = (RegExDot.RegEx.InputData a, RegExDot.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
@@ -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.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
@@ -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.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-import qualified RegExDot.BracketExpressionMember-import RegExDot.DSL((-:), (?:), (*:), (<~>))-import qualified RegExDot.Meta-import qualified RegExDot.RegEx-import RegExDot.RegEx((.*), (.*?))-import qualified RegExDot.Repeatable-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 -> (RegExDot.RegEx.Pattern Char -> RegExDot.RegEx.RepeatablePattern Char) -> RegExDot.RegEx.RepeatablePattern Char- exactly n f = (RegExDot.RegEx.captureGroup . map (RegExDot.Anchor.unanchored <~>) . return {-to List-monad-} . return {-to List-monad-} $ f testRequirement) ^# n- in ((Just RegExDot.Anchor.Bow, Just RegExDot.Anchor.Stern) <~>) . (`map` [RegExDot.Repeatable.zeroOrOne, RegExDot.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")) &&& RegExDot.RegEx.dock . (- `mkExtendedRegExFromRepeatableSingletonAlternative` (RegExDot.Anchor.unanchored <~>) [- (.*),- RegExDot.RegEx.simply $ RegExDot.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 RegExDot.Anchor.Bow, Just RegExDot.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 (- RegExDot.Anchor.unanchored <~> RegExDot.RegEx.captureGroup (- map (- (RegExDot.Anchor.unanchored <~>) . return {-to List-monad-} . (testRequirement ^#) . (2 ^)- ) [0 :: Int .. 1]- ) *: RegExDot.RegEx.Require (RegExDot.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 RegExDot.Anchor.Bow, Just RegExDot.Anchor.Stern) <~>) . return {-to List-monad-} . RegExDot.Repeatable.zeroOrMore . RegExDot.RegEx.captureGroup . map (RegExDot.Anchor.unanchored <~>) $ [RegExDot.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 RegExDot.Anchor.Bow, Just RegExDot.Anchor.Stern) <~>) . replicate i . RegExDot.Repeatable.one . RegExDot.RegEx.captureGroup . map (RegExDot.Anchor.unanchored <~>) $ [RegExDot.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 :: RegExDot.Repeatable.Repetitions- base = 2 -- Arbitrarily.-- bounds :: RegExDot.Repeatable.RepetitionBounds- bounds = (0, Just base)- in (`replicate` testChar) . (base ^) &&& RegExDot.RegEx.dock . (- (- mkExtendedRegExFromRepeatableSingletonAlternative (^#-># bounds) `iterate` (RegExDot.Anchor.unanchored <~>) [testRequirement ^#-># bounds] {-initial value-}- ) !!- ) . pred- ),-{-- "^((((((((((((((<testChar>*?)*?)*?)*?)*?)*?)*?)*?)*?)*?)*?)*?)*?)*?)*? ... $".- Explanation: interesting because all 'RegExDot.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) &&& RegExDot.RegEx.dock . (- (- mkExtendedRegExFromRepeatableSingletonAlternative RegExDot.Repeatable.zeroOrMore' `iterate` (RegExDot.Anchor.unanchored <~>) [RegExDot.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 RegExDot.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 :: RegExDot.Repeatable.Repetitions- base = 2 -- Arbitrarily.- in (`replicate` testChar) . (base ^) &&& RegExDot.RegEx.dock . head . (- (- iterate (- \extendedRegExList -> (- (RegExDot.Anchor.unanchored <~>) . return {-to List-monad-} . (^# base) . RegExDot.RegEx.captureGroup- ) `map` [extendedRegExList, reverse extendedRegExList]- ) $ map (- (RegExDot.Anchor.unanchored <~>) . return {-to List-monad-} . ($ RegExDot.RegEx.Require $ RegExDot.Meta.Literal testChar)- ) [RegExDot.Repeatable.one, RegExDot.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 RegExDot.Anchor.Bow, Just RegExDot.Anchor.Stern) <~>) $ map RegExDot.Repeatable.zeroOrMore [- RegExDot.RegEx.captureGroup . map (RegExDot.Anchor.unanchored <~>) $ (- \i -> ($ i) `map` [- (testRequirement ^#),- RegExDot.RegEx.simply . RegExDot.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 :: [RegExDot.RegEx.Concatenation Char] -> RegExDot.RegEx.Concatenation Char- zeroOrMoreAlternatives = return {-to List-monad-} . RegExDot.Repeatable.zeroOrMore . RegExDot.RegEx.captureGroup . map (RegExDot.Anchor.unanchored <~>)- in (- id &&& (- ((Just RegExDot.Anchor.Bow, Just RegExDot.Anchor.Stern) <~>) . (RegExDot.RegEx.Require (RegExDot.Meta.Literal testChar) ?:) . zeroOrMoreAlternatives . map (- \(a, b) -> zeroOrMoreAlternatives $ return {-to List-monad-} [a, b]- ) . init . tail . uncurry zip . (- map RegExDot.Repeatable.one . init &&& map RegExDot.Repeatable.zeroOrOne . tail- ) . bracket undefined . map (- RegExDot.RegEx.Require . RegExDot.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' = succ complexity-- testSet :: String- testSet = take complexity' . filter Data.Char.isAlphaNum $ filter Data.Char.isAscii [minBound .. maxBound]- in (- concat $ replicate complexity' testSet,- ((Just RegExDot.Anchor.Bow, Just RegExDot.Anchor.Stern) <~>) $ (- RegExDot.RegEx.captureGroup . map (RegExDot.Anchor.unanchored <~>) $ map (- return {-to List-monad-} . RegExDot.RegEx.simply . RegExDot.Meta.Literal- ) testSet- ) *: [- (- RegExDot.RegEx.captureGroup . map (RegExDot.Anchor.unanchored <~>) . return {-to List-monad-} $ RegExDot.RegEx.Require (RegExDot.Meta.Literal $ head testSet) -: (.*?) : RegExDot.RegEx.Require (RegExDot.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 RegExDot.Anchor.Bow, Just RegExDot.Anchor.Stern) <~>) . return {-to List-monad-} . RegExDot.Repeatable.zeroOrMore . RegExDot.RegEx.captureGroup . map (RegExDot.Anchor.unanchored <~>) . (- [- RegExDot.Repeatable.zeroOrMore testRequirement,- RegExDot.RegEx.simply . RegExDot.Meta.NoneOf $ map RegExDot.BracketExpressionMember.Literal [testChar]- ] :- ) . map (- return {-to List-monad-} . RegExDot.RegEx.simply . RegExDot.Meta.NoneOf . return {-to List-monad-} . RegExDot.BracketExpressionMember.Literal- ) . (- `take` tail [testChar ..]- )- )- )- ]- ) where- testChar :: Char- testChar = 'a'-- testRequirement :: RegExDot.RegEx.Pattern Char- testRequirement = RegExDot.RegEx.Require $ RegExDot.Meta.Literal testChar-- mkExtendedRegExFromRepeatableSingletonAlternative :: (RegExDot.RegEx.Pattern Char -> RegExDot.RegEx.RepeatablePattern Char) -> RegExDot.RegEx.ExtendedRegEx Char -> RegExDot.RegEx.ExtendedRegEx Char- mkExtendedRegExFromRepeatableSingletonAlternative makeRepetitions = (RegExDot.Anchor.unanchored <~>) . return {-to List-monad-} . makeRepetitions . RegExDot.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 * succ i `div` 2
− src/Grecce/Test/QC/ExtendedRegExChar.hs
@@ -1,188 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-{-- Copyright (C) 2010-2015 Dr. Alistair Ward-- This program is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This program is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this program. If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@] Dr. Alistair Ward-- [@DESCRIPTION@] 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 qualified Data.Maybe-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-import qualified RegExDot.BracketExpressionMember-import qualified RegExDot.Consumer-import qualified RegExDot.ConsumptionBounds-import qualified RegExDot.ConsumptionProfile-import RegExDot.DSL((-:), (?:), (+:), (<~>))-import qualified RegExDot.Meta-import qualified RegExDot.RegEx-import RegExDot.RegEx((+~))-import qualified RegExDot.RegExOpts-import qualified RegExDot.Repeatable-import qualified RegExDot.Result-import qualified Test.QuickCheck-import Test.QuickCheck((==>))-import qualified ToolShed.SelfValidate-import qualified ToolShed.Test.ReversibleIO---- | A specialised instance, required to instantiate 'Test.QuickCheck.Arbitrary'.-newtype AlternativesChar = MkAlternativesChar (RegExDot.RegEx.Alternatives Char) deriving (Eq, Read, Show)---- | Accessor.-deconstructAlternativesChar :: AlternativesChar -> RegExDot.RegEx.Alternatives Char-deconstructAlternativesChar (MkAlternativesChar a) = a--instance ToolShed.SelfValidate.SelfValidator AlternativesChar where- getErrors = ToolShed.SelfValidate.getErrors . deconstructAlternativesChar--instance RegExDot.Consumer.Consumer AlternativesChar where- consumptionProfile = RegExDot.Consumer.consumptionProfile . deconstructAlternativesChar- starHeight = RegExDot.Consumer.starHeight . deconstructAlternativesChar--instance Test.QuickCheck.Arbitrary AlternativesChar where- arbitrary = (MkAlternativesChar . RegExDot.RegEx.MkAlternatives . map ExtendedRegExChar.extendedRegEx) `fmap` (- Test.QuickCheck.elements [1, 2] >>= Test.QuickCheck.vector {-[ExtendedRegExChar]-}- ) -- TODO: permit zero alternatives.---- | A specialised instance, required to instantiate 'Test.QuickCheck.Arbitrary'.-newtype PatternChar = MkPatternChar (RegExDot.RegEx.Pattern Char) deriving (Eq, Read, Show)---- | Accessor.-deconstructPatternChar :: PatternChar -> RegExDot.RegEx.Pattern Char-deconstructPatternChar (MkPatternChar pattern) = pattern--instance ToolShed.SelfValidate.SelfValidator PatternChar where- getErrors = ToolShed.SelfValidate.getErrors . deconstructPatternChar--instance RegExDot.Consumer.Consumer PatternChar where- consumptionProfile = RegExDot.Consumer.consumptionProfile . deconstructPatternChar- starHeight = RegExDot.Consumer.starHeight . deconstructPatternChar--instance Test.QuickCheck.Arbitrary PatternChar where- arbitrary = MkPatternChar `fmap` Test.QuickCheck.frequency [- (4, (RegExDot.RegEx.Require . MetaChar.deconstruct) `fmap` Test.QuickCheck.arbitrary {-MetaChar-}),- (1, (RegExDot.RegEx.CaptureGroup . deconstructAlternativesChar) `fmap` Test.QuickCheck.arbitrary {-AlternativesChar-})- ] -- CAVEAT: may recurse forever if 'RegExDot.RegEx.CaptureGroup / RegExDot.RegEx.Require' is too high.---- | A specialised instance, required to instantiate 'Test.QuickCheck.Arbitrary'.-newtype RepeatablePatternChar = MkRepeatablePatternChar (RegExDot.Repeatable.Repeatable PatternChar) deriving (Eq, Read, Show)---- | Accessor.-deconstructRepeatablePatternChar :: RepeatablePatternChar -> RegExDot.Repeatable.Repeatable PatternChar-deconstructRepeatablePatternChar (MkRepeatablePatternChar repeatable) = repeatable--instance ToolShed.SelfValidate.SelfValidator RepeatablePatternChar where- getErrors = ToolShed.SelfValidate.getErrors . deconstructRepeatablePatternChar--instance RegExDot.Consumer.Consumer RepeatablePatternChar where- consumptionProfile = RegExDot.Consumer.consumptionProfile . deconstructRepeatablePatternChar- starHeight = RegExDot.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 `fmap` Test.QuickCheck.elements [max fewest 1 .. 9]]- isGreedy <- Test.QuickCheck.arbitrary {-Bool-}-- let- repetitionBounds :: RegExDot.Repeatable.RepetitionBounds- repetitionBounds = (fewest, most)-- return {-to Gen-monad-} $ MkRepeatablePatternChar RegExDot.Repeatable.MkRepeatable {- RegExDot.Repeatable.base = patternChar,- RegExDot.Repeatable.repetitionBounds = repetitionBounds,- RegExDot.Repeatable.isGreedy = RegExDot.Repeatable.hasPreciseBounds repetitionBounds || isGreedy -- Only specify non-greedy where space exists.- }--instance Test.QuickCheck.Arbitrary ExtendedRegExChar.ExtendedRegExChar where- arbitrary = do- hasBowAnchor <- Test.QuickCheck.arbitrary {-Bool-}- concatenation <- map (fmap deconstructPatternChar {-replace base-} . deconstructRepeatablePatternChar {-RegExDot.Repeatable.Repeatable PatternChar-}) `fmap` Test.QuickCheck.vector 2 {-[RepeatablePatternChar]-}- hasSternAnchor <- Test.QuickCheck.arbitrary {-Bool-}-- return {-to Gen-monad-} $ ExtendedRegExChar.MkExtendedRegExChar False RegExDot.RegEx.MkExtendedRegEx {- RegExDot.RegEx.bowAnchor = if hasBowAnchor then Just RegExDot.Anchor.Bow else Nothing,- RegExDot.RegEx.concatenation = concatenation,- RegExDot.RegEx.sternAnchor = if hasSternAnchor then Just RegExDot.Anchor.Stern else Nothing- }--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" $ Data.Maybe.maybe True (>= minData) maybeMaxData && (- not b == all (not . RegExDot.Consumer.getHasSpecificRequirement) (RegExDot.RegEx.concatenation $ ExtendedRegExChar.extendedRegEx r)- )- where- RegExDot.ConsumptionProfile.MkConsumptionProfile {- RegExDot.ConsumptionProfile.consumptionBounds = (minData, maybeMaxData),- RegExDot.ConsumptionProfile.hasSpecificRequirement = b- } = RegExDot.Consumer.consumptionProfile r-- prop_consumptionProfile2 r = RegExDot.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- RegExDot.ConsumptionProfile.MkConsumptionProfile {- RegExDot.ConsumptionProfile.hasSpecificRequirement = hasSpecificRequirement,- RegExDot.ConsumptionProfile.canConsumeAnything = canConsumeAnything- } = RegExDot.Consumer.consumptionProfile r-- prop_io = Test.QuickCheck.label "prop_io" . ToolShed.Test.ReversibleIO.isReversible- prop_io' r = Test.QuickCheck.label "prop_io'" $ ToolShed.Test.ReversibleIO.isReversible r' where r' = ExtendedRegExChar.extendedRegEx r -- Check "RegExDot.RegEx.ExtendedRegEx Char" too.- prop_isValid r = Test.QuickCheck.label "prop_isValid" $ ToolShed.SelfValidate.isValid r- prop_starHeight r = Test.QuickCheck.label "prop_starHeight" $ (RegExDot.Consumer.starHeight r == 0) == RegExDot.ConsumptionBounds.isPrecise (RegExDot.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" $ RegExDot.RegEx.extractDataFromMatchList `fmap` RegExDot.Result.getMatchList result == Just s where- s :: String- s = show $ d * 1e6 -- Implementation of show, uses exponential notation, when the number is large.-- result :: RegExDot.RegEx.Result Char- result = s +~ RegExDot.RegExOpts.mkRegEx (- (Just RegExDot.Anchor.Bow, Just RegExDot.Anchor.Stern) <~> sign ?: digits +: map (- RegExDot.Repeatable.zeroOrOne . RegExDot.RegEx.captureGroup . map (RegExDot.Anchor.unanchored <~>) . return {-to List-monad-}- ) [- RegExDot.RegEx.Require (RegExDot.Meta.Literal '.') -: digits +: [],- RegExDot.RegEx.Require (RegExDot.Meta.AnyOf $ map RegExDot.BracketExpressionMember.Literal "eE") -: sign ?: digits +: []- ]- ) where- sign, digits :: RegExDot.RegEx.Pattern Char- sign = RegExDot.RegEx.Require . RegExDot.Meta.AnyOf $ map RegExDot.BracketExpressionMember.Literal "+-"- digits = RegExDot.RegEx.Require . RegExDot.Meta.AnyOf $ map RegExDot.BracketExpressionMember.Literal ['0' .. '9']-
− src/Grecce/Test/QC/MetaChar.hs
@@ -1,67 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-{-- Copyright (C) 2010-2015 Dr. Alistair Ward-- This program is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This program is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this program. If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@] Dr. Alistair Ward-- [@DESCRIPTION@] 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 qualified Data.List-import qualified RegExChar.MetaChar as MetaChar -- CAVEAT: beware of the similar name.-import qualified RegExDot.BracketExpression-import qualified RegExDot.BracketExpressionMember-import qualified RegExDot.Meta-import qualified Test.QuickCheck-import qualified ToolShed.Test.ReversibleIO--testCharacters :: String-testCharacters = filter (`notElem` [RegExDot.Meta.shortcutToken, RegExDot.BracketExpression.negationToken]) [' ' .. '~'] -- TODO: permit all characters.--instance Test.QuickCheck.Arbitrary MetaChar.MetaChar where- arbitrary = Test.QuickCheck.oneof $ map (fmap MetaChar.MkMetaChar) [- return {-to Gen-monad-} RegExDot.Meta.Any,- RegExDot.Meta.Literal `fmap` Test.QuickCheck.elements testCharacters,- (RegExDot.Meta.AnyOf . map RegExDot.BracketExpressionMember.Literal . Data.List.nub) `fmap` arbitraryCharList 1 16, -- Test.QuickCheck.vector 16 {-Char-},- (RegExDot.Meta.NoneOf . map RegExDot.BracketExpressionMember.Literal . Data.List.nub) `fmap` 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)) `fmap` Test.QuickCheck.vector maxBound {-Char-}--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 = Test.QuickCheck.label "prop_io" . ToolShed.Test.ReversibleIO.isReversible-
− src/Grecce/Test/QC/QuickChecks.hs
@@ -1,49 +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@] Calls the /quickChecks/-functions for modules supporting this feature.--}--module Grecce.Test.QC.QuickChecks(--- * Functions- run-) where--import qualified Control.Arrow-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 = mapM_ (- uncurry (>>) . Control.Arrow.first putStrLn- ) [- ("MetaChar", Test.QC.MetaChar.quickChecks checker),- ("RepeatableMetaChar", Test.QC.RepeatableMetaChar.quickChecks checker),- ("ExtendedRegExChar", Test.QC.ExtendedRegExChar.quickChecks checker)- ] where- checker :: Test.QuickCheck.Testable t => t -> IO ()- checker- | isVerbose = Test.QuickCheck.verboseCheck- | otherwise = Test.QuickCheck.quickCheck-
− src/Grecce/Test/QC/RepeatableMetaChar.hs
@@ -1,92 +0,0 @@-{-- Copyright (C) 2010-2015 Dr. Alistair Ward-- This program is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This program is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this program. If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@] Dr. Alistair Ward-- [@DESCRIPTION@] 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 qualified Data.Maybe-import Grecce.Test.QC.MetaChar()-import qualified RegExChar.MetaChar as MetaChar-import qualified RegExDot.Consumer-import qualified RegExDot.ConsumptionBounds-import qualified RegExDot.ConsumptionProfile-import qualified RegExDot.Repeatable-import qualified Test.QuickCheck-import qualified ToolShed.SelfValidate-import qualified ToolShed.Test.ReversibleIO---- | A specialised instance, required to instantiate 'Test.QuickCheck.Arbitrary'.-newtype RepeatableMetaChar = MkRepeatableMetaChar (RegExDot.Repeatable.Repeatable MetaChar.MetaChar) deriving (Eq, Read, Show)---- | Accessor.-deconstruct :: RepeatableMetaChar -> RegExDot.Repeatable.Repeatable MetaChar.MetaChar-deconstruct (MkRepeatableMetaChar repeatableMetaChar) = repeatableMetaChar--instance ToolShed.SelfValidate.SelfValidator RepeatableMetaChar where- getErrors = ToolShed.SelfValidate.getErrors . deconstruct--instance RegExDot.Consumer.Consumer RepeatableMetaChar where- consumptionProfile = RegExDot.Consumer.consumptionProfile . deconstruct- starHeight = RegExDot.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 `fmap` Test.QuickCheck.elements [max fewest 1 .. 9]]- isGreedy <- Test.QuickCheck.arbitrary-- let- repetitionBounds :: RegExDot.Repeatable.RepetitionBounds- repetitionBounds = (fewest, most)-- return {-to Gen-monad-} . MkRepeatableMetaChar $ RegExDot.Repeatable.MkRepeatable {- RegExDot.Repeatable.base = metaChar,- RegExDot.Repeatable.repetitionBounds = repetitionBounds,- RegExDot.Repeatable.isGreedy = RegExDot.Repeatable.hasPreciseBounds repetitionBounds || isGreedy -- Only specify 'non-greedy' where space exists.- }--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" $ Data.Maybe.maybe True (>= minData) maybeMaxData && b == RegExDot.Consumer.getHasSpecificRequirement (RegExDot.Repeatable.base $ deconstruct r) where RegExDot.ConsumptionProfile.MkConsumptionProfile {- RegExDot.ConsumptionProfile.consumptionBounds = (minData, maybeMaxData),- RegExDot.ConsumptionProfile.hasSpecificRequirement = b- } = RegExDot.Consumer.consumptionProfile r-- prop_io (MkRepeatableMetaChar r) = Test.QuickCheck.label "prop_io" $ ToolShed.Test.ReversibleIO.isReversible r-- prop_isValid r = Test.QuickCheck.label "prop_isValid" $ ToolShed.SelfValidate.isValid r-- prop_starHeight (MkRepeatableMetaChar r) = Test.QuickCheck.label "prop_starHeight" $ RegExDot.Consumer.starHeight r == if RegExDot.ConsumptionBounds.isPrecise (RegExDot.Consumer.getConsumptionBounds r) then 0 else 1-
− src/Main.hs
@@ -1,207 +0,0 @@-{-- Copyright (C) 2010-2015 Dr. Alistair Ward-- This program is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This program is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this program. If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@] Dr. Alistair Ward-- [@DESCRIPTION@]-- * Contains the entry-point to the program.-- * Provides command-line access to "ExtendedRegExChar".-- * Defines the permissible command-line arguments; aping /egrep/, but with the addition of /--verbose/, which enables one to see the details of the data-capture.-- [@TODO@] Coordinate the output of 'printVersion' with the definition in '.cabal'; this may not be feasible.--}--module Main(main) where--import qualified Data.List-import qualified Data.Version-import qualified Distribution.Package-import qualified Distribution.Text-import qualified Distribution.Version-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 Paths_regexchar as Paths -- Either local stub, or package-instance autogenerated by 'Setup.hs build'.-import qualified RegExDot.CompilationOptions-import qualified RegExDot.ExecutionOptions-import qualified System.Console.GetOpt as G-import qualified System.Environment-import qualified System.Exit-import qualified System.IO-import qualified System.IO.Error-import qualified ToolShed.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'.---- | On failure to parse the specified string, returns an explanatory error.-read' :: Read a => String -> String -> a-read' errorMessage s = case reads s of- [(x, "")] -> x- _ -> error $ errorMessage ++ show s---- | On failure to parse a command-line argument, returns an explanatory error.-readCommandArg :: Read a => String -> a-readCommandArg = read' "failed to parse command-line argument "--{- |- * Parses the command-line arguments, to determine 'CommandOptions.CommandOptions'.-- * Arguments following 'CommandOptions.CommandOptions', are interpreted as a /regex/, followed by the names of the files to which it is applied.--}-main :: IO ()-main = do- progName <- System.Environment.getProgName-- let- usageMessage :: String- usageMessage = "Usage:\t" ++ G.usageInfo progName optDescrList ++ " <regex> [<file-name>, ...]"---- Define the command-line options, & the 'CommandLineAction's used to handle them.- optDescrList :: [G.OptDescr CommandLineAction]- optDescrList = [--- String [String] (G.ArgDescr CommandLineAction) String- G.Option "?" ["help"] (G.NoArg $ const printUsage) "Output:\tdisplay this help-text & then exit.",- G.Option "" ["verbose"] (G.NoArg $ return {-to IO-monad-} . CommandOptions.setVerbose) ("Output:\tsee how the input data was captured by the RegEx; default '" ++ show (CommandOptions.verbose ToolShed.Defaultable.defaultValue) ++ "'. CAVEAT: to be effective, it should precede other options."),- G.Option "" ["version"] (G.NoArg $ const printVersion) "Output:\tprint version-information & then exit.",- G.Option "c" ["count"] (G.NoArg $ return {-to IO-monad-} . CommandOptions.setCountMatches) ("Output:\tprint only a count of matching lines per file; default '" ++ show (CommandOptions.countMatches ToolShed.Defaultable.defaultValue) ++ "'."),- G.Option "l" ["files-with-matches"] (G.NoArg $ return {-to IO-monad-} . CommandOptions.setListFilesWithMatches) ("Output:\tlist names of files containing at least one match; default '" ++ show (CommandOptions.listFilesWithMatches ToolShed.Defaultable.defaultValue) ++ "'."),- G.Option "L" ["files-without-match"] (G.NoArg $ return {-to IO-monad-} . CommandOptions.setListFilesWithoutMatch) ("Output:\tlist names of files not containing any matches; default '" ++ show (CommandOptions.listFilesWithoutMatch ToolShed.Defaultable.defaultValue) ++ "'."),- G.Option "n" ["number"] (G.NoArg $ return {-to IO-monad-} . CommandOptions.setPrependLineNumbers) ("Output:\tprepend the line-number of the input, to the output; default '" ++ show (CommandOptions.prependLineNumbers ToolShed.Defaultable.defaultValue) ++ "'."),- G.Option "v" ["invert-match"] (G.NoArg $ return {-to IO-monad-} . CommandOptions.setInvertMatch) ("Output:\tselect non-matching lines; default '" ++ show (CommandOptions.invertMatch ToolShed.Defaultable.defaultValue) ++ "'."),- G.Option "" ["printCurrentSettings"] (G.NoArg printCurrentSettings) "Output:\tdisplay the currently defined options & then exit.",- G.Option "e" ["regex"] (setExtendedRegExChar `G.ReqArg` "<regex>") "Input:\tdefine the regex.",- G.Option "f" ["file"] (readExtendedRegExCharFromFile `G.ReqArg` "<file-name>") "Input:\tread the regex from file.",- G.Option "" ["performance"] (- extendedRegExCharPerformance `G.ReqArg` "(Int,Either [TestName])"- ) (- "Test:\tcompare performance with other RegEx-engines, using the specified pair (<Maximum complexity>, Left " ++ show [- minBound :: Test.Performance.ExtendedRegExTestsNegative.TestName .. maxBound- ] ++ " or Right " ++ show [- 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.",- G.Option "" ["assert"] (assertFromFile `G.ReqArg` "<file-name>") "Test:\trun the assertions, defined in the specified file, & then exit. NB: specify this option last.",- G.Option "" ["assertPosix"] (assertPosixFromFile `G.ReqArg` "<file-name>") "Test:\trun the assertions, defined in the specified file, against Text.Regex.Posix, & then exit. NB: specify this option last.",- G.Option "" ["abortTrialRepetitionsOnInherentFailure"] (setExecutionOption abortTrialRepetitionsOnInherentFailure `G.ReqArg` "Bool") ("Performance:\tif an alternative can't match, irrespective of the subsequent concatenation, then terminate futile trial repetitions; default '" ++ show (RegExDot.ExecutionOptions.abortTrialRepetitionsOnInherentFailure ToolShed.Defaultable.defaultValue) ++ "'."),--- G.Option "" ["abortTrialRepetitionsOnZeroConsumption"] (setExecutionOption abortTrialRepetitionsOnZeroConsumption `G.ReqArg` "Bool") ("Performance:\tcheck for zero data-consumption by the n-th repetition of an alternative, before considering (n + 1); default '" ++ show (RegExDot.ExecutionOptions.abortTrialRepetitionsOnZeroConsumption ToolShed.Defaultable.defaultValue) ++ "'."),--- G.Option "" ["bypassInputDataForLiberalConsumer"] (setExecutionOption bypassInputDataForLiberalConsumer `G.ReqArg` "Bool") ("Performance:\tbypass reading of the unconsumed input data, if the remaining RegEx can consume a sufficient quantity of anything; default '" ++ show (RegExDot.ExecutionOptions.bypassInputDataForLiberalConsumer ToolShed.Defaultable.defaultValue) ++ "'."),- G.Option "" ["catchIncompatibleAnchors"] (setExecutionOption catchIncompatibleAnchors `G.ReqArg` "Bool") ("Performance:\tavoid futile trial solutions, involving repetitions of anchored alternatives, which must consume data; default '" ++ show (RegExDot.ExecutionOptions.catchIncompatibleAnchors ToolShed.Defaultable.defaultValue) ++ "'."),- G.Option "" ["checkExistenceOfInelasticTail"] (setExecutionOption checkExistenceOfInelasticTail `G.ReqArg` "Bool") ("Performance:\tif the regex ends in one or more Meta-data, whose repetition-range is precisely defined, check that this sequence exists at the end of the input data; default '" ++ show (RegExDot.ExecutionOptions.checkExistenceOfInelasticTail ToolShed.Defaultable.defaultValue) ++ "'."),- G.Option "" ["checkForUnconsumableData"] (setExecutionOption checkForUnconsumableData `G.ReqArg` "Bool") ("Performance:\tcheck whether there's no possibility of consuming some of the input data; default '" ++ show (RegExDot.ExecutionOptions.checkForUnconsumableData ToolShed.Defaultable.defaultValue) ++ "'."),- G.Option "" ["moderateGreed"] (setExecutionOption moderateGreed `G.ReqArg` "Bool") ("Performance:\tgreedily consume data, only up to the limit beyond which, future requirements would be compromised; default '" ++ show (RegExDot.ExecutionOptions.moderateGreed ToolShed.Defaultable.defaultValue) ++ "'."),- G.Option "" ["permitReorderingOfAlternatives"] (setExecutionOption permitReorderingOfAlternatives `G.ReqArg` "Bool") ("Performance:\tpermit alternatives to be re-ordered, in an attempt to process the cheapest first; default '" ++ show (RegExDot.ExecutionOptions.permitReorderingOfAlternatives ToolShed.Defaultable.defaultValue) ++ "'."),- G.Option "" ["preferAlternativesWhichFeedTheGreedy"] (setExecutionOption preferAlternativesWhichFeedTheGreedy `G.ReqArg` "Bool") ("Preference:\tprefer solutions in which the choice of alternatives directs data from non-greedy to greedy consumers; default '" ++ show (RegExDot.ExecutionOptions.preferAlternativesWhichFeedTheGreedy ToolShed.Defaultable.defaultValue) ++ "'."),- G.Option "" ["preferAlternativesWhichMimickUnrolling"] (setExecutionOption preferAlternativesWhichMimickUnrolling `G.ReqArg` "Bool") ("Preference:\tprefer solutions in which the choice of alternatives consumes data like the unrolled repeatable group; default '" ++ show (RegExDot.ExecutionOptions.preferAlternativesWhichMimickUnrolling ToolShed.Defaultable.defaultValue) ++ "'."),- G.Option "" ["preferFewerRepeatedAlternatives"] (setExecutionOption preferFewerRepeatedAlternatives `G.ReqArg` "Bool") ("Preference:\tprefer solutions employing fewer repetitions of alternatives, to discourage the capture of null lists; default '" ++ show (RegExDot.ExecutionOptions.preferFewerRepeatedAlternatives ToolShed.Defaultable.defaultValue) ++ "'."),- G.Option "" ["unrollRepeatedSingletonAlternative"] (setExecutionOption unrollRepeatedSingletonAlternative `G.ReqArg` "Bool") ("Performance:\tunroll repetitions of singleton alternatives; this doesn't affect the result; default '" ++ show (RegExDot.ExecutionOptions.unrollRepeatedSingletonAlternative ToolShed.Defaultable.defaultValue) ++ "'."),- G.Option "" ["useFirstMatchAmongAlternatives"] (setExecutionOption useFirstMatchAmongAlternatives `G.ReqArg` "Bool") ("Performance:\trather than performing an exhaustive search for the optimal choice amongst alternatives, merely select the first that matches; conform to Perl rather than Posix; default '" ++ show (RegExDot.ExecutionOptions.useFirstMatchAmongAlternatives ToolShed.Defaultable.defaultValue) ++ "'."),- G.Option "" ["validateMinConsumptionOfAlternatives"] (setExecutionOption validateMinConsumptionOfAlternatives `G.ReqArg` "Bool") ("Performance:\twhen the number of repetitions of a group of alternatives is precisely specified, check the availability of the resulting minimum data-requirement; default '" ++ show (RegExDot.ExecutionOptions.validateMinConsumptionOfAlternatives ToolShed.Defaultable.defaultValue) ++ "'."),- G.Option "" ["complyStrictlyWithPosix"] (setCompilationOption complyStrictlyWithPosix `G.ReqArg` "Bool") ("Output:\tdefine the offset of captured data, corresponding to a sub-expression which matched zero times, as the artificial value -1 specified by Posix; currently only affects results of 'assert'; default '" ++ show (RegExDot.CompilationOptions.complyStrictlyWithPosix ToolShed.Defaultable.defaultValue) ++ "'.")- ] where- printVersion, printUsage :: IO CommandOptions.CommandOptions-- printVersion = System.IO.hPutStrLn System.IO.stderr (Distribution.Text.display packageIdentifier ++ "\n\nCopyright (C) 2010-2015 " ++ author ++ ".\nThis program comes with ABSOLUTELY NO WARRANTY.\nThis is free software, and you are welcome to redistribute it under certain conditions.\n\nWritten by " ++ author ++ ".") >> System.Exit.exitWith System.Exit.ExitSuccess where- packageIdentifier :: Distribution.Package.PackageIdentifier- packageIdentifier = Distribution.Package.PackageIdentifier {- Distribution.Package.pkgName = Distribution.Package.PackageName "grecce",- Distribution.Package.pkgVersion = Distribution.Version.Version (Data.Version.versionBranch Paths.version) []- }-- author :: String- author = "Dr. Alistair Ward"-- printUsage = System.IO.hPutStrLn System.IO.stderr usageMessage >> System.Exit.exitWith System.Exit.ExitSuccess-- printCurrentSettings, runQuickChecks :: CommandLineAction- printCurrentSettings commandOptions = print commandOptions >> System.Exit.exitWith System.Exit.ExitSuccess- runQuickChecks commandOptions = Test.QC.QuickChecks.run (CommandOptions.verbose commandOptions) >> System.Exit.exitWith System.Exit.ExitSuccess-- extendedRegExCharPerformance, readExtendedRegExCharFromFile, assertFromFile, assertPosixFromFile, setExtendedRegExChar :: String -> CommandLineAction- extendedRegExCharPerformance s commandOptions = (- uncurry $ Test.Performance.ExtendedRegEx.run (CommandOptions.executionOptions commandOptions)- ) (readCommandArg s) >> System.Exit.exitWith System.Exit.ExitSuccess-- readExtendedRegExCharFromFile s commandOptions = ((head . dropWhile null . lines) `fmap` readFile s) >>= (`setExtendedRegExChar` commandOptions)-- assertFromFile fileName commandOptions = do- failedTests <- Test.Assert.RegExOptsChar.findFailures (CommandOptions.compilationOptions commandOptions) (CommandOptions.executionOptions commandOptions) `fmap` Test.Assert.RegExOptsChar.readTests fileName-- if null failedTests- then System.Exit.exitWith System.Exit.ExitSuccess- else do- mapM_ print failedTests-- System.Exit.exitWith $ System.Exit.ExitFailure 2-- assertPosixFromFile fileName _ = do- failedTests <- Test.Assert.RegExOptsChar.findFailuresPosix `fmap` Test.Assert.RegExOptsChar.readTests fileName-- if null failedTests- then System.Exit.exitWith System.Exit.ExitSuccess- else do- mapM_ print failedTests-- System.Exit.exitWith $ System.Exit.ExitFailure 2-- setExtendedRegExChar s commandOptions = case CommandOptions.extendedRegExChar commandOptions of- Just extendedRegExChar -> fail $ "The regex has already been defined as /" ++ show extendedRegExChar ++ "/"- _ -> return {-to IO-monad-} commandOptions { CommandOptions.extendedRegExChar = Just $ readCommandArg s }-- abortTrialRepetitionsOnInherentFailure, {-abortTrialRepetitionsOnZeroConsumption, bypassInputDataForLiberalConsumer,-} catchIncompatibleAnchors, checkExistenceOfInelasticTail, checkForUnconsumableData, moderateGreed, permitReorderingOfAlternatives, preferAlternativesWhichFeedTheGreedy, preferAlternativesWhichMimickUnrolling, preferFewerRepeatedAlternatives, unrollRepeatedSingletonAlternative, useFirstMatchAmongAlternatives, validateMinConsumptionOfAlternatives :: CommandOptions.ExecutionOptionsMutator- abortTrialRepetitionsOnInherentFailure s e = e { RegExDot.ExecutionOptions.abortTrialRepetitionsOnInherentFailure = readCommandArg s }--- abortTrialRepetitionsOnZeroConsumption s e = e { RegExDot.ExecutionOptions.abortTrialRepetitionsOnZeroConsumption = readCommandArg s }--- bypassInputDataForLiberalConsumer s e = e { RegExDot.ExecutionOptions.bypassInputDataForLiberalConsumer = readCommandArg s }- catchIncompatibleAnchors s e = e { RegExDot.ExecutionOptions.catchIncompatibleAnchors = readCommandArg s }- checkExistenceOfInelasticTail s e = e { RegExDot.ExecutionOptions.checkExistenceOfInelasticTail = readCommandArg s }- checkForUnconsumableData s e = e { RegExDot.ExecutionOptions.checkForUnconsumableData = readCommandArg s }- moderateGreed s e = e { RegExDot.ExecutionOptions.moderateGreed = readCommandArg s }- permitReorderingOfAlternatives s e = e { RegExDot.ExecutionOptions.permitReorderingOfAlternatives = readCommandArg s }- preferAlternativesWhichFeedTheGreedy s e = e { RegExDot.ExecutionOptions.preferAlternativesWhichFeedTheGreedy = readCommandArg s }- preferAlternativesWhichMimickUnrolling s e = e { RegExDot.ExecutionOptions.preferAlternativesWhichMimickUnrolling = readCommandArg s }- preferFewerRepeatedAlternatives s e = e { RegExDot.ExecutionOptions.preferFewerRepeatedAlternatives = readCommandArg s }- unrollRepeatedSingletonAlternative s e = e { RegExDot.ExecutionOptions.unrollRepeatedSingletonAlternative = readCommandArg s }- useFirstMatchAmongAlternatives s e = e { RegExDot.ExecutionOptions.useFirstMatchAmongAlternatives = readCommandArg s }- validateMinConsumptionOfAlternatives s e = e { RegExDot.ExecutionOptions.validateMinConsumptionOfAlternatives = readCommandArg s }-- complyStrictlyWithPosix :: CommandOptions.CompilationOptionsMutator- complyStrictlyWithPosix s e = e { RegExDot.CompilationOptions.complyStrictlyWithPosix = readCommandArg s }-- setCompilationOption :: CommandOptions.CompilationOptionsMutator -> String -> CommandLineAction- setCompilationOption compilationOptionsMutator s = return {-to IO-monad-} . CommandOptions.setCompilationOption compilationOptionsMutator s-- setExecutionOption :: CommandOptions.ExecutionOptionsMutator -> String -> CommandLineAction- setExecutionOption executionOptionsMutator s = return {-to IO-monad-} . CommandOptions.setExecutionOption executionOptionsMutator s-- args <- System.Environment.getArgs---- G.getOpt :: G.ArgOrder CommandLineAction -> [G.OptDescr Action] -> [String] -> ([Action], [String], [String])- case G.getOpt G.RequireOrder optDescrList args of- (commandLineActions, nonOptions, []) -> do- commandOptions <- Data.List.foldl' (>>=) (return {-to IO-monad-} ToolShed.Defaultable.defaultValue) commandLineActions -- Sequentially transform the 'CommandOptions', using 'CommandLineAction's corresponding to the specified command-line flags.-- Grep.grep commandOptions nonOptions -- Perform the requested match, against the list of input-data files.- (_, _, errors) -> System.IO.Error.ioError . System.IO.Error.userError $ concat errors ++ usageMessage -- Throw.-
− src/RegExChar/ExtendedRegExChar.hs
@@ -1,207 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-{-- Copyright (C) 2010-2015 Dr. Alistair Ward-- This program is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This program is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this program. If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@] Dr. Alistair Ward-- [@DESCRIPTION@] An 'RegExDot.RegEx.ExtendedRegEx', which has been specialised for 'Char', to create a tradition non-polymorphic /regex/.--}--module RegExChar.ExtendedRegExChar(--- * Types--- ** Type-synonyms- ExtendedRegExChar(..),- InputData,--- * Functions--- ** Operators- (+~),- (=~),- (/~)-) where--import Prelude hiding ((<$>), (<*>)) -- The "Prelude" from 'base-4.8' exports this symbol.-import Control.Applicative((<$>), (<*>))-import qualified Data.List-import qualified RegExChar.MetaChar as MetaChar-import qualified RegExDot.Anchor-import qualified RegExDot.Consumer-import RegExDot.DSL((<~>), (-:))-import qualified RegExDot.RegEx-import qualified RegExDot.RegExOpts-import qualified RegExDot.Repeatable-import qualified Text.ParserCombinators.Parsec as Parsec-import Text.ParserCombinators.Parsec((<?>), (<|>))-import qualified ToolShed.Data.Pair-import qualified ToolShed.SelfValidate--infix 4 +~, =~, /~ -- Same as (==) & (/=).---- | Specialise a 'RegExDot.RegEx.ExtendedRegEx' for 'Char', & encapsulate it to permit tailored instance-declarations.-data ExtendedRegExChar = MkExtendedRegExChar {- hasNonCapturingTopLevelAlternatives :: Bool, -- ^ The string from which a 'RegExDot.RegEx.ExtendedRegEx' is read, may, if data-capture isn't required, omit explicit delimiters around top-level 'RegExDot.RegEx.Alternatives'.- extendedRegEx :: RegExDot.RegEx.ExtendedRegEx Char-} deriving Eq---- | Abbreviation.-type InputData = RegExDot.RegEx.InputData Char--instance ToolShed.SelfValidate.SelfValidator ExtendedRegExChar where- getErrors MkExtendedRegExChar {- hasNonCapturingTopLevelAlternatives = hasNonCapturingTopLevelAlternatives',- extendedRegEx = extendedRegEx'- }- | not $ ToolShed.SelfValidate.isValid extendedRegEx' = ToolShed.SelfValidate.getErrors extendedRegEx'- | otherwise = ToolShed.SelfValidate.extractErrors [- (hasNonCapturingTopLevelAlternatives' && any (not . RegExDot.RegEx.isCaptureGroup . RegExDot.Repeatable.base) (RegExDot.RegEx.concatenation extendedRegEx'), "Invalid NonCapturingTopLevelAlternatives.")- ]--instance RegExDot.Consumer.Consumer ExtendedRegExChar where- consumptionProfile = RegExDot.Consumer.consumptionProfile . extendedRegEx- starHeight = RegExDot.Consumer.starHeight . extendedRegEx--instance RegExDot.RegEx.ShortcutExpander Char where- expand c = error $ "RegExDot.RegEx.ShortcutExpander.expand RegExChar.ExtendedRegExChar:\tunrecognised shortcut '" ++ show c ++ "'."--instance Read ExtendedRegExChar where- readsPrec _ s- | s == reverse RegExDot.Anchor.tokens = [- (- MkExtendedRegExChar {- hasNonCapturingTopLevelAlternatives = False,- extendedRegEx = (Just RegExDot.Anchor.Bow, Just RegExDot.Anchor.Stern) <~> []- },- ""- )- ] -- The order of adjacent zero-width assertions is irrelevant.- | otherwise = let- extendedRegExCharParser :: Parsec.Parser ExtendedRegExChar- extendedRegExCharParser = reduce {-correct prior assumption-} <$> alternativesParser {-assume non-capturing top-level Alternatives-} where- reduce :: RegExDot.RegEx.Alternatives Char -> ExtendedRegExChar- reduce alternatives- | RegExDot.RegEx.isSingletonAlternatives alternatives = MkExtendedRegExChar False . head $ RegExDot.RegEx.deconstructAlternatives alternatives- | otherwise = MkExtendedRegExChar True $ RegExDot.Anchor.unanchored <~> RegExDot.RegEx.CaptureGroup alternatives -: [] -- Infer non-capturing top-level 'RegExDot.RegEx.Alternatives' from the presence of 'RegExDot.RegEx.alternativeExtendedRegExSeparatorToken's.-- alternativesParser :: Parsec.Parser (RegExDot.RegEx.Alternatives Char)- alternativesParser = RegExDot.RegEx.MkAlternatives <$> extendedRegExParser `Parsec.sepBy1` (Parsec.char RegExDot.RegEx.alternativeExtendedRegExSeparatorToken <?> "RegExDot.RegEx.alternativeExtendedRegExSeparatorToken " ++ show RegExDot.RegEx.alternativeExtendedRegExSeparatorToken) where- extendedRegExParser :: Parsec.Parser (RegExDot.RegEx.ExtendedRegEx Char)- extendedRegExParser = do- maybeBowAnchor <- Parsec.option Nothing {-default-} $ (Parsec.char RegExDot.Anchor.bowToken <?> "RegExDot.Anchor.bowToken " ++ show RegExDot.Anchor.bowToken) >> return {-to ParsecT-monad-} (Just RegExDot.Anchor.Bow)- repeatableRequirementList <- repeatableRequirementListParser-- (- do- repeatableCaptureGroup <- RegExDot.Repeatable.repeatableParser . RegExDot.RegEx.CaptureGroup =<< uncurry Parsec.between (- ToolShed.Data.Pair.mirror Parsec.char RegExDot.RegEx.captureGroupDelimiters- ) alternativesParser {-recurse-} <?> "RegExDot.RegEx.captureGroupDelimiters " ++ show RegExDot.RegEx.captureGroupDelimiters-- extendedRegEx' <- extendedRegExParser -- Recurse.-- return {-to ParsecT-monad-} $ RegExDot.RegEx.transformExtendedRegEx ((repeatableRequirementList ++) . (repeatableCaptureGroup :)) extendedRegEx' { RegExDot.RegEx.bowAnchor = maybeBowAnchor }- ) <|> (- do- maybeSternAnchor <- Parsec.option Nothing {-default-} $ (Parsec.char RegExDot.Anchor.sternToken <?> "RegExDot.Anchor.sternToken " ++ show RegExDot.Anchor.sternToken) >> return {-to ParsecT-monad-} (Just RegExDot.Anchor.Stern)-- return {-to ParsecT-monad-} RegExDot.RegEx.MkExtendedRegEx {- RegExDot.RegEx.bowAnchor = maybeBowAnchor,- RegExDot.RegEx.concatenation = repeatableRequirementList,- RegExDot.RegEx.sternAnchor = maybeSternAnchor- }- )- where- repeatableRequirementListParser :: Parsec.Parser (RegExDot.RegEx.Concatenation Char)- repeatableRequirementListParser = Parsec.choice [- Parsec.try . Parsec.lookAhead $ (- Parsec.char RegExDot.Anchor.sternToken <?> "RegExDot.Anchor.sternToken " ++ show RegExDot.Anchor.sternToken- ) >> (- (- Parsec.eof >> return {-to ParsecT-monad-} []- ) <|> (- Parsec.oneOf [RegExDot.RegEx.alternativeExtendedRegExSeparatorToken, snd RegExDot.RegEx.captureGroupDelimiters] >> return {-to ParsecT-monad-} []- )- ),- (:) <$> (- MetaChar.metaCharParser >>= RegExDot.Repeatable.repeatableParser . RegExDot.RegEx.Require . MetaChar.deconstruct- ) <*> repeatableRequirementListParser, {-recurse-}- return {-to ParsecT-monad-} []- ]-- in (- error . ("readsPrec RegExChar.ExtendedRegExChar:\tparse-error; " ++) . show -- Failure to parse.- ) `either` (- \pair@(extendedRegExChar, _) -> if ToolShed.SelfValidate.isValid extendedRegExChar- then [pair]- else error $ ToolShed.SelfValidate.getFirstError extendedRegExChar -- Parsed OK, but invalid.- ) $ Parsec.parse (- (,) <$> extendedRegExCharParser <*> Parsec.getInput- ) "ExtendedRegExChar" s--instance Show ExtendedRegExChar where- showsPrec _ MkExtendedRegExChar {- hasNonCapturingTopLevelAlternatives = hasNonCapturingTopLevelAlternatives',- extendedRegEx = RegExDot.RegEx.MkExtendedRegEx {- RegExDot.RegEx.bowAnchor = maybeBowAnchor,- RegExDot.RegEx.concatenation = concatenation',- RegExDot.RegEx.sternAnchor = maybeSternAnchor- }- } = RegExDot.RegEx.showsMaybeAnchor maybeBowAnchor . foldl (.) (showString "") (- (- let- showAlternatives :: RegExDot.RegEx.Alternatives Char -> [ShowS]- showAlternatives = Data.List.intersperse (showChar RegExDot.RegEx.alternativeExtendedRegExSeparatorToken) . map (shows . MkExtendedRegExChar False) . RegExDot.RegEx.deconstructAlternatives- in if hasNonCapturingTopLevelAlternatives'- then map (- \repeatablePattern -> case RegExDot.Repeatable.base repeatablePattern of- RegExDot.RegEx.CaptureGroup alternatives -> foldr (.) (showString "") $ showAlternatives alternatives- _ -> error $ "Show RegExChar.ExtendedRegExChar: unexpected " ++ show repeatablePattern- )- else map (- \repeatablePattern -> (- case RegExDot.Repeatable.base repeatablePattern of- RegExDot.RegEx.Require meta -> shows $ MetaChar.MkMetaChar meta- RegExDot.RegEx.CaptureGroup alternatives -> showChar (- fst RegExDot.RegEx.captureGroupDelimiters- ) . foldr (.) (- showChar $ snd RegExDot.RegEx.captureGroupDelimiters -- Initial value.- ) (- showAlternatives alternatives- )- ) . RegExDot.Repeatable.showSuffix repeatablePattern- )- ) concatenation'- ) . RegExDot.RegEx.showsMaybeAnchor maybeSternAnchor---- | A veneer over the underlying polymorphic operator, 'RegExDot.RegEx.+~'.-(+~)- :: InputData -- ^ The input-data string.- -> RegExDot.RegExOpts.RegExOpts ExtendedRegExChar -- ^ The match-options, parameterised by the regex-specification.- -> RegExDot.RegEx.Result Char-inputData +~ regExOpts = inputData RegExDot.RegEx.+~ fmap extendedRegEx regExOpts -- CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.---- | A veneer over the underlying polymorphic operator, 'RegExDot.RegEx.=~'.-(=~)- :: InputData -- ^ The input-data string.- -> RegExDot.RegExOpts.RegExOpts ExtendedRegExChar -- ^ The match-options, parameterised by the regex-specification.- -> Bool-inputData =~ regExOpts = inputData RegExDot.RegEx.=~ fmap extendedRegEx regExOpts -- CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.---- | Pattern-mismatch operator.-(/~)- :: InputData -- ^ The input-data string.- -> RegExDot.RegExOpts.RegExOpts ExtendedRegExChar -- ^ The match-options, parameterised by the regex-specification.- -> Bool-(/~) inputData = not . (inputData =~)-
− src/RegExChar/MetaChar.hs
@@ -1,253 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-{-- Copyright (C) 2010-2015 Dr. Alistair Ward-- This program is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This program is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this program. If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@] Dr. Alistair Ward-- [@DESCRIPTION@]-- * Implements 'RegExDot.Meta.ShortcutExpander' 'Char', to implement some of the traditional /Perl-style shortcuts/:-- [@\\d@] => Any 'Data.Char.isDigit'-character.-- [@\\D@] => Any non-'Data.Char.isDigit' character.-- [@\\s@] => Any 'Data.Char.isSpace'-character.-- [@\\S@] => Any non-'Data.Char.isSpace' character.-- [@\\w@] => Any 'Data.Char.isWord' letter.-- [@\\W@] => Any non-'Data.Char.isWord' letter.-- * Enables one to compose concise regexen, containing any 'Char' that's a member of one of these predefined sets.-- * Though 'RegExDot.Meta.Meta' is polymorphic, & the type-parameter can't be assumed to implement either 'Enum' or 'Ord',- 'Char' actually does, so this module is able to implement ranges within a /Bracket-expression/.-- * Defines specialised instances of 'Read' & 'Show', to cope with /Perl-style shortcuts/, /Posix Character-classes/ & /Bracket-expression/ range-specifications.-- [@TODO@] Use @Environment.getLocale@. Regrettably, this returns in the "IO"-monad, & even it didn't, how does one pass that information to 'Read' ?!--}--module RegExChar.MetaChar(--- * Types--- AssociationList,--- Dictionary,--- ** Type-synonyms- MetaChar(..),--- * Constants--- bracketExpressionRangeToken,--- posixCharacterClassDelimiters,--- * Functions--- implementPerlShortcut,- metaCharParser,--- ** Accessors (Deconstructors)- deconstruct-) where--import Prelude hiding ((<$>), (<*>)) -- The "Prelude" from 'base-4.8' exports this symbol.-import Control.Applicative((<$>), (<*>))-import Control.Arrow((***))-import qualified Data.Char-import qualified Data.Map-import qualified RegExDot.BracketExpression-import qualified RegExDot.BracketExpressionMember-import qualified RegExDot.Consumer-import qualified RegExDot.Meta-import qualified RegExDot.RegEx-import qualified RegExDot.Repeatable-import qualified RegExDot.ShowablePredicate-import qualified Text.ParserCombinators.Parsec as Parsec-import Text.ParserCombinators.Parsec((<?>))-import qualified ToolShed.Data.Pair-import qualified ToolShed.SelfValidate---- | Holds a mapping from an escape-sequence & the predicate via which it is implemented.-type AssociationList key = [(key, RegExDot.ShowablePredicate.Predicate Char)]---- | Holds a mapping from an escape-sequence & the predicate via which it is implemented.-type Dictionary key = Data.Map.Map key (RegExDot.ShowablePredicate.Predicate Char)--instance RegExDot.BracketExpressionMember.ShortcutExpander Char where- findPredicate shortcut = RegExDot.ShowablePredicate.MkShowablePredicate [RegExDot.Meta.shortcutToken, shortcut] <$> shortcut `Data.Map.lookup` dictionary where- dictionary :: Dictionary Char- dictionary = Data.Map.fromList $ perlShortcuts ++ invert perlShortcuts where- perlShortcuts :: AssociationList Char- perlShortcuts = [- ('d', Data.Char.isDigit),- ('s', Data.Char.isSpace),- ('w', \c -> ($ c) `any` [Data.Char.isAlphaNum, (== '_')])- ]-- invert :: AssociationList Char -> AssociationList Char- invert = map (Data.Char.toUpper *** (not .))--instance RegExDot.Meta.ShortcutExpander Char where- expand c = case RegExDot.BracketExpressionMember.findPredicate c of- Just showablePredicate -> RegExDot.Meta.Predicate showablePredicate- _ -> RegExDot.Meta.Literal c---- | A specialised instance, encapsulated to permit tailored instance-declarations.-newtype MetaChar = MkMetaChar (RegExDot.Meta.Meta Char) deriving Eq---- | Accessor.-deconstruct :: MetaChar -> RegExDot.Meta.Meta Char-deconstruct (MkMetaChar metaChar) = metaChar--instance ToolShed.SelfValidate.SelfValidator MetaChar where- getErrors = ToolShed.SelfValidate.getErrors . deconstruct--instance RegExDot.Consumer.Consumer MetaChar where- consumptionProfile = RegExDot.Consumer.consumptionProfile . deconstruct- starHeight = RegExDot.Consumer.starHeight . deconstruct---- | Builds a parser of traditional regex-syntax, which understands 'Char'-specific concepts like /Perl-style shortcuts/ & /Posix Character-classes/.-metaCharParser :: Parsec.Parser MetaChar-metaCharParser = MkMetaChar <$> Parsec.choice [- (Parsec.char RegExDot.Meta.anyToken <?> "RegExDot.Meta.anyToken " ++ show RegExDot.Meta.anyToken) >> return {-to ParsecT-monad-} RegExDot.Meta.Any,- (Parsec.char RegExDot.Meta.shortcutToken <?> "RegExDot.Meta.shortcutToken " ++ show RegExDot.Meta.shortcutToken) >> RegExDot.Meta.expand <$> Parsec.anyChar,- uncurry Parsec.between (ToolShed.Data.Pair.mirror Parsec.char RegExDot.BracketExpression.delimiterTokens) (- do- let- implementPosixCharacterClass :: String -> Maybe (RegExDot.ShowablePredicate.ShowablePredicate Char)- implementPosixCharacterClass identifier = RegExDot.ShowablePredicate.MkShowablePredicate (- fst posixCharacterClassDelimiters ++ identifier ++ snd posixCharacterClassDelimiters- ) <$> identifier `Data.Map.lookup` dictionary where- dictionary :: Dictionary String- dictionary = Data.Map.fromList $ posixCharacterClasses ++ invert posixCharacterClasses where- posixCharacterClasses :: AssociationList String- posixCharacterClasses = [- ("alnum", Data.Char.isAlphaNum),- ("alpha", Data.Char.isAlpha),- ("ascii", Data.Char.isAscii),- ("blank", (`elem` " \t")),- ("cntrl", Data.Char.isControl),- ("digit", Data.Char.isDigit),- ("graph", \c -> not $ ($ c) `any` [Data.Char.isSpace, Data.Char.isControl]),- ("lower", Data.Char.isLower),- ("print", Data.Char.isPrint),- ("punct", Data.Char.isPunctuation),- ("space", Data.Char.isSpace),- ("upper", Data.Char.isUpper),- ("word", \c -> ($ c) `any` [Data.Char.isAlphaNum, (== '_')]),- ("xdigit", Data.Char.isHexDigit)- ]-- invert :: AssociationList String -> AssociationList String- invert = map $ (RegExDot.BracketExpression.negationToken :) *** (not .)-- cTor <- Parsec.option RegExDot.Meta.AnyOf {-default-} $ (Parsec.char RegExDot.BracketExpression.negationToken <?> "RegExDot.BracketExpression.negationToken " ++ show RegExDot.BracketExpression.negationToken) >> return {-to ParsecT-monad-} RegExDot.Meta.NoneOf-- literalBracketExpressionTerminator <- Parsec.option [] {-default-} $ return {-to List-monad-} . RegExDot.BracketExpressionMember.Literal <$> (- Parsec.char (snd RegExDot.BracketExpression.delimiterTokens) <?> "Literal Bracket-expression terminator " ++ show (snd RegExDot.BracketExpression.delimiterTokens)- ) -- If the first item in a BracketExpression (or negated BracketExpression) is the terminator-token, then it is treated as a 'RegExDot.BracketExpressionMember.Literal'.-- cTor . (literalBracketExpressionTerminator ++) <$> Parsec.many {-potentially zero-} (- Parsec.choice [- (- do- _ <- Parsec.char RegExDot.Meta.shortcutToken <?> "RegExDot.Meta.shortcutToken " ++ show RegExDot.Meta.shortcutToken- c <- Parsec.anyChar-- return {-to ParsecT-monad-} $ case RegExDot.BracketExpressionMember.findPredicate c of- Just showablePredicate -> RegExDot.BracketExpressionMember.Predicate showablePredicate- _ -> RegExDot.BracketExpressionMember.Literal c -- Escaped literal.- ) <?> "Perl-style shortcut",- Parsec.try (- uncurry Parsec.between (ToolShed.Data.Pair.mirror Parsec.string posixCharacterClassDelimiters) (- do- identifier <- Parsec.many1 $ Parsec.noneOf [head $ snd posixCharacterClassDelimiters]-- case implementPosixCharacterClass identifier of- Just showablePredicate -> return {-to ParsecT-monad-} $ RegExDot.BracketExpressionMember.Predicate showablePredicate- _ -> Parsec.unexpected $ "MetaChar.metaCharParser:\tunrecognised Posix Character-class; " ++ show identifier- ) <?> "Posix Character-class " ++ show posixCharacterClassDelimiters- ), -- Regurgitate erroneously consumed input.- Parsec.try (- (- do- rangeStart <- Parsec.noneOf [snd RegExDot.BracketExpression.delimiterTokens]- _ <- Parsec.char bracketExpressionRangeToken <?> "bracketExpressionRangeToken " ++ show bracketExpressionRangeToken- rangeEnd <- Parsec.noneOf [snd RegExDot.BracketExpression.delimiterTokens]-- return {-to ParsecT-monad-} . RegExDot.BracketExpressionMember.Predicate . RegExDot.ShowablePredicate.MkShowablePredicate [- rangeStart,- bracketExpressionRangeToken,- rangeEnd- ] $ \c -> rangeStart <= c && c <= rangeEnd -- Create custom predicate, utilising "Ord Char".- ) <?> "Bracket-expression range"- ), -- Regurgitate erroneously consumed input.- RegExDot.BracketExpressionMember.Literal <$> Parsec.noneOf [snd RegExDot.BracketExpression.delimiterTokens] <?> "RegExDot.BracketExpressionMember.Literal" -- TODO: the first Char-member can be a literal ']'.- ] <?> "Bracket-expression member"- ) <?> "Bracket-expression member-list"- ) <?> "RegExDot.BracketExpression.delimiterTokens " ++ show RegExDot.BracketExpression.delimiterTokens,- RegExDot.Meta.Literal <$> Parsec.noneOf RegExDot.RegEx.tokens- ]--instance Read MetaChar where- readsPrec _ = (- (fail . ("readsPrec RegExChar.MetaChar:\tparse-error; " ++) . show) `either` return- ) . Parsec.parse (- (,) <$> metaCharParser <*> Parsec.getInput- ) "MetaChar"---- | The token used to signify an ordered range of members in a /Bracket-expression/.-bracketExpressionRangeToken :: Char-bracketExpressionRangeToken = '-'---- | The delimiters of a /Posix Character-class/.-posixCharacterClassDelimiters :: (String, String)-posixCharacterClassDelimiters = ("[:", ":]")--instance Show MetaChar where- showsPrec _ (MkMetaChar RegExDot.Meta.Any) = showChar RegExDot.Meta.anyToken- showsPrec _ (MkMetaChar (RegExDot.Meta.Literal c)) = (- if c `elem` [- fst RegExDot.BracketExpression.delimiterTokens,- fst RegExDot.Repeatable.rangeDelimiters,- RegExDot.Repeatable.oneOrMoreToken,- RegExDot.Repeatable.zeroOrMoreToken,- RegExDot.Repeatable.zeroOrOneToken,- RegExDot.Meta.anyToken,- RegExDot.Meta.shortcutToken- ] ++ RegExDot.RegEx.tokens- then showChar RegExDot.Meta.shortcutToken- else id- ) . showChar c- showsPrec _ (MkMetaChar (RegExDot.Meta.AnyOf bracketExpression)) = showChar (- fst RegExDot.BracketExpression.delimiterTokens- ) . showString (- foldr (- \e -> case e of- RegExDot.BracketExpressionMember.Predicate showablePredicate -> shows showablePredicate- RegExDot.BracketExpressionMember.Literal literal -> (- if literal `elem` [- bracketExpressionRangeToken, -- CAVEAT: only unambiguously literal when at the start or end of a "BracketExpression".- RegExDot.Meta.shortcutToken,- snd RegExDot.BracketExpression.delimiterTokens -- CAVEAT: only unambiguously literal when at the start of a "BracketExpression".- ]- then showChar RegExDot.Meta.shortcutToken- else id- ) . showChar literal- ) (- showChar (snd RegExDot.BracketExpression.delimiterTokens) "" -- Initial value.- ) bracketExpression- )- showsPrec _ (MkMetaChar (RegExDot.Meta.NoneOf bracketExpression)) = showChar x . showChar RegExDot.BracketExpression.negationToken . showString xs where (x : xs) = show . MkMetaChar $ RegExDot.Meta.AnyOf bracketExpression- showsPrec _ (MkMetaChar m) = shows m-
− src/RegExChar/RegExOptsChar.hs
@@ -1,219 +0,0 @@-{-# LANGUAGE MultiParamTypeClasses, TypeSynonymInstances, FlexibleInstances, FlexibleContexts #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--{-- Copyright (C) 2010-2015 Dr. Alistair Ward-- This program is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This program is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this program. If not, see <http://www.gnu.org/licenses/>.--}-{- |- [@AUTHOR@] Dr. Alistair Ward-- [@DESCRIPTION@] Creates a /back-end/ implementation, conforming to <http://hackage.haskell.org/packages/archive/regex-base/latest/doc/html/Text-Regex-Base-RegexLike.html>.-- [@CAVEATS@]-- * The underlying polymorphic (in terms of the base-type of the list of input data) 'RegExDot.RegEx.ExtendedRegEx'-engine is never going to be a drop-in replacement for other /regex/-engines,- so this standard interface has only been implemented for traditional input data-type ['Char'].-- * The standard interface requires many Haskell-extensions, but since this implementation is just a thin layer over the underlying portable polymorphic 'RegExDot.RegEx.ExtendedRegEx'-engine,- the latter can still be used directly, where any of these extensions are unavailable.--}--module RegExChar.RegExOptsChar(--- * Types--- ** Type-synonyms- MatchSpan,--- MatchDataSpan,- RegExOptsChar,--- * Functions--- exciseNonCapturingTopLevelAlternatives,--- matchUntilFailure,--- toMatchDataSpanList,- toZeroIndexedArray,--- ** Operators- (=~)--- ** Predicates--- complyStrictlyWithPosix,--- hasNonCapturingTopLevelAlternatives-) where--import qualified Data.Array.IArray-import qualified RegExChar.ExtendedRegExChar as ExtendedRegExChar-import qualified RegExDot.CompilationOptions-import qualified RegExDot.ConsumptionBounds-import qualified RegExDot.DataSpan-import qualified RegExDot.DataSpanTree-import qualified RegExDot.ExecutionOptions-import qualified RegExDot.RegEx-import qualified RegExDot.RegExOpts-import qualified RegExDot.Result-import qualified RegExDot.Tree-import qualified Text.Regex.Base.RegexLike as RegexLike-import Text.Regex.Base.Context() -- Instance-declarations.-import qualified ToolShed.Data.List-import qualified ToolShed.Defaultable-import qualified ToolShed.Options--infix 4 =~ -- Same as (==).---- | Defines a specific instance of the polymorphic base-type.-type RegExOptsChar = RegExDot.RegExOpts.RegExOpts ExtendedRegExChar.ExtendedRegExChar---- | Convenience accessor-function.-hasNonCapturingTopLevelAlternatives :: RegExOptsChar -> Bool-hasNonCapturingTopLevelAlternatives = ExtendedRegExChar.hasNonCapturingTopLevelAlternatives . RegExDot.RegExOpts.regEx---- | Convenience accessor-function.-complyStrictlyWithPosix :: RegExOptsChar -> Bool-complyStrictlyWithPosix = RegExDot.CompilationOptions.complyStrictlyWithPosix . RegExDot.RegExOpts.compilationOptions--instance RegexLike.RegexOptions RegExOptsChar RegExDot.CompilationOptions.CompilationOptions RegExDot.ExecutionOptions.ExecutionOptions where- blankCompOpt = ToolShed.Options.blankValue- blankExecOpt = ToolShed.Options.blankValue- defaultCompOpt = ToolShed.Defaultable.defaultValue- defaultExecOpt = ToolShed.Defaultable.defaultValue- setExecOpts e r = r { RegExDot.RegExOpts.executionOptions = e }- getExecOpts = RegExDot.RegExOpts.executionOptions---- Newer versions of this 'Text.Regex.Base.RegexLike.RegexMaker' have additional monadic methods, which can fail on parse-errors.-instance RegexLike.RegexMaker RegExOptsChar RegExDot.CompilationOptions.CompilationOptions RegExDot.ExecutionOptions.ExecutionOptions String where- makeRegexOpts c e source = RegExDot.RegExOpts.MkRegExOpts {- RegExDot.RegExOpts.compilationOptions = c,- RegExDot.RegExOpts.executionOptions = e,- RegExDot.RegExOpts.regEx = read source- }---- | The offset and length of the 'RegExDot.RegEx.InputData' consumed in one 'RegExDot.RegEx.Match'.-type MatchSpan = (RegexLike.MatchOffset, RegexLike.MatchLength)--{- |- * The 'MatchSpan' augmented by the consumed 'ExtendedRegExChar.InputData' to which it refers.-- * Similar to 'RegExDot.DataSpan.DataSpan'.--}-type MatchDataSpan = (ExtendedRegExChar.InputData, MatchSpan)--{- |- * Convert the 'RegExDot.RegEx.MatchList', into the 'MatchDataSpan's required by "Text.Regex.Base.RegexLike".-- * Treat the whole 'ExtendedRegExChar.ExtendedRegExChar' as an additional implicit capture-group.-- * CAVEAT: @RegExDot.DataSpanTree.extractCaptureGroups True@ may return some 'RegExDot.DataSpan.DataSpan's containing the artificial offset @-1@ specified by Posix => don't use this as the basis for any arithmetic.--}-toMatchDataSpanList- :: Bool- -> RegExDot.ConsumptionBounds.DataLength -- ^ The offset to use for zero-length match.- -> RegExDot.RegEx.MatchList Char -- ^ The list of matches.- -> [MatchDataSpan]-toMatchDataSpanList _ offset [] = [RegExDot.DataSpan.empty offset] -- The whole regex matched, but consumed nothing.-toMatchDataSpanList strictPosixCompliance _ matchList = RegExDot.DataSpanTree.extractCaptureGroups strictPosixCompliance . return {-to List-monad-} . RegExDot.Tree.Node . return {-to List-monad-} $ RegExDot.DataSpanTree.toTreeList matchList---- | Optionally remove the 2nd element from the list, where it represents the data captured by a groups of top-level 'Alternatives', which weren't explicitly delimited & therefore are non-capturing.-exciseNonCapturingTopLevelAlternatives :: RegExOptsChar -> [MatchDataSpan] -> [MatchDataSpan]-exciseNonCapturingTopLevelAlternatives regExOptsChar- | hasNonCapturingTopLevelAlternatives regExOptsChar = ToolShed.Data.List.excise 1 -- The zeroeth represents the whole regex, the 1st represents the top-level non-capturing group of Alternatives, & the remainder represent explicitly delimited groups.- | otherwise = id---- | Convert the specified list, into a zero-indexed array.-toZeroIndexedArray :: [e] -> Data.Array.IArray.Array Int e-toZeroIndexedArray l = Data.Array.IArray.listArray (0, pred $ length l) l--{- |- * Repeatedly apply the 'RegExOptsChar' to the 'ExtendedRegExChar.InputData', forwarding unmatched input data to the next match-attempt, until it's all been consumed.-- * The offsets, from the start of the input data, of all matches after the first, are shifted to account for input data already consumed by previous matches.--}-matchUntilFailure- :: RegExOptsChar -- ^ The match-criteria.- -> ExtendedRegExChar.InputData -- ^ The input-data, to be consumed by repeated matching.- -> [RegExDot.RegEx.MatchList Char]-matchUntilFailure regExOptsChar inputData = shiftOffsets 0 $ matchUntilFailure' inputData where- matchUntilFailure' :: ExtendedRegExChar.InputData -> [RegExDot.RegEx.MatchList Char]- matchUntilFailure' unmatchedInputData = case unmatchedInputData ExtendedRegExChar.+~ regExOptsChar of -- CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.- (_, Just matchList, maybeSternAnchorResult) -> matchList : if null (RegExDot.RegEx.extractDataFromMatchList matchList) then [] else matchUntilFailure' (RegExDot.RegEx.extractDataFromMatch' maybeSternAnchorResult)- _ -> []-- shiftOffsets :: RegExDot.ConsumptionBounds.DataLength -> [RegExDot.RegEx.MatchList Char] -> [RegExDot.RegEx.MatchList Char]- shiftOffsets offset (matchList : matchLists) = let- shiftedMatchList :: RegExDot.RegEx.MatchList Char- shiftedMatchList = RegExDot.RegEx.shiftMatchList offset matchList- in shiftedMatchList : shiftOffsets (RegExDot.DataSpan.after . last . RegExDot.DataSpanTree.flattenTreeList 0 $ RegExDot.DataSpanTree.toTreeList shiftedMatchList) matchLists {-recurse-}- shiftOffsets _ _ = []--instance RegexLike.RegexLike RegExOptsChar ExtendedRegExChar.InputData where-{- E.g.:- ("xabcdxabdxxadx" RegExOptsChar.=~ "a(b(c?))*d") :: [Text.Regex.Base.RegexLike.MatchArray]- [array (0,2) [(0,(1,4)),(1,(2,2)),(2,(3,1))],array (0,2) [(0,(6,3)),(1,(7,1)),(2,(8,0))],array (0,1) [(0,(11,2)),(1,(-1,0))]]--}--- matchAll :: RegExOptsChar -> ExtendedRegExChar.InputData -> [RegexLike.MatchArray]- matchAll regExOptsChar = map (toZeroIndexedArray . map snd {-span-} . exciseNonCapturingTopLevelAlternatives regExOptsChar . toMatchDataSpanList (complyStrictlyWithPosix regExOptsChar) 0) . matchUntilFailure regExOptsChar--{- E.g.:- ("xabcdxabdxxadx" RegExOptsChar.=~ "a(b(c?))*d") :: [Text.Regex.Base.RegexLike.MatchText String]- [array (0,2) [(0,("abcd",(1,4))),(1,("bc",(2,2))),(2,("c",(3,1)))],array (0,2) [(0,("abd",(6,3))),(1,("b",(7,1))),(2,("",(8,0)))],array (0,1) [(0,("ad",(11,2))),(1,("",(-1,0)))]]--}--- matchAllText :: RegExOptsChar -> ExtendedRegExChar.InputData -> [RegexLike.MatchText ExtendedRegExChar.InputData]- matchAllText regExOptsChar = map (toZeroIndexedArray . exciseNonCapturingTopLevelAlternatives regExOptsChar . toMatchDataSpanList (complyStrictlyWithPosix regExOptsChar) 0) . matchUntilFailure regExOptsChar--{- E.g.:- ("xabcdx" RegExOptsChar.=~ "a(b(c?))*d") :: Text.Regex.Base.RegexLike.MatchArray- array (0,2) [(0,(1,4)),(1,(2,2)),(2,(3,1))]--}--- matchOnce :: RegExOptsChar -> ExtendedRegExChar.InputData -> Maybe RegexLike.MatchArray- matchOnce regExOptsChar inputData = (- toZeroIndexedArray . map snd {-span-} . exciseNonCapturingTopLevelAlternatives regExOptsChar . toMatchDataSpanList (- complyStrictlyWithPosix regExOptsChar- ) (- RegExDot.RegEx.externalMatchLength $ RegExDot.Result.getPreMatch extendedRegExResult- )- ) `fmap` RegExDot.Result.getMatchList extendedRegExResult where- extendedRegExResult :: RegExDot.RegEx.Result Char- extendedRegExResult = inputData ExtendedRegExChar.+~ regExOptsChar -- CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.--{- E.g.:- ("xabcdx" RegExOptsChar.=~ "a(b(c?))*d") :: (String, Text.Regex.Base.RegexLike.MatchText String, String)- ("x",array (0,2) [(0,("abcd",(1,4))),(1,("bc",(2,2))),(2,("c",(3,1)))],"x")--}--- matchOnceText :: RegExOptsChar -> ExtendedRegExChar.InputData -> Maybe (ExtendedRegExChar.InputData, MatchText ExtendedRegExChar.InputData, ExtendedRegExChar.InputData)- matchOnceText regExOptsChar inputData = case inputData ExtendedRegExChar.+~ regExOptsChar of -- CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.- (maybeBowAnchorResult, Just matchList, maybeSternAnchorResult) -> Just (- RegExDot.RegEx.extractDataFromMatch' maybeBowAnchorResult,- toZeroIndexedArray . exciseNonCapturingTopLevelAlternatives regExOptsChar $ toMatchDataSpanList (- complyStrictlyWithPosix regExOptsChar- ) (- RegExDot.RegEx.externalMatchLength maybeBowAnchorResult- ) matchList,- RegExDot.RegEx.extractDataFromMatch' maybeSternAnchorResult- )- _ -> Nothing--{- E.g.:- ("xabcdx" RegExOptsChar.=~ "a(b(c?))*d") :: Bool- True--}--- matchTest :: RegExOptsChar -> ExtendedRegExChar.InputData -> Bool- matchTest = flip (ExtendedRegExChar.=~) -- CAVEAT: <http://hackage.haskell.org/trac/haskell-prime/wiki/QualifiedOperators>.--{- |- * Match-operator.-- * The polymorphic return-type is resolved by the caller's "RegexLike.RegexContext".--}-(=~) :: RegexLike.RegexContext RegExOptsChar ExtendedRegExChar.InputData target- => ExtendedRegExChar.InputData -- ^ The input data.- -> String -- ^ The string from which to read the regex-specification.- -> target -- ^ The polymorphic return-type.-inputData =~ s = (RegexLike.makeRegex s :: RegExOptsChar) `RegexLike.match` inputData-