diff --git a/.travis.yml b/.travis.yml index 2f0fa04..aa3ade9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,6 +40,12 @@ matrix: # For the Stack builds we can pass in arbitrary Stack arguments via the ARGS # variable, such as using --stack-yaml to point to a different file. + # Linux/stack/hackage release + + - env: BUILD=release-stack GHCVER=8.0.1 STACK_YAML=stack-8.0.yaml + compiler: ": #stack 8.0.1" + addons: {apt: {packages: [libgmp-dev]}} + # Linux/stack - env: BUILD=stack GHCVER=7.10.3 STACK_YAML=stack-7.10.yaml @@ -121,7 +127,7 @@ install: - | set -ex case "$BUILD" in - stack) + release-stack|stack) stack --no-terminal --install-ghc $ARGS test --coverage --bench --only-dependencies ;; cabal) @@ -140,6 +146,30 @@ script: - | set -ex case "$BUILD" in + release-stack) + lpn=regex-$(cat lib/version.txt) + epn=regex-examples-$(cat lib/version.txt) + ltb=releases/${lpn}.tar.gz + etb=releases/${epn}.tar.gz + if [ -f ${ltb} ]; then + echo "installing ${lpn}" + tar xzf ${ltb} + cp ${STACK_YAML} ${lpn} + cd ${lpn} + stack --no-terminal $ARGS install --bench --coverage --no-run-benchmarks --haddock --no-haddock-deps + echo "testing ${epn}" + cd .. + tar xzf ${etb} + sed -e "s/extra-deps: \\[\\]/extra-deps: [${lpn}]/" ${STACK_YAML} > ${epn}/${STACK_YAML} + cd ${epn} + stack --no-terminal $ARGS test --bench --coverage --no-run-benchmarks --haddock --no-haddock-deps + cd .. + else + echo "***" + echo "*** no Hackage release to test" + echo "***" + fi + ;; stack) stack --no-terminal $ARGS test --bench --coverage --no-run-benchmarks --haddock --no-haddock-deps ;; diff --git a/README.md b/README.md index df6f13e..9dd117d 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ two packages: - [X] 2017-02-21 v0.2.0.3 Tweak README/index layout - [X] 2017-02-22 v0.2.0.4 [Repair re-gen-modules-test for Windows](https://github.com/iconnect/regex/issues/47) - [X] 2017-02-26 v0.3.0.0 [API adjustments](https://github.com/iconnect/regex/milestone/2) -- [ ] 2017-03-05 v0.5.0.0 [Ready for review: tutorials and examples finalized](https://github.com/iconnect/regex/milestone/6) +- [X] 2017-03-05 v0.5.0.0 [Ready for review: API, tutorials and examples finalized](https://github.com/iconnect/regex/milestone/6) - [ ] 2017-03-20 v1.0.0.0 [First stable release](https://github.com/iconnect/regex/milestone/3) - [ ] 2017-08-31 v2.0.0.0 [Fast text replacement with benchmarks](https://github.com/iconnect/regex/milestone/4) diff --git a/Text/RE/Capture.lhs b/Text/RE/Capture.lhs index 04af6fe..6ba44ae 100644 --- a/Text/RE/Capture.lhs +++ b/Text/RE/Capture.lhs @@ -3,6 +3,7 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE DeriveDataTypeable #-} \end{code} \begin{code} @@ -40,6 +41,7 @@ module Text.RE.Capture \begin{code} import Data.Array import Data.Maybe +import Data.Typeable import Text.Regex.Base import Text.RE.CaptureID @@ -56,7 +58,7 @@ data Matches a = { matchesSource :: !a -- ^ the source text being matched , allMatches :: ![Match a] -- ^ all captures found, left to right } - deriving (Show,Eq) + deriving (Show,Eq,Typeable) \end{code} \begin{code} @@ -74,7 +76,7 @@ data Match a = -- text matched by the -- whole RE } - deriving (Show,Eq) + deriving (Show,Eq,Typeable) \end{code} \begin{code} diff --git a/Text/RE/Internal/AddCaptureNames.hs b/Text/RE/Internal/AddCaptureNames.hs index d8c83a6..f5c3fc8 100644 --- a/Text/RE/Internal/AddCaptureNames.hs +++ b/Text/RE/Internal/AddCaptureNames.hs @@ -1,11 +1,69 @@ +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE OverloadedStrings #-} + module Text.RE.Internal.AddCaptureNames where +import qualified Data.ByteString.Lazy.Char8 as LBS +import qualified Data.ByteString.Char8 as B +import Data.Dynamic +import Data.Maybe +import qualified Data.Sequence as S +import qualified Data.Text as T +import qualified Data.Text.Lazy as TL +import Prelude.Compat import Text.RE +import Unsafe.Coerce +-- | a convenience function used by the API modules to insert +-- capture names extracted from the parsed RE into the (*=~) result addCaptureNamesToMatches :: CaptureNames -> Matches a -> Matches a addCaptureNamesToMatches cnms mtchs = mtchs { allMatches = map (addCaptureNamesToMatch cnms) $ allMatches mtchs } +-- | a convenience function used by the API modules to insert +-- capture names extracted from the parsed RE into the (?=~) result addCaptureNamesToMatch :: CaptureNames -> Match a -> Match a addCaptureNamesToMatch cnms mtch = mtch { captureNames = cnms } + +-- | a hairy dynamically-typed function used with the legacy (=~) and (=~~) +-- to see if it can/should add the capture names extracted from the RE +-- into the polymorphic result of the operator (it does for any Match +-- or Matches type, provided it is parameterised over a recognised type). +-- The test suite is all over this one, testing all of these cases. +addCaptureNames :: Typeable a => CaptureNames -> a -> a +addCaptureNames cnms x = fromMaybe x $ listToMaybe $ catMaybes + [ test_match x ( proxy :: String ) + , test_matches x ( proxy :: String ) + , test_match x ( proxy :: B.ByteString ) + , test_matches x ( proxy :: B.ByteString ) + , test_match x ( proxy :: LBS.ByteString ) + , test_matches x ( proxy :: LBS.ByteString ) + , test_match x ( proxy :: T.Text ) + , test_matches x ( proxy :: T.Text ) + , test_match x ( proxy :: TL.Text ) + , test_matches x ( proxy :: TL.Text ) + , test_match x ( proxy :: S.Seq Char ) + , test_matches x ( proxy :: S.Seq Char ) + ] + where + test_match :: Typeable t => r -> t -> Maybe r + test_match r t = f r t $ addCaptureNamesToMatch cnms <$> fromDynamic dyn + where + f :: r' -> t' -> Maybe (Match t') -> Maybe r' + f _ _ = unsafeCoerce + + test_matches :: Typeable t => r -> t -> Maybe r + test_matches r t = f r t $ addCaptureNamesToMatches cnms <$> fromDynamic dyn + where + f :: r' -> t' -> Maybe (Matches t') -> Maybe r' + f _ _ = unsafeCoerce + + dyn :: Dynamic + dyn = toDyn x + + proxy :: a + proxy = error "addCaptureNames" diff --git a/Text/RE/PCRE/ByteString.hs b/Text/RE/PCRE/ByteString.hs index 0860ab5..aec0156 100644 --- a/Text/RE/PCRE/ByteString.hs +++ b/Text/RE/PCRE/ByteString.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} @@ -25,7 +26,9 @@ module Text.RE.PCRE.ByteString , module Text.RE.PCRE.RE ) where +import Prelude.Compat import qualified Data.ByteString as B +import Data.Typeable import Text.Regex.Base import Text.RE import Text.RE.Internal.AddCaptureNames @@ -46,23 +49,26 @@ import qualified Text.Regex.PCRE as PCRE (?=~) bs rex = addCaptureNamesToMatch (reCaptureNames rex) $ match (reRegex rex) bs -- | the regex-base polymorphic match operator -(=~) :: ( RegexContext PCRE.Regex B.ByteString a +(=~) :: ( Typeable a + , RegexContext PCRE.Regex B.ByteString a , RegexMaker PCRE.Regex PCRE.CompOption PCRE.ExecOption String ) => B.ByteString -> RE -> a -(=~) bs rex = match (reRegex rex) bs +(=~) bs rex = addCaptureNames (reCaptureNames rex) $ match (reRegex rex) bs -- | the regex-base monadic, polymorphic match operator (=~~) :: ( Monad m + , Functor m + , Typeable a , RegexContext PCRE.Regex B.ByteString a , RegexMaker PCRE.Regex PCRE.CompOption PCRE.ExecOption String ) => B.ByteString -> RE -> m a -(=~~) bs rex = matchM (reRegex rex) bs +(=~~) bs rex = addCaptureNames (reCaptureNames rex) <$> matchM (reRegex rex) bs instance IsRegex RE B.ByteString where matchOnce = flip (?=~) diff --git a/Text/RE/PCRE/ByteString/Lazy.hs b/Text/RE/PCRE/ByteString/Lazy.hs index ea1d26e..60e7539 100644 --- a/Text/RE/PCRE/ByteString/Lazy.hs +++ b/Text/RE/PCRE/ByteString/Lazy.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} @@ -25,7 +26,9 @@ module Text.RE.PCRE.ByteString.Lazy , module Text.RE.PCRE.RE ) where +import Prelude.Compat import qualified Data.ByteString.Lazy as LBS +import Data.Typeable import Text.Regex.Base import Text.RE import Text.RE.Internal.AddCaptureNames @@ -46,23 +49,26 @@ import qualified Text.Regex.PCRE as PCRE (?=~) bs rex = addCaptureNamesToMatch (reCaptureNames rex) $ match (reRegex rex) bs -- | the regex-base polymorphic match operator -(=~) :: ( RegexContext PCRE.Regex LBS.ByteString a +(=~) :: ( Typeable a + , RegexContext PCRE.Regex LBS.ByteString a , RegexMaker PCRE.Regex PCRE.CompOption PCRE.ExecOption String ) => LBS.ByteString -> RE -> a -(=~) bs rex = match (reRegex rex) bs +(=~) bs rex = addCaptureNames (reCaptureNames rex) $ match (reRegex rex) bs -- | the regex-base monadic, polymorphic match operator (=~~) :: ( Monad m + , Functor m + , Typeable a , RegexContext PCRE.Regex LBS.ByteString a , RegexMaker PCRE.Regex PCRE.CompOption PCRE.ExecOption String ) => LBS.ByteString -> RE -> m a -(=~~) bs rex = matchM (reRegex rex) bs +(=~~) bs rex = addCaptureNames (reCaptureNames rex) <$> matchM (reRegex rex) bs instance IsRegex RE LBS.ByteString where matchOnce = flip (?=~) diff --git a/Text/RE/PCRE/Sequence.hs b/Text/RE/PCRE/Sequence.hs index 63140e3..b79032d 100644 --- a/Text/RE/PCRE/Sequence.hs +++ b/Text/RE/PCRE/Sequence.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} @@ -25,7 +26,9 @@ module Text.RE.PCRE.Sequence , module Text.RE.PCRE.RE ) where +import Prelude.Compat import qualified Data.Sequence as S +import Data.Typeable import Text.Regex.Base import Text.RE import Text.RE.Internal.AddCaptureNames @@ -46,23 +49,26 @@ import qualified Text.Regex.PCRE as PCRE (?=~) bs rex = addCaptureNamesToMatch (reCaptureNames rex) $ match (reRegex rex) bs -- | the regex-base polymorphic match operator -(=~) :: ( RegexContext PCRE.Regex (S.Seq Char) a +(=~) :: ( Typeable a + , RegexContext PCRE.Regex (S.Seq Char) a , RegexMaker PCRE.Regex PCRE.CompOption PCRE.ExecOption String ) => (S.Seq Char) -> RE -> a -(=~) bs rex = match (reRegex rex) bs +(=~) bs rex = addCaptureNames (reCaptureNames rex) $ match (reRegex rex) bs -- | the regex-base monadic, polymorphic match operator (=~~) :: ( Monad m + , Functor m + , Typeable a , RegexContext PCRE.Regex (S.Seq Char) a , RegexMaker PCRE.Regex PCRE.CompOption PCRE.ExecOption String ) => (S.Seq Char) -> RE -> m a -(=~~) bs rex = matchM (reRegex rex) bs +(=~~) bs rex = addCaptureNames (reCaptureNames rex) <$> matchM (reRegex rex) bs instance IsRegex RE (S.Seq Char) where matchOnce = flip (?=~) diff --git a/Text/RE/PCRE/String.hs b/Text/RE/PCRE/String.hs index d6bf51d..f874237 100644 --- a/Text/RE/PCRE/String.hs +++ b/Text/RE/PCRE/String.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} @@ -25,7 +26,9 @@ module Text.RE.PCRE.String , module Text.RE.PCRE.RE ) where +import Prelude.Compat +import Data.Typeable import Text.Regex.Base import Text.RE import Text.RE.Internal.AddCaptureNames @@ -46,23 +49,26 @@ import qualified Text.Regex.PCRE as PCRE (?=~) bs rex = addCaptureNamesToMatch (reCaptureNames rex) $ match (reRegex rex) bs -- | the regex-base polymorphic match operator -(=~) :: ( RegexContext PCRE.Regex String a +(=~) :: ( Typeable a + , RegexContext PCRE.Regex String a , RegexMaker PCRE.Regex PCRE.CompOption PCRE.ExecOption String ) => String -> RE -> a -(=~) bs rex = match (reRegex rex) bs +(=~) bs rex = addCaptureNames (reCaptureNames rex) $ match (reRegex rex) bs -- | the regex-base monadic, polymorphic match operator (=~~) :: ( Monad m + , Functor m + , Typeable a , RegexContext PCRE.Regex String a , RegexMaker PCRE.Regex PCRE.CompOption PCRE.ExecOption String ) => String -> RE -> m a -(=~~) bs rex = matchM (reRegex rex) bs +(=~~) bs rex = addCaptureNames (reCaptureNames rex) <$> matchM (reRegex rex) bs instance IsRegex RE String where matchOnce = flip (?=~) diff --git a/Text/RE/Replace.lhs b/Text/RE/Replace.lhs index 7bbf7c2..1a81ff6 100644 --- a/Text/RE/Replace.lhs +++ b/Text/RE/Replace.lhs @@ -44,6 +44,7 @@ import Text.Heredoc import Text.RE.Capture import Text.RE.CaptureID import Text.RE.Options +import Text.Read import Text.Regex.TDFA import Text.Regex.TDFA.Text() import Text.Regex.TDFA.Text.Lazy() @@ -402,29 +403,31 @@ lift_phi phi_ = phi \begin{code} parseTemplateE' :: ( Replace a - , RegexContext Regex a (Matches a) - , RegexMaker Regex CompOption ExecOption String - ) - => (a->String) - -> a - -> Match a - -> Location - -> Capture a - -> Maybe a + , RegexContext Regex a (Matches a) + , RegexMaker Regex CompOption ExecOption String + ) + => (a->String) + -> a + -> Match a + -> Location + -> Capture a + -> Maybe a parseTemplateE' unpack tpl mtch _ _ = Just $ replaceAllCaptures TOP phi $ - tpl $=~ [here|\$(\$|[0-9]+|\{([^{}]+)\})|] + tpl $=~ [here|\$(\$|[0-9]|\{([^{}]+)\})|] where - phi t_mtch _ _ = case t_mtch !$? c2 of - Just cap -> this $ IsCaptureName $ CaptureName txt + phi t_mtch _ _ = case t_mtch !$? c2 of + Just cap -> case readMaybe stg of + Nothing -> this $ IsCaptureName $ CaptureName $ T.pack stg + Just cn -> this $ IsCaptureOrdinal $ CaptureOrdinal cn where - txt = T.pack $ unpack $ capturedText cap + stg = unpack $ capturedText cap Nothing -> case s == "$" of True -> Just t False -> this $ IsCaptureOrdinal $ CaptureOrdinal $ read s where - s = unpack t - t = capturedText $ capture c1 t_mtch + s = unpack t + t = capturedText $ capture c1 t_mtch this cid = capturedText <$> mtch !$? cid diff --git a/Text/RE/TDFA/ByteString.hs b/Text/RE/TDFA/ByteString.hs index 9ea2a3a..cdadd69 100644 --- a/Text/RE/TDFA/ByteString.hs +++ b/Text/RE/TDFA/ByteString.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} @@ -25,7 +26,9 @@ module Text.RE.TDFA.ByteString , module Text.RE.TDFA.RE ) where +import Prelude.Compat import qualified Data.ByteString as B +import Data.Typeable import Text.Regex.Base import Text.RE import Text.RE.Internal.AddCaptureNames @@ -46,23 +49,26 @@ import qualified Text.Regex.TDFA as TDFA (?=~) bs rex = addCaptureNamesToMatch (reCaptureNames rex) $ match (reRegex rex) bs -- | the regex-base polymorphic match operator -(=~) :: ( RegexContext TDFA.Regex B.ByteString a +(=~) :: ( Typeable a + , RegexContext TDFA.Regex B.ByteString a , RegexMaker TDFA.Regex TDFA.CompOption TDFA.ExecOption String ) => B.ByteString -> RE -> a -(=~) bs rex = match (reRegex rex) bs +(=~) bs rex = addCaptureNames (reCaptureNames rex) $ match (reRegex rex) bs -- | the regex-base monadic, polymorphic match operator (=~~) :: ( Monad m + , Functor m + , Typeable a , RegexContext TDFA.Regex B.ByteString a , RegexMaker TDFA.Regex TDFA.CompOption TDFA.ExecOption String ) => B.ByteString -> RE -> m a -(=~~) bs rex = matchM (reRegex rex) bs +(=~~) bs rex = addCaptureNames (reCaptureNames rex) <$> matchM (reRegex rex) bs instance IsRegex RE B.ByteString where matchOnce = flip (?=~) diff --git a/Text/RE/TDFA/ByteString/Lazy.hs b/Text/RE/TDFA/ByteString/Lazy.hs index f36e351..fcbb85b 100644 --- a/Text/RE/TDFA/ByteString/Lazy.hs +++ b/Text/RE/TDFA/ByteString/Lazy.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} @@ -25,7 +26,9 @@ module Text.RE.TDFA.ByteString.Lazy , module Text.RE.TDFA.RE ) where +import Prelude.Compat import qualified Data.ByteString.Lazy.Char8 as LBS +import Data.Typeable import Text.Regex.Base import Text.RE import Text.RE.Internal.AddCaptureNames @@ -46,23 +49,26 @@ import qualified Text.Regex.TDFA as TDFA (?=~) bs rex = addCaptureNamesToMatch (reCaptureNames rex) $ match (reRegex rex) bs -- | the regex-base polymorphic match operator -(=~) :: ( RegexContext TDFA.Regex LBS.ByteString a +(=~) :: ( Typeable a + , RegexContext TDFA.Regex LBS.ByteString a , RegexMaker TDFA.Regex TDFA.CompOption TDFA.ExecOption String ) => LBS.ByteString -> RE -> a -(=~) bs rex = match (reRegex rex) bs +(=~) bs rex = addCaptureNames (reCaptureNames rex) $ match (reRegex rex) bs -- | the regex-base monadic, polymorphic match operator (=~~) :: ( Monad m + , Functor m + , Typeable a , RegexContext TDFA.Regex LBS.ByteString a , RegexMaker TDFA.Regex TDFA.CompOption TDFA.ExecOption String ) => LBS.ByteString -> RE -> m a -(=~~) bs rex = matchM (reRegex rex) bs +(=~~) bs rex = addCaptureNames (reCaptureNames rex) <$> matchM (reRegex rex) bs instance IsRegex RE LBS.ByteString where matchOnce = flip (?=~) diff --git a/Text/RE/TDFA/Sequence.hs b/Text/RE/TDFA/Sequence.hs index 2ca0f9d..151b3fc 100644 --- a/Text/RE/TDFA/Sequence.hs +++ b/Text/RE/TDFA/Sequence.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} @@ -25,7 +26,9 @@ module Text.RE.TDFA.Sequence , module Text.RE.TDFA.RE ) where +import Prelude.Compat import qualified Data.Sequence as S +import Data.Typeable import Text.Regex.Base import Text.RE import Text.RE.Internal.AddCaptureNames @@ -46,23 +49,26 @@ import qualified Text.Regex.TDFA as TDFA (?=~) bs rex = addCaptureNamesToMatch (reCaptureNames rex) $ match (reRegex rex) bs -- | the regex-base polymorphic match operator -(=~) :: ( RegexContext TDFA.Regex (S.Seq Char) a +(=~) :: ( Typeable a + , RegexContext TDFA.Regex (S.Seq Char) a , RegexMaker TDFA.Regex TDFA.CompOption TDFA.ExecOption String ) => (S.Seq Char) -> RE -> a -(=~) bs rex = match (reRegex rex) bs +(=~) bs rex = addCaptureNames (reCaptureNames rex) $ match (reRegex rex) bs -- | the regex-base monadic, polymorphic match operator (=~~) :: ( Monad m + , Functor m + , Typeable a , RegexContext TDFA.Regex (S.Seq Char) a , RegexMaker TDFA.Regex TDFA.CompOption TDFA.ExecOption String ) => (S.Seq Char) -> RE -> m a -(=~~) bs rex = matchM (reRegex rex) bs +(=~~) bs rex = addCaptureNames (reCaptureNames rex) <$> matchM (reRegex rex) bs instance IsRegex RE (S.Seq Char) where matchOnce = flip (?=~) diff --git a/Text/RE/TDFA/String.hs b/Text/RE/TDFA/String.hs index 26884a5..41a12ec 100644 --- a/Text/RE/TDFA/String.hs +++ b/Text/RE/TDFA/String.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} @@ -25,7 +26,9 @@ module Text.RE.TDFA.String , module Text.RE.TDFA.RE ) where +import Prelude.Compat +import Data.Typeable import Text.Regex.Base import Text.RE import Text.RE.Internal.AddCaptureNames @@ -46,23 +49,26 @@ import qualified Text.Regex.TDFA as TDFA (?=~) bs rex = addCaptureNamesToMatch (reCaptureNames rex) $ match (reRegex rex) bs -- | the regex-base polymorphic match operator -(=~) :: ( RegexContext TDFA.Regex String a +(=~) :: ( Typeable a + , RegexContext TDFA.Regex String a , RegexMaker TDFA.Regex TDFA.CompOption TDFA.ExecOption String ) => String -> RE -> a -(=~) bs rex = match (reRegex rex) bs +(=~) bs rex = addCaptureNames (reCaptureNames rex) $ match (reRegex rex) bs -- | the regex-base monadic, polymorphic match operator (=~~) :: ( Monad m + , Functor m + , Typeable a , RegexContext TDFA.Regex String a , RegexMaker TDFA.Regex TDFA.CompOption TDFA.ExecOption String ) => String -> RE -> m a -(=~~) bs rex = matchM (reRegex rex) bs +(=~~) bs rex = addCaptureNames (reCaptureNames rex) <$> matchM (reRegex rex) bs instance IsRegex RE String where matchOnce = flip (?=~) diff --git a/Text/RE/TDFA/Text.hs b/Text/RE/TDFA/Text.hs index 79cccce..4358802 100644 --- a/Text/RE/TDFA/Text.hs +++ b/Text/RE/TDFA/Text.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} @@ -25,7 +26,9 @@ module Text.RE.TDFA.Text , module Text.RE.TDFA.RE ) where +import Prelude.Compat import qualified Data.Text as T +import Data.Typeable import Text.Regex.Base import Text.RE import Text.RE.Internal.AddCaptureNames @@ -46,23 +49,26 @@ import qualified Text.Regex.TDFA as TDFA (?=~) bs rex = addCaptureNamesToMatch (reCaptureNames rex) $ match (reRegex rex) bs -- | the regex-base polymorphic match operator -(=~) :: ( RegexContext TDFA.Regex T.Text a +(=~) :: ( Typeable a + , RegexContext TDFA.Regex T.Text a , RegexMaker TDFA.Regex TDFA.CompOption TDFA.ExecOption String ) => T.Text -> RE -> a -(=~) bs rex = match (reRegex rex) bs +(=~) bs rex = addCaptureNames (reCaptureNames rex) $ match (reRegex rex) bs -- | the regex-base monadic, polymorphic match operator (=~~) :: ( Monad m + , Functor m + , Typeable a , RegexContext TDFA.Regex T.Text a , RegexMaker TDFA.Regex TDFA.CompOption TDFA.ExecOption String ) => T.Text -> RE -> m a -(=~~) bs rex = matchM (reRegex rex) bs +(=~~) bs rex = addCaptureNames (reCaptureNames rex) <$> matchM (reRegex rex) bs instance IsRegex RE T.Text where matchOnce = flip (?=~) diff --git a/Text/RE/TDFA/Text/Lazy.hs b/Text/RE/TDFA/Text/Lazy.hs index 806036a..b42467e 100644 --- a/Text/RE/TDFA/Text/Lazy.hs +++ b/Text/RE/TDFA/Text/Lazy.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} @@ -25,7 +26,9 @@ module Text.RE.TDFA.Text.Lazy , module Text.RE.TDFA.RE ) where +import Prelude.Compat import qualified Data.Text.Lazy as TL +import Data.Typeable import Text.Regex.Base import Text.RE import Text.RE.Internal.AddCaptureNames @@ -46,23 +49,26 @@ import qualified Text.Regex.TDFA as TDFA (?=~) bs rex = addCaptureNamesToMatch (reCaptureNames rex) $ match (reRegex rex) bs -- | the regex-base polymorphic match operator -(=~) :: ( RegexContext TDFA.Regex TL.Text a +(=~) :: ( Typeable a + , RegexContext TDFA.Regex TL.Text a , RegexMaker TDFA.Regex TDFA.CompOption TDFA.ExecOption String ) => TL.Text -> RE -> a -(=~) bs rex = match (reRegex rex) bs +(=~) bs rex = addCaptureNames (reCaptureNames rex) $ match (reRegex rex) bs -- | the regex-base monadic, polymorphic match operator (=~~) :: ( Monad m + , Functor m + , Typeable a , RegexContext TDFA.Regex TL.Text a , RegexMaker TDFA.Regex TDFA.CompOption TDFA.ExecOption String ) => TL.Text -> RE -> m a -(=~~) bs rex = matchM (reRegex rex) bs +(=~~) bs rex = addCaptureNames (reCaptureNames rex) <$> matchM (reRegex rex) bs instance IsRegex RE TL.Text where matchOnce = flip (?=~) diff --git a/changelog b/changelog index 29b1952..c666d55 100644 --- a/changelog +++ b/changelog @@ -1,5 +1,14 @@ -*-change-log-*- +0.5.0.0 Chris Dornan 2017-03-05 + * Fix inter-operation of =~ & =~~ and named captures (#55) + * Add escaping functions (#37) + * Test Hackage release tarballs on Travis CI (#51) + * Fix up template replace ordinals (#52) + * Complete the web site (#39) + * Complete the Tutorial, Tests and Examples (#38) + * Complete narrative in literate modules (#8) + 0.3.0.0 Chris Dornan 2017-02-26 * Clean up API to use camelCase conventions * Use -Werror in development and testing, -Warn for Hackage diff --git a/docs/Capture.html b/docs/Capture.html index c60d340..d7e21d9 100644 --- a/docs/Capture.html +++ b/docs/Capture.html @@ -69,7 +69,8 @@

Text.RE.Capture

{-# LANGUAGE RecordWildCards            #-}
 {-# LANGUAGE FlexibleInstances          #-}
 {-# LANGUAGE UndecidableInstances       #-}
-{-# LANGUAGE MultiParamTypeClasses      #-}
+{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE DeriveDataTypeable #-}
module Text.RE.Capture
   ( Matches(..)
   , Match(..)
@@ -101,6 +102,7 @@ 

Text.RE.Capture

) where
import           Data.Array
 import           Data.Maybe
+import           Data.Typeable
 import           Text.Regex.Base
 import           Text.RE.CaptureID
 
@@ -112,7 +114,7 @@ 

Text.RE.Capture

{ matchesSource :: !a -- ^ the source text being matched , allMatches :: ![Match a] -- ^ all captures found, left to right } - deriving (Show,Eq)
+ deriving (Show,Eq,Typeable)
-- | the result of matching a RE to a text once, listing the text that
 -- was matched and the named captures in the RE and all of the substrings
 -- matched, with the text captured by the whole RE; a complete failure
@@ -127,7 +129,7 @@ 

Text.RE.Capture

-- text matched by the -- whole RE } - deriving (Show,Eq)
+ deriving (Show,Eq,Typeable)
-- | the matching of a single sub-expression against part of the source
 -- text
 data Capture a =
diff --git a/docs/Replace.html b/docs/Replace.html
index 32104fb..ba66f61 100644
--- a/docs/Replace.html
+++ b/docs/Replace.html
@@ -111,6 +111,7 @@ 

Text.RE.Replace

import Text.RE.Capture import Text.RE.CaptureID import Text.RE.Options +import Text.Read import Text.Regex.TDFA import Text.Regex.TDFA.Text() import Text.Regex.TDFA.Text.Lazy()
@@ -415,29 +416,31 @@

Text.RE.Replace

where phi caps' loc' cap' = return $ phi_ caps' loc' cap'
parseTemplateE' :: ( Replace a
-              , RegexContext Regex a (Matches a)
-              , RegexMaker   Regex CompOption ExecOption String
-              )
-           => (a->String)
-           -> a
-           -> Match a
-           -> Location
-           -> Capture a
-           -> Maybe a
+                   , RegexContext Regex a (Matches a)
+                   , RegexMaker   Regex CompOption ExecOption String
+                   )
+                   => (a->String)
+                   -> a
+                   -> Match a
+                   -> Location
+                   -> Capture a
+                   -> Maybe a
 parseTemplateE' unpack tpl mtch _ _ =
     Just $ replaceAllCaptures TOP phi $
-      tpl $=~ [here|\$(\$|[0-9]+|\{([^{}]+)\})|]
+      tpl $=~ [here|\$(\$|[0-9]|\{([^{}]+)\})|]
   where
-    phi t_mtch _ _ = case t_mtch !$? c2  of
-      Just cap -> this $ IsCaptureName $ CaptureName txt
+    phi t_mtch _ _ = case t_mtch !$? c2 of
+      Just cap -> case readMaybe stg of
+          Nothing -> this $ IsCaptureName    $ CaptureName $ T.pack stg
+          Just cn -> this $ IsCaptureOrdinal $ CaptureOrdinal cn
         where
-          txt = T.pack $ unpack $ capturedText cap
+          stg = unpack $ capturedText cap
       Nothing -> case s == "$" of
         True  -> Just t
         False -> this $ IsCaptureOrdinal $ CaptureOrdinal $ read s
       where
-        s  = unpack t
-        t  = capturedText $ capture c1 t_mtch
+        s = unpack t
+        t = capturedText $ capture c1 t_mtch
 
         this cid = capturedText <$> mtch !$? cid
 
diff --git a/docs/badges/hackage.svg b/docs/badges/hackage.svg
index fb91b09..77ee2d3 100644
--- a/docs/badges/hackage.svg
+++ b/docs/badges/hackage.svg
@@ -15,7 +15,7 @@
   
     hackage
     hackage
-    v0.3.0.0
-    v0.3.0.0
+    v0.5.0.0
+    v0.5.0.0
   
 
diff --git a/docs/changelog.html b/docs/changelog.html
index c4d9ce7..27429ce 100644
--- a/docs/changelog.html
+++ b/docs/changelog.html
@@ -126,6 +126,15 @@ 

-*-change-log-*-
 
+0.5.0.0 Chris Dornan <chris.dornan@irisconnect.co.uk> 2017-03-05
+  * Fix inter-operation of =~ & =~~ and named captures (#55)
+  * Add escaping functions (#37)
+  * Test Hackage release tarballs on Travis CI (#51)
+  * Fix up template replace ordinals (#52)
+  * Complete the web site (#39)
+  * Complete the Tutorial, Tests and Examples (#38)
+  * Complete narrative in literate modules (#8)
+
 0.3.0.0 Chris Dornan <chris.dornan@irisconnect.co.uk> 2017-02-26
   * Clean up API to use camelCase conventions
   * Use -Werror in development and testing, -Warn for Hackage
diff --git a/docs/examples.html b/docs/examples.html
index 0211107..4b271e1 100644
--- a/docs/examples.html
+++ b/docs/examples.html
@@ -95,11 +95,11 @@ 

The Test Suite

-

The Library Tests examples/re-tests.lhs contains various test suites for exercising various components of the library.

+

The Library Tests examples/re-tests.lhs contains a number of test suites for exercising various components of the library.

The NGINX Log Processor

-

The Log Processor Example examples/re-nginx-log-processor.lhs provides an extended example of large-scale RE development with the regex test bench.

+

The NGINX Log Processor Example examples/re-nginx-log-processor.lhs provides an extended example of large-scale RE development with the regex test bench.

The Regex Tools

diff --git a/docs/index.html b/docs/index.html index c0853d7..eee4cb9 100644 --- a/docs/index.html +++ b/docs/index.html @@ -150,7 +150,7 @@

2017-02-26 v0.3.0.0 API adjustments
  • -2017-03-05 v0.5.0.0 Ready for review: tutorials and examples finalized +2017-03-05 v0.5.0.0 Ready for review: API, tutorials and examples finalized
  • 2017-03-20 v1.0.0.0 First stable release diff --git a/docs/re-gen-cabals.html b/docs/re-gen-cabals.html index 5d931f6..2a6e01f 100644 --- a/docs/re-gen-cabals.html +++ b/docs/re-gen-cabals.html @@ -66,6 +66,9 @@

    examples/re-gen-cabals.lhs

    +

    Regex Cabal Gen

    +

    This tool generates the cabal files for the regex and regex-examples packages as well as the cabal file for the development tree (contaiing the combined targets of both packages). In addition it contains scripts for bumping the version number and generating the Hackage releases.

    +

    The tool is self-testing: run it with no arguments (or cabal test).

    {-# LANGUAGE NoImplicitPrelude          #-}
     {-# LANGUAGE RecordWildCards            #-}
     {-# LANGUAGE TemplateHaskell            #-}
    @@ -93,22 +96,32 @@ 

    examples/re-gen-cabals.lhs

    import TestKit import Text.Printf import Text.RE.TDFA.ByteString.Lazy +import Text.RE.TDFA.Text as T main :: IO () main = do (pn,as) <- (,) <$> getProgName <*> getArgs case as of - [] -> test - ["test"] -> test - ["sdist"] -> sdist - ["gen"] -> do + [] -> test + ["test"] -> test + ["bump-version",vrn] -> bumpVersion vrn + ["sdist"] -> sdist + ["gen"] -> do gen "lib/cabal-masters/mega-regex.cabal" "lib/mega-regex.cabal" gen "lib/cabal-masters/regex.cabal" "lib/regex.cabal" gen "lib/cabal-masters/regex-examples.cabal" "lib/regex-examples.cabal" establish "mega-regex" "regex" - _ -> do - hPutStrLn stderr $ "usage: " ++ pn ++ " [test|sdist|gen]" + _ -> do + let prg = ((" "++pn++" ")++) + hPutStr stderr $ unlines + [ "usage:" + , prg "--help" + , prg "[test]" + , prg "bump-version <version>" + , prg "sdist" + , prg "gen" + ] exitWith $ ExitFailure 1 test :: IO () @@ -288,7 +301,9 @@

    examples/re-gen-cabals.lhs

    establish "mega-regex" "regex" vrn_t <- T.pack . presentVrn <$> readCurrentVersion smy_t <- summary - SH.shelly $ SH.verbosely $ + SH.shelly $ SH.verbosely $ do + SH.run_ "git" ["add","--all"] + SH.run_ "git" ["commit","-m",vrn_t<>": "<>smy_t] SH.run_ "git" ["tag",vrn_t,"-m",smy_t] sdist' :: T.Text -> IO () @@ -296,14 +311,16 @@

    examples/re-gen-cabals.lhs

    establish nm nm SH.shelly $ SH.verbosely $ do SH.cp readme "README.markdown" - SH.run_ "cabal" ["clean"] - SH.run_ "cabal" ["configure"] - SH.run_ "cabal" ["sdist"] - vrn <- SH.liftIO readCurrentVersion - let tb = nm<>"-"<>T.pack(presentVrn vrn)<>".tar.gz" - SH.cp (SH.fromText $ "dist/"<>tb) $ SH.fromText $ "releases/"<>tb + SH.run_ "stack" ["sdist","--stack-yaml","stack-8.0.yaml"] + (pth,tb) <- analyse_so <$> SH.lastStderr + SH.cp (SH.fromText $ pth) $ SH.fromText $ "releases/"<>tb where - readme = SH.fromText $ "lib/README-"<>nm<>".md" + readme = SH.fromText $ "lib/README-"<>nm<>".md" + + analyse_so so = (mtch!$$[cp|pth|],mtch!$$[cp|tb|]) + where + mtch = so T.?=~ + [re|^.*Wrote sdist tarball to ${pth}(.*${tb}(regex-.*\.tar\.gz))$|] establish :: T.Text -> T.Text -> IO () establish nm nm' = SH.shelly $ SH.verbosely $ do diff --git a/docs/re-gen-modules.html b/docs/re-gen-modules.html index 6cadfc2..144ec64 100644 --- a/docs/re-gen-modules.html +++ b/docs/re-gen-modules.html @@ -66,6 +66,9 @@

    examples/re-gen-modules.lhs

    +

    Regex Module Gen

    +

    All of the modules that make up the API are generated from the Text.RE.TDFA.ByteString.Lazy module using this script.

    +

    The tool is self-testing: run it with no arguments (or cabal test).

    {-# LANGUAGE NoImplicitPrelude          #-}
     {-# LANGUAGE TemplateHaskell            #-}
     {-# LANGUAGE QuasiQuotes                #-}
    diff --git a/docs/re-include.html b/docs/re-include.html
    index f2edb14..c79300d 100644
    --- a/docs/re-include.html
    +++ b/docs/re-include.html
    @@ -66,6 +66,11 @@
     

    examples/re-include.lhs

    +

    Example: Include Processor

    +

    This example looks for lines like

    +
    %include "lib/md/load-tutorial-cabal-incl.md"
    +

    on its input and replaces them with the contents of the names file.

    +

    The tool is self-testing: run it with no arguments (or cabal test).

    {-# LANGUAGE NoImplicitPrelude          #-}
     {-# LANGUAGE RecordWildCards            #-}
     {-# LANGUAGE GeneralizedNewtypeDeriving #-}
    diff --git a/docs/re-nginx-log-processor.html b/docs/re-nginx-log-processor.html
    index 5d139f9..17ff981 100644
    --- a/docs/re-nginx-log-processor.html
    +++ b/docs/re-nginx-log-processor.html
    @@ -66,6 +66,10 @@
     

    examples/re-nginx-log-processor.lhs

    +

    Example: NGINX Log Processor

    +

    This example program reads lines from NGINX error-log files and access-log files converts them into a unified output format.

    +

    It is an example of developing REs at scale using macros with the regex test bench.

    +

    The tool is self-testing: run it with no arguments (or cabal test).

    {-# LANGUAGE NoImplicitPrelude          #-}
     {-# LANGUAGE RecordWildCards            #-}
     {-# LANGUAGE GeneralizedNewtypeDeriving #-}
    diff --git a/docs/re-prep.html b/docs/re-prep.html
    index 1c99819..010ba15 100644
    --- a/docs/re-prep.html
    +++ b/docs/re-prep.html
    @@ -66,6 +66,9 @@
     

    examples/re-prep.lhs

    +

    Regex (Page) Prep

    +

    This tool turns the markdown and literate Haskell into the HTML that makes up the website and the README.md for GitHub and for Hackage (based on the index.md for the website) and the test suite based on the tutorial.

    +

    The tool is self-testing: run it with no arguments (or cabal test).

    {-# LANGUAGE NoImplicitPrelude          #-}
     {-# LANGUAGE RecordWildCards            #-}
     {-# LANGUAGE GeneralizedNewtypeDeriving #-}
    @@ -90,6 +93,7 @@ 

    examples/re-prep.lhs

    import qualified Shelly as SH import System.Directory import System.Environment +import System.IO import TestKit import Text.Heredoc import Text.RE.Edit @@ -117,7 +121,7 @@

    examples/re-prep.lhs

    usage = do pnm <- getProgName let prg = ((" "++pnm++" ")++) - putStr $ unlines + hPutStr stderr $ unlines [ "usage:" , prg "--help" , prg "[test]" diff --git a/docs/re-tests.html b/docs/re-tests.html index b1eccb1..b2a112d 100644 --- a/docs/re-tests.html +++ b/docs/re-tests.html @@ -66,6 +66,8 @@

    examples/re-tests.lhs

    +

    Regex Test Suite

    +

    All of the regex exampes are self-testing and together make up the regex test suite run during development and over each release of the test suite. But here we have the unit an small-check tests used to systematically probe the library for weak points and guard against regressions.

    {-# LANGUAGE NoImplicitPrelude          #-}
     {-# LANGUAGE RecordWildCards            #-}
     {-# LANGUAGE OverloadedStrings          #-}
    @@ -89,6 +91,7 @@ 

    examples/re-tests.lhs

    import Data.String import qualified Data.Text as T import qualified Data.Text.Lazy as LT +import Data.Typeable import Language.Haskell.TH.Quote import Prelude.Compat import Test.SmallCheck.Series @@ -100,6 +103,7 @@

    examples/re-tests.lhs

    import qualified Text.Regex.TDFA as TDFA_ import Text.RE +import Text.RE.Internal.AddCaptureNames import Text.RE.Internal.NamedCaptures import Text.RE.Internal.PreludeMacros import Text.RE.Internal.QQ @@ -117,10 +121,8 @@

    examples/re-tests.lhs

    import qualified Text.RE.TDFA.ByteString.Lazy as TLBS import qualified Text.RE.TDFA.Sequence as T_SQ import qualified Text.RE.TDFA.Text as T_TX -import qualified Text.RE.TDFA.Text.Lazy as TLTX - - -main :: IO () +import qualified Text.RE.TDFA.Text.Lazy as TLTX
    +
    main :: IO ()
     main = defaultMain $
       testGroup "Tests"
         [ prelude_tests
    @@ -130,9 +132,11 @@ 

    examples/re-tests.lhs

    , options_tests , namedCapturesTestTree , many_tests + , escapeTests + , add_capture_names_tests , misc_tests - ] - + ]
    +
    -- | check that our self-testing macro environments are good
     prelude_tests :: TestTree
     prelude_tests = testGroup "Prelude"
       [ tc TDFA TDFA.preludeEnv
    @@ -142,95 +146,107 @@ 

    examples/re-tests.lhs

    tc rty m_env = testCase (show rty) $ do dumpMacroTable "macros" rty m_env - assertBool "testMacroEnv" =<< testMacroEnv "prelude" rty m_env - + assertBool "testMacroEnv" =<< testMacroEnv "prelude" rty m_env
    +

    Core Match/Replace Tests

    +

    +test vectors +

    +

    The core tests rely on these simple test vectors.

    +
    -- | our standard test strings
     str_, str' :: String
     str_      = "a bbbb aa b"
     str'      = "foo"
     
    +-- | standard test REs
     regex_, regex_alt :: RE
     regex_    = [re|(a+) (b+)|]
     regex_alt = [re|(a+)|(b+)|]
     
    +-- | golden matches result 1
     regex_str_matches :: Matches String
     regex_str_matches =
       Matches
    -    { matchesSource = "a bbbb aa b"
    +    { matchesSource = str_
         , allMatches =
             [ regex_str_match
             , regex_str_match_2
             ]
         }
     
    +-- | golden match result 1
     regex_str_match :: Match String
     regex_str_match =
       Match
    -    { matchSource   = "a bbbb aa b"
    +    { matchSource   = str_
         , captureNames  = noCaptureNames
         , matchArray    = array (0,2)
    -        [ (0,Capture {captureSource = "a bbbb aa b", capturedText = "a bbbb", captureOffset = 0, captureLength = 6})
    -        , (1,Capture {captureSource = "a bbbb aa b", capturedText = "a"     , captureOffset = 0, captureLength = 1})
    -        , (2,Capture {captureSource = "a bbbb aa b", capturedText = "bbbb"  , captureOffset = 2, captureLength = 4})
    +        [ (0,Capture {captureSource = str_, capturedText = "a bbbb", captureOffset = 0, captureLength = 6})
    +        , (1,Capture {captureSource = str_, capturedText = "a"     , captureOffset = 0, captureLength = 1})
    +        , (2,Capture {captureSource = str_, capturedText = "bbbb"  , captureOffset = 2, captureLength = 4})
             ]
         }
     
    +-- | golden match result 2
     regex_str_match_2 :: Match String
     regex_str_match_2 =
       Match
    -    { matchSource   = "a bbbb aa b"
    +    { matchSource   = str_
         , captureNames  = noCaptureNames
         , matchArray    = array (0,2)
    -        [ (0,Capture {captureSource = "a bbbb aa b", capturedText = "aa b", captureOffset = 7 , captureLength = 4})
    -        , (1,Capture {captureSource = "a bbbb aa b", capturedText = "aa"  , captureOffset = 7 , captureLength = 2})
    -        , (2,Capture {captureSource = "a bbbb aa b", capturedText = "b"   , captureOffset = 10, captureLength = 1})
    +        [ (0,Capture {captureSource = str_, capturedText = "aa b", captureOffset = 7 , captureLength = 4})
    +        , (1,Capture {captureSource = str_, capturedText = "aa"  , captureOffset = 7 , captureLength = 2})
    +        , (2,Capture {captureSource = str_, capturedText = "b"   , captureOffset = 10, captureLength = 1})
             ]
         }
     
    +-- | golden match result 2
     regex_alt_str_matches :: Matches String
     regex_alt_str_matches =
       Matches
    -    { matchesSource = "a bbbb aa b"
    +    { matchesSource = str_
         , allMatches    =
             [ Match
    -            { matchSource   = "a bbbb aa b"
    +            { matchSource   = str_
                 , captureNames  = noCaptureNames
                 , matchArray    = array (0,2)
    -                [ (0,Capture {captureSource = "a bbbb aa b", capturedText = "a", captureOffset = 0, captureLength = 1})
    -                , (1,Capture {captureSource = "a bbbb aa b", capturedText = "a", captureOffset = 0, captureLength = 1})
    -                , (2,Capture {captureSource = "a bbbb aa b", capturedText = "", captureOffset = -1, captureLength = 0})
    +                [ (0,Capture {captureSource = str_, capturedText = "a", captureOffset = 0, captureLength = 1})
    +                , (1,Capture {captureSource = str_, capturedText = "a", captureOffset = 0, captureLength = 1})
    +                , (2,Capture {captureSource = str_, capturedText = "", captureOffset = -1, captureLength = 0})
                     ]
                 }
             , Match
    -            { matchSource   = "a bbbb aa b"
    +            { matchSource   = str_
                 , captureNames  = noCaptureNames
                 , matchArray    = array (0,2)
    -                [ (0,Capture {captureSource = "a bbbb aa b", capturedText = "bbbb", captureOffset = 2 , captureLength = 4})
    -                , (1,Capture {captureSource = "a bbbb aa b", capturedText = ""    , captureOffset = -1, captureLength = 0})
    -                , (2,Capture {captureSource = "a bbbb aa b", capturedText = "bbbb", captureOffset = 2 , captureLength = 4})
    +                [ (0,Capture {captureSource = str_, capturedText = "bbbb", captureOffset = 2 , captureLength = 4})
    +                , (1,Capture {captureSource = str_, capturedText = ""    , captureOffset = -1, captureLength = 0})
    +                , (2,Capture {captureSource = str_, capturedText = "bbbb", captureOffset = 2 , captureLength = 4})
                     ]
                 }
             , Match
    -            { matchSource   = "a bbbb aa b"
    +            { matchSource   = str_
                 , captureNames  = noCaptureNames
                 , matchArray    = array (0,2)
    -                [ (0,Capture {captureSource = "a bbbb aa b", capturedText = "aa", captureOffset = 7 , captureLength = 2})
    -                , (1,Capture {captureSource = "a bbbb aa b", capturedText = "aa", captureOffset = 7 , captureLength = 2})
    -                , (2,Capture {captureSource = "a bbbb aa b", capturedText = ""  , captureOffset = -1, captureLength = 0})
    +                [ (0,Capture {captureSource = str_, capturedText = "aa", captureOffset = 7 , captureLength = 2})
    +                , (1,Capture {captureSource = str_, capturedText = "aa", captureOffset = 7 , captureLength = 2})
    +                , (2,Capture {captureSource = str_, capturedText = ""  , captureOffset = -1, captureLength = 0})
                     ]
                 }
             , Match
    -            { matchSource   = "a bbbb aa b"
    +            { matchSource   = str_
                 , captureNames  = noCaptureNames
                 , matchArray    = array (0,2)
    -                [ (0,Capture {captureSource = "a bbbb aa b", capturedText = "b", captureOffset = 10, captureLength = 1})
    -                , (1,Capture {captureSource = "a bbbb aa b", capturedText = "" , captureOffset = -1, captureLength = 0})
    -                , (2,Capture {captureSource = "a bbbb aa b", capturedText = "b", captureOffset = 10, captureLength = 1})
    +                [ (0,Capture {captureSource = str_, capturedText = "b", captureOffset = 10, captureLength = 1})
    +                , (1,Capture {captureSource = str_, capturedText = "" , captureOffset = -1, captureLength = 0})
    +                , (2,Capture {captureSource = str_, capturedText = "b", captureOffset = 10, captureLength = 1})
                     ]
                 }
             ]
    -    }
    -
    -parsing_tests :: TestTree
    +    }
    +

    +testing the compileRegex functions +

    +
    parsing_tests :: TestTree
     parsing_tests = testGroup "Parsing"
       [ testCase "complete check (matchM/ByteString)" $ do
           r    <- compileRegex () $ reSource regex_
    @@ -238,9 +254,11 @@ 

    examples/re-tests.lhs

    , testCase "matched (matchM/Text)" $ do r <- compileRegex () $ reSource regex_ assertEqual "matched" True $ matched $ T.pack str_ ?=~ r - ] - -core_tests :: TestTree + ]
    +

    +core tests +

    +
    core_tests :: TestTree
     core_tests = testGroup "Match"
       [ testCase "text (=~~Text.Lazy)" $ do
           txt <- LT.pack str_ =~~ [re|(a+) (b+)|] :: IO (LT.Text)
    @@ -267,9 +285,11 @@ 

    examples/re-tests.lhs

    , testCase "fail (all)" $ do let mtchs = str' =~ regex_ :: Matches String assertEqual "not.anyMatches" False $ anyMatches mtchs - ] - -replaceMethodstests :: TestTree + ]
    +

    +testing the replace functions at different types +

    +
    replaceMethodstests :: TestTree
     replaceMethodstests = testGroup "Replace"
       [ testCase "String/single" $ do
           let m = str_ =~ regex_ :: Match String
    @@ -319,9 +339,11 @@ 

    examples/re-tests.lhs

    fmt _ (Location i j) Capture{..} = Just $ "(" <> packE (show i) <> ":" <> packE (show_co j) <> ":" <> capturedText <> ")" - show_co (CaptureOrdinal j) = show j - -options_tests :: TestTree + show_co (CaptureOrdinal j) = show j
    +

    +Testing The Options +

    +
    options_tests :: TestTree
     options_tests = testGroup "Simple Options"
       [ testGroup "TDFA Simple Options"
           [ testCase "default (MultilineSensitive)" $ assertEqual "#" 2 $
    @@ -349,9 +371,11 @@ 

    examples/re-tests.lhs

    ] ] where - s = "0a\nbb\nFe\nA5" :: String - -many_tests :: TestTree + s = "0a\nbb\nFe\nA5" :: String
    +

    +Exercising Our Many APIs +

    +
    many_tests :: TestTree
     many_tests = testGroup "Many Tests"
         [ testCase "PCRE a"               $ test (PCRE.*=~) (PCRE.?=~) (PCRE.=~) (PCRE.=~~) matchOnce matchMany id          re_pcre
         , testCase "PCRE ByteString"      $ test (P_BS.*=~) (P_BS.?=~) (P_BS.=~) (P_BS.=~~) matchOnce matchMany B.pack      re_pcre
    @@ -398,9 +422,131 @@ 

    examples/re-tests.lhs

    re_pcre = fromMaybe oops $ PCRE.compileRegex () "[0-9]{4}-[0-9]{2}-[0-9]{2}" re_tdfa = fromMaybe oops $ TDFA.compileRegex () "[0-9]{4}-[0-9]{2}-[0-9]{2}" - oops = error "many_tests" + oops = error "many_tests"
    +

    Testing the RE Escape Functions

    +
    escapeTests :: TestTree
    +escapeTests = testGroup "Escape Tests"
    +    [ testGroup "PCRE"
    +        [ testCase  "Escaping empty string" $
    +            assertBool "empty string" $
    +              tst P_ST.escape (P_ST.?=~) ""
    +        , testCase  "Escaping RE metacharacters" $
    +            assertBool "metacharacters" $
    +              tst P_ST.escape (P_ST.?=~) metacharacters
    +        , localOption (SmallCheckDepth 6) $
    +            SC.testProperty "matched $ <s> ?=~ [re|^escape(<s>)$|]" $
    +              tst P_ST.escape (P_ST.?=~)
    +        ]
    +    , testGroup "TDFA"
    +        [ testCase  "Escaping empty string" $
    +            assertBool "empty string" $
    +              tst T_ST.escape (T_ST.?=~) ""
    +        , testCase  "Escaping RE metacharacters" $
    +            assertBool "metacharacters" $
    +              tst T_ST.escape (T_ST.?=~) metacharacters
    +        , localOption (SmallCheckDepth 6) $
    +            SC.testProperty "matched $ <s> ?=~ [re|^escape(<s>)$|]" $
    +              tst T_ST.escape (T_ST.?=~)
    +        ]
    +    ]
    +  where
    +    tst :: ((String->String)->String->a)
    +        -> (String->a->Match String)
    +        -> String
    +        -> Bool
    +    tst esc (%=~) s = matched $ s %=~ esc (("^" ++) . (++ "$")) s
    +
    +    metacharacters :: String
    +    metacharacters = "^\\.|*+?()[]{}$"
    +

    Named Capture Tests

    +
    namedCapturesTestTree :: TestTree
    +namedCapturesTestTree = localOption (SmallCheckDepth 4) $
    +  testGroup "NamedCaptures"
    +    [ formatScanTestTree
    +    , analyseTokensTestTree
    +    ]
    +
    +instance Monad m => Serial m Token
    +
    +formatScanTestTree :: TestTree
    +formatScanTestTree =
    +  testGroup "FormatToken/Scan Properties"
    +    [ localOption (SmallCheckDepth 4) $
    +        SC.testProperty "formatTokens == formatTokens0" $
    +          \tks -> formatTokens tks == formatTokens0 tks
    +    , localOption (SmallCheckDepth 4) $
    +        SC.testProperty "scan . formatTokens' idFormatTokenOptions == id" $
    +          \tks -> all validToken tks ==>
    +                    scan (formatTokens' idFormatTokenOptions tks) == tks
    +    ]
     
    -misc_tests :: TestTree
    +analyseTokensTestTree :: TestTree
    +analyseTokensTestTree =
    +  testGroup "Analysing [Token] Unit Tests"
    +    [ tc [here|foobar|]                                       []
    +    , tc [here||]                                             []
    +    , tc [here|$([0-9]{4})|]                                  []
    +    , tc [here|${x}()|]                                       [(1,"x")]
    +    , tc [here|${}()|]                                        []
    +    , tc [here|${}()${foo}()|]                                [(2,"foo")]
    +    , tc [here|${x}(${y()})|]                                 [(1,"x")]
    +    , tc [here|${x}(${y}())|]                                 [(1,"x"),(2,"y")]
    +    , tc [here|${a}(${b{}())|]                                [(1,"a")]
    +    , tc [here|${y}([0-9]{4})-${m}([0-9]{2})-${d}([0-9]{2})|] [(1,"y"),(2,"m"),(3,"d")]
    +    , tc [here|@$(@|\{${name}([^{}]+)\})|]                    [(2,"name")]
    +    , tc [here|${y}[0-9]{4}|]                                 []
    +    , tc [here|${}([0-9]{4})|]                                []
    +    ]
    +  where
    +    tc s al =
    +      testCase s $ assertEqual "CaptureNames"
    +        (xnc s)
    +        (HM.fromList
    +          [ (CaptureName $ T.pack n,CaptureOrdinal i)
    +              | (i,n)<-al
    +              ]
    +        )
    +
    +    xnc = either oops fst . extractNamedCaptures
    +      where
    +        oops = error "analyseTokensTestTree: unexpected parse failure"
    +

    AddCaptureNames Tests

    +
    add_capture_names_tests :: TestTree
    +add_capture_names_tests = testGroup "AddCaptureNames Tests"
    +    [ test_add_capture_name "Match   String"          test_match                    regex_str_match
    +    , test_add_capture_name "Matches String"          test_matches                  regex_str_matches
    +    , test_add_capture_name "Match   B.ByteString"    test_match   $ B.pack     <$> regex_str_match
    +    , test_add_capture_name "Matches B.ByteString"    test_matches $ B.pack     <$> regex_str_matches
    +    , test_add_capture_name "Match   LBS.ByteString"  test_match   $ LBS.pack   <$> regex_str_match
    +    , test_add_capture_name "Matches LBS.ByteString"  test_matches $ LBS.pack   <$> regex_str_matches
    +    , test_add_capture_name "Match   T.Text"          test_match   $ T.pack     <$> regex_str_match
    +    , test_add_capture_name "Matches T.Text"          test_matches $ T.pack     <$> regex_str_matches
    +    , test_add_capture_name "Match   LT.Text"         test_match   $ LT.pack    <$> regex_str_match
    +    , test_add_capture_name "Matches LT.Text"         test_matches $ LT.pack    <$> regex_str_matches
    +    , test_add_capture_name "Match   (Seq Char)"      test_match   $ S.fromList <$> regex_str_match
    +    , test_add_capture_name "Matches (Seq Char)"      test_matches $ S.fromList <$> regex_str_matches
    +    ]
    +
    +test_matches :: CaptureNames -> Matches a -> Bool
    +test_matches cnms = all (test_match cnms) . allMatches
    +
    +test_match :: CaptureNames -> Match a -> Bool
    +test_match cnms mtch = captureNames mtch == cnms
    +
    +test_add_capture_name :: Typeable a
    +                      => String
    +                      -> (CaptureNames->a->Bool)
    +                      -> a
    +                      -> TestTree
    +test_add_capture_name lab tst x = testCase lab $
    +    assertBool lab $ tst cnms $ addCaptureNames cnms x
    +  where
    +    cnms = HM.fromList
    +      [ (CaptureName "x",1)
    +      , (CaptureName "y",2)
    +      ]
    +

    The Miscelaneous Tests

    +
    misc_tests :: TestTree
     misc_tests = testGroup "Miscelaneous Tests"
         [ testGroup "QQ"
             [ qq_tc "expression"  quoteExp
    @@ -414,6 +560,8 @@ 

    examples/re-tests.lhs

    , valid_string "preludeMacroSources" preludeMacroSources , valid_macro "preludeMacroSource" preludeMacroSource ] + -- because HPC can't measure our testing of [re|..|] forms, + -- we are eliminating them from our enquiries , testGroup "RE" [ valid_res TDFA [ TDFA.re @@ -441,6 +589,8 @@

    examples/re-tests.lhs

    [ ne_string (presentPreludeMacro pm) $ TDFA.preludeSource pm | pm <- tdfa_prelude_macros ] + -- because HPC can't measure our testing of [re|..|] forms, + -- we are eliminating them from our enquiries , valid_res PCRE [ PCRE.re , PCRE.reMS @@ -522,57 +672,6 @@

    examples/re-tests.lhs

    tdfa_prelude_macros :: [PreludeMacro] tdfa_prelude_macros = [minBound..maxBound]
    -

    Testing : FormatToken/Scan Properties

    -
    namedCapturesTestTree :: TestTree
    -namedCapturesTestTree = localOption (SmallCheckDepth 4) $
    -  testGroup "NamedCaptures"
    -    [ formatScanTestTree
    -    , analyseTokensTestTree
    -    ]
    -
    instance Monad m => Serial m Token
    -

    Testing : FormatToken/Scan Properties

    -
    formatScanTestTree :: TestTree
    -formatScanTestTree =
    -  testGroup "FormatToken/Scan Properties"
    -    [ localOption (SmallCheckDepth 4) $
    -        SC.testProperty "formatTokens == formatTokens0" $
    -          \tks -> formatTokens tks == formatTokens0 tks
    -    , localOption (SmallCheckDepth 4) $
    -        SC.testProperty "scan . formatTokens' idFormatTokenOptions == id" $
    -          \tks -> all validToken tks ==>
    -                    scan (formatTokens' idFormatTokenOptions tks) == tks
    -    ]
    -

    Testing : Analysing [Token] Unit Tests

    -
    analyseTokensTestTree :: TestTree
    -analyseTokensTestTree =
    -  testGroup "Analysing [Token] Unit Tests"
    -    [ tc [here|foobar|]                                       []
    -    , tc [here||]                                             []
    -    , tc [here|$([0-9]{4})|]                                  []
    -    , tc [here|${x}()|]                                       [(1,"x")]
    -    , tc [here|${}()|]                                        []
    -    , tc [here|${}()${foo}()|]                                [(2,"foo")]
    -    , tc [here|${x}(${y()})|]                                 [(1,"x")]
    -    , tc [here|${x}(${y}())|]                                 [(1,"x"),(2,"y")]
    -    , tc [here|${a}(${b{}())|]                                [(1,"a")]
    -    , tc [here|${y}([0-9]{4})-${m}([0-9]{2})-${d}([0-9]{2})|] [(1,"y"),(2,"m"),(3,"d")]
    -    , tc [here|@$(@|\{${name}([^{}]+)\})|]                    [(2,"name")]
    -    , tc [here|${y}[0-9]{4}|]                                 []
    -    , tc [here|${}([0-9]{4})|]                                []
    -    ]
    -  where
    -    tc s al =
    -      testCase s $ assertEqual "CaptureNames"
    -        (xnc s)
    -        (HM.fromList
    -          [ (CaptureName $ T.pack n,CaptureOrdinal i)
    -              | (i,n)<-al
    -              ]
    -        )
    -
    -    xnc = either oops fst . extractNamedCaptures
    -      where
    -        oops = error "analyseTokensTestTree: unexpected parse failure"