matchers 0.18.0.0 → 0.24.0.0
raw patch · 14 files changed
Files
- LICENSE +1/−1
- README +0/−3
- README.md +19/−0
- Text/Matchers.hs +0/−102
- Text/Matchers/Pcre.hs +0/−26
- Text/Matchers/Pcre/Base.hsc +0/−135
- Text/Matchers/Times.hs +0/−139
- current-versions.txt +0/−48
- lib/Matchers.hs +66/−0
- lib/Matchers/Pcre.hs +24/−0
- lib/Matchers/Pcre/Base.hsc +127/−0
- matchers.cabal +13/−14
- minimum-versions.txt +0/−49
- sunlight-test.hs +0/−15
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2011-2013 Omari Norman.+Copyright (c) 2011-2015 Omari Norman. All rights reserved. Redistribution and use in source and binary forms, with or without
− README
@@ -1,3 +0,0 @@-This package contains helpers for performing text matches using-regular expressions as well as simpler matching that sees if a-particular substring exists within a larger string.
+ README.md view
@@ -0,0 +1,19 @@+# matchers++This package contains helpers for performing text matches using+regular expressions as well as simpler matching that sees if a+particular substring exists within a larger string.++It was written mostly for use with+[Penny](http://www.github.com/massysett/penny) so it doesn't have much+of a stable API.++It uses the pcre C library, so make sure you have that installed.++## Test results++[](https://travis-ci.org/massysett/matchers)++The link above takes you to the archive of Travis build results. If+you have trouble building matchers, look there, as it will show you+what dependencies were successfully used in past builds.
− Text/Matchers.hs
@@ -1,102 +0,0 @@-module Text.Matchers- ( CaseSensitive(..)- , pcre- , within- , exact- , anyTime- , time- ) where--import Data.Text (Text, pack, unpack, toCaseFold, isInfixOf)-import qualified Text.Parsec as P-import qualified Data.Time as Time-import Text.Matchers.Times (dateTime)-import Text.Matchers.Pcre as PCRE-import qualified Data.Prednote.Predbox as R--data CaseSensitive = Sensitive | Insensitive deriving (Eq, Ord, Show)--descSensitive :: CaseSensitive -> String-descSensitive c = case c of- Sensitive -> " (case sensitive)"- Insensitive -> " (case insensitive)"---- | Uses the PCRE regular expression engine.-pcre- :: CaseSensitive-- -> Text- -- ^ Pattern-- -> Either Text (R.Predbox Text)- -- ^ The Predbox if the pattern is good; if the pattern is bad,- -- returns an error message.--pcre c t = case PCRE.compile (c == Insensitive) t of- Left e -> Left . pack $ e- Right r ->- let mrDesc = pack $ "matches the PCRE pattern \""- ++ unpack t ++ "\"" ++ descSensitive c- mr = maybe False id . PCRE.exec r- in return $ R.predicate mrDesc mr---- | Matcher that succeeds if the pattern text is found anywhere--- within the subject.-within- :: CaseSensitive-- -> Text- -- ^ The pattern-- -> R.Predbox Text-within cs t = R.predicate mrDesc mr- where- mrDesc = pack $ "contains the text \"" ++ unpack t- ++ "\"" ++ descSensitive cs- mr = txtMatch isInfixOf cs t---- | Matcher that succeeds if the pattern text exactly matches the--- subject (with case sensitivity as appropriate.)-exact :: CaseSensitive -> Text -> R.Predbox Text-exact cs t = R.predicate mrDesc mr- where- mrDesc = pack $ "matches the text \"" ++ unpack t- ++ "\"" ++ descSensitive cs- mr = txtMatch (==) cs t--txtMatch :: (Text -> Text -> Bool)- -> CaseSensitive- -> Text- -> Text -> Bool-txtMatch f c p t = pat `f` txt where- txt = flipCase t- pat = flipCase p- flipCase = case c of- Sensitive -> id- Insensitive -> toCaseFold---- | Matches any valid time.-anyTime :: R.Predbox Text-anyTime = R.predicate mrDesc mr- where- mrDesc = pack "any valid time"- mr x = case P.parse dateTime "" x of- Left _ -> False- Right _ -> True---- | If the given ordering is @r@, the given time is @t@, and the--- time of the subject is @s@, the Predbox returns @compare s t == r@.--- Always returns False if the subject is not a valid time.-time- :: Ordering- -- ^ @r@- -> Time.UTCTime- -- ^ @t@- -> R.Predbox Text-time ord ti = R.compareByMaybe desc (pack "time") mr ord- where- desc = pack . show $ ti- mr x = case P.parse dateTime "" x of- Left _ -> Nothing- Right g -> Just $ (g `compare` ti)-
− Text/Matchers/Pcre.hs
@@ -1,26 +0,0 @@-{-# LANGUAGE Trustworthy #-}-module Text.Matchers.Pcre- ( B.Caseless- , B.Regex- , B.reCaseless- , B.rePattern- , compile- , exec- ) where--import qualified Text.Matchers.Pcre.Base as B-import qualified Data.Text as X-import System.IO.Unsafe (unsafePerformIO)--compile- :: B.Caseless- -> X.Text- -> Either String B.Regex-compile cl x = unsafePerformIO $ B.compile cl x--exec- :: B.Regex- -> X.Text- -> Maybe Bool-exec r x = unsafePerformIO $ B.exec r x-
− Text/Matchers/Pcre/Base.hsc
@@ -1,135 +0,0 @@-{-# LANGUAGE ForeignFunctionInterface, EmptyDataDecls #-}--module Text.Matchers.Pcre.Base- ( caseless- , PCRE- , PCRE_Extra- , c_free- , c_pcre_compile- , c_pcre_exec- , Caseless- , pcre_compile- , pcre_exec- , Regex- , reCaseless- , rePattern- , compile- , exec- ) where--import Foreign.Storable-import Foreign.C-import Foreign.Marshal.Alloc-import Foreign.Marshal.Array-import Foreign.ForeignPtr.Safe-import Foreign.Ptr-import Data.ByteString-import Data.Text-import Data.Text.Encoding--#include <pcre.h>-#include <stdlib.h>--caseless :: CInt-caseless = #const PCRE_CASELESS--data PCRE--instance Show PCRE where- show _ = "PCRE"--data PCRE_Extra--instance Show PCRE_Extra where- show _ = "PCRE_Extra"--foreign import ccall unsafe "stdlib.h &free"- c_free :: FunPtr (Ptr a -> IO ())--foreign import ccall unsafe "pcre.h pcre_compile"- c_pcre_compile- :: CString- -- ^ Pattern- -> CInt- -- ^ Options- -> Ptr CString- -- ^ OUT error message- -> Ptr CInt- -- ^ OUT Error offset- -> Ptr CUChar- -- ^ Pointer to character table. Use NULL for default.- -> IO (Ptr PCRE)--foreign import ccall unsafe "pcre.h pcre_exec"- c_pcre_exec- :: Ptr PCRE- -- ^ Regex- -> Ptr PCRE_Extra- -- ^ Result of study- -> CString- -- ^ Subject- -> CInt- -- ^ Length of subject string. (Apparently it does not have to be- -- null terminated?)- -> CInt- -- ^ Start at this offset in the subject string.- -> CInt- -- ^ Options- -> Ptr CInt- -- ^ OUT Output vector. Information about matching substrings is- -- stored in this array.- -> CInt- -- ^ Output vector size- -> IO CInt- -- ^ One more than the highest numbered pair that has been set.--type Caseless = Bool--pcre_compile- :: Caseless- -> Text- -- ^ Pattern- -> IO (Either String (Ptr PCRE))- -- ^ Errors are indicated with a Left with the error message.-pcre_compile cl pat- = useAsCString (encodeUtf8 pat) $ \patC ->- alloca $ \ptrMsg ->- alloca $ \ptrOffset -> do- let cOpt = if cl then caseless else 0- ptrPcre <- c_pcre_compile patC cOpt ptrMsg ptrOffset nullPtr- if ptrPcre == nullPtr- then do- ptrErr <- peek ptrMsg- msg <- peekCAString ptrErr- return . Left $ msg- else return . Right $ ptrPcre- - -pcre_exec :: Ptr PCRE -> Text -> IO (Maybe Bool)-pcre_exec ptr txt- = useAsCStringLen (encodeUtf8 txt) $ \(ptrSubj, len) ->- allocaArray 30 $ \array -> do- r <- c_pcre_exec ptr nullPtr ptrSubj (fromIntegral len)- 0 0 array 30- return $ case () of- _ | r == (-1) -> Just False- | r < (-1) -> Nothing- | otherwise -> Just True- -data Regex = Regex- { reCaseless :: Caseless- , rePattern :: Text- , _rePtr :: ForeignPtr PCRE- } deriving Show--compile :: Caseless -> Text -> IO (Either String Regex)-compile cl pat = do- ei <- pcre_compile cl pat- case ei of- Left e -> return . Left $ e- Right ptr -> do- fp <- newForeignPtr c_free ptr- return . Right $ Regex cl pat fp--exec :: Regex -> Text -> IO (Maybe Bool)-exec (Regex _ _ fp) s = withForeignPtr fp $ \p -> pcre_exec p s
− Text/Matchers/Times.hs
@@ -1,139 +0,0 @@--- | Time parsers.------ "Text.Matchers" allows you to perform matching based on times.--- Times are parsed using the parsers in this module.-module Text.Matchers.Times where--import Data.Fixed-import Data.Maybe-import Control.Applicative-import Control.Monad-import qualified Data.Time as Time-import Text.Parsec (satisfy)-import qualified Text.Parsec as P-import Text.Parsec.Text (Parser)---- | A four-digit year.-year :: Parser Integer-year = read <$> replicateM 4 P.digit---- | A two-digit month (exactly 2 digits.)-month :: Parser Int-month = read <$> replicateM 2 P.digit---- | A two-digit day (exactly 2 digits.)-day :: Parser Int-day = read <$> replicateM 2 P.digit---- | A valid Gregorian day, in YYYY-MM-DD format. Each separator--- may be a hyphen or a slash. Fails if the day is not valid.-pDate :: Parser Time.Day-pDate = p >>= failOnErr- where- p = Time.fromGregorianValid- <$> year <* satisfy dateSep- <*> month <* satisfy dateSep- <*> day- failOnErr = maybe (fail "could not parse date") return---- | Date separator (slash or hyphen).-dateSep :: Char -> Bool-dateSep c = c == '/' || c == '-'--digit :: Char -> Bool-digit c = c >= '0' && c <= '9'--colon :: Char -> Bool-colon = (== ':')---- | Two digits for the hour (exactly two digits). Must be between--- 0 and 23.-hours :: Parser Int-hours = p >>= (maybe (fail "could not parse hours") return)- where- p = f <$> satisfy digit <*> satisfy digit- f d1 d2 =- let r = read [d1,d2]- in if r < 0 || r > 23- then Nothing- else Just r----- | Two digits for the minutes (exactly two digits). Must be--- between 0 and 59.-minutes :: Parser Int-minutes = p >>= maybe (fail "could not parse minutes") return- where- p = f <$ satisfy colon <*> satisfy digit <*> satisfy digit- f d1 d2 =- let r = read [d1, d2]- in if r < 0 || r > 59- then Nothing- else Just r---- | Two digits for seconds (exactly two digits). Must be between 0--- and 59; there are no leap seconds.-seconds :: Parser Pico-seconds = p >>= maybe (fail "could not parse seconds") return- where- p = f <$ satisfy colon <*> satisfy digit <*> satisfy digit- f d1 d2 =- let r = read [d1, d2] :: Int- in if r < 0 || r > 59- then Nothing- else Just . fromIntegral $ r---- | Hours and minutes, separated by colons, with optional seconds.-time :: Parser Time.TimeOfDay-time = f <$> hours <*> minutes <*> optional seconds- where- f h m ms = Time.TimeOfDay h m (fromMaybe 0 ms)---- | Time zone sign, plus or minus.-tzSign :: Parser (Int -> Int)-tzSign = (id <$ satisfy plus) <|> (negate <$ satisfy minus)- where- plus = (== '+')- minus = (== '-')---- | Time zone offset, exactly 4 digits.-tzNumber :: Parser Int-tzNumber = read <$> replicateM 4 (satisfy digit)---- | Time zone; that is, sign and offset. Both the sign and offset--- are required. The number of minutes may not exceed 840.-timeZone :: Parser Time.TimeZone-timeZone = p >>= maybe (fail "could not parse time zone") return- where- p = f <$> tzSign <*> tzNumber- f s = minsToOffset . s- minsToOffset m = if abs m > 840- then Nothing- else Just (Time.TimeZone m False "")---- | Space or tab.-white :: Char -> Bool-white c = c == ' ' || c == '\t'---- | Time of day, with optional time zone.-timeWithZone :: Parser (Time.TimeOfDay, Maybe Time.TimeZone)-timeWithZone =- (,) <$> time <* many (satisfy white) <*> optional timeZone----- | Day, followed by optional whitespace, followed by optional time--- with zone.-dateTime :: Parser Time.UTCTime-dateTime =- f <$> pDate <* many (satisfy white) <*> optional timeWithZone- where- f d mayTwithZ = Time.zonedTimeToUTC zt- where- zt = Time.ZonedTime lt tz- lt = Time.LocalTime d tod- (tod, tz) = case mayTwithZ of- Nothing -> (Time.midnight, Time.utc)- Just (t, mayZ) -> case mayZ of- Nothing -> (t, Time.utc)- Just z -> (t, z)-
− current-versions.txt
@@ -1,48 +0,0 @@-This package was tested to work with these dependency-versions and compiler version.-These are the default versions fetched by cabal install.-Tested as of: 2014-03-02 02:11:53.267528 UTC-Path to compiler: ghc-7.6.3-Compiler description: 7.6.3--/opt/ghc-7.6.3/lib/ghc-7.6.3/package.conf.d:- Cabal-1.16.0- array-0.4.0.1- base-4.6.0.1- bin-package-db-0.0.0.0- binary-0.5.1.1- bytestring-0.10.0.2- containers-0.5.0.0- deepseq-1.3.0.1- directory-1.2.0.1- filepath-1.3.0.1- (ghc-7.6.3)- ghc-prim-0.3.0.0- (haskell2010-1.1.1.0)- (haskell98-2.0.0.2)- hoopl-3.9.0.0- hpc-0.6.0.0- integer-gmp-0.5.0.0- old-locale-1.0.0.5- old-time-1.1.0.1- pretty-1.1.1.0- process-1.1.0.2- rts-1.0- template-haskell-2.8.0.0- time-1.4.0.1- unix-2.6.0.1--/home/massysett/matchers/sunlight-26440/db:- contravariant-0.4.4- matchers-0.18.0.0- mtl-2.1.2- parsec-3.1.5- prednote-0.22.0.0- rainbow-0.6.0.4- split-0.2.2- tagged-0.7- terminfo-0.4.0.0- text-1.1.0.0- transformers-0.3.0.0- transformers-compat-0.1.1.1-
+ lib/Matchers.hs view
@@ -0,0 +1,66 @@+{-# LANGUAGE OverloadedStrings #-}+module Matchers+ ( CaseSensitive(..)+ , pcre+ , within+ , exact+ ) where++import Data.Text (Text, pack, toCaseFold, isInfixOf)+import Matchers.Pcre as PCRE+import qualified Prednote as R+import Data.Monoid++pcre+ :: CaseSensitive+ -> Text+ -- ^ Pattern+ -> Either String (R.Pred Text)+pcre cs txt = fmap f $ compile cs txt+ where+ f pc = R.predicate cond pd+ where+ cond = "matches the PCRE regular expression "+ <> (pack . show $ txt) <> ", " <> s+ s = case cs of+ Sensitive -> "case sensitive"+ Insensitive -> "case insensitive"+ pd x = case exec pc x of+ Nothing -> False+ Just b -> b++within+ :: CaseSensitive+ -> Text+ -- ^ Pattern+ -> R.Pred Text+within cs txt = txtMatch isInfixOf st cs txt+ where+ st = "contains the text " <> pack (show txt)++exact+ :: CaseSensitive+ -> Text+ -> R.Pred Text+exact cs txt = txtMatch (==) st cs txt+ where+ st = "exactly matches the text " <> pack (show txt)++txtMatch+ :: (Text -> Text -> Bool)+ -> Text+ -- ^ Condition description+ -> CaseSensitive+ -> Text+ -- ^ Pattern+ -> R.Pred Text+txtMatch f lbl c p = R.predicate cond pd+ where+ cond = lbl <> ", " <> cs+ (cs, flipCase) = case c of+ Sensitive -> ("case sensitive", id)+ Insensitive -> ("case insensitive", toCaseFold)+ pd t = f pat txt+ where+ txt = flipCase t+ pat = flipCase p
+ lib/Matchers/Pcre.hs view
@@ -0,0 +1,24 @@+{-# LANGUAGE Trustworthy #-}+module Matchers.Pcre+ ( B.CaseSensitive(..)+ , B.PCRE+ , compile+ , exec+ ) where++import qualified Matchers.Pcre.Base as B+import qualified Data.Text as X+import System.IO.Unsafe (unsafePerformIO)++compile+ :: B.CaseSensitive+ -> X.Text+ -> Either String B.PCRE+compile cl x = unsafePerformIO $ B.compile cl x++exec+ :: B.PCRE+ -> X.Text+ -> Maybe Bool+exec r x = unsafePerformIO $ B.exec r x+
+ lib/Matchers/Pcre/Base.hsc view
@@ -0,0 +1,127 @@+{-# LANGUAGE ForeignFunctionInterface, EmptyDataDecls #-}++module Matchers.Pcre.Base+ ( CaseSensitive(..)+ , PCRE+ , compile+ , exec+ ) where++import Foreign.Storable+import Foreign.C+import Foreign.Marshal.Alloc+import Foreign.Marshal.Array+import Foreign.ForeignPtr.Safe+import Foreign.Ptr+import Data.ByteString+import Data.Text+import Data.Text.Encoding+++#include <pcre.h>+#include <stdlib.h>++data CaseSensitive = Sensitive | Insensitive+ deriving (Eq, Ord, Show)++caseless :: CInt+caseless = #const PCRE_CASELESS++data PCREData++instance Show PCREData where+ show _ = "PCREData"++data PCRE_Extra++instance Show PCRE_Extra where+ show _ = "PCRE_Extra"++foreign import ccall unsafe "stdlib.h &free"+ c_free :: FunPtr (Ptr a -> IO ())++foreign import ccall unsafe "pcre.h pcre_compile"+ c_pcre_compile+ :: CString+ -- ^ Pattern+ -> CInt+ -- ^ Options+ -> Ptr CString+ -- ^ OUT error message+ -> Ptr CInt+ -- ^ OUT Error offset+ -> Ptr CUChar+ -- ^ Pointer to character table. Use NULL for default.+ -> IO (Ptr PCREData)++foreign import ccall unsafe "pcre.h pcre_exec"+ c_pcre_exec+ :: Ptr PCREData+ -- ^ Regex++ -> Ptr PCRE_Extra+ -- ^ Result of study. Just pass NULL if you did not study the+ -- pattern.++ -> CString+ -- ^ Subject+ -> CInt+ -- ^ Length of subject string. (Apparently it does not have to be+ -- null terminated?)+ -> CInt+ -- ^ Start at this offset in the subject string.+ -> CInt+ -- ^ Options+ -> Ptr CInt+ -- ^ OUT Output vector. Information about matching substrings is+ -- stored in this array.+ -> CInt+ -- ^ Output vector size+ -> IO CInt+ -- ^ One more than the highest numbered pair that has been set.++pcre_compile+ :: CaseSensitive+ -> Text+ -- ^ Pattern+ -> IO (Either String (Ptr PCREData))+ -- ^ Errors are indicated with a Left with the error message.+pcre_compile cl pat+ = useAsCString (encodeUtf8 pat) $ \patC ->+ alloca $ \ptrMsg ->+ alloca $ \ptrOffset -> do+ let cOpt = if cl == Insensitive then caseless else 0+ ptrPcre <- c_pcre_compile patC cOpt ptrMsg ptrOffset nullPtr+ if ptrPcre == nullPtr+ then do+ ptrErr <- peek ptrMsg+ msg <- peekCAString ptrErr+ return . Left $ msg+ else return . Right $ ptrPcre+ + +pcre_exec :: Ptr PCREData -> Text -> IO (Maybe Bool)+pcre_exec ptr txt+ = useAsCStringLen (encodeUtf8 txt) $ \(ptrSubj, len) ->+ allocaArray 30 $ \array -> do+ r <- c_pcre_exec ptr nullPtr ptrSubj (fromIntegral len)+ 0 0 array 30+ return $ case () of+ _ | r == (-1) -> Just False+ | r < (-1) -> Nothing+ | otherwise -> Just True+ +newtype PCRE = PCRE (ForeignPtr PCREData)+ deriving Show++compile :: CaseSensitive -> Text -> IO (Either String PCRE)+compile cl pat = do+ ei <- pcre_compile cl pat+ case ei of+ Left e -> return . Left $ e+ Right ptr -> do+ fp <- newForeignPtr c_free ptr+ return . Right $ PCRE fp++exec :: PCRE -> Text -> IO (Maybe Bool)+exec (PCRE fp) s = withForeignPtr fp $ \p -> pcre_exec p s
matchers.cabal view
@@ -1,9 +1,9 @@ Name: matchers-Version: 0.18.0.0+Version: 0.24.0.0 Cabal-version: >=1.8 Build-Type: Simple License: BSD3-Copyright: 2012-2014 Omari Norman.+Copyright: 2011-2015 Omari Norman. author: Omari Norman maintainer: omari@smileystation.com stability: Experimental@@ -11,13 +11,14 @@ bug-reports: omari@smileystation.com Category: Console License-File: LICENSE-tested-with: GHC==7.4.1, GHC==7.6.3 synopsis: Text matchers extra-source-files:- current-versions.txt minimum-versions.txt sunlight-test.hs- README+ README.md description: Helpers for performing text matches.+ For more information, please see the Github homepage at+ .+ <http://www.github.com/massysett/matchers> source-repository head type: git@@ -26,19 +27,17 @@ Library Build-depends: base >=4.5.0.0 && < 5- , bytestring >=0.9.2.1- , parsec >= 3.1.2- , prednote >= 0.22.0.0- , text >=0.11.2.0- , time >=1.4+ , bytestring >=0.9.2.1 && < 0.11+ , text >=0.11.2.0 && < 1.3+ , prednote >= 0.28.0.0 && < 0.29 Exposed-modules:- Text.Matchers- , Text.Matchers.Pcre- , Text.Matchers.Pcre.Base- , Text.Matchers.Times+ Matchers+ , Matchers.Pcre+ , Matchers.Pcre.Base extra-libraries: pcre ghc-options: -Wall+ hs-source-dirs: lib
− minimum-versions.txt
@@ -1,49 +0,0 @@-This package was tested to work with these dependency-versions and compiler version.-These are the minimum versions given in the .cabal file.-Tested as of: 2014-03-02 02:11:53.267528 UTC-Path to compiler: ghc-7.4.1-Compiler description: 7.4.1--/opt/ghc/7.4.1/lib/ghc-7.4.1/package.conf.d:- Cabal-1.14.0- array-0.4.0.0- base-4.5.0.0- bin-package-db-0.0.0.0- binary-0.5.1.0- bytestring-0.9.2.1- containers-0.4.2.1- deepseq-1.3.0.0- directory-1.1.0.2- extensible-exceptions-0.1.1.4- filepath-1.3.0.0- (ghc-7.4.1)- ghc-prim-0.2.0.0- (haskell2010-1.1.0.1)- (haskell98-2.0.0.1)- hoopl-3.8.7.3- hpc-0.5.1.1- integer-gmp-0.4.0.0- old-locale-1.0.0.4- old-time-1.1.0.0- pretty-1.1.1.0- process-1.1.0.1- rts-1.0- template-haskell-2.7.0.0- time-1.4- unix-2.5.1.0--/home/massysett/matchers/sunlight-26440/db:- contravariant-0.4.4- matchers-0.18.0.0- mtl-2.1.2- parsec-3.1.2- prednote-0.22.0.0- rainbow-0.6.0.4- split-0.2.2- tagged-0.7- terminfo-0.4.0.0- text-0.11.2.0- transformers-0.3.0.0- transformers-compat-0.1.1.1-
− sunlight-test.hs
@@ -1,15 +0,0 @@-module Main where--import Test.Sunlight--ghc v = (v, "ghc-" ++ v, "ghc-pkg-" ++ v)--inputs = TestInputs- { tiDescription = Nothing- , tiCabal = "cabal"- , tiLowest = ghc "7.4.1"- , tiDefault = [ ghc "7.4.1", ghc "7.6.3" ]- , tiTest = []- }--main = runTests inputs