packages feed

regex-tdfa 1.2.3.3 → 1.3.2.5

raw patch · 29 files changed

Files

CHANGELOG.md view
@@ -1,62 +1,211 @@-# 1.2.3.3+### 1.3.2.5 +_2025-09-29 Andreas Abel_++- Bump `cabal-version` to 2.0 to support field `autogen-modules` ([#70](https://github.com/haskell-hvr/regex-tdfa/pull/70))+- SPECIALIZE pragmas for faster `Text` matching ([#72](https://github.com/haskell-hvr/regex-tdfa/pull/72))+- Address deprecation warning for `sizeofMutualByteArray#` ([#71](https://github.com/haskell-hvr/regex-tdfa/issues/71))+- Tested with GHC 8.0 - 9.12.2++### 1.3.2.4++_2025-05-09 Andreas Abel_++- Fix definition of `[[:graph:]]` character class ([#66](https://github.com/haskell-hvr/regex-tdfa/issues/66))+- Tested with GHC 8.0 - 9.12.2++### 1.3.2.3++_2025-03-02 Andreas Abel_++- Drop support for GHC 7+- Allow `containers < 1`+- Tested with GHC 8.0 - 9.12.1++### 1.3.2.2++_2023-08-02, Andreas Abel_++- Fix return type in `memcpy` FFI signature ([#52](https://github.com/haskell-hvr/regex-tdfa/pull/52))+- Refactor `regexec` to avoid partial functions `tail` and `(!0)`+- Tested with GHC 7.4 - 9.8.1-alpha1++### 1.3.2.1++_2023-05-19, Andreas Abel_++- Fix haddock rendering of code examples in top-level documentation+  ([#50](https://github.com/haskell-hvr/regex-tdfa/issues/50))+- Tested with GHC 7.4 - 9.6++## 1.3.2++_2022-07-18, Andreas Abel_++- Export `decodePatternSet` and `decodeCharacterClass` from `Text.Regex.TDFA.Pattern`+  ([#16](https://github.com/haskell-hvr/regex-tdfa/issues/16))+- Extend and correct docs for `Pattern` module+- Tested with GHC 7.4 - 9.4++### 1.3.1.5++_2022-07-18, Andreas Abel_++- Allow dash (`-`) as start of a range, e.g. `[--z]`+  ([#1](https://github.com/haskell-hvr/regex-tdfa/issues/1),+  [#45](https://github.com/haskell-hvr/regex-tdfa/pull/45))+- Tested with GHC 7.4 - 9.4++### 1.3.1.4++_2022-07-17, Andreas Abel_++- Fix parsing of dashes in bracket expressions, e.g. `[-a-z]` ([#1](https://github.com/haskell-hvr/regex-tdfa/issues/1))+- Fix a deprecation warning except for on GHC 8.2 ([#21](https://github.com/haskell-hvr/regex-tdfa/issues/21))+- Documentation: link `defaultComptOpt` to its definition  ([#13](https://github.com/haskell-hvr/regex-tdfa/issues/13))+- Verify documentation examples with new `doc-test` testsuite+- Tested with GHC 7.4 - 9.4++### 1.3.1.3++_2022-07-14, Andreas Abel_++- Fix an `undefined` in `Show PatternSet` ([#37](https://github.com/haskell-hvr/regex-tdfa/issues/37))+- Document POSIX character classes (e.g. `[[:digit:]]`) in README+- Tested with GHC 7.4 - 9.4++### 1.3.1.2 Revision 1++_2022-05-25, Andreas Abel_++- Allow `base >= 4.17` (GHC 9.4)++### 1.3.1.2++_2022-02-19, Andreas Abel_+- No longer rely on the `MonadFail` instance for `ST`+  (future `base` library change, see [#29](https://github.com/haskell-hvr/regex-tdfa/pull/29)).+- Silence warning `incomplete-uni-patterns` (GHC >= 9.2).+- Import from `Data.List` explicitly or qualified (warning `compat-unqualified-imports`).+- Import from `Control.Monad` to allow `mtl-2.3` in its `rc3` incarnation.++### 1.3.1.1 Revision 3++_2022-01-31, Andreas Abel_+- Speculatively allow unreleased `mtl-2.3` (works with release candidate `mtl-2.3-rc4`).++### 1.3.1.1 Revision 2++_2021-12-26, Andreas Abel_+- Allow `text-2.0`.++### 1.3.1.1 Revision 1++_2021-08-12, Andreas Abel_+- Compatibility with `base-4.16` (GHC 9.2).++### 1.3.1.1++_2021-06-03, Andreas Abel_+- Removed extension `NoMonoPatBinds` from `.cabal`-file for GHC 9.2 compatibility.+- Removed some outdated documentation.++### 1.3.1.0 Revision 2++_2021-02-20, Andreas Abel_+- Compatibility with `base-4.15` (GHC 9.0) and `bytestring-0.11`.++### 1.3.1.0 Revision 1++_2020-03-26, phadej_+- Compatibility with `base-4.14` (GHC 8.10).++## 1.3.1.0++_2019-11-26, hvr_+- Merge <http://hackage.haskell.org/package/regex-tdfa-text> into `regex-tdfa`; see <https://github.com/haskell-hvr/regex-tdfa/issues/4>.+- Don't inject `ghc-options: -O2` by default anymore (see #7 for rationale) and introduce `force-O2` cabal flag to control the injection of `ghc-options: -O2`.+  Note that you can conveniently control optimization levels on a per-package granularity via `cabal.project` files; see [cabal's user-guide](https://cabal.readthedocs.io/en/latest/nix-local-build.html#configuring-builds-with-cabal-project) for more details.++## 1.3.0 Revision 1++_2019-11-26, hvr_+- Tighten bounds on `base`, `mtl`, `parsec` and fail.++# 1.3.0++_2019-09-29, Artyom_+- Same as 1.2.3.3 release, but in accordance to PVP;+  see <https://github.com/ChrisKuklewicz/regex-tdfa/issues/29>.+- Compatibility with GHC 8.8 and regex-base-0.9.4 (h/t @asr).+- Turned `regex-tdfa-unittest` into a `regex-tdfa` testsuite.++### 1.2.3.3 (deprecated, not following PVP)+ * Compatibility with GHC 8.8 and regex-base-0.9.4 (h/t @asr). * Turned `regex-tdfa-unittest` into a `regex-tdfa` testsuite. -# 1.2.3.2+### 1.2.3.2 +_2019-05-09, Artyom_ * Significantly improved documentation (h/t William Yao). -# 1.2.3.1+### 1.2.3.1 +_2018-06-22, Artyom_ * Compatibility with `containers-0.6`. -# 1.2.3+## 1.2.3 +_2018-03-10, Artyom_ * Added `Semigroup` instances for some types (h/t Herbert Valerio Riedel). -# 1.2.2+## 1.2.2 +_2016-04-28, Artyom_ * New maintainer. * Now we don't reexport the problematic `Show` instance for functions. -# 1.2.1+## 1.2.1 +_2015-08-29, Chris Kuklewicz_ * Updated dependency versions.  # 1.2.0 -"Almost ghc-7.8" with the array 0.4 changes for `Data.Array.Unsafe`+_2014-02-02, Chris Kuklewicz_+* "Almost ghc-7.8" with the array 0.4 changes for `Data.Array.Unsafe` -# 1.1.8 -Make ghc-7.0.2 on platorm 2011.2.0.0.0 happy+## 1.1.8 -# 1.1.7+Make ghc-7.0.2 on platform 2011.2.0.0.0 happy +## 1.1.7+ fix url below -# 1.1.6+## 1.1.6  Fix bug preventing `[]] [-] [^]] [^-]` (thanks to Maxime Henrion) -# 1.1.5+## 1.1.5  try `needUniqTags` in `POr` in CorePattern.hs, try `toAdvice b` for `PStar child` -# 1.1.4+## 1.1.4  fixed -# 1.1.3+## 1.1.3  BROKEN after 100 characters the `compressOrbit` dies! -# 1.1.2+## 1.1.2  worked -# 1.1.1+## 1.1.1  add gnu escapes @@ -64,19 +213,19 @@  NewDFA code working -# 1.0.7+## 1.0.7  make NewDFA directory and String_NC -# 1.0.6+## 1.0.6  try NewDFATest_SBS with `uncons` -# 1.0.5+## 1.0.5  use `uncons` on SBS -# 1.0.4+## 1.0.4  try repaired NewDFATest_SBS @@ -93,11 +242,11 @@ * np3:  `!off` the multi? No * np2:  comment out all Testing code? No -# 1.0.3+## 1.0.3  try to alter `matchTest` to not have the `Bool` args? No -# 1.0.2+## 1.0.2  arg, the prof is fast and the normal slow! @@ -105,70 +254,70 @@  add NewDFATest.hs -# 0.99.20+## 0.99.20  go to many vs single? -# 0.99.19+## 0.99.19  try for pre-comparison of orbit-logs! -# 0.99.18+## 0.99.18  try alternate lazy/strict strategy in NewDFA. Fix offset laziness. -# 0.99.17+## 0.99.17  radical removal of flag array and adding of `SetVal` to handle groups -# 0.99.16+## 0.99.16  performance? up to v15 -# 0.99.15+## 0.99.15  get string with NewDFA testing, unit tests and 1000 random regex pass -# 0.99.14+## 0.99.14  start changing to the new real DFA -# 0.99.13+## 0.99.13  more cleanup -# 0.99.12+## 0.99.12  try to debug 0.99.11: fixed `updateWinner` -# 0.99.11+## 0.99.11  improve above fix and make stuff work better – HAS BUG, along with old TDFA! -# 0.99.10+## 0.99.10  fixed `((.?)*)*` patterns by changing `PStar nullView` when `mayFirstBeNull` -# 0.99.9+## 0.99.9  testing changing `bestTrans`/`chooseWith`/`choose` to include `enterOrbit`/`newFlags`/`(_,True)` info -# 0.99.8+## 0.99.8  testing changing `Maximize` to `Minimize` for `Tag`s, decide `(a*)*` is canonical problem -# 0.99.7+## 0.99.7  Use `(PGroup Nothing)` in `Pattern` to decompose `PBound` -# 0.99.6+## 0.99.6  change to nested `nonEmpty` calls for `PBound` -# 0.99.5+## 0.99.5  remove `PNonEmpty` constructor -# 0.99.4+## 0.99.4  tests `pnonempty' = \ p -> POr [ PEmpty, p ]` instead of `PNonEmpty`
LICENSE view
@@ -1,4 +1,4 @@-This modile is under this "3 clause" BSD license:+This module is under this "3 clause" BSD license:  Copyright (c) 2007-2009, Christopher Kuklewicz All rights reserved.
+ README.md view
@@ -0,0 +1,221 @@+[![Hackage version](https://img.shields.io/hackage/v/regex-tdfa.svg?label=Hackage&color=informational)](http://hackage.haskell.org/package/regex-tdfa)+[![Stackage Nightly](http://stackage.org/package/regex-tdfa/badge/nightly)](http://stackage.org/nightly/package/regex-tdfa)+[![Stackage LTS](http://stackage.org/package/regex-tdfa/badge/lts)](http://stackage.org/lts/package/regex-tdfa)+[![Haskell-CI](https://github.com/haskell-hvr/regex-tdfa/actions/workflows/haskell-ci.yml/badge.svg?branch=master&event=push)](https://github.com/haskell-hvr/regex-tdfa/actions/workflows/haskell-ci.yml)+[![License](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)+# regex-tdfa++This is [`regex-tdfa`](http://hackage.haskell.org/package/regex-tdfa) which is a pure Haskell regular expression library (for POSIX extended regular expressions) originally written by Christopher Kuklewicz.++The name "tdfa" stands for Tagged-DFA.++## Getting started++### Importing and using++[Declare a dependency](https://www.haskell.org/cabal/users-guide/developing-packages.html#pkg-field-build-depends) on the `regex-tdfa` library in your `.cabal` file:++```+build-depends: regex-tdfa ^>= 1.3.2+```++In Haskell modules where you need to use regexes `import` the respective `regex-tdfa` module:++```haskell+import Text.Regex.TDFA+```++### Basics++```haskell+λ> emailRegex = "[a-zA-Z0-9+._-]+@[a-zA-Z-]+\\.[a-z]+"+λ> "my email is email@email.com" =~ emailRegex :: Bool+>>> True++-- non-monadic+<to-match-against> =~ <regex>++-- monadic, uses 'fail' on lack of match+<to-match-against> =~~ <regex>+```++`(=~)` and `(=~~)` are polymorphic in their return type. This is so that+regex-tdfa can pick the most efficient way to give you your result based on+what you need. For instance, if all you want is to check whether the regex+matched or not, there's no need to allocate a result string. If you only want+the first match, rather than all the matches, then the matching engine can stop+after finding a single hit.++This does mean, though, that you may sometimes have to explicitly specify the+type you want, especially if you're trying things out at the REPL.++### Common use cases++#### Get the first match++```haskell+-- returns empty string if no match+a =~ b :: String  -- or ByteString, or Text...++λ> "alexis-de-tocqueville" =~ "[a-z]+" :: String+>>> "alexis"++λ> "alexis-de-tocqueville" =~ "[[:digit:]]+" :: String+>>> ""+```++#### Check if it matched at all++```haskell+a =~ b :: Bool++λ> "alexis-de-tocqueville" =~ "[a-z]+" :: Bool+>>> True+```++#### Get first match + text before/after++```haskell+-- if no match, will just return whole+-- string in the first element of the tuple+a =~ b :: (String, String, String)++λ> "alexis-de-tocqueville" =~ "de" :: (String, String, String)+>>> ("alexis-", "de", "-tocqueville")++λ> "alexis-de-tocqueville" =~ "kant" :: (String, String, String)+>>> ("alexis-de-tocqueville", "", "")+```++#### Get first match + submatches++```haskell+-- same as above, but also returns a list of /just/ submatches+-- submatch list is empty if regex doesn't match at all+a =~ b :: (String, String, String, [String])++λ> "div[attr=1234]" =~ "div\\[([a-z]+)=([^]]+)\\]"+     :: (String, String, String, [String])+>>> ("", "div[attr=1234]", "", ["attr","1234"])+```++#### Get all non-overlapping matches++```haskell+-- can also return Data.Array instead of List+getAllTextMatches (a =~ b) :: [String]++λ> getAllTextMatches ("john anne yifan" =~ "[a-z]+") :: [String]+>>> ["john","anne","yifan"]++λ> getAllTextMatches ("0a0b0" =~ "0[[:lower:]]0") :: [String]+>>> ["0a0"]+```+Note that `"0b0"` is not included in the result since it overlaps with `"0a0"`.++#### Special characters++`regex-tdfa` only supports a small set of special characters and is much less+featureful than some other regex engines you might be used to, such as PCRE.++* ``\` `` &mdash; Match start of entire text (similar to `^` in other regex engines)+* `\'` &mdash; Match end of entire text (similar to `$` in other regex engines)+* `\<` &mdash; Match beginning of word+* `\>` &mdash; Match end of word+* `\b` &mdash; Match beginning or end of word+* `\B` &mdash; Match neither beginning nor end of word++While shorthands like `\d` (for digit) are not recognized, one can use the respective+POSIX character class inside `[...]`.  E.g., `[[:digit:][:lower:]_]` is short for+`[0-9a-z_]`.  The supported character classes are listed on+[Wikipedia](https://en.wikipedia.org/w/index.php?title=Regular_expression&oldid=1095256273#Character_classes)+and defined in module+[`TNFA`](https://github.com/haskell-hvr/regex-tdfa/blob/95d47cb982d2cf636b2cb6260a866f9907341c45/lib/Text/Regex/TDFA/TNFA.hs#L804-L816).++Please also consult a variant of this documentation which is part of the+[Text.Regex.TDFA haddock](http://hackage.haskell.org/package/regex-tdfa/docs/Text-Regex-TDFA.html),+and the original documentation at the [Haskell wiki](https://wiki.haskell.org/Regular_expressions#regex-tdfa).++### Less common stuff++#### Get match indices++```haskell+-- can also return Data.Array instead of List+getAllMatches (a =~ b) :: [(Int, Int)]  -- (index, length)++λ> getAllMatches ("john anne yifan" =~ "[a-z]+") :: [(Int, Int)]+>>> [(0,4), (5,4), (10,5)]+``````++#### Get submatch indices++```haskell+-- match of __entire__ regex is first element, not first capture+-- can also return Data.Array instead of List+getAllSubmatches (a =~ b) :: [(Int, Int)]  -- (index, length)++λ> getAllSubmatches ("div[attr=1234]" =~ "div\\[([a-z]+)=([^]]+)\\]")+     :: [(Int, Int)]+>>> [(0,14), (4,4), (9,4)]+```++### Replacement++`regex-tdfa` does not provide find-and-replace.++## Avoiding backslashes++If you find yourself writing a lot of regexes, take a look at+[raw-strings-qq](http://hackage.haskell.org/package/raw-strings-qq). It'll+let you write regexes without needing to escape all your backslashes.++```haskell+{-# LANGUAGE QuasiQuotes #-}++import Text.RawString.QQ+import Text.Regex.TDFA++λ> "2 * (3 + 1) / 4" =~ [r|\([^)]+\)|] :: String+>>> "(3 + 1)"+```++## Known bugs and infelicities++* Regexes with large character classes combined with `{m,n}` are very slow and memory-hungry ([#3][]).++  > An example of such a regex is `^[\x0020-\xD7FF]{1,255}$`.++* POSIX submatch semantics are broken in some rare cases ([#2][]).++[#2]: https://github.com/haskell-hvr/regex-tdfa/issues/2+[#3]: https://github.com/haskell-hvr/regex-tdfa/issues/3++## About this package++This was inspired by the algorithm (and Master's thesis) behind the regular expression library known as [TRE or libtre](https://github.com/laurikari/tre/).  This was created by Ville Laurikari and tackled the difficult issue of efficient sub-match capture for POSIX regular expressions.++By building on this thesis and adding a few more optimizations, regex-tdfa matching input text of length N should have O(N) runtime, and should have a maximum memory bounded by the pattern size that does not scale with N. It should do this while returning well defined (and correct) values for the parenthesized sub-matches.++Regardless of performance, nearly every single OS and Libra for POSIX regular expressions has bugs in sub-matches.  This was detailed on the [Regex POSIX Haskell wiki page](https://wiki.haskell.org/Regex_Posix), and can be demonstrated with the [regex-posix-unittest](http://hackage.haskell.org/package/regex-posix-unittest) suite of checks.  Test [regex-tdfa-unittest](http://hackage.haskell.org/package/regex-tdfa-unittest) should show regex-tdfa passing these same checks.  I owe my understanding of the correct behvior and many of these unit tests to Glenn Fowler at AT&T ("An Interpretation of the POSIX regex Standard").++### Maintenance history++The original Darcs repository was at [code.haskell.org](http://code.haskell.org/regex-tdfa/).+For a while a fork was maintained by Roman Cheplyaka as+[regex-tdfa-rc](http://hackage.haskell.org/package/regex-tdfa-rc).++Then the repository moved to <https://github.com/ChrisKuklewicz/regex-tdfa>,+which was primarily maintained by [Artyom (neongreen)](https://github.com/neongreen).++Finally, maintainership was passed on again and the repository moved to its current location+at <https://github.com/haskell-hvr/regex-tdfa>.++## Other related packages++Searching for "tdfa" on [hackage](http://hackage.haskell.org/packages/search?terms=tdfa)+finds some related packages (unmaintained as of 2022-07-14).++## Document notes++This README was originally written 2016-04-30.
− Setup.hs
@@ -1,6 +0,0 @@-#!/usr/bin/env runhaskell---- I usually compile this with "ghc --make -o setup Setup.hs"--import Distribution.Simple(defaultMain)-main = defaultMain
lib/Data/IntMap/CharMap2.hs view
@@ -9,7 +9,7 @@ import Data.Char as C(ord) import Data.List as L (map) import qualified Data.IntMap as M-#if MIN_VERSION_containers(0,6,0)+#if MIN_VERSION_containers(0,5,11) import qualified Data.IntMap.Internal.Debug as MD #else import qualified Data.IntMap as MD
lib/Data/IntMap/EnumMap2.hs view
@@ -2,9 +2,9 @@  module Data.IntMap.EnumMap2 where -import Data.Foldable(Foldable(..))+import Data.Foldable as F (Foldable(foldMap)) import qualified Data.IntMap as M-#if MIN_VERSION_containers(0,6,0)+#if MIN_VERSION_containers(0,5,11) import qualified Data.IntMap.Internal.Debug as MD #else import qualified Data.IntMap as MD@@ -27,8 +27,8 @@ instance Ord k => Functor (EnumMap k) where   fmap f (EnumMap m) = EnumMap (fmap f m) -instance Ord k => Foldable (EnumMap k) where-  foldMap f (EnumMap m) = foldMap f m+instance Ord k => F.Foldable (EnumMap k) where+  foldMap f (EnumMap m) = F.foldMap f m  (!) :: (Enum key) => EnumMap key a -> key -> a (!) (EnumMap m) k = (M.!) m (fromEnum k)@@ -50,7 +50,7 @@  {-# INLINE lookup #-} lookup :: (Enum key) => key -> EnumMap key a -> Maybe a-lookup k (EnumMap m) = maybe (fail "EnumMap.lookup failed") return $ M.lookup (fromEnum k) m+lookup k (EnumMap m) = M.lookup (fromEnum k) m  findWithDefault :: (Enum key) => a -> key -> EnumMap key a -> a findWithDefault a k (EnumMap m) = M.findWithDefault a (fromEnum k) m
lib/Text/Regex/TDFA.hs view
@@ -1,4 +1,9 @@ {-|+Module: Text.Regex.TDFA+Copyright: (c) Chris Kuklewicz 2007-2009+SPDX-License-Identifier: BSD-3-Clause+Maintainer: Andreas Abel+Stability: stable  The "Text.Regex.TDFA" module provides a backend for regular expressions. It provides instances for the classes defined and@@ -7,33 +12,39 @@ qualified imports (with renaming for convenience).  This regex-tdfa package implements, correctly, POSIX extended regular-expressions.  It is highly unlikely that the regex-posix package on+expressions.  It is highly unlikely that the @regex-posix@ package on your operating system is correct, see <http://www.haskell.org/haskellwiki/Regex_Posix> for examples of your OS's bugs.  = Importing and using -Add to your package.yaml/cabal file:--> dependencies:->   - regex-tdfa+Declare a dependency on the @regex-tdfa@ library in your @.cabal@ file: -In modules where you need to use regexes:+> build-depends: regex-tdfa ^>= 1.3.2 -> import Text.Regex.TDFA+In Haskell modules where you want to use regexes simply @import@ /this/ module: -Note that regex-tdfa does not provide support for @Text@ by default.-If you need this functionality, add <https://hackage.haskell.org/package/regex-tdfa-text regex-tdfa-text>-as a dependency and @import Text.Regex.TDFA.Text ()@.+@+import "Text.Regex.TDFA"+@  = Basics -@-λ> let emailRegex = "[a-zA-Z0-9+.\_-]+\@[a-zA-Z-]+\\\\.[a-z]+"-λ> "my email is email@email.com" '=~' emailRegex :: Bool->>> True+>>> let emailRegex = "[a-zA-Z0-9+._-]+\\@[-a-zA-Z]+\\.[a-z]+"+>>> "my email is first-name.lastname_1974@e-mail.com" =~ emailRegex :: Bool+True +>>> "invalid@mail@com" =~ emailRegex :: Bool+False++>>> "invalid@mail.COM" =~ emailRegex :: Bool+False++>>> "#@invalid.com" =~ emailRegex :: Bool+False++@ /-- non-monadic/ λ> \<to-match-against\> '=~' \<regex\> @@ -58,36 +69,36 @@ @ /-- returns empty string if no match/ a '=~' b :: String  /-- or ByteString, or Text.../+@ -λ> "alexis-de-tocqueville" '=~' "[a-z]+" :: String->>> "alexis"+>>> "alexis-de-tocqueville" =~ "[a-z]+" :: String+"alexis" -λ> "alexis-de-tocqueville" '=~' "[0-9]+" :: String->>> ""-@+>>> "alexis-de-tocqueville" =~ "[0-9]+" :: String+""  == Check if it matched at all  @ a '=~' b :: Bool--λ> "alexis-de-tocqueville" '=~' "[a-z]+" :: Bool->>> True @ +>>> "alexis-de-tocqueville" =~ "[a-z]+" :: Bool+True+ == Get first match + text before/after  @ /-- if no match, will just return whole/ /-- string in the first element of the tuple/ a =~ b :: (String, String, String)+@ -λ> "alexis-de-tocqueville" '=~' "de" :: (String, String, String)->>> ("alexis-", "de", "-tocqueville")+>>> "alexis-de-tocqueville" =~ "de" :: (String, String, String)+("alexis-","de","-tocqueville") -λ> "alexis-de-tocqueville" '=~' "kant" :: (String, String, String)->>> ("alexis-de-tocqueville", "", "")-@+>>> "alexis-de-tocqueville" =~ "kant" :: (String, String, String)+("alexis-de-tocqueville","","")  == Get first match + submatches @@ -95,31 +106,31 @@ /-- same as above, but also returns a list of just submatches./ /-- submatch list is empty if regex doesn't match at all/ a '=~' b :: (String, String, String, [String])--λ> "div[attr=1234]" '=~' "div\\\\[([a-z]+)=([^]]+)\\\\]" :: (String, String, String, [String])->>> ("", "div[attr=1234]", "", ["attr","1234"]) @ +>>> "div[attr=1234]" =~ "div\\[([a-z]+)=([^]]+)\\]" :: (String, String, String, [String])+("","div[attr=1234]","",["attr","1234"])+ == Get /all/ matches  @ /-- can also return Data.Array instead of List/ 'getAllTextMatches' (a '=~' b) :: [String]--λ> 'getAllTextMatches' ("john anne yifan" '=~' "[a-z]+") :: [String]->>> ["john","anne","yifan"] @ +>>> getAllTextMatches ("john anne yifan" =~ "[a-z]+") :: [String]+["john","anne","yifan"]++>>> getAllTextMatches ("* - . a + z" =~ "[--z]+") :: [String]+["-",".","a","z"]+ = Feature support  This package does provide captured parenthesized subexpressions.  Depending on the text being searched this package supports Unicode.-The @[Char]@ and @(Seq Char)@ text types support Unicode.  The @ByteString@-and @ByteString.Lazy@ text types only support ASCII.  It is possible to-support utf8 encoded @ByteString.Lazy@ by using regex-tdfa and-<http://hackage.haskell.org/package/regex-tdfa-utf8 regex-tdfa-utf8>-packages together (required the utf8-string package).+The @[Char]@, @Text@, @Text.Lazy@, and @(Seq Char)@ text types support Unicode.  The @ByteString@+and @ByteString.Lazy@ text types only support ASCII.  As of version 1.1.1 the following GNU extensions are recognized, all anchors:@@ -144,6 +155,10 @@ ASCII only, valid classes are alnum, digit, punct, alpha, graph, space, blank, lower, upper, cntrl, print, xdigit, word. +>>> getAllTextMatches ("john anne yifan" =~ "[[:lower:]]+") :: [String]+["john","anne","yifan"]++ This package does not provide "basic" regular expressions.  This package does not provide back references inside regular expressions. @@ -166,7 +181,7 @@ import Text.Regex.TDFA  λ> "2 * (3 + 1) / 4" '=~' [r|\\([^)]+\\)|] :: String->>> "(3 + 1)"+"(3 + 1)" @  -}@@ -182,6 +197,8 @@ import Text.Regex.TDFA.String() import Text.Regex.TDFA.ByteString() import Text.Regex.TDFA.ByteString.Lazy()+import Text.Regex.TDFA.Text()+import Text.Regex.TDFA.Text.Lazy() import Text.Regex.TDFA.Sequence() import Text.Regex.TDFA.Common(Regex,CompOption(..),ExecOption(..)) --import Text.Regex.TDFA.Wrap(Regex,CompOption(..),ExecOption(..),(=~),(=~~))
lib/Text/Regex/TDFA/ByteString.hs view
@@ -63,18 +63,16 @@     Left err -> Left ("parseRegex for Text.Regex.TDFA.ByteString failed:"++show err)     Right pattern -> Right (patternToRegex pattern compOpt execOpt) -execute :: Regex      -- ^ Compiled regular expression+execute :: Regex        -- ^ Compiled regular expression         -> B.ByteString -- ^ ByteString to match against         -> Either String (Maybe MatchArray) execute r bs = Right (matchOnce r bs) -regexec :: Regex      -- ^ Compiled regular expression+regexec :: Regex        -- ^ Compiled regular expression         -> B.ByteString -- ^ ByteString to match against         -> Either String (Maybe (B.ByteString, B.ByteString, B.ByteString, [B.ByteString]))-regexec r bs =-  case matchOnceText r bs of-    Nothing -> Right (Nothing)-    Just (pre,mt,post) ->-      let main = fst (mt!0)-          rest = map fst (tail (elems mt)) -- will be []-      in Right (Just (pre,main,post,rest))+regexec r txt = Right $+  case matchOnceText r txt of+    Just (pre, mt, post) | main:rest <- map fst (elems mt)+      -> Just (pre, main, post, rest)+    _ -> Nothing
lib/Text/Regex/TDFA/ByteString/Lazy.hs view
@@ -79,18 +79,16 @@     Left err -> Left ("parseRegex for Text.Regex.TDFA.ByteString failed:"++show err)     Right pattern -> Right (patternToRegex pattern compOpt execOpt) -execute :: Regex      -- ^ Compiled regular expression+execute :: Regex        -- ^ Compiled regular expression         -> L.ByteString -- ^ ByteString to match against         -> Either String (Maybe MatchArray) execute r bs = Right (matchOnce r bs) -regexec :: Regex      -- ^ Compiled regular expression+regexec :: Regex        -- ^ Compiled regular expression         -> L.ByteString -- ^ ByteString to match against         -> Either String (Maybe (L.ByteString, L.ByteString, L.ByteString, [L.ByteString]))-regexec r bs =-  case matchOnceText r bs of-    Nothing -> Right (Nothing)-    Just (pre,mt,post) ->-      let main = fst (mt!0)-          rest = map fst (tail (elems mt)) -- will be []-      in Right (Just (pre,main,post,rest))+regexec r txt = Right $+  case matchOnceText r txt of+    Just (pre, mt, post) | main:rest <- map fst (elems mt)+      -> Just (pre, main, post, rest)+    _ -> Nothing
lib/Text/Regex/TDFA/Common.hs view
@@ -1,7 +1,9 @@ {-# OPTIONS -funbox-strict-fields #-}--- | Common provides simple functions to the backend.  It defines most--- of the data types.  All modules should call error via the--- common_error function below.++-- | Common provides simple functions to the backend.+-- It defines most of the data types.+-- All modules should call 'error' via the 'common_error' function below.+ module Text.Regex.TDFA.Common where  import Text.Regex.Base(RegexOptions(..))@@ -26,19 +28,19 @@  common_error :: String -> String -> a common_error moduleName message =-  error ("Explict error in module "++moduleName++" : "++message)+  error ("Explicit error in module "++moduleName++" : "++message)  on :: (t1 -> t1 -> t2) -> (t -> t1) -> t -> t -> t2 f `on` g = (\x y -> (g x) `f` (g y)) --- | after 'sort' or 'sortBy' the use of 'nub'\/'nubBy' can be replaced by 'norep'\/'norepBy'+-- | After 'sort' or 'sortBy' the use of 'nub' or 'nubBy' can be replaced by 'norep' or 'norepBy'. norep :: (Eq a) => [a]->[a] norep [] = [] norep x@[_] = x norep (a:bs@(c:cs)) | a==c = norep (a:cs)                     | otherwise = a:norep bs --- | after 'sort' or 'sortBy' the use of 'nub'\/'nubBy' can be replaced by 'norep'\/'norepBy'+-- | After 'sort' or 'sortBy' the use of 'nub' or 'nubBy' can be replaced by 'norep' or 'norepBy'. norepBy :: (a -> a -> Bool) -> [a] -> [a] norepBy _ [] = [] norepBy _ x@[_] = x@@ -68,8 +70,7 @@ noWin :: WinTags -> Bool noWin = null --- | Used to track elements of the pattern that accept characters or --- are anchors+-- | Used to track elements of the pattern that accept characters or are anchors. newtype DoPa = DoPa {dopaIndex :: Int} deriving (Eq,Ord)  instance Enum DoPa where@@ -82,48 +83,58 @@ -- | Control whether the pattern is multiline or case-sensitive like Text.Regex and whether to -- capture the subgroups (\\1, \\2, etc).  Controls enabling extra anchor syntax. data CompOption = CompOption {-    caseSensitive :: Bool    -- ^ True in blankCompOpt and defaultCompOpt-  , multiline :: Bool {- ^ False in blankCompOpt, True in defaultCompOpt. Compile for-                      newline-sensitive matching.  "By default, newline is a completely ordinary-                      character with no special meaning in either REs or strings.  With this flag,-                      inverted bracket expressions and . never match newline, a ^ anchor matches the-                      null string after any newline in the string in addition to its normal-                      function, and the $ anchor matches the null string before any newline in the-                      string in addition to its normal function." -}-  , rightAssoc :: Bool       -- ^ True (and therefore Right associative) in blankCompOpt and defaultCompOpt-  , newSyntax :: Bool        -- ^ False in blankCompOpt, True in defaultCompOpt. Add the extended non-POSIX syntax described in "Text.Regex.TDFA" haddock documentation.-  , lastStarGreedy ::  Bool  -- ^ False by default.  This is POSIX correct but it takes space and is slower.-                            -- Setting this to true will improve performance, and should be done-                            -- if you plan to set the captureGroups execoption to False.+    caseSensitive :: Bool+      -- ^ True in 'blankCompOpt' and 'defaultCompOpt'.+  , multiline :: Bool+      -- ^ False in 'blankCompOpt', True in 'defaultCompOpt'.+      -- Compile for newline-sensitive matching.+      --+      -- From [regexp man page](https://www.tcl.tk/man/tcl8.4/TclCmd/regexp.html#M8):+      -- "By default, newline is a completely ordinary character with no special meaning in either REs or strings.+      -- With this flag, inverted bracket expressions @[^@ and @.@ never match newline,+      -- a @^@ anchor matches the null string after any newline in the string in addition to its normal function,+      -- and the @$@ anchor matches the null string before any newline in the string in addition to its normal function."+  , rightAssoc :: Bool+      -- ^ True (and therefore right associative) in 'blankCompOpt' and 'defaultCompOpt'.+  , newSyntax :: Bool+      -- ^ False in 'blankCompOpt', True in 'defaultCompOpt'.+      -- Enables the extended non-POSIX syntax described in "Text.Regex.TDFA" haddock documentation.+  , lastStarGreedy ::  Bool+      -- ^ False by default.  This is POSIX correct but it takes space and is slower.+      -- Setting this to True will improve performance, and should be done+      -- if you plan to set the 'captureGroups' 'ExecOption' to False.   } deriving (Read,Show)  data ExecOption = ExecOption {     captureGroups :: Bool    -- ^ True by default.  Set to False to improve speed (and space).   } deriving (Read,Show) --- | Used by implementation to name certain Postions during--- matching. Identity of Position tag to set during a transition+-- | Used by implementation to name certain 'Position's during+-- matching. Identity of 'Position' tag to set during a transition. type Tag = Int--- | Internal use to indicate type of tag and preference for larger or smaller Positions++-- | Internal use to indicate type of tag and preference for larger or smaller 'Position's. data OP = Maximize | Minimize | Orbit | Ignore deriving (Eq,Show)--- | Internal NFA node identity number++-- | Internal NFA node identity number. type Index = Int--- | Internal DFA identity is this Set of NFA Index++-- | Internal DFA identity is this 'Set' of NFA 'Index'. type SetIndex = IntSet {- Index -}--- | Index into the text being searched++-- | Index into the text being searched. type Position = Int --- | GroupIndex is for indexing submatches from capturing--- parenthesized groups (PGroup\/Group)+-- | 'GroupIndex' is for indexing submatches from capturing parenthesized groups ('PGroup' or 'Group'). type GroupIndex = Int--- | GroupInfo collects the parent and tag information for an instance --- of a group++-- | 'GroupInfo' collects the parent and tag information for an instance of a group. data GroupInfo = GroupInfo {     thisIndex, parentIndex :: GroupIndex   , startTag, stopTag, flagTag :: Tag   } deriving Show --- | The TDFA backend specific 'Regex' type, used by this module's RegexOptions and RegexMaker+-- | The TDFA backend specific 'Regex' type, used by this module's 'RegexOptions' and 'RegexMaker'. data Regex = Regex {     regex_dfa :: DFA                             -- ^ starting DFA state   , regex_init :: Index                          -- ^ index of starting state@@ -161,10 +172,10 @@               | WinTest WhichTest (Maybe WinEmpty) (Maybe WinEmpty)   deriving Show --- | Internal NFA node type+-- | Internal NFA node type. data QNFA = QNFA {q_id :: Index, q_qt :: QT} --- | Internal to QNFA type.+-- | Internal to 'QNFA' type. data QT = Simple { qt_win :: WinTags -- ^ empty transitions to the virtual winning state                  , qt_trans :: CharMap QTrans -- ^ all ways to leave this QNFA to other or the same QNFA                  , qt_other :: QTrans -- ^ default ways to leave this QNFA to other or the same QNFA@@ -182,29 +193,38 @@ -- Also support for GNU extensions is being added: \\\` beginning of -- buffer, \\\' end of buffer, \\\< and \\\> for begin and end of words, \\b -- and \\B for word boundary and not word boundary.-data WhichTest = Test_BOL | Test_EOL -- '^' and '$' (affected by multiline option)-               | Test_BOB | Test_EOB -- \` and \' begin and end buffer-               | Test_BOW | Test_EOW -- \< and \> begin and end word-               | Test_EdgeWord | Test_NotEdgeWord -- \b and \B word boundaries+data WhichTest+  = Test_BOL          -- ^ @^@ (affected by multiline option)+  | Test_EOL          -- ^ @$@ (affected by multiline option)+  | Test_BOB          -- ^ @\\`@ beginning of buffer+  | Test_EOB          -- ^ @\\'@ end ofbuffer+  | Test_BOW          -- ^ @\\<@ beginning of word+  | Test_EOW          -- ^ @\\>@ end of word+  | Test_EdgeWord     -- ^ @\\b@ word boundary+  | Test_NotEdgeWord  -- ^ @\\B@ not word boundary   deriving (Show,Eq,Ord,Enum) --- | The things that can be done with a Tag.  TagTask and--- ResetGroupStopTask are for tags with Maximize or Minimize OP--- values.  ResetOrbitTask and EnterOrbitTask and LeaveOrbitTask are+-- | The things that can be done with a Tag.  'TagTask' and+-- 'ResetGroupStopTask' are for tags with Maximize or Minimize OP+-- values.  'ResetOrbitTask' and 'EnterOrbitTask' and 'LeaveOrbitTask' are -- for tags with Orbit OP value. data TagTask = TagTask | ResetGroupStopTask | SetGroupStopTask              | ResetOrbitTask | EnterOrbitTask | LeaveOrbitTask deriving (Show,Eq) --- | Ordered list of tags and their associated Task+-- | Ordered list of tags and their associated Task. type TagTasks = [(Tag,TagTask)]+ -- | When attached to a QTrans the TagTask can be done before or after -- accepting the character. data TagUpdate = PreUpdate TagTask | PostUpdate TagTask deriving (Show,Eq)+ -- | Ordered list of tags and their associated update operation. type TagList = [(Tag,TagUpdate)]+ -- | A TagList and the location of the item in the original pattern -- that is being accepted. type TagCommand = (DoPa,TagList)+ -- | Ordered list of tags and their associated update operation to -- perform on an empty transition to the virtual winning state. type WinTags = TagList@@ -226,27 +246,26 @@                    , dt_a,dt_b :: DT      -- ^ use dt_a if test is True else use dt_b                    } --- | Internal type to repesent the commands for the tagged transition.--- The outer IntMap is for the destination Index and the inner IntMap+-- | Internal type to represent the commands for the tagged transition.+-- The outer 'IntMap' is for the destination Index and the inner 'IntMap' -- is for the Source Index.  This is convenient since all runtime data -- going to the same destination must be compared to find the best. ----- A Destination IntMap entry may have an empty Source IntMap if and--- only if the destination is the starting index and the NFA\/DFA.+-- A Destination 'IntMap' entry may have an empty Source 'IntMap' if and+-- only if the destination is the starting index and the NFA or DFA. -- This instructs the matching engine to spawn a new entry starting at -- the post-update position. type DTrans = IntMap {- Index of Destination -} (IntMap {- Index of Source -} (DoPa,Instructions)) -- type DTrans = IntMap {- Index of Destination -} (IntMap {- Index of Source -} (DoPa,RunState ()))--- | Internal convenience type for the text display code++-- | Internal convenience type for the text display code. type DTrans' = [(Index, [(Index, (DoPa, ([(Tag, (Position,Bool))],[String])))])] --- | Positions for which a * was re-started while looping.  Need to+-- | Positions for which a @*@ was re-started while looping.  Need to -- append locations at back but compare starting with front, so use--- Seq as a Queue.  The initial position is saved in basePos (and a--- Maximize Tag), the middle positions in the Seq, and the final+-- 'Seq' as a queue.  The initial position is saved in 'basePos' (and a+-- Maximize Tag), the middle positions in the 'Seq', and the final -- position is NOT saved in the Orbits (only in a Maximize Tag).------ The orderinal code is being written XXX TODO document it. data Orbits = Orbits   { inOrbit :: !Bool        -- True if enterOrbit, False if LeaveOrbit   , basePos :: Position@@ -290,7 +309,7 @@   where foo :: CharMap QTrans -> [(Char,[(Index,[TagCommand])])]         foo = mapSnd foo' . Map.toAscList         foo' :: QTrans -> [(Index,[TagCommand])]-        foo' = IMap.toList +        foo' = IMap.toList showQT (Testing test dopas a b) = "{Testing "++show test++" "++show (Set.toList dopas)                               ++"\n"++indent' a                               ++"\n"++indent' b++"}"
lib/Text/Regex/TDFA/CorePattern.hs view
@@ -34,8 +34,11 @@                                   ,TestInfo,OP(..),SetTestInfo(..),NullView                                   ,patternToQ,cleanNullView,cannotAccept,mustAccept) where -import Control.Monad.RWS {- all -}+import Control.Monad (liftM2, forM, replicateM)+import Control.Monad.RWS (RWS, runRWS, ask, local, listens, tell, get, put)+ import Data.Array.IArray(Array,(!),accumArray,listArray)+import Data.Either (partitionEithers, rights) import Data.List(sort) import Data.IntMap.EnumMap2(EnumMap) import qualified Data.IntMap.EnumMap2 as Map(singleton,null,assocs,keysSet)@@ -43,6 +46,7 @@ import Data.IntSet.EnumSet2(EnumSet) import qualified Data.IntSet.EnumSet2 as Set(singleton,toList,isSubsetOf) import Data.Semigroup as Sem+ import Text.Regex.TDFA.Common {- all -} import Text.Regex.TDFA.Pattern(Pattern(..),starTrans) -- import Debug.Trace@@ -249,7 +253,7 @@ -- from the dfs of the children and simultaneously down in the form of -- pre and post HandleTag data.  This bidirectional flow is handled -- declaratively by using the MonadFix (i.e. mdo).--- +-- -- Invariant: A tag should exist in Q in exactly one place (and will -- be in a preTag,postTag, or getOrbit field).  This is partly because -- PGroup needs to know the tags are around precisely the expression@@ -264,11 +268,11 @@ -- -- There is a final "qwin of Q {postTag=ISet.singleton 1}" and an -- implied initial index tag of 0.--- +-- -- favoring pushing Apply into the child postTag makes PGroup happier -type PM = RWS (Maybe GroupIndex) [Either Tag GroupInfo] ([OP]->[OP],Tag) -type HHQ = HandleTag  -- m1 : info about left boundaary / preTag+type PM = RWS (Maybe GroupIndex) [Either Tag GroupInfo] ([OP]->[OP],Tag)+type HHQ = HandleTag  -- m1 : info about left boundary / preTag         -> HandleTag  -- m2 : info about right boundary / postTag         -> PM Q @@ -277,18 +281,6 @@ makeGroupArray maxGroupIndex groups = accumArray (\earlier later -> later:earlier) [] (1,maxGroupIndex) filler     where filler = map (\gi -> (thisIndex gi,gi)) groups -fromRight :: [Either Tag GroupInfo] -> [GroupInfo]-fromRight [] = []-fromRight ((Right x):xs) = x:fromRight xs-fromRight ((Left _):xs) = fromRight xs--partitionEither :: [Either Tag GroupInfo] -> ([Tag],[GroupInfo])-partitionEither = helper id id where-  helper :: ([Tag]->[Tag]) -> ([GroupInfo]->[GroupInfo]) -> [Either Tag GroupInfo] -> ([Tag],[GroupInfo])-  helper ls rs [] = (ls [],rs [])-  helper ls rs ((Right x):xs) = helper  ls      (rs.(x:)) xs-  helper ls rs ((Left  x):xs) = helper (ls.(x:)) rs       xs- -- Partial function: assumes starTrans has been run on the Pattern -- Note that the lazy dependency chain for this very zigzag: --   varies information is sent up the tree@@ -298,13 +290,13 @@ --       and lazily looks resetGroupTags from aGroups, the result of all writer (Right _) --       preReset stores the resetGroupTags result of the lookup in the tree --     makeOrbit sends some tags to the writer (Left _)---     withOrbit listens to children send orbit info to writer for resetOrbitTags +--     withOrbit listens to children send orbit info to writer for resetOrbitTags --   nullQ depends m1 m2 and resetOrbitTags and resetGroupTags and is sent up the tree patternToQ :: CompOption -> (Pattern,(GroupIndex,DoPa)) -> (Q,Array Tag OP,Array GroupIndex [GroupInfo]) patternToQ compOpt (pOrig,(maxGroupIndex,_)) = (tnfa,aTags,aGroups) where   (tnfa,(tag_dlist,nextTag),groups) = runRWS monad startReader startState   aTags = listArray (0,pred nextTag) (tag_dlist [])-  aGroups = makeGroupArray maxGroupIndex (fromRight groups)+  aGroups = makeGroupArray maxGroupIndex (rights groups)    -- implicitly inside a PGroup 0 converted into a GroupInfo 0 undefined 0 1   monad = go (starTrans pOrig) (Advice 0) (Advice 1)@@ -353,8 +345,7 @@   -- withOrbit uses MonadWriter(listens to makeOrbit/Left), collects   -- children at all depths   withOrbit :: PM a -> PM (a,[Tag])-  withOrbit = listens childStars-    where childStars x = let (ts,_) = partitionEither x in ts+  withOrbit = listens $ fst . partitionEithers    {-# INLINE makeGroup #-}   -- makeGroup usesMonadWriter(tell/Right)@@ -374,12 +365,12 @@   -- withParent uses MonadReader(local) to set getParentIndex to return (Just this)   -- withParent uses MonadWriter(listens to makeGroup/Right) to return contained group indices (stopTag)   -- withParent is only safe if getParentIndex has been checked to be not equal to Nothing (see PGroup below)-  -- Note use of laziness: the immediate children's group index is used to look up all copies of the +  -- Note use of laziness: the immediate children's group index is used to look up all copies of the   -- group in aGroups, including copies that are not immediate children.   withParent :: GroupIndex -> PM a -> PM (a,[Tag])   withParent this = local (const (Just this)) . listens childGroupInfo     where childGroupInfo x =-            let (_,gs) = partitionEither x+            let gs = snd $ partitionEithers x                 children :: [GroupIndex]                 children = norep . sort . map thisIndex                            -- filter to get only immediate children (efficiency)@@ -388,7 +379,7 @@    -- combineConcat is a partial function: Must not pass in an empty list   -- Policy choices:-  --  * pass tags to apply to children and have no preTag or postTag here (so none addded to nullQ)+  --  * pass tags to apply to children and have no preTag or postTag here (so none added to nullQ)   --  * middle 'mid' tag: give to left/front child as postTag so a Group there might claim it as a stopTag   --  * if parent is Group then preReset will become non-empty   combineConcat :: [Pattern] -> HHQ@@ -464,7 +455,7 @@                newUniq = if needUniqTags then uniq "POr branch" else return bAdvice --           trace ("\nPOr sub "++show aAdvice++" "++show bAdvice++"needsTags is "++show needTags) $ return ()            -- The "bs" values are allocated in left-to-right order before the children in "qs"-           -- optimiztion: low priority for last branch is implicit, do not create separate tag here.+           -- optimization: low priority for last branch is implicit, do not create separate tag here.            bs <- fmap (++[bAdvice]) $ replicateM (pred $ length branches) newUniq -- 2 <= length ps            -- create all the child branches in left-to-right order after the "bs"            qs <- forM (zip branches bs) (\(branch,bTag) ->  (go branch aAdvice bTag))@@ -543,7 +534,7 @@          --          -- If the parent index is Nothing then this is part of a          -- non-capturing subtree and ignored.  This is a lazy and-         -- efficient alternative to rebuidling the tree with PGroup+         -- efficient alternative to rebuilding the tree with PGroup          -- Nothing replacing PGroup (Just _).          --          -- Guarded by the getParentIndex /= Nothing check is the@@ -592,7 +583,7 @@          -- PNonEmpty means the child pattern p can be skipped by          -- bypassing the pattern.  This is only used in the case p          -- can accept 0 and can accept more than zero characters-         -- (thus the assertions, enforcted by CorePattern.starTrans).+         -- (thus the assertions, enforced by CorePattern.starTrans).          -- The important thing about this case is intercept the          -- "accept 0" possibility and replace with "skip".          PNonEmpty p -> mdo@@ -630,5 +621,5 @@   -- xxx todo--- +-- -- see of PNonEmpty -> NonEmpty -> TNFA is really smarter than POr about tags
lib/Text/Regex/TDFA/NewDFA/Engine.hs view
@@ -1,6 +1,12 @@+{-# LANGUAGE CPP #-}++#if __GLASGOW_HASKELL__ >= 902+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}+#endif+ -- | This is the code for the main engine.  This captures the posix subexpressions. This 'execMatch' -- also dispatches to "Engine_NC", "Engine_FA", and "Engine_FC_NA"--- +-- -- It is polymorphic over the internal Uncons type class, and specialized to produce the needed -- variants. module Text.Regex.TDFA.NewDFA.Engine(execMatch) where@@ -10,7 +16,8 @@ -- #ifdef __GLASGOW_HASKELL__ import GHC.Arr(STArray(..)) import GHC.ST(ST(..))-import GHC.Prim(MutableByteArray#,RealWorld,Int#,sizeofMutableByteArray#,unsafeCoerce#)+import GHC.Exts(MutableByteArray#,RealWorld,Int#,getSizeofMutableByteArray#,State#)+import Unsafe.Coerce (unsafeCoerce) {- -- #else import Control.Monad.ST(ST)@@ -26,10 +33,11 @@ import Data.IntMap(IntMap) import qualified Data.IntMap as IMap(null,toList,lookup,insert) import Data.Maybe(catMaybes)-import Data.Monoid(Monoid(..))+import Data.Monoid as Mon(Monoid(..)) import qualified Data.IntSet as ISet(toAscList) import Data.Array.IArray((!))-import Data.List(partition,sort,foldl',sortBy,groupBy)+import Data.List(partition,sort,sortBy,groupBy)+import qualified Data.List as List import Data.STRef(STRef,newSTRef,readSTRef,writeSTRef) import qualified Control.Monad.ST.Lazy as L(ST,runST,strictToLazyST) import qualified Control.Monad.ST.Strict as S(ST)@@ -37,6 +45,9 @@ import qualified Data.Sequence as Seq(null) import qualified Data.ByteString.Char8 as SBS(ByteString) import qualified Data.ByteString.Lazy.Char8 as LBS(ByteString)+import qualified Data.Text as T+import qualified Data.Text.Lazy as TL+import Foreign.Ptr(Ptr)  import Text.Regex.Base(MatchArray,MatchOffset,MatchLength) import qualified Text.Regex.TDFA.IntArrTrieSet as Trie(lookupAsc)@@ -69,11 +80,13 @@ {-# INLINE set #-} set :: (MArray a e (S.ST s),Ix i) => a i e -> Int -> e -> S.ST s () set = unsafeWrite- + {-# SPECIALIZE execMatch :: Regex -> Position -> Char -> ([] Char) -> [MatchArray] #-} {-# SPECIALIZE execMatch :: Regex -> Position -> Char -> (Seq Char) -> [MatchArray] #-} {-# SPECIALIZE execMatch :: Regex -> Position -> Char -> SBS.ByteString -> [MatchArray] #-} {-# SPECIALIZE execMatch :: Regex -> Position -> Char -> LBS.ByteString -> [MatchArray] #-}+{-# SPECIALIZE execMatch :: Regex -> Position -> Char -> T.Text -> [MatchArray] #-}+{-# SPECIALIZE execMatch :: Regex -> Position -> Char -> TL.Text -> [MatchArray] #-} execMatch :: Uncons text => Regex -> Position -> Char -> text -> [MatchArray] execMatch r@(Regex { regex_dfa = DFA {d_id=didIn,d_dt=dtIn}                    , regex_init = startState@@ -100,7 +113,7 @@   orbitTags :: [Tag]   !orbitTags = map fst . filter ((Orbit==).snd) . assocs $ aTags -  !test = mkTest newline         +  !test = mkTest newline    comp :: C s   comp = {-# SCC "matchHere.comp" #-} ditzyComp'3 aTags@@ -172,7 +185,7 @@ -- position and the basePos position.  Entries in different groups -- will never be comparable in the future so they can be processed -- separately.  Groups could probably be even more finely--- distinguished, as a futher optimization, but the justification will+-- distinguished, as a further optimization, but the justification will -- be tricky. -- -- Current Tag-0 values are at most offset and all newly spawned@@ -187,7 +200,7 @@ -- is avoided. -- -- An entry in a group can only collide with that group's--- descendents. compressOrbit sends each group to the compressGroup+-- descendants. compressOrbit sends each group to the compressGroup -- command. -- -- compressGroup on a single record checks whether it's Seq can be@@ -195,19 +208,19 @@ -- this this not particularly important). -- -- compressGroup on many records sorts and groups the members and zips--- the groups with their new ordinal value.  The comparision is based+-- the groups with their new ordinal value.  The comparison is based -- on the old ordinal value, then the inOrbit value, and then the (Seq -- Position) data. -- -- The old ordinals of the group will all be Nothing or all be Just, -- but this condition is neither checked nor violations detected.--- This comparision is justified because once records get different+-- This comparison is justified because once records get different -- ordinals assigned they will never change places. -- -- The inOrbit Bool is only different if one of them has set the stop--- position to at most (succ offset).  They will obly be compared if+-- position to at most (succ offset).  They will only be compared if -- the other one leaves, an its stop position will be at least offset.--- The previous sentence is justified by inspectin of the "assemble"+-- The previous sentence is justified by inspection of the "assemble" -- function in the TDFA module: there is no (PostUpdate -- LeaveOrbitTask) so the largest possible value for the stop Tag is -- (pred offset). Thus the record with inOrbit==False would beat (be@@ -215,7 +228,7 @@ -- -- The Seq comparison is safe because the largest existing Position -- value is (pred offset) and the smallest future Position value is--- offset.  The previous sentence is justified by inspectin of the+-- offset.  The previous sentence is justified by inspection of the -- "assemble" function in the TDFA module: there is no (PostUpdate -- EnterOrbitTags) so the largest possible value in the Seq is (pred -- offset).@@ -237,7 +250,7 @@                                     _ -> return Nothing )                 let compressGroup [((state,_),orbit)] | Seq.null (getOrbits orbit) = return ()                                                       | otherwise =-                      set (m_orbit s1) state +                      set (m_orbit s1) state                       . (IMap.insert tag $! (orbit { ordinal = Nothing, getOrbits = mempty}))                       =<< m_orbit s1 !! state @@ -285,7 +298,8 @@                     challenge x1@((_si1,ins1),_p1,_o1) x2@((_si2,ins2),_p2,_o2) = {-# SCC "goNext.findTrans.challenge" #-} do                       check <- comp offset x1 (newPos ins1) x2 (newPos ins2)                       if check==LT then return x2 else return x1-                (first:rest) <- mapM prep (IMap.toList sources)+                first_rest <- mapM prep (IMap.toList sources)+                let first:rest = first_rest                 set which destIndex =<< foldM challenge first rest           let dl = IMap.toList dtrans           mapM_ findTransTo dl@@ -298,9 +312,9 @@           earlyStart <- fmap minimum $ mapM performTransTo dl           -- findTrans part 3           earlyWin <- readSTRef (mq_earliest winQ)-          if earlyWin < earlyStart +          if earlyWin < earlyStart             then do-              winners <- fmap (foldl' (\ rest ws -> ws : rest) []) $+              winners <- fmap (List.foldl' (\ rest ws -> ws : rest) []) $                            getMQ earlyStart winQ               writeSTRef storeNext (next s2 s1 did' dt' (succ offset) prev' input')               mapM (tagsToGroupsST aGroups) winners@@ -316,7 +330,7 @@ -- A non-empty winner from the startState might obscure a potential -- empty winner (form the startState at the current offset).  This -- winEmpty possibility is also checked for. (unit test pattern ".*")--- (futher test "(.+|.+.)*" on "aa\n")+-- (further test "(.+|.+.)*" on "aa\n")          {-# INLINE processWinner #-}         processWinner s1 did dt w offset = {-# SCC "goNext.newWinnerThenProceed" #-} do@@ -376,7 +390,7 @@           set newerPos 0 preTag           doActions preTag newerPos (newPos winInstructions)           putMQ (WScratch newerPos) winQ-                +         newWinner preTag ((_sourceIndex,winInstructions),oldPos,_newOrbit) = {-# SCC "goNext.newWinner" #-} do           newerPos <- newA_ b_tags           copySTU oldPos newerPos@@ -384,7 +398,7 @@           putMQ (WScratch newerPos) winQ          finalizeWinners = do-          winners <- fmap (foldl' (\ rest mqa -> mqa_ws mqa : rest) []) $+          winners <- fmap (List.foldl' (\ rest mqa -> mqa_ws mqa : rest) []) $                        readSTRef (mq_list winQ) -- reverses the winner list           resetMQ winQ           writeSTRef storeNext (return [])@@ -437,7 +451,7 @@     then writeSTRef earliest start >> writeSTRef list [mqa]     else do   old <- readSTRef list-  let !rest = dropWhile (\ m -> start <= mqa_start m) old +  let !rest = dropWhile (\ m -> start <= mqa_start m) old       !new = mqa : rest   writeSTRef list new @@ -498,7 +512,7 @@ showWS :: WScratch s -> ST s String showWS (WScratch pos) = do   a <- getAssocs pos-  return $ unlines [ "WScratch" +  return $ unlines [ "WScratch"                    , indent (show a)] -} {- CREATING INITIAL MUTABLE SCRATCH DATA STRUCTURES -}@@ -523,7 +537,7 @@ newMScratch :: (Index,Index) -> S.ST s (MScratch s) newMScratch b_index = do   pos's <- newArray b_index Nothing-  orbit's <- newArray b_index mempty+  orbit's <- newArray b_index Mon.mempty   return (MScratch pos's orbit's)  {- COMPOSE A FUNCTION CLOSURE TO COMPARE TAG VALUES -}@@ -560,7 +574,7 @@    allcomps :: Tag -> [F s]   allcomps tag | tag > top = [F (\ _ _ _ _ _ _ -> return EQ)]-               | otherwise = +               | otherwise =     case aTagOP ! tag of       Orbit -> F (challenge_Orb tag) : allcomps (succ tag)       Maximize -> F (challenge_Max tag) : allcomps (succ tag)@@ -609,7 +623,7 @@               else return (compare p1 p2)   challenge_Max _ [] _ _ _ _ _ = err "impossible 9384324" -  challenge_Orb !tag (F next:comps) preTag x1@(_state1,_pos1,orbit1') np1 x2@(_state2,_pos2,orbit2') np2 = +  challenge_Orb !tag (F next:comps) preTag x1@(_state1,_pos1,orbit1') np1 x2@(_state2,_pos2,orbit2') np2 =     let s1 = IMap.lookup tag orbit1'         s2 = IMap.lookup tag orbit2'     in case (s1,s2) of@@ -630,7 +644,7 @@ comparePos EmptyL EmptyL = EQ comparePos EmptyL _      = GT comparePos _      EmptyL = LT-comparePos (p1 :< ps1) (p2 :< ps2) = +comparePos (p1 :< ps1) (p2 :< ps2) =   compare p1 p2 `mappend` comparePos (viewl ps1) (viewl ps2)  {- CONVERT WINNERS TO MATCHARRAY -}@@ -698,8 +712,11 @@  -- #ifdef __GLASGOW_HASKELL__ foreign import ccall unsafe "memcpy"-    memcpy :: MutableByteArray# RealWorld -> MutableByteArray# RealWorld -> Int# -> IO ()+    memcpyIO :: MutableByteArray# RealWorld -> MutableByteArray# RealWorld -> Int# -> IO (Ptr a) +memcpyST :: MutableByteArray# s -> MutableByteArray# s -> Int# -> State# s -> (# State# s, Ptr a #)+memcpyST = unsafeCoerce memcpyIO+ {- Prelude Data.Array.Base> :i STUArray data STUArray s i e@@ -709,16 +726,16 @@ -- This has been updated for ghc 6.8.3 and still works with ghc 6.10.1 {-# INLINE copySTU #-} copySTU :: (Show i,Ix i,MArray (STUArray s) e (S.ST s)) => STUArray s i e -> STUArray s i e -> S.ST s () -- (STUArray s i e)-copySTU _souce@(STUArray _ _ _ msource) _destination@(STUArray _ _ _ mdest) =+copySTU _source@(STUArray _ _ _ msource) _destination@(STUArray _ _ _ mdest) = -- do b1 <- getBounds s1 --  b2 <- getBounds s2 --  when (b1/=b2) (error ("\n\nWTF copySTU: "++show (b1,b2)))   ST $ \s1# ->-    case sizeofMutableByteArray# msource        of { n# ->-    case unsafeCoerce# memcpy mdest msource n# s1# of { (# s2#, () #) ->+    case getSizeofMutableByteArray# msource s1# of { (# s1'#, n# #) ->+    case memcpyST mdest msource n# s1'# of { (# s2#, _ #) ->     (# s2#, () #) }} {--#else /* !__GLASGOW_HASKELL__ */+-- #else /* !__GLASGOW_HASKELL__ */  copySTU :: (MArray (STUArray s) e (S.ST s))=> STUArray s Tag e -> STUArray s Tag e -> S.ST s (STUArray s i e) copySTU source destination = do@@ -729,5 +746,5 @@   forM_ (range b) $ \index ->     set destination index =<< source !! index   return destination-#endif /* !__GLASGOW_HASKELL__ */+-- #endif /* !__GLASGOW_HASKELL__ */ -}
lib/Text/Regex/TDFA/NewDFA/Engine_FA.hs view
@@ -1,22 +1,23 @@+{-# LANGUAGE CPP #-}++#if __GLASGOW_HASKELL__ >= 902+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}+#endif+ -- | This is the code for the main engine.  This captures the posix -- subexpressions.  There is also a non-capturing engine, and a -- testing engine.--- +-- -- It is polymorphic over the internal Uncons type class, and -- specialized to produce the needed variants.+ module Text.Regex.TDFA.NewDFA.Engine_FA(execMatch) where  import Data.Array.Base(unsafeRead,unsafeWrite,STUArray(..))--- #ifdef __GLASGOW_HASKELL__+ import GHC.Arr(STArray(..)) import GHC.ST(ST(..))-import GHC.Prim(MutableByteArray#,RealWorld,Int#,sizeofMutableByteArray#,unsafeCoerce#)-{---- #else-import Control.Monad.ST(ST)-import Data.Array.ST(STArray)--- #endif--}+import GHC.Exts(MutableByteArray#,RealWorld,Int#,getSizeofMutableByteArray#,unsafeCoerce#,State#)  import Prelude hiding ((!!)) import Control.Monad(when,unless,forM,forM_,liftM2,foldM)@@ -27,7 +28,8 @@ import Data.IntMap(IntMap) import qualified Data.IntMap as IMap(null,toList,lookup,insert) import Data.Maybe(catMaybes)-import Data.Monoid(Monoid(..))+import Data.Monoid as Mon(Monoid(..))+import Data.IntSet(IntSet) import qualified Data.IntSet as ISet(toAscList,null) import Data.Array.IArray((!)) import Data.List(sortBy,groupBy)@@ -37,6 +39,7 @@ import qualified Data.Sequence as Seq(null) import qualified Data.ByteString.Char8 as SBS(ByteString) import qualified Data.ByteString.Lazy.Char8 as LBS(ByteString)+import Foreign.Ptr(Ptr)  import Text.Regex.Base(MatchArray,MatchOffset,MatchLength) import Text.Regex.TDFA.Common hiding (indent)@@ -60,12 +63,12 @@  noSource :: ((Index, Instructions),STUArray s Tag Position,OrbitLog) noSource = ((-1,err "noSource"),err "noSource",err "noSource")- + {-# SPECIALIZE execMatch :: Regex -> Position -> Char -> ([] Char) -> [MatchArray] #-} {-# SPECIALIZE execMatch :: Regex -> Position -> Char -> (Seq Char) -> [MatchArray] #-} {-# SPECIALIZE execMatch :: Regex -> Position -> Char -> SBS.ByteString -> [MatchArray] #-} {-# SPECIALIZE execMatch :: Regex -> Position -> Char -> LBS.ByteString -> [MatchArray] #-}-execMatch :: Uncons text => Regex -> Position -> Char -> text -> [MatchArray]+execMatch :: forall text. Uncons text => Regex -> Position -> Char -> text -> [MatchArray] execMatch (Regex { regex_dfa =  DFA {d_id=didIn,d_dt=dtIn}                  , regex_init = startState                  , regex_b_index = b_index@@ -81,12 +84,13 @@   orbitTags :: [Tag]   !orbitTags = map fst . filter ((Orbit==).snd) . assocs $ aTags -  !test = mkTest newline         +  test :: WhichTest -> Index -> Char -> text -> Bool+  !test = mkTest newline    comp :: C s   comp = {-# SCC "matchHere.comp" #-} ditzyComp'3 aTags -  goNext :: ST s [MatchArray]+  goNext :: forall s. ST s [MatchArray]   goNext = {-# SCC "goNext" #-} do     (SScratch s1In s2In (winQ,blank,which)) <- newScratch b_index b_tags     spawnAt b_tags blank startState s1In offsetIn@@ -112,14 +116,14 @@ -- compressOrbit. -- -- compressOrbit on such a Tag loops through all the NFS states'--- m_orbit record, discardind ones that are Nothing and discarding+-- m_orbit record, discarding ones that are Nothing and discarding -- ones that are too new to care about (after the cutoff value). -- -- compressOrbit then groups the Orbits records by the Tag-0 start--- position and the basePos position.  Entried in different groups+-- position and the basePos position.  Entries in different groups -- will never be comparable in the future so they can be processed -- separately.  Groups could probably be even more finely--- distinguished, as a futher optimization, but the justification will+-- distinguished, as a further optimization, but the justification will -- be tricky. -- -- Current Tag-0 values are at most offset and all newly spawned@@ -134,7 +138,7 @@ -- is avoided. -- -- An entry in a group can only collide with that group's--- descendents. compressOrbit sends each group to the compressGroup+-- descendants. compressOrbit sends each group to the compressGroup -- command. -- -- compressGroup on a single record checks whether it's Seq can be@@ -142,19 +146,19 @@ -- this this not particularly important). -- -- compressGroup on many records sorts and groups the members and zips--- the groups with their new ordinal value.  The comparision is based+-- the groups with their new ordinal value.  The comparison is based -- on the old ordinal value, then the inOrbit value, and then the (Seq -- Position) data. -- -- The old ordinals of the group will all be Nothing or all be Just, -- but this condition is neither checked nor violations detected.--- This comparision is justified because once records get different+-- This comparison is justified because once records get different -- ordinals assigned they will never change places. -- -- The inOrbit Bool is only different if one of them has set the stop -- position to at most (succ offset).  They will obly be compared if -- the other one leaves, an its stop position will be at least offset.--- The previous sentence is justified by inspectin of the "assemble"+-- The previous sentence is justified by inspection of the "assemble" -- function in the TDFA module: there is no (PostUpdate -- LeaveOrbitTask) so the largest possible value for the stop Tag is -- (pred offset). Thus the record with inOrbit==False would beat (be@@ -162,7 +166,7 @@ -- -- The Seq comparison is safe because the largest existing Position -- value is (pred offset) and the smallest future Position value is--- offset.  The previous sentence is justified by inspectin of the+-- offset.  The previous sentence is justified by inspection of the -- "assemble" function in the TDFA module: there is no (PostUpdate -- EnterOrbitTags) so the largest possible value in the Seq is (pred -- offset).@@ -170,6 +174,7 @@ -- The updated Orbits get the new ordinal value and an empty (Seq -- Position). +        compressOrbits :: MScratch s -> IntSet -> Position -> ST s ()         compressOrbits s1 did offset = do           let getStart state = do start <- maybe (err "compressOrbit,1") (!! 0) =<< m_pos s1 !! state                                   return (state,start)@@ -184,7 +189,7 @@                                     _ -> return Nothing )                 let compressGroup [((state,_),orbit)] | Seq.null (getOrbits orbit) = return ()                                                       | otherwise =-                      set (m_orbit s1) state +                      set (m_orbit s1) state                       . (IMap.insert tag $! (orbit { ordinal = Nothing, getOrbits = mempty}))                       =<< m_orbit s1 !! state @@ -214,6 +219,17 @@ -- "storeNext".  If no winners are ready to be released then the -- computation continues immediately. +        findTrans+          :: MScratch s+          -> MScratch s+          -> IntSet+          -> SetIndex+          -> DT+          -> DTrans+          -> Index+          -> Char+          -> text+          -> ST s [MatchArray]         findTrans s1 s2 did did' dt' dtrans offset prev' input' =  {-# SCC "goNext.findTrans" #-} do           -- findTrans part 0           -- MAGIC TUNABLE CONSTANT 100 (and 100-1). TODO: (offset .&. 127 == 127) instead?@@ -231,7 +247,8 @@                     challenge x1@((_si1,ins1),_p1,_o1) x2@((_si2,ins2),_p2,_o2) = {-# SCC "goNext.findTrans.challenge" #-} do                       check <- comp offset x1 (newPos ins1) x2 (newPos ins2)                       if check==LT then return x2 else return x1-                (first:rest) <- mapM prep (IMap.toList sources)+                first_rest <- mapM prep (IMap.toList sources)+                let first:rest = first_rest                 set which destIndex =<< foldM challenge first rest           let dl = IMap.toList dtrans           mapM_ findTransTo dl@@ -253,9 +270,10 @@ -- A non-empty winner from the startState might obscure a potential -- empty winner (form the startState at the current offset).  This -- winEmpty possibility is also checked for. (unit test pattern ".*")--- (futher test "(.+|.+.)*" on "aa\n")+-- (further test "(.+|.+.)*" on "aa\n")          {-# INLINE processWinner #-}+        processWinner :: MScratch s -> IntMap Instructions -> Position -> ST s ()         processWinner s1 w offset = {-# SCC "goNext.newWinnerThenProceed" #-} do           let prep x@(sourceIndex,instructions) = {-# SCC "goNext.newWinnerThenProceed.prep" #-} do                 pos <- maybe (err "newWinnerThenProceed,1") return =<< m_pos s1 !! sourceIndex@@ -271,12 +289,14 @@             [] -> return ()             (first:rest) -> newWinner offset =<< foldM challenge first rest +        newWinner :: Position -> ((a, Instructions), STUArray s Tag Position, c) -> ST s ()         newWinner preTag ((_sourceIndex,winInstructions),oldPos,_newOrbit) = {-# SCC "goNext.newWinner" #-} do           newerPos <- newA_ b_tags           copySTU oldPos newerPos           doActions preTag newerPos (newPos winInstructions)           putMQ (WScratch newerPos) winQ +        finalizeWinner :: ST s [MatchArray]         finalizeWinner = do           mWinner <- readSTRef (mq_mWin winQ)           case mWinner of@@ -357,7 +377,7 @@ showWS :: WScratch s -> ST s String showWS (WScratch pos) = do   a <- getAssocs pos-  return $ unlines [ "WScratch" +  return $ unlines [ "WScratch"                    , indent (show a)] -} {- CREATING INITIAL MUTABLE SCRATCH DATA STRUCTURES -}@@ -382,7 +402,7 @@ newMScratch :: (Index,Index) -> S.ST s (MScratch s) newMScratch b_index = do   pos's <- newArray b_index Nothing-  orbit's <- newArray b_index mempty+  orbit's <- newArray b_index Mon.mempty   return (MScratch pos's orbit's)  {- COMPOSE A FUNCTION CLOSURE TO COMPARE TAG VALUES -}@@ -419,7 +439,7 @@    allcomps :: Tag -> [F s]   allcomps tag | tag > top = [F (\ _ _ _ _ _ _ -> return EQ)]-               | otherwise = +               | otherwise =     case aTagOP ! tag of       Orbit -> F (challenge_Orb tag) : allcomps (succ tag)       Maximize -> F (challenge_Max tag) : allcomps (succ tag)@@ -427,6 +447,15 @@       Minimize -> err "allcomps Minimize"    where top = snd (bounds aTagOP) +  challenge_Ignore+    :: Int+    -> [F s1]+    -> Position+    -> ((Int, Instructions), STUArray s1 Tag Position, IntMap Orbits)+    -> [(Int, Action)]+    -> ((Int, Instructions), STUArray s1 Tag Position, IntMap Orbits)+    -> [(Int, Action)]+    -> ST s1 Ordering   challenge_Ignore !tag (F next:comps) preTag x1 np1 x2 np2 =     case np1 of       ((t1,_):rest1) | t1==tag ->@@ -439,6 +468,15 @@           _ ->  next comps preTag x1 np1 x2 np2   challenge_Ignore _ [] _ _ _ _ _ = err "impossible 2347867" +  challenge_Max+    :: Int+    -> [F s1]+    -> Position+    -> ((Int, Instructions), STUArray s1 Tag Position, IntMap Orbits)+    -> [(Int, Action)]+    -> ((Int, Instructions), STUArray s1 Tag Position, IntMap Orbits)+    -> [(Int, Action)]+    -> ST s1 Ordering   challenge_Max !tag (F next:comps) preTag x1@(_state1,pos1,_orbit1') np1 x2@(_state2,pos2,_orbit2') np2 =     case np1 of       ((t1,b1):rest1) | t1==tag ->@@ -468,7 +506,16 @@               else return (compare p1 p2)   challenge_Max _ [] _ _ _ _ _ = err "impossible 9384324" -  challenge_Orb !tag (F next:comps) preTag x1@(_state1,_pos1,orbit1') np1 x2@(_state2,_pos2,orbit2') np2 = +  challenge_Orb+    :: Int+    -> [F s1]+    -> Position+    -> ((Int, Instructions), STUArray s1 Tag Position, IntMap Orbits)+    -> [(Int, Action)]+    -> ((Int, Instructions), STUArray s1 Tag Position, IntMap Orbits)+    -> [(Int, Action)]+    -> ST s1 Ordering+  challenge_Orb !tag (F next:comps) preTag x1@(_state1,_pos1,orbit1') np1 x2@(_state2,_pos2,orbit2') np2 =     let s1 = IMap.lookup tag orbit1'         s2 = IMap.lookup tag orbit2'     in case (s1,s2) of@@ -489,7 +536,7 @@ comparePos EmptyL EmptyL = EQ comparePos EmptyL _      = GT comparePos _      EmptyL = LT-comparePos (p1 :< ps1) (p2 :< ps2) = +comparePos (p1 :< ps1) (p2 :< ps2) =   compare p1 p2 `mappend` comparePos (viewl ps1) (viewl ps2)  {- CONVERT WINNERS TO MATCHARRAY -}@@ -556,8 +603,11 @@  -- #ifdef __GLASGOW_HASKELL__ foreign import ccall unsafe "memcpy"-    memcpy :: MutableByteArray# RealWorld -> MutableByteArray# RealWorld -> Int# -> IO ()+    memcpyIO :: MutableByteArray# RealWorld -> MutableByteArray# RealWorld -> Int# -> IO (Ptr a) +memcpyST :: MutableByteArray# s -> MutableByteArray# s -> Int# -> State# s -> (# State# s, Ptr a #)+memcpyST = unsafeCoerce# memcpyIO+ {- Prelude Data.Array.Base> :i STUArray data STUArray s i e@@ -567,16 +617,16 @@ -- This has been updated for ghc 6.8.3 and still works with ghc 6.10.1 {-# INLINE copySTU #-} copySTU :: (Show i,Ix i,MArray (STUArray s) e (S.ST s)) => STUArray s i e -> STUArray s i e -> S.ST s () -- (STUArray s i e)-copySTU _souce@(STUArray _ _ _ msource) _destination@(STUArray _ _ _ mdest) =+copySTU _source@(STUArray _ _ _ msource) _destination@(STUArray _ _ _ mdest) = -- do b1 <- getBounds s1 --  b2 <- getBounds s2 --  when (b1/=b2) (error ("\n\nWTF copySTU: "++show (b1,b2)))   ST $ \s1# ->-    case sizeofMutableByteArray# msource        of { n# ->-    case unsafeCoerce# memcpy mdest msource n# s1# of { (# s2#, () #) ->+    case getSizeofMutableByteArray# msource s1# of { (# s1'#, n# #) ->+    case memcpyST mdest msource n# s1'# of { (# s2#, _ #) ->     (# s2#, () #) }} {--#else /* !__GLASGOW_HASKELL__ */+-- #else /* !__GLASGOW_HASKELL__ */  copySTU :: (MArray (STUArray s) e (S.ST s))=> STUArray s Tag e -> STUArray s Tag e -> S.ST s (STUArray s i e) copySTU source destination = do@@ -587,5 +637,5 @@   forM_ (range b) $ \index ->     set destination index =<< source !! index   return destination-#endif /* !__GLASGOW_HASKELL__ */+-- #endif /* !__GLASGOW_HASKELL__ */ -}
lib/Text/Regex/TDFA/NewDFA/Engine_NC.hs view
@@ -18,6 +18,8 @@ import Data.Sequence(Seq) import qualified Data.ByteString.Char8 as SBS(ByteString) import qualified Data.ByteString.Lazy.Char8 as LBS(ByteString)+import qualified Data.Text as T+import qualified Data.Text.Lazy as TL  import Text.Regex.Base(MatchArray,MatchOffset,MatchLength) import qualified Text.Regex.TDFA.IntArrTrieSet as Trie(lookupAsc)@@ -44,6 +46,8 @@ {-# SPECIALIZE execMatch :: Regex -> Position -> Char -> (Seq Char) -> [MatchArray] #-} {-# SPECIALIZE execMatch :: Regex -> Position -> Char -> SBS.ByteString -> [MatchArray] #-} {-# SPECIALIZE execMatch :: Regex -> Position -> Char -> LBS.ByteString -> [MatchArray] #-}+{-# SPECIALIZE execMatch :: Regex -> Position -> Char -> T.Text -> [MatchArray] #-}+{-# SPECIALIZE execMatch :: Regex -> Position -> Char -> TL.Text -> [MatchArray] #-} execMatch :: Uncons text => Regex -> Position -> Char -> text -> [MatchArray] execMatch (Regex { regex_dfa = (DFA {d_id=didIn,d_dt=dtIn})                  , regex_init = startState@@ -52,7 +56,7 @@                  , regex_compOptions = CompOption { multiline = newline } } )           offsetIn prevIn inputIn = L.runST runCaptureGroup where -  !test = mkTest newline         +  !test = mkTest newline    runCaptureGroup = {-# SCC "runCaptureGroup" #-} do     obtainNext <- L.strictToLazyST constructNewEngine@@ -199,7 +203,7 @@     then writeSTRef earliest start >> writeSTRef list [ws]     else do       old <- readSTRef list-      let !rest = dropWhile (\ w -> start <= ws_start w) old +      let !rest = dropWhile (\ w -> start <= ws_start w) old           !new = ws : rest       writeSTRef list new 
lib/Text/Regex/TDFA/NewDFA/Engine_NC_FA.hs view
@@ -16,6 +16,8 @@ import Data.Sequence(Seq) import qualified Data.ByteString.Char8 as SBS(ByteString) import qualified Data.ByteString.Lazy.Char8 as LBS(ByteString)+import qualified Data.Text as T+import qualified Data.Text.Lazy as TL  import Text.Regex.Base(MatchArray,MatchOffset,MatchLength) import Text.Regex.TDFA.Common hiding (indent)@@ -31,6 +33,8 @@ {-# SPECIALIZE execMatch :: Regex -> Position -> Char -> (Seq Char) -> [MatchArray] #-} {-# SPECIALIZE execMatch :: Regex -> Position -> Char -> SBS.ByteString -> [MatchArray] #-} {-# SPECIALIZE execMatch :: Regex -> Position -> Char -> LBS.ByteString -> [MatchArray] #-}+{-# SPECIALIZE execMatch :: Regex -> Position -> Char -> T.Text -> [MatchArray] #-}+{-# SPECIALIZE execMatch :: Regex -> Position -> Char -> TL.Text -> [MatchArray] #-} execMatch :: Uncons text => Regex -> Position -> Char -> text -> [MatchArray] execMatch (Regex { regex_dfa = DFA {d_dt=dtIn} })           offsetIn _prevIn inputIn = S.runST goNext where
lib/Text/Regex/TDFA/NewDFA/Tester.hs view
@@ -1,5 +1,5 @@ -- | Like Engine, but merely checks to see whether any match at all is found.--- +-- module Text.Regex.TDFA.NewDFA.Tester(matchTest) where  import qualified Data.IntMap.CharMap2 as CMap(findWithDefault)@@ -9,6 +9,8 @@ import Data.Sequence(Seq) import qualified Data.ByteString.Char8 as SBS(ByteString) import qualified Data.ByteString.Lazy.Char8 as LBS(ByteString)+import qualified Data.Text as T+import qualified Data.Text.Lazy as TL  import Text.Regex.Base() import Text.Regex.TDFA.Common hiding (indent)@@ -19,6 +21,8 @@ {-# SPECIALIZE matchTest :: Regex -> (Seq Char) -> Bool #-} {-# SPECIALIZE matchTest :: Regex -> SBS.ByteString -> Bool #-} {-# SPECIALIZE matchTest :: Regex -> LBS.ByteString -> Bool #-}+{-# SPECIALIZE matchTest :: Regex -> T.Text -> Bool #-}+{-# SPECIALIZE matchTest :: Regex -> TL.Text -> Bool #-} matchTest :: Uncons text => Regex -> text -> Bool matchTest (Regex { regex_dfa = dfaIn                  , regex_isFrontAnchored = ifa } )
lib/Text/Regex/TDFA/NewDFA/Uncons.hs view
@@ -3,6 +3,8 @@ import qualified Data.ByteString.Char8 as SBS(ByteString,uncons) import qualified Data.ByteString.Lazy.Char8 as LBS(ByteString,uncons) import Data.Sequence(Seq,viewl,ViewL(EmptyL,(:<)))+import qualified Data.Text as T+import qualified Data.Text.Lazy as TL  class Uncons a where   {- INLINE uncons #-}@@ -26,3 +28,13 @@ instance Uncons LBS.ByteString where   {- INLINE uncons #-}   uncons = LBS.uncons++-- | @since 1.3.1+instance Uncons T.Text where+  {- INLINE uncons #-}+  uncons = T.uncons++-- | @since 1.3.1+instance Uncons TL.Text where+  {- INLINE uncons #-}+  uncons = TL.uncons
lib/Text/Regex/TDFA/Pattern.hs view
@@ -1,6 +1,9 @@+{-# OPTIONS_GHC -fno-warn-incomplete-uni-patterns #-}+ -- | This "Text.Regex.TDFA.Pattern" module provides the 'Pattern' data -- type and its subtypes.  This 'Pattern' type is used to represent--- the parsed form of a Regular Expression.  +-- the parsed form of a regular expression.+ module Text.Regex.TDFA.Pattern     (Pattern(..)     ,PatternSet(..)@@ -9,52 +12,82 @@     ,PatternSetEquivalenceClass(..)     ,GroupIndex     ,DoPa(..)+    ,decodeCharacterClass, decodePatternSet     ,showPattern -- ** Internal use     ,starTrans--- ** Internal use, Operations to support debugging under ghci+-- ** Internal use, operations to support debugging under @ghci@     ,starTrans',simplify',dfsPattern     ) where  {- By Chris Kuklewicz, 2007. BSD License, see the LICENSE file. -}  import Data.List(intersperse,partition)-import qualified Data.Set as Set(toAscList,toList)-import Data.Set(Set) -- XXX EnumSet+import qualified Data.Set as Set+import Data.Set (Set) import Text.Regex.TDFA.Common(DoPa(..),GroupIndex,common_error)  err :: String -> a err = common_error "Text.Regex.TDFA.Pattern" --- | Pattern is the type returned by the regular expression parser.--- This is consumed by the CorePattern module and the tender leaves--- are nibbled by the TNFA module.-data Pattern = PEmpty-             | PGroup  (Maybe GroupIndex) Pattern -- Nothing to indicate non-matching PGroup (Nothing never used!)-             | POr     [Pattern]                  -- flattened by starTrans-             | PConcat [Pattern]                  -- flattened by starTrans-             | PQuest  Pattern                    -- eliminated by starTrans-             | PPlus   Pattern                    -- eliminated by starTrans-             | PStar   Bool Pattern               -- True means mayFirstBeNull is True-             | PBound  Int (Maybe Int) Pattern    -- eliminated by starTrans-             -- The rest of these need an index of where in the regex string it is from-             | PCarat  {getDoPa::DoPa}-             | PDollar {getDoPa::DoPa}-             -- The following test and accept a single character-             | PDot    {getDoPa::DoPa}            -- Any character (newline?) at all-             | PAny    {getDoPa::DoPa,getPatternSet::PatternSet} -- Square bracketed things-             | PAnyNot {getDoPa::DoPa,getPatternSet::PatternSet} -- Inverted square bracketed things-             | PEscape {getDoPa::DoPa,getPatternChar::Char}      -- Backslashed Character-             | PChar   {getDoPa::DoPa,getPatternChar::Char}      -- Specific Character-             -- The following are semantic tags created in starTrans, not the parser-             | PNonCapture Pattern               -- introduced by starTrans-             | PNonEmpty Pattern                 -- introduced by starTrans-               deriving (Eq,Show)+-- | 'Pattern' is the type returned by the regular expression parser 'parseRegex'.+-- This is consumed by the "Text.Regex.TDFA.CorePattern" module and the tender leaves+-- are nibbled by the "Text.Regex.TDFA.TNFA" module.+--+-- The 'DoPa' field is the index of the component in the regex string @r@.+data Pattern+  = PEmpty+      -- ^ @()@, matches the empty string.+  | PGroup  (Maybe GroupIndex) Pattern+      -- ^ Group @(r)@.  @Nothing@ indicates non-matching 'PGroup'+      -- (never produced by parser 'parseRegex').+  | POr     [Pattern]+      -- ^ Alternative @r|s@ (flattened by 'starTrans').+  | PConcat [Pattern]+      -- ^ Sequence @rs@ (flattened by 'starTrans').+  | PQuest  Pattern+      -- ^ Zero or one repetitions @r?@ (eliminated by 'starTrans').+  | PPlus   Pattern+      -- ^ One or more repetitions @r+@ (eliminated by 'starTrans').+  | PStar   Bool Pattern+      -- ^ Zero or more repetitions @r*@.+      -- @True@ (default) means may accept the empty string on its first iteration.+  | PBound  Int (Maybe Int) Pattern+      -- ^ Given number or repetitions @r{n}@ or @r{n,m}@+      -- (eliminated by 'starTrans'). --- | I have not been checking, but this should have the property that--- parsing the resulting string should result in an identical Pattern.--- This is not true if starTrans has created PNonCapture and PNonEmpty--- values or a (PStar False).  The contents of a "[ ]" grouping are+  -- The rest of these need an index of where in the regex string it is from+  | PCarat  { getDoPa :: DoPa }+      -- ^ @^@ matches beginning of input.+  | PDollar { getDoPa :: DoPa }+      -- ^ @$@ matches end of input.++  -- The following test and accept a single character+  | PDot    { getDoPa :: DoPa }+      -- ^ @.@ matches any character.+  | PAny    { getDoPa :: DoPa, getPatternSet :: PatternSet }+      -- ^ Bracket expression @[...]@.+  | PAnyNot { getDoPa :: DoPa, getPatternSet :: PatternSet }+      -- ^ Inverted bracket expression @[^...]@.+  | PEscape { getDoPa :: DoPa, getPatternChar :: Char }+      -- ^ Backslashed character @\c@, may have special meaning.+  | PChar   { getDoPa :: DoPa, getPatternChar :: Char }+      -- ^ Single character, matches given character.++  -- The following are semantic tags created in starTrans, not the parser+  | PNonCapture Pattern+     -- ^ Tag for internal use, introduced by 'starTrans'.+  | PNonEmpty Pattern+     -- ^ Tag for internal use, introduced by 'starTrans'.+  deriving (Eq, Show)++-- Andreas Abel, 2022-07-18, issue #47:+-- The following claim is FALSE:+--+-- I have not been checking, but this should have the property that+-- parsing the resulting string should result in an identical 'Pattern'.+-- This is not true if 'starTrans' has created 'PNonCapture' and 'PNonEmpty'+-- values or a @'PStar' False@.  The contents of a @[...]@ grouping are -- always shown in a sorted canonical order. showPattern :: Pattern -> String showPattern pIn =@@ -88,13 +121,20 @@                             else x:'-':(toEnum (pred n+fromEnum x)):[] -}         paren s = ('(':s)++")"-       ++-- | Content of a bracket expression @[...]@ organized into+-- characters,+-- POSIX character classes (e.g. @[[:alnum:]]@),+-- collating elements (e.g. @[.ch.]@, unused), and+-- equivalence classes (e.g. @[=a=]@, treated as characters).+-- data PatternSet = PatternSet (Maybe (Set Char))                              (Maybe (Set PatternSetCharacterClass))                              (Maybe (Set PatternSetCollatingElement))                              (Maybe (Set PatternSetEquivalenceClass))                              deriving (Eq) +-- | Hand-rolled implementation, giving textual rather than Haskell representation. instance Show PatternSet where   showsPrec i (PatternSet s scc sce sec) =     let (special,normal) = maybe ("","") ((partition (`elem` "]-")) . Set.toAscList) s@@ -105,44 +145,88 @@     in shows charSpec        . showsPrec i scc' . showsPrec i sce' . showsPrec i sec'        . if '-' `elem` special then showChar '-' else id-    where byRange xAll@(x:xs) | length xAll <=3 = xAll-                              | otherwise = groupRange x 1 xs-          byRange _ = undefined+    where byRange xAll@(~(x:xs))+            | length xAll <=3 = xAll+            | otherwise       = groupRange x 1 xs           groupRange x n (y:ys) = if (fromEnum y)-(fromEnum x) == n then groupRange x (succ n) ys                                   else (if n <=3 then take n [x..]                                         else x:'-':(toEnum (pred n+fromEnum x)):[]) ++ groupRange y 1 ys           groupRange x n [] = if n <=3 then take n [x..]                               else x:'-':(toEnum (pred n+fromEnum x)):[] +-- | Content of @[: :]@, e.g. @"alnum"@ for @[:alnum:]@. newtype PatternSetCharacterClass   = PatternSetCharacterClass   {unSCC::String}   deriving (Eq,Ord)++-- | Content of @[. .]@, e.g. @"ch"@ for @[.ch.]@. newtype PatternSetCollatingElement = PatternSetCollatingElement {unSCE::String}   deriving (Eq,Ord)++-- | Content of @[= =]@, e.g. @"a"@ for @[=a=]@. newtype PatternSetEquivalenceClass = PatternSetEquivalenceClass {unSEC::String}   deriving (Eq,Ord) +-- | Hand-rolled implementation, giving textual rather than Haskell representation. instance Show PatternSetCharacterClass where   showsPrec _ p = showChar '[' . showChar ':' . shows (unSCC p) . showChar ':' . showChar ']'++-- | Hand-rolled implementation, giving textual rather than Haskell representation. instance Show PatternSetCollatingElement where   showsPrec _ p = showChar '[' . showChar '.' . shows (unSCE p) . showChar '.' . showChar ']'++-- | Hand-rolled implementation, giving textual rather than Haskell representation. instance Show PatternSetEquivalenceClass where   showsPrec _ p = showChar '[' . showChar '=' . shows (unSEC p) . showChar '=' . showChar ']' --- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == +-- | @decodePatternSet@ cannot handle collating element and treats+-- equivalence classes as just their definition and nothing more.+--+-- @since 1.3.2+decodePatternSet :: PatternSet -> Set Char+decodePatternSet (PatternSet msc mscc _ msec) =+  let baseMSC = maybe Set.empty id msc+      withMSCC = foldl (flip Set.insert) baseMSC  (maybe [] (concatMap decodeCharacterClass . Set.toAscList) mscc)+      withMSEC = foldl (flip Set.insert) withMSCC (maybe [] (concatMap unSEC . Set.toAscList) msec)+  in withMSEC +-- | This returns the strictly ascending list of characters+-- represented by @[: :]@ POSIX character classes.+-- Unrecognized class names return an empty string.+--+-- @since 1.3.2+decodeCharacterClass :: PatternSetCharacterClass -> String+decodeCharacterClass (PatternSetCharacterClass s) =+  case s of+    "alnum"  -> ['0'..'9']++['A'..'Z']++['a'..'z']+    "digit"  -> ['0'..'9']+    "punct"  -> ['\33'..'\47']++['\58'..'\64']++['\91'..'\96']++['\123'..'\126']+    "alpha"  -> ['A'..'Z']++['a'..'z']+    "graph"  -> ['\33'..'\126']+    "space"  -> "\t\n\v\f\r "+    "blank"  -> "\t "+    "lower"  -> ['a'..'z']+    "upper"  -> ['A'..'Z']+    "cntrl"  -> ['\0'..'\31']++"\127" -- with NUL+    "print"  -> ['\32'..'\126']+    "xdigit" -> ['0'..'9']++['A'..'F']++['a'..'f']+    "word"   -> ['0'..'9']++['A'..'Z']++"_"++['a'..'z']+    _ -> []++-- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- ==+ -- | Do the transformation and simplification in a single traversal.--- This removes the PPlus, PQuest, and PBound values, changing to POr--- and PEmpty and PStar True\/False.  For some PBound values it adds--- PNonEmpty and PNonCapture semantic marker.  It also simplifies to--- flatten out nested POr and PConcat instances and eliminate some--- unneeded PEmpty values.+-- This removes the 'PPlus', 'PQuest', and 'PBound' values, changing to 'POr'+-- and 'PEmpty' and 'PStar'.  For some 'PBound' values it adds+-- 'PNonEmpty' and 'PNonCapture' semantic marker.  It also simplifies to+-- flatten out nested 'POr' and 'PConcat' instances and eliminate some+-- unneeded 'PEmpty' values. starTrans :: Pattern -> Pattern starTrans = dfsPattern (simplify' . starTrans') --- | Apply a Pattern transfomation function depth first-dfsPattern :: (Pattern -> Pattern)  -- ^ The transformation function-           -> Pattern               -- ^ The Pattern to transform-           -> Pattern               -- ^ The transformed Pattern+-- | Apply a 'Pattern' transformation function depth first.+dfsPattern :: (Pattern -> Pattern)  -- ^ The transformation function.+           -> Pattern               -- ^ The 'Pattern' to transform.+           -> Pattern               -- ^ The transformed 'Pattern'. dfsPattern f = dfs  where unary c = f . c . dfs        dfs pattern = case pattern of@@ -200,7 +284,7 @@    simplicity, only use ! when p can match 0 characters but not only 0    characters. -   Call this (PNonEmpty p) in the Pattern type. +   Call this (PNonEmpty p) in the Pattern type.    p! is PNonEmpty p is POr [PEmpty,p]    IS THIS TRUE?  Use QuickCheck? @@ -225,8 +309,8 @@    if p can match 0 or non-zero characters then cases are    p{0,0} is (), p{0,1} is (p)?, p{0,2} is (pp!)?, p{0,3} is (pp!p!)?    p{1,1} is p, p{1,2} is pp!, p{1,3} is pp!p!, p{1,4} is pp!p!p!-   p{2,2} is p'p, -   p{2,3} is p'pp!, +   p{2,2} is p'p,+   p{2,3} is p'pp!,    p{2,4} is p'pp!p! or p'p(pp!)!    p{2,5} is p'pp!p!p! or p'p(p(pp!)!)!    p{3,3} is p'p'p, p{3,4} is p'p'pp!, p{3,5} is p'p'pp!p!, p{3,6} is p'p'pp!p!p!@@ -236,7 +320,7 @@    p{0,1} is p?, p{0,2} is (pp?)?, p{0,3} is (p(pp?)?)?, p{0,4} is (pp{0,3})?    p{1,1} is p, p{1,j} is pp{0,pred j}    p{2,2} is p'p, p{2,3} is p'pp?, p{2,4} is p'p(pp?)?, p{2,5} = p'p{1,4} = p'(pp{0,3})-   p{3,3} is p'p'p, p{3,4} is p'p'pp?, p{3,5} is p'p'p(pp?)?, p{3,6} is +   p{3,3} is p'p'p, p{3,4} is p'p'pp?, p{3,5} is p'p'p(pp?)?, p{3,6} is     And by this logic, the PStar False is really p*!  So p{0,} is p*    and p{1,} is pp*! and p{2,} is p'pp*! and p{3,} is p'p'pp*!@@ -320,10 +404,10 @@     pass = pIn  -- | Function to transform a pattern into an equivalent, but less--- redundant form.  Nested 'POr' and 'PConcat' are flattened. PEmpty+-- redundant form.  Nested 'POr' and 'PConcat' are flattened. 'PEmpty' -- is propagated. simplify' :: Pattern -> Pattern-simplify' x@(POr _) = +simplify' x@(POr _) =   let ps' = case span notPEmpty (flatten x) of               (notEmpty,[]) -> notEmpty               (notEmpty,_:rest) -> notEmpty ++ (PEmpty:filter notPEmpty rest) -- keep 1st PEmpty only@@ -342,7 +426,7 @@ --simplify' (PNonEmpty PEmpty) = err "simplify' (PNonEmpty PEmpty) = should be Impossible!" -- 2009 simplify' other = other --- | Function to flatten nested POr or nested PConcat applicataions.+-- | Function to flatten nested 'POr' or nested 'PConcat' applicataions. flatten :: Pattern -> [Pattern] flatten (POr ps) = (concatMap (\x -> case x of                                        POr ps' -> ps'@@ -356,8 +440,8 @@ notPEmpty PEmpty = False notPEmpty _      = True --- | Determines if pIn will fail or accept [] and never accept any--- characters. Treat PCarat and PDollar as True.+-- | Determines if 'Pattern' will fail or accept @[]@ and never accept any+-- characters. Treat 'PCarat' and 'PDollar' as @True@. canOnlyMatchNull :: Pattern -> Bool canOnlyMatchNull pIn =   case pIn of
lib/Text/Regex/TDFA/ReadRegex.hs view
@@ -1,24 +1,32 @@-{-# OPTIONS_GHC -fno-warn-missing-signatures #-} -- | This is a POSIX version of parseRegex that allows NUL characters. -- Lazy\/Possessive\/Backrefs are not recognized.  Anchors \^ and \$ are -- recognized. ----- The PGroup returned always have (Maybe GroupIndex) set to (Just _)--- and never to Nothing.+-- A 'PGroup' returned always has @(Maybe 'GroupIndex')@ set to @(Just _)@+-- and never to @Nothing@.+ module Text.Regex.TDFA.ReadRegex (parseRegex) where  {- By Chris Kuklewicz, 2007. BSD License, see the LICENSE file. -}  import Text.Regex.TDFA.Pattern {- all -} import Text.ParserCombinators.Parsec((<|>), (<?>),-  unexpected, try, runParser, many, getState, setState, CharParser, ParseError,+  try, runParser, many, getState, setState, CharParser, ParseError,   sepBy1, option, notFollowedBy, many1, lookAhead, eof, between,   string, noneOf, digit, char, anyChar)-import Control.Monad(liftM, when, guard)++import Control.Monad (liftM, guard)++import Data.Foldable (asum) import qualified Data.Set as Set(fromList) --- | BracketElement is internal to this module-data BracketElement = BEChar Char | BEChars String | BEColl String | BEEquiv String | BEClass String+-- | An element inside @[...]@, denoting a character class.+data BracketElement+  = BEChar  Char       -- ^ A single character.+  | BERange Char Char  -- ^ A character range (e.g. @a-z@).+  | BEColl  String     -- ^ @foo@ in @[.foo.]@.+  | BEEquiv String     -- ^ @bar@ in @[=bar=]@.+  | BEClass String     -- ^ A POSIX character class (candidate), e.g. @alpha@ parsed from @[:alpha:]@.  -- | Return either an error message or a tuple of the Pattern and the -- largest group index and the largest DoPa index (both have smallest@@ -30,40 +38,49 @@                              (lastGroupIndex,lastDopa) <- getState                              return (pat,(lastGroupIndex,DoPa lastDopa))) (0,0) x x -p_regex :: CharParser (GroupIndex,Int) Pattern+type P = CharParser (GroupIndex, Int)++p_regex :: P Pattern p_regex = liftM POr $ sepBy1 p_branch (char '|') --- man re_format helps alot, it says one-or-more pieces so this is+-- man re_format helps a lot, it says one-or-more pieces so this is -- many1 not many.  Use "()" to indicate an empty piece.+p_branch :: P Pattern p_branch = liftM PConcat $ many1 p_piece +p_piece :: P Pattern p_piece = (p_anchor <|> p_atom) >>= p_post_atom -- correct specification +p_atom :: P Pattern p_atom =  p_group <|> p_bracket <|> p_char <?> "an atom" -group_index :: CharParser (GroupIndex,Int) (Maybe GroupIndex)+group_index :: P (Maybe GroupIndex) group_index = do   (gi,ci) <- getState   let index = succ gi   setState (index,ci)   return (Just index) +p_group :: P Pattern p_group = lookAhead (char '(') >> do   index <- group_index   liftM (PGroup index) $ between (char '(') (char ')') p_regex  -- p_post_atom takes the previous atom as a parameter+p_post_atom :: Pattern -> P Pattern p_post_atom atom = (char '?' >> return (PQuest atom))                <|> (char '+' >> return (PPlus atom))                <|> (char '*' >> return (PStar True atom))-               <|> p_bound atom +               <|> p_bound atom                <|> return atom +p_bound :: Pattern -> P Pattern p_bound atom = try $ between (char '{') (char '}') (p_bound_spec atom) +p_bound_spec :: Pattern -> P Pattern p_bound_spec atom = do lowS <- many1 digit                        let lowI = read lowS-                       highMI <- option (Just lowI) $ try $ do +                       highMI <- option (Just lowI) $ try $ do                                    _ <- char ','   -- parsec note: if 'many digits' fails below then the 'try' ensures   -- that the ',' will not match the closing '}' in p_bound, same goes@@ -76,36 +93,41 @@                        return (PBound lowI highMI atom)  -- An anchor cannot be modified by a repetition specifier+p_anchor :: P Pattern p_anchor = (char '^' >> liftM PCarat char_index)        <|> (char '$' >> liftM PDollar char_index)-       <|> try (do _ <- string "()" +       <|> try (do _ <- string "()"                    index <- group_index-                   return $ PGroup index PEmpty) +                   return $ PGroup index PEmpty)        <?> "empty () or anchor ^ or $" +char_index :: P DoPa char_index = do (gi,ci) <- getState                 let ci' = succ ci                 setState (gi,ci')                 return (DoPa ci') +p_char :: P Pattern p_char = p_dot <|> p_left_brace <|> p_escaped <|> p_other_char where   p_dot = char '.' >> char_index >>= return . PDot   p_left_brace = try $ (char '{' >> notFollowedBy digit >> char_index >>= return . (`PChar` '{'))   p_escaped = char '\\' >> anyChar >>= \c -> char_index >>= return . (`PEscape` c)-  p_other_char = noneOf specials >>= \c -> char_index >>= return . (`PChar` c) +  p_other_char = noneOf specials >>= \c -> char_index >>= return . (`PChar` c)     where specials  = "^.[$()|*+?{\\"  -- parse [bar] and [^bar] sets of characters+p_bracket :: P Pattern p_bracket = (char '[') >> ( (char '^' >> p_set True) <|> (p_set False) ) --- p_set :: Bool -> GenParser Char st Pattern-p_set invert = do initial <- (option "" ((char ']' >> return "]") <|> (char '-' >> return "-")))+p_set :: Bool -> P Pattern+p_set invert = do initial <- option "" (char ']' >> return "]")                   values <- if null initial then many1 p_set_elem else many p_set_elem                   _ <- char ']'                   ci <- char_index-                  let chars = maybe'set $ initial-                                          ++ [c | BEChar c <- values ]-                                          ++ concat [s | BEChars s <- values ]+                  let chars = maybe'set $ concat $+                        initial :+                        [ c | BEChar c <- values ] :+                        [ [start..end] | BERange start end <- values ]                       colls = maybe'set [PatternSetCollatingElement coll | BEColl coll <- values ]                       equivs = maybe'set [PatternSetEquivalenceClass equiv | BEEquiv equiv <- values]                       class's = maybe'set [PatternSetCharacterClass a'class | BEClass a'class <- values]@@ -115,31 +137,57 @@  -- From here down the code is the parser and functions for pattern [ ] set things -p_set_elem = p_set_elem_class <|> p_set_elem_equiv <|> p_set_elem_coll-         <|> p_set_elem_range <|> p_set_elem_char <?> "Failed to parse bracketed string"+p_set_elem :: P BracketElement+p_set_elem = checkBracketElement =<< asum+  [ p_set_elem_class+  , p_set_elem_equiv+  , p_set_elem_coll+  , p_set_elem_range+  , p_set_elem_char+  , fail "Failed to parse bracketed string"+  ] +p_set_elem_class :: P BracketElement p_set_elem_class = liftM BEClass $   try (between (string "[:") (string ":]") (many1 $ noneOf ":]")) +p_set_elem_equiv :: P BracketElement p_set_elem_equiv = liftM BEEquiv $   try (between (string "[=") (string "=]") (many1 $ noneOf "=]")) +p_set_elem_coll :: P BracketElement p_set_elem_coll =  liftM BEColl $   try (between (string "[.") (string ".]") (many1 $ noneOf ".]")) -p_set_elem_range = try $ do -  start <- noneOf "]-"+p_set_elem_range :: P BracketElement+p_set_elem_range = try $ do+  start <- noneOf "]"   _  <- char '-'   end <- noneOf "]"-  -- bug fix: check start <= end before "return (BEChars [start..end])"-  if start <= end-    then return (BEChars [start..end])-    else unexpected "End point of dashed character range is less than starting point"+  return $ BERange start end -p_set_elem_char = do +p_set_elem_char :: P BracketElement+p_set_elem_char = do   c <- noneOf "]"-  when (c == '-') $ do-    atEnd <- (lookAhead (char ']') >> return True) <|> (return False)-    when (not atEnd) (unexpected "A dash is in the wrong place in a bracket")   return (BEChar c) +-- | Fail when 'BracketElement' is invalid, e.g. empty range @1-0@.+-- This failure should not be caught.+--+checkBracketElement :: BracketElement -> P BracketElement+checkBracketElement e =+  case e of+    BERange start end+      | start > end -> fail $ unwords+          [ "End point"+          , show end+          , "of dashed character range is less than starting point"+          , show start+          ]+      | otherwise -> ok+    BEChar  _ -> ok+    BEClass _ -> ok+    BEColl  _ -> ok+    BEEquiv _ -> ok+  where+    ok = return e
lib/Text/Regex/TDFA/Sequence.hs view
@@ -61,25 +61,23 @@  compile :: CompOption -- ^ Flags (summed together)         -> ExecOption -- ^ Flags (summed together)-        -> (Seq Char) -- ^ The regular expression to compile+        -> Seq Char   -- ^ The regular expression to compile         -> Either String Regex -- ^ Returns: the compiled regular expression compile compOpt execOpt bs =   case parseRegex (F.toList bs) of     Left err -> Left ("parseRegex for Text.Regex.TDFA.Sequence failed:"++show err)     Right pattern -> Right (patternToRegex pattern compOpt execOpt) -execute :: Regex      -- ^ Compiled regular expression-        -> (Seq Char) -- ^ ByteString to match against+execute :: Regex    -- ^ Compiled regular expression+        -> Seq Char -- ^ String to match against         -> Either String (Maybe MatchArray) execute r bs = Right (matchOnce r bs) -regexec :: Regex      -- ^ Compiled regular expression-        -> (Seq Char) -- ^ ByteString to match against-        -> Either String (Maybe ((Seq Char), (Seq Char), (Seq Char), [(Seq Char)]))-regexec r bs =-  case matchOnceText r bs of-    Nothing -> Right (Nothing)-    Just (pre,mt,post) ->-      let main = fst (mt!0)-          rest = map fst (tail (elems mt)) -- will be []-      in Right (Just (pre,main,post,rest))+regexec :: Regex    -- ^ Compiled regular expression+        -> Seq Char -- ^ String to match against+        -> Either String (Maybe (Seq Char, Seq Char, Seq Char, [Seq Char]))+regexec r txt = Right $+  case matchOnceText r txt of+    Just (pre, mt, post) | main:rest <- map fst (elems mt)+      -> Just (pre, main, post, rest)+    _ -> Nothing
lib/Text/Regex/TDFA/String.hs view
@@ -1,4 +1,4 @@-{- | +{- | This modules provides 'RegexMaker' and 'RegexLike' instances for using 'String' with the TDFA backend. @@ -58,15 +58,13 @@ regexec :: Regex      -- ^ Compiled regular expression         -> String     -- ^ String to match against         -> Either String (Maybe (String, String, String, [String]))-regexec r s =-  case matchOnceText r s of-    Nothing -> Right Nothing-    Just (pre,mt,post) ->-      let main = fst (mt!0)-          rest = map fst (tail (elems mt)) -- will be []-      in Right (Just (pre,main,post,rest))+regexec r txt = Right $+  case matchOnceText r txt of+    Just (pre, mt, post) | main:rest <- map fst (elems mt)+      -> Just (pre, main, post, rest)+    _ -> Nothing --- Minimal defintion for now+-- Minimal definition for now instance RegexLike Regex String where   matchOnce r s = listToMaybe (matchAll r s)   matchAll r s = execMatch r 0 '\n' s
lib/Text/Regex/TDFA/TDFA.hs view
@@ -1,12 +1,12 @@ -- | "Text.Regex.TDFA.TDFA" converts the QNFA from TNFA into the DFA.--- A DFA state corresponds to a Set of QNFA states, repesented as list+-- A DFA state corresponds to a Set of QNFA states, represented as list -- of Index which are used to lookup the DFA state in a lazy Trie -- which holds all possible subsets of QNFA states. module Text.Regex.TDFA.TDFA(patternToRegex,DFA(..),DT(..)                             ,examineDFA,nfaToDFA,dfaMap) where  --import Control.Arrow((***))-import Data.Monoid(Monoid(..))+import Data.Monoid as Mon(Monoid(..)) import Control.Monad.State(State,MonadState(..),execState) import Data.Array.IArray(Array,(!),bounds,{-assocs-}) import Data.IntMap(IntMap)@@ -18,7 +18,7 @@ import qualified Data.IntMap.CharMap2 as Map(empty) --import Data.IntSet(IntSet) import qualified Data.IntSet as ISet(empty,singleton,null)-import Data.List(foldl')+import qualified Data.List as List import qualified Data.Map (Map,empty,member,insert,elems) import Data.Sequence as S((|>),{-viewl,ViewL(..)-}) @@ -138,7 +138,7 @@                    -> [(IMap.Key, Transition)]               fuse [] y = fmap (fmap (mergeDTrans o1)) y               fuse x [] = fmap (fmap (mergeDTrans o2)) x-              fuse x@((xc,xa):xs) y@((yc,ya):ys) = +              fuse x@((xc,xa):xs) y@((yc,ya):ys) =                 case compare xc yc of                   LT -> (xc,mergeDTrans o2 xa) : fuse xs y                   EQ -> (xc,mergeDTrans xa ya) : fuse xs ys@@ -165,7 +165,7 @@     if i `Data.Map.member` old       then old       else let new = Data.Map.insert i d old-           in foldl' seen new (flattenDT dt)+           in List.foldl' seen new (flattenDT dt)  -- Get all trans_many states flattenDT :: DT -> [DFA]@@ -211,7 +211,7 @@ -- is free to mutatate the old state.  If the QTrans has only one -- entry then all we need to do is mutate that entry when making a -- transition.--- +-- pickQTrans :: Array Tag OP -> QTrans -> [({-Destination-}Index,(DoPa,Instructions))] pickQTrans op tr = mapSnd (bestTrans op) . IMap.toList $ tr @@ -223,7 +223,7 @@ bestTrans aTagOP (f:fs) | null fs = canonical f                         | otherwise = answer -- if null toDisplay then answer else trace toDisplay answer  where-  answer = foldl' pick (canonical f) fs+  answer = List.foldl' pick (canonical f) fs   {- toDisplay | null fs = ""                | otherwise = unlines $ "bestTrans" : show (answer) : "from among" : concatMap (\x -> [show x, show (toInstructions (snd x))]) (f:fs) -}   canonical :: TagCommand -> (DoPa,Instructions)@@ -246,7 +246,7 @@    mergeTagOrbit xx [] = xx   mergeTagOrbit [] yy = yy-  mergeTagOrbit xx@(x:xs) yy@(y:ys) = +  mergeTagOrbit xx@(x:xs) yy@(y:ys) =     case compare (fst x) (fst y) of       GT -> y : mergeTagOrbit xx ys       LT -> x : mergeTagOrbit xs yy@@ -264,14 +264,14 @@                     -- this needs a leading Minimize tag inside at least the parent * operator       Ignore -> GT -- XXX this is a guess in analogy with Maximize for the end bit of a group       Orbit -> LT -- trace ("choose LT! Just "++show tag++" < Nothing") LT -- 2009 XXX : comment out next line and use the Orbit instead---      Orbit -> err $ "bestTrans.choose : Very Unexpeted Orbit in Just Nothing: "++show (tag,post,aTagOP,f:fs)+--      Orbit -> err $ "bestTrans.choose : Very Unexpected Orbit in Just Nothing: "++show (tag,post,aTagOP,f:fs)   choose (Just (tag,post1)) (Just (_,post2)) =     case aTagOP!tag of       Maximize -> order       Minimize -> flipOrder order       Ignore -> EQ       Orbit -> EQ---      Orbit -> err $ "bestTrans.choose : Very Unexpeted Orbit in Just Just: "++show (tag,(post1,post2),aTagOP,f:fs)+--      Orbit -> err $ "bestTrans.choose : Very Unexpected Orbit in Just Just: "++show (tag,(post1,post2),aTagOP,f:fs)    where order = case (post1,post2) of                    (SetPre,SetPre) -> EQ                    (SetPost,SetPost) -> EQ@@ -293,7 +293,7 @@     cw xx [] = foldr (\x rest -> comp (Just x) Nothing  `mappend` rest) mempty xx     cw [] yy = foldr (\y rest -> comp Nothing  (Just y) `mappend` rest) mempty yy -                   + isDFAFrontAnchored :: DFA -> Bool isDFAFrontAnchored = isDTFrontAnchored . d_dt  where@@ -424,7 +424,7 @@   newOrbit pos = Orbits { inOrbit = inOrbit'                         , basePos = pos                         , ordinal = Nothing-                        , getOrbits = mempty}+                        , getOrbits = Mon.mempty}   updateOrbit pos _tag new old | inOrbit old = old { inOrbit = inOrbit'                                                    , getOrbits = getOrbits old |> pos }                                | otherwise = new
lib/Text/Regex/TDFA/TNFA.hs view
@@ -7,11 +7,11 @@ -- (That will require re-organizing the continuation data a bit)  -- | "Text.Regex.TDFA.TNFA" converts the CorePattern Q\/P data (and its--- Pattern leafs) to a QNFA tagged non-deterministic finite automata.--- +-- Pattern leaves) to a QNFA tagged non-deterministic finite automata.+-- -- This holds every possible way to follow one state by another, while -- in the DFA these will be reduced by picking a single best--- transition for each (soure,destination) pair.  The transitions are+-- transition for each (source,destination) pair.  The transitions are -- heavily and often redundantly annotated with tasks to perform, and -- this redundancy is reduced when picking the best transition.  So -- far, keeping all this information has helped fix bugs in both the@@ -19,18 +19,20 @@ -- -- The QNFA for a Pattern with a starTraned Q\/P form with N one -- character accepting leaves has at most N+1 nodes.  These nodes--- repesent the future choices after accepting a leaf.  The processing+-- represent the future choices after accepting a leaf.  The processing -- of Or nodes often reduces this number by sharing at the end of the -- different paths.  Turning off capturing while compiling the pattern -- may (future extension) reduce this further for some patterns by -- processing Star with optimizations.  This compact design also means -- that tags are assigned not just to be updated before taking a -- transition (PreUpdate) but also after the transition (PostUpdate).--- +-- -- Uses recursive do notation. -module Text.Regex.TDFA.TNFA(patternToNFA-                            ,QNFA(..),QT(..),QTrans,TagUpdate(..)) where+module Text.Regex.TDFA.TNFA+  ( patternToNFA+  , QNFA(..), QT(..), QTrans, TagUpdate(..)+  ) where  {- By Chris Kuklewicz, 2007. BSD License, see the LICENSE file. -} @@ -38,7 +40,7 @@ import Control.Monad.State(State,runState,execState,get,put,modify) import Data.Array.IArray(Array,array) import Data.Char(toLower,toUpper,isAlpha,ord)-import Data.List(foldl')+import qualified Data.List as List import Data.IntMap (IntMap) import qualified Data.IntMap as IMap(toAscList,null,unionWith,singleton,fromList,fromDistinctAscList) import Data.IntMap.CharMap2(CharMap(..))@@ -47,8 +49,8 @@ import Data.IntSet.EnumSet2(EnumSet) import qualified Data.IntSet.EnumSet2 as Set(singleton,toList,insert) import Data.Maybe(catMaybes,isNothing)-import Data.Monoid(mempty,mappend)-import qualified Data.Set as S(Set,insert,toAscList,empty)+import Data.Monoid as Mon(Monoid(..))+import qualified Data.Set as S (insert, toAscList)  import Text.Regex.TDFA.Common(QT(..),QNFA(..),QTrans,TagTask(..),TagUpdate(..),DoPa(..)                              ,CompOption(..)@@ -57,8 +59,7 @@ import Text.Regex.TDFA.CorePattern(Q(..),P(..),OP(..),WhichTest,cleanNullView,NullView                                   ,SetTestInfo(..),Wanted(..),TestInfo                                   ,mustAccept,cannotAccept,patternToQ)-import Text.Regex.TDFA.Pattern(Pattern(..),PatternSet(..),unSEC,PatternSetCharacterClass(..))---import Debug.Trace+import Text.Regex.TDFA.Pattern (Pattern(..), decodePatternSet)  ecart :: String -> a -> a ecart _ = id@@ -71,7 +72,7 @@  qtwin,qtlose :: QT -- qtwin is the continuation after matching the whole pattern.  It has--- no futher transitions and sets tag #1 to the current position.+-- no further transitions and sets tag #1 to the current position. qtwin = Simple {qt_win=[(1,PreUpdate TagTask)],qt_trans=mempty,qt_other=mempty} -- qtlose is the continuation to nothing, used when ^ or $ tests fail. qtlose = Simple {qt_win=mempty,qt_trans=mempty,qt_other=mempty}@@ -86,7 +87,7 @@       msg = unlines [ show q ]   in debug msg (qToNFA compOpt q,tags,groups) --- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == +-- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- Query function on Q  nullable :: Q -> Bool@@ -105,7 +106,7 @@ usesQNFA (Q {wants=WantsQNFA}) = True usesQNFA _ = False --- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == +-- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- Functions related to QT  -- dumb smart constructor used by qToQNFA@@ -138,7 +139,7 @@ -- processing Or. applyNullViews :: NullView -> QT -> QT applyNullViews [] win = win-applyNullViews nvs win = foldl' (dominate win) qtlose (reverse $ cleanNullView nvs) where+applyNullViews nvs win = List.foldl' (dominate win) qtlose (reverse $ cleanNullView nvs)  -- This is used to prefer to view "win" through NullView.  Losing is -- replaced by the plain win.  This is employed by Star patterns to@@ -146,9 +147,9 @@ -- skipping the NullView occurs if the match fails. preferNullViews :: NullView -> QT -> QT preferNullViews [] win = win-preferNullViews nvs win = foldl' (dominate win) win (reverse $ cleanNullView nvs) where+preferNullViews nvs win = List.foldl' (dominate win) win (reverse $ cleanNullView nvs) -{- +{- dominate is common to applyNullViews and preferNullViews above.  Even I no longer understand it without study.@@ -171,7 +172,7 @@ dominate win lose x@(SetTestInfo sti,tags) = debug ("dominate "++show x) $   let -- The winning states are reached through the SetTag       win' = prependTags' tags win-      -- get the SetTestInfo +      -- get the SetTestInfo       winTests = listTestInfo win $ mempty       allTests = (listTestInfo lose $ winTests) `mappend` (EMap.keysSet sti)       -- The first and second arguments of useTest are sorted@@ -204,7 +205,7 @@   applyTest' q@(Simple {}) =     mkTesting $ Testing {qt_test = wt                         ,qt_dopas = Set.singleton dopa-                        ,qt_a = q +                        ,qt_a = q                         ,qt_b = qtlose}   applyTest' q@(Testing {qt_test=wt'}) =     case compare wt wt' of@@ -221,7 +222,7 @@ -- are handled. -- -- mergeQT_2nd is used by the NonEmpty case and always discards the--- first argument's win and uses the second argment's win.+-- first argument's win and uses the second argument's win. -- -- mergeAltQT is used by the Or cases and is biased to the first -- argument's winning transition, if present.@@ -238,7 +239,7 @@               | nullQT q2 = q1  -- union wins               | otherwise = mergeQTWith mappend q1 q2 -- no preference, win with combined SetTag XXX is the wrong thing! "(.?)*" --- This takes a function which implements a policy on mergining+-- This takes a function which implements a policy on merging -- winning transitions and then merges all the transitions.  It opens -- the CharMap newtype for more efficient operation, then rewraps it. mergeQTWith :: (WinTags -> WinTags -> WinTags) -> QT -> QT -> QT@@ -302,7 +303,7 @@          , qt_other = prependQTrans o }   where prependQTrans = fmap (map (\(d,tcs) -> (d,tcs' `mappend` tcs))) --- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == +-- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- define type S which is a State monad, this allows the creation of the uniq QNFA ids and storing the QNFA -- in an ascending order difference list for later placement in an array. @@ -330,7 +331,7 @@   put $! (futureI, oldQs . ((thisI,qnfa):))   return qnfa --- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == +-- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- E related functions  fromQNFA :: QNFA -> E@@ -408,7 +409,7 @@ getE (eLoop,Just accepting,_) = fromQT (mergeQT (getQT eLoop) (getQT accepting)) getE (eLoop,Nothing,_) = eLoop --- 2009: See coment for addTest.  Here is a case where the third component might be a (Just qnfa) and it+-- 2009: See comment for addTest.  Here is a case where the third component might be a (Just qnfa) and it -- is being lost even though the added test might be redundant. addTestAC :: TestInfo -> ActCont -> ActCont addTestAC ti (e,mE,_) = (addTest ti e@@ -439,7 +440,7 @@ addWinTagsAC wtags (e,mE,mQNFA) = (addWinTags wtags e                                   ,fmap (addWinTags wtags) mE                                   ,fmap (addWinTags wtags) mQNFA)--- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == +-- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- == -- ==  -- Initial preTag of 0th tag is implied. No other general pre-tags would be expected. -- The qtwin contains the preTag of the 1st tag and is only set when a match is completed.@@ -530,7 +531,7 @@                                                                  | otherwise =     debug (">< inStar/2 "++show qIn++" <>") $     return . fmap (prependGroupResets resets . prependPreTag pre) =<< inStarNullableTagless qIn (addTag post . addGroupSets sets $ eLoop)-    +   inStarNullableTagless qIn eLoop = debug (">< inStarNullableTagless "++show qIn++" <>") $ do     case unQ qIn of       Empty -> return Nothing -- with Or this discards () branch in "(^|foo|())*"@@ -619,7 +620,7 @@   actNullableTagless qIn ac@(eLoop,mAccepting,mQNFA) = debug (">< actNullableTagless "++show (qIn)++" <>") $ do     case unQ qIn of       Seq q1 q2 -> actNullable q1 =<< actNullable q2 ac   -- We know q1 and q2 are nullable-                      +       Or [] -> return ac       Or [q] -> actNullableTagless q ac       Or qs -> do@@ -660,7 +661,7 @@                             in ((nQ eLoop,fmap nQ mAccepting,Nothing),False)         if cannotAccept q then return ac0 else mdo           mChildAccepting <- act q (this,Nothing,Nothing)-          (thisAC@(this,_,_),ansAC) <- +          (thisAC@(this,_,_),ansAC) <-             case mChildAccepting of               Nothing -> err $ "Weird pattern in getTransTagless/Star: " ++ show (qTop,qIn)               Just childAccepting -> do@@ -756,22 +757,22 @@            let trans = toMap mempty . S.toAscList . addNewline . decodePatternSet $ ps            in Simple { qt_win = mempty, qt_trans = trans, qt_other = target }          _ -> err ("Cannot acceptTrans pattern "++show (qTop,pIn))-    where  -- Take a common destination and a sorted list of unique chraceters+    where  -- Take a common destination and a sorted list of unique characters            -- and create a map from those characters to the common destination       toMap :: IntMap [(DoPa,[(Tag, TagUpdate)])] -> [Char]             -> CharMap (IntMap [(DoPa,[(Tag, TagUpdate)])])       toMap dest | caseSensitive compOpt = CharMap . IMap.fromDistinctAscList . map (\c -> (ord c,dest))-                 | otherwise = CharMap . IMap.fromList . ($ []) +                 | otherwise = CharMap . IMap.fromList . ($ [])                                . foldr (\c dl -> if isAlpha c                                                    then (dl.((ord (toUpper c),dest):)                                                            .((ord (toLower c),dest):)                                                         )                                                    else (dl.((ord c,dest):))-                                       ) id +                                       ) id       addNewline | multiline compOpt = S.insert '\n'                  | otherwise = id       dotTrans | multiline compOpt = Map.singleton '\n' mempty-               | otherwise = mempty+               | otherwise = Mon.mempty  {- @@ -783,44 +784,5 @@   ADD ORPHAN ID check and make this a fatal error while testing---}---- | decodePatternSet cannot handle collating element and treats--- equivalence classes as just their definition and nothing more.-decodePatternSet :: PatternSet -> S.Set Char-decodePatternSet (PatternSet msc mscc _ msec) =-  let baseMSC = maybe S.empty id msc-      withMSCC = foldl (flip S.insert) baseMSC  (maybe [] (concatMap decodeCharacterClass . S.toAscList) mscc)-      withMSEC = foldl (flip S.insert) withMSCC (maybe [] (concatMap unSEC . S.toAscList) msec)-  in withMSEC---- | This returns the disctince ascending list of characters--- represented by [: :] values in legalCharacterClasses; unrecognized--- class names return an empty string-decodeCharacterClass :: PatternSetCharacterClass -> String-decodeCharacterClass (PatternSetCharacterClass s) =-  case s of-    "alnum" -> ['0'..'9']++['a'..'z']++['A'..'Z']-    "digit" -> ['0'..'9']-    "punct" -> ['\33'..'\47']++['\58'..'\64']++['\91'..'\95']++"\96"++['\123'..'\126']-    "alpha" -> ['a'..'z']++['A'..'Z']-    "graph" -> ['\41'..'\126']-    "space" -> "\t\n\v\f\r "-    "blank" -> "\t "-    "lower" -> ['a'..'z']-    "upper" -> ['A'..'Z']-    "cntrl" -> ['\0'..'\31']++"\127" -- with NUL-    "print" -> ['\32'..'\126']-    "xdigit" -> ['0'..'9']++['a'..'f']++['A'..'F']-    "word" -> ['0'..'9']++['a'..'z']++['A'..'Z']++"_"-    _ -> []--{---- | This is the list of recognized [: :] character classes, others--- are decoded as empty.-legalCharacterClasses :: [String]-legalCharacterClasses = ["alnum","digit","punct","alpha","graph"-  ,"space","blank","lower","upper","cntrl","print","xdigit","word"]  -}
+ lib/Text/Regex/TDFA/Text.hs view
@@ -0,0 +1,99 @@+{-|+Module      :  Text.Regex.TDFA.Text+Copyright   :  Chris Kuklewicz 2007-2009, shelarcy 2012+License     :  BSD-style (see the file LICENSE)++This modules provides 'RegexMaker' and 'RegexLike' instances for using+'Text' with the TDFA backend ("Text.Regex.TDFA.NewDFA.Engine" and+"Text.Regex.TDFA.NewDFA.Tester").++This exports instances of the high level API and the medium level+API of 'compile','execute', and 'regexec'.++@since 1.3.1+-}+module Text.Regex.TDFA.Text(+  Regex+ ,CompOption+ ,ExecOption+ ,compile+ ,execute+ ,regexec+ ) where++import Data.Array((!),elems)+import qualified Data.Text as T(Text,unpack)++import Text.Regex.Base(RegexLike(..),RegexMaker(..),Extract(..),MatchArray,RegexContext(..))+import Text.Regex.Base.Impl(polymatch,polymatchM)+import Text.Regex.TDFA.ReadRegex(parseRegex)+import Text.Regex.TDFA.String() -- piggyback on RegexMaker for String+import Text.Regex.TDFA.TDFA(patternToRegex)+import Text.Regex.TDFA.Common(Regex(..),CompOption,ExecOption(captureGroups),Position)++import Data.Maybe(listToMaybe)+import Text.Regex.TDFA.NewDFA.Uncons(Uncons)+import qualified Text.Regex.TDFA.NewDFA.Engine as Engine(execMatch)+import qualified Text.Regex.TDFA.NewDFA.Tester as Tester(matchTest)++-- | @since 1.3.1+instance RegexContext Regex T.Text T.Text where+  match = polymatch+  matchM = polymatchM++-- | @since 1.3.1+instance RegexMaker Regex CompOption ExecOption T.Text where+  makeRegexOptsM c e source = makeRegexOptsM c e (T.unpack source)++-- | @since 1.3.1+{-# SPECIALIZE execMatch :: Regex -> Position -> Char -> T.Text -> [MatchArray] #-}+execMatch :: Uncons text => Regex -> Position -> Char -> text -> [MatchArray]+execMatch = Engine.execMatch++-- | @since 1.3.1+{-# SPECIALIZE myMatchTest :: Regex -> T.Text -> Bool #-}+myMatchTest :: Uncons text => Regex -> text -> Bool+myMatchTest = Tester.matchTest++-- | @since 1.3.1+instance RegexLike Regex T.Text where+  matchOnce r s = listToMaybe (matchAll r s)+  matchAll r s = execMatch r 0 '\n' s+  matchCount r s = length (matchAll r' s)+    where r' = r { regex_execOptions = (regex_execOptions r) {captureGroups = False} }+  matchTest = myMatchTest+  matchOnceText regex source =+    fmap (\ma -> let (o,l) = ma!0+                 in (before o source+                    ,fmap (\ol -> (extract ol source,ol)) ma+                    ,after (o+l) source))+         (matchOnce regex source)+  matchAllText regex source =+    map (fmap (\ol -> (extract ol source,ol)))+        (matchAll regex source)++-- | @since 1.3.1+compile :: CompOption -- ^ Flags (summed together)+        -> ExecOption -- ^ Flags (summed together)+        -> T.Text -- ^ The regular expression to compile+        -> Either String Regex -- ^ Returns: the compiled regular expression+compile compOpt execOpt txt =+  case parseRegex (T.unpack txt) of+    Left err -> Left ("parseRegex for Text.Regex.TDFA.Text failed:"++show err)+    Right pattern -> Right (patternToRegex pattern compOpt execOpt)++-- | @since 1.3.1+execute :: Regex  -- ^ Compiled regular expression+        -> T.Text -- ^ Text to match against+        -> Either String (Maybe MatchArray)+execute r txt = Right (matchOnce r txt)++-- | @since 1.3.1+regexec :: Regex  -- ^ Compiled regular expression+        -> T.Text -- ^ Text to match against+        -> Either String (Maybe (T.Text, T.Text, T.Text, [T.Text]))+regexec r txt = Right $+  case matchOnceText r txt of+    Just (pre, mt, post) | main:rest <- map fst (elems mt)+      -> Just (pre, main, post, rest)+    _ -> Nothing
+ lib/Text/Regex/TDFA/Text/Lazy.hs view
@@ -0,0 +1,107 @@+{-|+Module      :  Text.Regex.TDFA.Text.Lazy+Copyright   :  Chris Kuklewicz 2007-2009, shelarcy 2012+License     :  BSD-style (see the file LICENSE)++This modules provides 'RegexMaker' and 'RegexLike' instances for using+'Text' with the TDFA backend ("Text.Regex.TDFA.NewDFA.Engine" and+"Text.Regex.TDFA.NewDFA.Tester").++This exports instances of the high level API and the medium level+API of 'compile','execute', and 'regexec'.++@since 1.3.1+-}+module Text.Regex.TDFA.Text.Lazy(+  Regex+ ,CompOption+ ,ExecOption+ ,compile+ ,execute+ ,regexec+ ) where++import Data.Array.IArray(Array,(!),elems)+import qualified Data.Text.Lazy as L(Text,unpack)++import Text.Regex.Base(MatchArray,RegexContext(..),Extract(..),RegexMaker(..),RegexLike(..))+import Text.Regex.Base.Impl(polymatch,polymatchM)+import Text.Regex.TDFA.ReadRegex(parseRegex)+import Text.Regex.TDFA.String() -- piggyback on RegexMaker for String+import Text.Regex.TDFA.TDFA(patternToRegex)+import Text.Regex.TDFA.Common(Regex(..),CompOption,ExecOption(captureGroups),Position)++import Data.Maybe(listToMaybe)+import Text.Regex.TDFA.NewDFA.Uncons(Uncons)+import qualified Text.Regex.TDFA.NewDFA.Engine as Engine(execMatch)+import qualified Text.Regex.TDFA.NewDFA.Tester as Tester(matchTest)++-- | @since 1.3.1+instance RegexContext Regex L.Text L.Text where+  match = polymatch+  matchM = polymatchM++-- | @since 1.3.1+instance RegexMaker Regex CompOption ExecOption L.Text where+  makeRegexOptsM c e source = makeRegexOptsM c e (L.unpack source)++-- | @since 1.3.1+{-# SPECIALIZE execMatch :: Regex -> Position -> Char -> L.Text -> [MatchArray] #-}+execMatch :: Uncons text => Regex -> Position -> Char -> text -> [MatchArray]+execMatch = Engine.execMatch++-- | @since 1.3.1+{-# SPECIALIZE myMatchTest :: Regex -> L.Text -> Bool #-}+myMatchTest :: Uncons text => Regex -> text -> Bool+myMatchTest = Tester.matchTest++-- | @since 1.3.1+instance RegexLike Regex L.Text where+  matchOnce r s = listToMaybe (matchAll r s)+  matchAll r s = execMatch r 0 '\n' s+  matchCount r s = length (matchAll r' s)+    where r' = r { regex_execOptions = (regex_execOptions r) {captureGroups = False} }+  matchTest = myMatchTest+  matchOnceText regex source =+    fmap (\ ma ->+            let (o,l) = ma!0+            in (before o source+               ,fmap (\ ol -> (extract ol source,ol)) ma+               ,after (o+l) source))+         (matchOnce regex source)+  matchAllText regex source =+    let go :: Int -> L.Text -> [Array Int (Int, Int)] -> [Array Int (L.Text, (Int, Int))]+        go i _ _ | i `seq` False = undefined+        go _i _t [] = []+        go i t (x:xs) =+          let (off0,len0) = x!0+              trans pair@(off,len) = (extract (off-i,len) t,pair)+              t' = after (off0+(len0-i)) t+          in fmap trans x : seq t' (go (off0+len0) t' xs)+    in go 0 source (matchAll regex source)++-- | @since 1.3.1+compile :: CompOption -- ^ Flags (summed together)+        -> ExecOption -- ^ Flags (summed together)+        -> L.Text -- ^ The regular expression to compile+        -> Either String Regex -- ^ Returns: the compiled regular expression+compile compOpt execOpt txt =+  case parseRegex (L.unpack txt) of+    Left err -> Left ("parseRegex for Text.Regex.TDFA.Text.Lazy failed:"++show err)+    Right pattern -> Right (patternToRegex pattern compOpt execOpt)++-- | @since 1.3.1+execute :: Regex  -- ^ Compiled regular expression+        -> L.Text -- ^ Text to match against+        -> Either String (Maybe MatchArray)+execute r txt = Right (matchOnce r txt)++-- | @since 1.3.1+regexec :: Regex  -- ^ Compiled regular expression+        -> L.Text -- ^ Text to match against+        -> Either String (Maybe (L.Text, L.Text, L.Text, [L.Text]))+regexec r txt = Right $+  case matchOnceText r txt of+    Just (pre, mt, post) | main:rest <- map fst (elems mt)+      -> Just (pre, main, post, rest)+    _ -> Nothing
regex-tdfa.cabal view
@@ -1,99 +1,185 @@-Name:                   regex-tdfa-Version:                1.2.3.3-License:                BSD3-License-File:           LICENSE-Copyright:              Copyright (c) 2007, Christopher Kuklewicz-Author:                 Christopher Kuklewicz-Maintainer:             Artyom <yom@artyom.me>, Christopher Kuklewicz <TextRegexLazy@personal.mightyreason.com>-Stability:              Seems to work, but not POSIX yet-Homepage:               https://github.com/ChrisKuklewicz/regex-tdfa-Bug-Reports:            https://github.com/ChrisKuklewicz/regex-tdfa/issues-Synopsis:               Replaces/Enhances Text.Regex-Description:            A new all Haskell "tagged" DFA regex engine, inspired by libtre-Category:               Text-Tested-With:            GHC==7.6.3-                      , GHC==7.8.4-                      , GHC==7.10.3-                      , GHC==8.0.2-                      , GHC==8.2.2-                      , GHC==8.4.4-                      , GHC==8.6.5-                      , GHC==8.8.1-Build-Type:             Simple-extra-source-files:+cabal-version:          2.0+name:                   regex-tdfa+version:                1.3.2.5++build-Type:             Simple+license:                BSD3+license-file:           LICENSE+copyright:              Copyright (c) 2007-2009, Christopher Kuklewicz+author:                 Christopher Kuklewicz+maintainer:             Andreas Abel+homepage:               https://wiki.haskell.org/Regular_expressions+bug-reports:            https://github.com/haskell-hvr/regex-tdfa/issues++category:               Text+synopsis:               Pure Haskell Tagged DFA Backend for "Text.Regex" (regex-base)+description:+  This package provides a pure Haskell \"Tagged\" DFA regex engine for <//hackage.haskell.org/package/regex-base regex-base>. This implementation was inspired by the algorithm (and Master's thesis) behind the regular expression library known as <https://github.com/laurikari/tre/ TRE or libtre>.+  .+  Please consult the "Text.Regex.TDFA" module for API documentation including a tutorial with usage examples;+  see also <https://wiki.haskell.org/Regular_expressions> for general information about regular expression support in Haskell.++extra-doc-files:   CHANGELOG.md+  README.md++extra-source-files:   test/cases/*.txt-Cabal-Version:          >= 1.8 +tested-with:+  GHC == 9.12.2+  GHC == 9.10.3+  GHC == 9.8.4+  GHC == 9.6.7+  GHC == 9.4.8+  GHC == 9.2.8+  GHC == 9.0.2+  GHC == 8.10.7+  GHC == 8.8.4+  GHC == 8.6.5+  GHC == 8.4.4+  GHC == 8.2.2+  GHC == 8.0.2+ source-repository head   type:                git-  location:            git://github.com/ChrisKuklewicz/regex-tdfa.git+  location:            https://github.com/haskell-hvr/regex-tdfa.git -flag devel-  description: enable flags that are useful for development+source-repository this+  type:                git+  location:            https://github.com/haskell-hvr/regex-tdfa.git+  tag:                 v1.3.2.5++flag force-O2   default: False   manual: True+  description:+    Force building @regex-tdfa@ with \"@ghc-options: -O2@\".+    .+    __NOTE__: This flag is mostly provided for legacy use-cases. Nowadays you can conveniently control optimization levels on a per-package granularity via @cabal.project@ files; see <https://cabal.readthedocs.io/en/latest/nix-local-build.html#configuring-builds-with-cabal-project cabal's user-guide> for more details. +flag doctest+  default: True+  manual: False+  description:+    Include test-suite doctest.+ library   hs-source-dirs:       lib-  Build-Depends:        array              >= 0.4 && < 0.6-                      , base               >= 4 && < 5-                      , bytestring         >= 0.10 && < 0.11-                      , containers         >= 0.5 && < 0.7-                      , ghc-prim-                      , mtl                == 2.*-                      , parsec             == 3.*-                      , regex-base         >= 0.93.1 && < 0.95 -  -- Support Semigroup instances uniformly-  ---  -- See also-  --  https://prime.haskell.org/wiki/Libraries/Proposals/SemigroupMonoid#RecommendedVariant-  ---  -- NB: This is the same logic `parsec.cabal` uses, so this doesn't-  -- add any new dependency that isn't already incurred by-  -- `regex-tdfa`'s transitive deps-  if !impl(ghc >= 8.0)-    build-depends:      fail-                      , semigroups == 0.18.*+  exposed-modules:      Data.IntMap.CharMap2+                        Data.IntMap.EnumMap2+                        Data.IntSet.EnumSet2+                        Text.Regex.TDFA+                        Text.Regex.TDFA.ByteString+                        Text.Regex.TDFA.ByteString.Lazy+                        Text.Regex.TDFA.Common+                        Text.Regex.TDFA.CorePattern+                        Text.Regex.TDFA.IntArrTrieSet+                        Text.Regex.TDFA.NewDFA.Engine+                        Text.Regex.TDFA.NewDFA.Engine_FA+                        Text.Regex.TDFA.NewDFA.Engine_NC+                        Text.Regex.TDFA.NewDFA.Engine_NC_FA+                        Text.Regex.TDFA.NewDFA.Tester+                        Text.Regex.TDFA.NewDFA.Uncons+                        Text.Regex.TDFA.NewDFA.MakeTest+                        Text.Regex.TDFA.Pattern+                        Text.Regex.TDFA.ReadRegex+                        Text.Regex.TDFA.Sequence+                        Text.Regex.TDFA.String+                        Text.Regex.TDFA.TDFA+                        Text.Regex.TDFA.TNFA+                        Text.Regex.TDFA.Text+                        Text.Regex.TDFA.Text.Lazy -  other-modules:          Paths_regex_tdfa-  Exposed-Modules:        Data.IntMap.CharMap2-                          Data.IntMap.EnumMap2-                          Data.IntSet.EnumSet2-                          Text.Regex.TDFA-                          Text.Regex.TDFA.ByteString-                          Text.Regex.TDFA.ByteString.Lazy-                          Text.Regex.TDFA.Common-                          Text.Regex.TDFA.CorePattern-                          Text.Regex.TDFA.IntArrTrieSet-                          Text.Regex.TDFA.NewDFA.Engine-                          Text.Regex.TDFA.NewDFA.Engine_FA-                          Text.Regex.TDFA.NewDFA.Engine_NC-                          Text.Regex.TDFA.NewDFA.Engine_NC_FA-                          Text.Regex.TDFA.NewDFA.Tester-                          Text.Regex.TDFA.NewDFA.Uncons-                          Text.Regex.TDFA.NewDFA.MakeTest-                          Text.Regex.TDFA.Pattern-                          Text.Regex.TDFA.ReadRegex-                          Text.Regex.TDFA.Sequence-                          Text.Regex.TDFA.String-                          Text.Regex.TDFA.TDFA-                          Text.Regex.TDFA.TNFA-  Buildable:              True-  Extensions:             MultiParamTypeClasses, FunctionalDependencies, BangPatterns, MagicHash, RecursiveDo, NoMonoPatBinds, ForeignFunctionInterface, UnboxedTuples, TypeOperators, FlexibleContexts, ExistentialQuantification, UnliftedFFITypes, TypeSynonymInstances, FlexibleInstances-  GHC-Options:            -Wall -funbox-strict-fields -fspec-constr-count=10 -O2 -fno-warn-orphans-  if flag(devel)-    ghc-prof-options: -auto-all+  autogen-modules:      Paths_regex_tdfa+  other-modules:        Paths_regex_tdfa +  build-depends:        array              >= 0.5    && < 0.6+                      , base               >= 4.9    && < 5+                      , bytestring         >= 0.10   && < 0.13+                      , containers         >= 0.5    && < 1+                          -- containers >= 0.5.11.0 (GHC 8.4) will allow to drop some #if+                      , mtl                >= 2.1.3  && < 2.4+                      , parsec             == 3.1.*+                      , regex-base         == 0.94.*+                      , text               >= 1.2.3  && < 2.2++  default-language:     Haskell2010+  default-extensions:   BangPatterns+                        ExistentialQuantification+                        FlexibleContexts+                        FlexibleInstances+                        ForeignFunctionInterface+                        FunctionalDependencies+                        MagicHash+                        MultiParamTypeClasses+                        NondecreasingIndentation+                        RecursiveDo+                        ScopedTypeVariables+                        TypeOperators+                        TypeSynonymInstances+                        UnboxedTuples+                        UnliftedFFITypes+  other-extensions:     CPP++  ghc-options:+      -funbox-strict-fields+      -fspec-constr-count=10+      -Wall+      -Wno-orphans+      -Wcompat++  if flag(force-O2)+    ghc-options:+      -O2++ test-suite regex-tdfa-unittest   type:                 exitcode-stdio-1.0-  build-depends:        regex-base >= 0.93.1, base >=4  && < 5, regex-tdfa >= 0.92, bytestring, containers, array, mtl, file-embed, filepath, utf8-string+   hs-source-dirs:       test   main-is:              Main.hs-  extensions:           FlexibleInstances, FlexibleContexts,Rank2Types-  GHC-Options:          -Wall -O2 -funbox-strict-fields-  if flag(devel)-    ghc-prof-options: -auto-all-  if !impl(ghc >= 8.0)-    build-depends:      fail++  -- intra-package dependency+  build-depends:        regex-tdfa++  -- dependencies whose version constraints are inherited via intra-package 'regex-tdfa' dependency+  build-depends:        array+                      , base+                      , bytestring+                      , filepath+                      , regex-base++  -- component-specific dependencies not inherited via 'regex-tdfa'+                      , directory          >= 1.1.0  && < 1.4+                      , filepath           >= 1.3.0  && < 1.6+                      , utf8-string        >= 1.0.1  && < 1.1++  default-language:     Haskell2010+  default-extensions:   FlexibleInstances+                        FlexibleContexts+                        Rank2Types+  other-extensions:     GeneralizedNewtypeDeriving++  ghc-options:          -funbox-strict-fields+                        -Wall+                        -Wcompat++  if flag(force-O2)+    ghc-options:        -O2++test-suite doctest+  type:           exitcode-stdio-1.0+  hs-source-dirs: test+  main-is:        DocTestMain.hs++  build-depends:+      base             >= 4.9    && < 5+    , doctest-parallel >= 0.2.2+        -- doctest-parallel-0.2.2 is the first to filter out autogen-modules++  default-language:     Haskell2010++  if !flag(doctest)+    buildable: False
+ test/DocTestMain.hs view
@@ -0,0 +1,16 @@+module Main where++import System.Environment+         ( getArgs )+import Test.DocTest+         ( mainFromLibrary )+import Test.DocTest.Helpers+         ( extractSpecificCabalLibrary, findCabalPackage )++main :: IO ()+main = do+  args <- getArgs+  pkg  <- findCabalPackage "regex-tdfa"+  -- Need to give the library name, otherwise the parser does not find it.+  lib  <- extractSpecificCabalLibrary Nothing pkg+  mainFromLibrary lib args
test/Main.hs view
@@ -1,28 +1,30 @@-{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE CPP                        #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MonoLocalBinds             #-} +#if __GLASGOW_HASKELL__ >= 900+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}+#endif+ module Main where -import Control.Monad-import Control.Monad.Error()-import Data.Array-import Data.List-import Control.Applicative---import Data.Monoid-import Data.Sequence(Seq)-import Data.String-import Data.Typeable-import Data.Version()-import System.Environment-import Text.Regex.Base-import qualified Data.Foldable as F-import Data.FileEmbed+import           Control.Applicative  as App+import           Control.Monad+import qualified Control.Monad.Fail   as Fail+import           Data.Array+import qualified Data.ByteString      as BS import qualified Data.ByteString.UTF8 as UTF8-import qualified Control.Monad.Fail as Fail-import System.Exit+import           Data.List            (isInfixOf, mapAccumL, sort)+import           Data.String+import           Data.Typeable+import           Data.Version         ()+import           System.Directory     (getDirectoryContents)+import           System.Environment+import           System.Exit+import           System.FilePath      ((</>))+import           Text.Regex.Base -import qualified Text.Regex.TDFA.Common as TDFA-import qualified Text.Regex.TDFA as TDFA+import qualified Text.Regex.TDFA      as TDFA  default(Int) @@ -55,18 +57,18 @@  toTest :: String -> (Int,String,String,String) toTest line = let [n,regex,input,output] = words line-                  noQ [] = []+                  noQ []       = []                   noQ ('?':xs) = '-':'1':noQ xs-                  noQ (x:xs) = x:noQ xs+                  noQ (x:xs)   = x:noQ xs                   input' = if input == "NULL" then "" else unN input               in (read n,regex,input',noQ output)  toTest' :: String -> String -> (String,(Int,String,String,String)) toTest' oldRegex line =   let [n,regex,input,output] = words line-      noQ [] = []+      noQ []       = []       noQ ('?':xs) = '-':'1':noQ xs-      noQ (x:xs) = x:noQ xs+      noQ (x:xs)   = x:noQ xs       input' = if input == "NULL" then "" else input       regex' = if regex == "SAME" then oldRegex else regex   in (regex',(read n,regex',input',noQ output))@@ -107,22 +109,26 @@                 p ("Actual result  : "++show output')                 return (if n<0 then [] else [n]) -checkFile :: (RType -> RSource -> Result A) -> (String, String) -> IO (String,[Int])+checkFile :: (RType -> RSource -> Result A) -> (FilePath, String) -> IO (FilePath,[Int]) checkFile opM (filepath, contents) = do   putStrLn $ "\nUsing Tests from: "++filepath   vals <- liftM concat (mapM (checkTest opM) (load' contents))   return (filepath,vals) -checkTests :: (RType -> RSource -> Result A) -> IO [(String, [Int])]-checkTests opM = mapM (checkFile opM) testCases+checkTests :: (RType -> RSource -> Result A) -> [(FilePath,String)] -> IO [(String, [Int])]+checkTests opM testCases = mapM (checkFile opM) testCases -testCases :: [(String, String)]-testCases =-  map (\(filename, contents) -> (filename, UTF8.toString contents)) $-  $(embedDir =<< makeRelativeToProject "test/cases")+readTestCases :: FilePath -> IO [(String, String)]+readTestCases folder = do+  fns <- filter (".txt" `isInfixOf`) <$> getDirectoryContents folder+  when (null fns) $+    fail ("readTestCases: No test-cases found in " ++ show folder)+  forM (sort fns) $ \fn -> do+    bs <- BS.readFile (folder </> fn)+    return (fn, UTF8.toString bs)  newtype Result a = Result (Either String a)-  deriving (Eq, Show, Functor, Applicative, Monad)+  deriving (Eq, Show, Functor, App.Applicative, Monad)  instance Fail.MonadFail Result where   fail = Result . Left@@ -137,8 +143,8 @@  unN :: String -> String unN ('\\':'n':xs) = '\n':unN xs-unN (x:xs) = x:unN xs-unN [] = []+unN (x:xs)        = x:unN xs+unN []            = []  manual :: [String] -> IO () manual [sIn,rIn] = do@@ -172,13 +178,13 @@       putStrLn $ "Explanation and discussion of these tests on the wiki at http://www.haskell.org/haskellwiki/Regex_Posix including comparing results from different operating systems"       putStrLn $ "Questions about this package to the author at email <TextRegexLazy@personal.mightyreason.com>"       putStrLn $ "The type of both the pattern and test is " ++ show (typeOf (undefined :: RType))-      putStrLn $ "Without extactly two arguments:"+      putStrLn $ "Without exactly two arguments:"       putStrLn $ "    This program runs all test files listed in test/data-dir/test-manifest.txt"       putStrLn $ "    Lines with negative number are expected to fail, others are expected to pass."       putStrLn $ "With exactly two arguments:"       putStrLn $ "    The first argument is the text to be searched."       putStrLn $ "    The second argument is the regular expression pattern to search with."-      vals <- checkTests posix+      vals <- checkTests posix =<< readTestCases ("test" </> "cases")       if null (concatMap snd vals)         then putStrLn "\nWow, all the tests passed!"         else do
test/cases/basic3.txt view
@@ -140,3 +140,6 @@    144		.*(/000).*			/000			(0,4)(0,4)    145		.*(\\000).*			\000			(0,4)(0,4)    146		\\000				\000			(0,4)+   147		[[:graph:]]+		!#}~		(0,4)+   148		[[:xdigit:]]+		dE4db3eF		(0,8)+   149		[[:print:]]+		!#}~		(0,4)