packages feed

regex-tdfa 1.3.1.3 → 1.3.1.4

raw patch · 8 files changed

+222/−95 lines, 8 filesdep +doctest-paralleldep ~base

Dependencies added: doctest-parallel

Dependency ranges changed: base

Files

CHANGELOG.md view
@@ -1,11 +1,22 @@ For the package version policy (PVP), see  http://pvp.haskell.org/faq . +### 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:]]`)+- 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 
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
@@ -4,7 +4,7 @@  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
lib/Text/Regex/TDFA.hs view
@@ -32,10 +32,19 @@ = 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\> @@ -61,11 +70,12 @@ /-- 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@@ -73,8 +83,9 @@ @ a '=~' b :: Bool -λ> "alexis-de-tocqueville" '=~' "[a-z]+" :: Bool->>> True+>>> "alexis-de-tocqueville" =~ "[a-z]+" :: Bool+True+ @  == Get first match + text before/after@@ -84,11 +95,12 @@ /-- 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@@ -98,8 +110,9 @@ /-- 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@@ -108,8 +121,9 @@ /-- 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"]+ @  = Feature support@@ -143,6 +157,12 @@ 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. @@ -165,7 +185,7 @@ import Text.Regex.TDFA  λ> "2 * (3 + 1) / 4" '=~' [r|\\([^)]+\\)|] :: String->>> "(3 + 1)"+"(3 + 1)" @  -}
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(..))@@ -31,14 +33,14 @@ 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,19 +83,26 @@ -- | 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 {@@ -102,28 +110,31 @@   } deriving (Read,Show)  -- | Used by implementation to name certain Postions during--- matching. Identity of Position tag to set during a transition+-- 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 Positions. 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@@ -227,26 +247,25 @@                    }  -- | Internal type to represent the commands for the tagged transition.--- The outer IntMap is for the destination Index and the inner IntMap+-- 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 original code is being written XXX TODO document it. data Orbits = Orbits   { inOrbit :: !Bool        -- True if enterOrbit, False if LeaveOrbit   , basePos :: Position
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,37 +38,46 @@                              (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 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                <|> 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@@ -76,6 +93,7 @@                        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 "()"@@ -83,11 +101,13 @@                    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` '{'))@@ -96,16 +116,18 @@     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 :: Bool -> P Pattern p_set invert = do initial <- (option "" ((char ']' >> return "]") <|> (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 :: 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 :: 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
regex-tdfa.cabal view
@@ -1,6 +1,6 @@ cabal-version:          1.12 name:                   regex-tdfa-version:                1.3.1.3+version:                1.3.1.4  build-Type:             Simple license:                BSD3@@ -46,7 +46,7 @@ source-repository this   type:                git   location:            https://github.com/haskell-hvr/regex-tdfa.git-  tag:                 v1.3.1.3+  tag:                 v1.3.1.4  flag force-O2   default: False@@ -173,3 +173,16 @@    if flag(force-O2)     ghc-options:        -O2++test-suite doc-test+  type:           exitcode-stdio-1.0+  hs-source-dirs: test+  main-is:        DocTestMain.hs++  build-depends:+      base+    , regex-tdfa+    , doctest-parallel >= 0.2.2+        -- doctest-parallel-0.2.2 is the first to filter out autogen-modules++  default-language:     Haskell2010
+ 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