quickcheck-regex (empty) → 0.0.1
raw patch · 3 files changed
+81/−0 lines, 3 filesdep +QuickCheckdep +basedep +containerssetup-changed
Dependencies added: QuickCheck, base, containers, regex-genex, regex-tdfa
Files
- Setup.lhs +4/−0
- quickcheck-regex.cabal +17/−0
- src/Test/QuickCheck/Regex.hs +60/−0
+ Setup.lhs view
@@ -0,0 +1,4 @@+#!/usr/bin/env runhaskell+> import Distribution.Simple+> main :: IO ()+> main = defaultMainWithHooks simpleUserHooks
+ quickcheck-regex.cabal view
@@ -0,0 +1,17 @@+Name: quickcheck-regex+Version: 0.0.1+Homepage: http://github.com/audreyt/quickcheck-regex/+Category: QuickCheck, test, regex+Synopsis: QuickCheck instances for arbitrary regular expressions and regex-conforming strings+Description: QuickCheck instances for arbitrary regular expressions and regex-conforming strings+License: PublicDomain+Author: Audrey Tang+Maintainer: audreyt@audreyt.org+Copyright: Audrey Tang 2014 (CC0)+Cabal-Version: >= 1.2+Build-Type: Simple++Library+ Build-Depends: base>=3 && <5, QuickCheck >= 2, regex-genex >= 0.6.0, regex-tdfa, containers+ Exposed-modules: Test.QuickCheck.Regex+ Hs-source-dirs: src
+ src/Test/QuickCheck/Regex.hs view
@@ -0,0 +1,60 @@+{-|++This module exports a 'matching' function that turns a regular expression+into a generator for strings matching that regex. For example:++>>> import Test.QuickCheck.Regex (matching)+>>> import Test.QuickCheck (generate)+>>> generate (matching "[-a-z0-9._%]+@[-a-z0-9.]+\\.[a-z]{3,18}\\.(asia|eu|today)")+"9%az4rmek@rar1d8qvo04jkd1.agzy.asia"++-}+module Test.QuickCheck.Regex (matching) where++import Test.QuickCheck (Gen, oneof, arbitrary, listOf, listOf1, choose, generate)+import Data.List ((\\))+import Control.Monad (replicateM)+import Data.Monoid (mempty)+import Regex.Genex.Normalize (normalize)+import Text.Regex.TDFA.Pattern (Pattern(..), PatternSet(..))+import Text.Regex.TDFA.ReadRegex (parseRegex)+import qualified Data.Set as Set++matching :: String -> Gen String+matching regex = case parseRegex regex of+ Left x -> fail $ show x+ Right (pattern, _) -> go $ normalize mempty pattern+ where+ go :: Pattern -> Gen String+ go pat = case pat of+ PEmpty -> return ""+ POr ps -> oneof (map go ps)+ PConcat ps -> concat `fmap` mapM go ps+ PQuest p -> oneof [return "", go p]+ PDot{} -> (:[]) `fmap` arbitrary+ PPlus p -> concat `fmap` listOf (go p)+ PStar _ p -> concat `fmap` listOf1 (go p)+ PBound low high p -> do+ n <- choose (low, maybe 10 id high)+ concat `fmap` replicateM n (go p)+ PChar{ getPatternChar = ch } -> return [ch]+ PEscape{ getPatternChar = ch } -> oneChar $ expandEscape ch+ PAny{ getPatternSet = PatternSet (Just cset) _ _ _ } -> oneChar $ Set.toList cset+ PAnyNot{ getPatternSet = PatternSet (Just cset) _ _ _ } -> oneChar $ charExclude (Set.toList cset)+ _ -> fail $ "Invalid pattern: " ++ show pat+ oneChar = oneof . map (return . (:[]))+ charExclude = (['\0'..'\255'] \\)+ expandEscape ch = case ch of+ 'n' -> "\n"+ 't' -> "\t"+ 'r' -> "\r"+ 'f' -> "\f"+ 'a' -> "\a"+ 'e' -> "\ESC"+ 'd' -> ['0'..'9']+ 'w' -> ['0'..'9'] ++ '_' : ['a'..'z'] ++ ['A'..'Z']+ 's' -> "\9\32"+ 'D' -> charExclude $ ['0'..'9']+ 'W' -> charExclude $ ['0'..'9'] ++ '_' : ['a'..'z'] ++ ['A'..'Z']+ 'S' -> charExclude "\9\32"+ _ -> [ch]