rex 0.3.1 → 0.4.1
raw patch · 9 files changed
+313/−262 lines, 9 filesdep −MissingHdep ~haskell-src-metaPVP ok
version bump matches the API change (PVP)
Dependencies removed: MissingH
Dependency ranges changed: haskell-src-meta
API changes (from Hackage documentation)
- Text.Regex.PCRE.Rex: rex, brex :: QuasiQuoter
- Text.Regex.PCRE.Rex: rexConf :: Bool -> Bool -> String -> [PCREOption] -> [PCREExecOption] -> QuasiQuoter
- Text.Regex.PCRE.Rex: rexPCREOptions :: [PCREOption]
+ Text.Regex.PCRE.Rex: RexConf :: Bool -> Bool -> String -> [PCREOption] -> [PCREExecOption] -> RexConf
+ Text.Regex.PCRE.Rex: brex :: QuasiQuoter
+ Text.Regex.PCRE.Rex: data RexConf
+ Text.Regex.PCRE.Rex: defaultRexConf :: RexConf
+ Text.Regex.PCRE.Rex: rex :: QuasiQuoter
+ Text.Regex.PCRE.Rex: rexByteString :: RexConf -> Bool
+ Text.Regex.PCRE.Rex: rexCompiled :: RexConf -> Bool
+ Text.Regex.PCRE.Rex: rexPCREExecOpts :: RexConf -> [PCREExecOption]
+ Text.Regex.PCRE.Rex: rexPCREOpts :: RexConf -> [PCREOption]
+ Text.Regex.PCRE.Rex: rexView :: RexConf -> String
+ Text.Regex.PCRE.Rex: rexWithConf :: RexConf -> QuasiQuoter
Files
- Bench.hs +5/−5
- CHANGES.md +25/−0
- Demo.hs +71/−0
- README +0/−79
- README.md +90/−0
- Test.hs +0/−70
- Text/Regex/PCRE/Precompile.hs +24/−28
- Text/Regex/PCRE/Rex.hs +93/−76
- rex.cabal +5/−4
Bench.hs view
@@ -3,22 +3,22 @@ module Main where import Text.Regex.PCRE.Rex-import Test+import qualified Demo import Criterion.Main addEm :: Int -> [Double]-addEm n = map math $+addEm n = map Demo.math $ zipWith (++) (zipWith (++) (map show [1..n]) (concat $ repeat ["+", "/", "-", "*"])) (map show [100..]) -peanoize n = map (\i -> peano . (++"Z") . concat $ replicate i "S ") [0..n]+peanoize n = map (\i -> Demo.peano . (++"Z") . concat $ replicate i "S ") [0..n] -testPairs n = [parsePair $ "<" ++ replicate i ' ' ++ "a, pair>" | i <- [0..n]]+testPairs n = [Demo.parsePair $ "<" ++ replicate i ' ' ++ "a, pair>" | i <- [0..n]] -testDate n = [parseDate $ show i ++ ".10.20" | i <- [1900 .. 1900 + n]]+testDate n = [Demo.parseDate $ show i ++ ".10.20" | i <- [1900 .. 1900 + n]] --NOTE: benchmark time includes test generation
+ CHANGES.md view
@@ -0,0 +1,25 @@+0.1: Jul 25 2011+----------------+ * initial release++0.2: Sep 24 2011+----------------+ * Added custom configuration of PCRE options.+ * Added non-precompiling quasiquoters.+ * Fixed a bug where patterns with no captures would fail.+ * Decided to remove the defaulting to 'read' - too much magic.++0.3: Sep 25 2011+ * Fixed a capture indexing bug, where capture fields which aren't bound would+ cause subsequent bound captures to be incorrect.+ * Above bug fix actually neatened up code.+ * Added configuration of default mapping pattern.++0.4: Oct 11 2012+----------------+ * Made configuration into a datatype.++0.4.1: Feb 4 2013+------------------+ * Precompilation bugs fixed by [takano-akio](https://github.com/takano-akio)!+ Now precompilation is on by default.
+ Demo.hs view
@@ -0,0 +1,71 @@+{-# LANGUAGE TemplateHaskell, QuasiQuotes, ViewPatterns #-}++module Demo where++import Text.Regex.PCRE.Rex++import qualified Data.ByteString.Char8 as B+import Data.Maybe (catMaybes, isJust)++main =+ do demonstrate "math" math "1 + 3"+ demonstrate "math" math "3 * 2 + 100"+ demonstrate "math" math "20 / 3 + 100 * 2"+ demonstrate "peano" peano "S Z"+ demonstrate "peano" peano "S S S S Z"+ demonstrate "peano" peano "S S Z"+ demonstrate "parsePair" parsePair "<-1, 3>"+ demonstrate "parsePair" parsePair "<-4,3b0>"+ demonstrate "parsePair" parsePair "< a, -30 >"+ demonstrate "parsePair" parsePair "< a, other>"+ demonstrate "parseDate" parseDate "1993.8.10"+ demonstrate "parseDate" parseDate "1993.08.10"+ demonstrate "parseDate" parseDate "2003.02.28"+ demonstrate "parseDate" parseDate "2004.02.28"+ demonstrate "parseDate" parseDate "2003.02.27"+ demonstrate "disjunct" disjunct "a"+ demonstrate "disjunct" disjunct "ab"+ demonstrate "disjunct" disjunct "abc"+ print $ "btest: " ++ show btest++demonstrate n f input = putStrLn $ n ++ " \"" ++ input ++ "\" == " ++ show (f input)++math x = mathl x 0++mathl [] x = x+mathl [rex|^ \s*(?{ read -> y }\d+)\s*(?{ s }.*)$|] x = mathl s y+mathl [rex|^\+\s*(?{ read -> y }\d+)\s*(?{ s }.*)$|] x = mathl s $ x + y+mathl [rex|^ -\s*(?{ read -> y }\d+)\s*(?{ s }.*)$|] x = mathl s $ x - y+mathl [rex|^\*\s*(?{ read -> y }\d+)\s*(?{ s }.*)$|] x = mathl s $ x * y+mathl [rex|^ /\s*(?{ read -> y }\d+)\s*(?{ s }.*)$|] x = mathl s $ x / y+mathl str x = error str++peano :: String -> Maybe Int+peano = [rex|^(?{ length . filter (=='S') } \s* (?:S\s+)*Z)\s*$|]++parsePair :: String -> Maybe (String, String)+parsePair = [rex|^<\s* (?{ }[^\s,>]+) \s*,\s* (?{ }[^\s,>]+) \s*>$|]++-- From http://www.regular-expressions.info/dates.html+parseDate :: String -> Maybe (Int, Int, Int)+parseDate [rex|^(?{ read -> y }(?:19|20)\d\d)[- /.]+ (?{ read -> m }0[1-9]|1[012])[- /.]+ (?{ read -> d }0[1-9]|[12][0-9]|3[01])$|]+ | (d > 30 && (m `elem` [4, 6, 9, 11]))+ || (m == 2 &&+ (d ==29 && not (mod y 4 == 0 && (mod y 100 /= 0 || mod y 400 == 0)))+ || (d > 29)) = Nothing+ | otherwise = Just (y, m, d)+parseDate _ = Nothing++onNull a f [] = a+onNull _ f xs = f xs++nonNull = onNull Nothing++disjunct [rex| ^(?:(?{nonNull $ Just . head -> a} .)+ | (?{nonNull $ Just . head -> b} ..)+ | (?{nonNull $ Just . last -> c} ...))$|] =+ head $ catMaybes [a, b, c]++btest = [brex|(?{}hello)|] (B.pack "hello") == Just (B.pack "hello")
− README
@@ -1,79 +0,0 @@-http://hackage.haskell.org/package/rex--Provides a quasi-quoter for regular expressions which yields a tuple, of -appropriate arity and types, representing the results of the captures. Allows -the user to specify parsers for captures as inline Haskell. Can also be used to-provide typeful pattern matching in function definitions and case patterns.--To build / install:--./Setup.hs configure --user-./Setup.hs build-./Setup.hs install--See the haddock or Text/Regex/PCRE/QQT.hs for documentation.--Some examples (verbatim from Test.hs):-- math x = mathl x 0-- mathl [] x = x- mathl [rex|^ \s*(?{ read -> y }\d+)\s*(?{ s }.*)$|] x = mathl s y- mathl [rex|^\+\s*(?{ read -> y }\d+)\s*(?{ s }.*)$|] x = mathl s $ x + y- mathl [rex|^ -\s*(?{ read -> y }\d+)\s*(?{ s }.*)$|] x = mathl s $ x - y- mathl [rex|^\*\s*(?{ read -> y }\d+)\s*(?{ s }.*)$|] x = mathl s $ x * y- mathl [rex|^ /\s*(?{ read -> y }\d+)\s*(?{ s }.*)$|] x = mathl s $ x / y- mathl str x = error str----- math "1 + 3" == 4.0--- math "3 * 2 + 100" == 106.0--- math "20 / 3 + 100 * 2" == 213.33333333333334--- peano :: String -> Maybe Int- peano = [rex|^(?{ length . filter (=='S') } \s* (?:S\s+)*Z)\s*$|]---- peano "S Z" == Just 1--- peano "S S S S Z" == Just 4--- peano "S S Z" == Just 2-- parsePair :: String -> Maybe (String, String)- parsePair = [rex|^<\s* (?{ }[^\s,>]+) \s*,\s* (?{ }[^\s,>]+) \s*>$|]---- parsePair "<-1, 3>" == Just ("-1","3")--- parsePair "<-4,3b0>" == Just ("-4","3b0")--- parsePair "< a, -30 >" == Just ("a","-30")--- parsePair "< a, other>" == Just ("a","other")----- From http://www.regular-expressions.info/dates.html- parseDate :: String -> Maybe (Int, Int, Int)- parseDate [rex|^(?{ read -> y }(?:19|20)\d\d)[- /.]- (?{ read -> m }0[1-9]|1[012])[- /.]- (?{ read -> d }0[1-9]|[12][0-9]|3[01])$|]- | (d > 30 && (m `elem` [4, 6, 9, 11]))- || (m == 2 &&- (d ==29 && not (mod y 4 == 0 && (mod y 100 /= 0 || mod y 400 == 0)))- || (d > 29)) = Nothing- | otherwise = Just (y, m, d)- parseDate _ = Nothing---- parseDate "1993.8.10" == Nothing--- parseDate "1993.08.10" == Just (1993,8,10)--- parseDate "2003.02.28" == Just (2003,2,28)--- parseDate "2003.02.27" == Just (2003,2,27)-- onNull a f [] = a- onNull _ f xs = f xs-- nonNull = onNull Nothing-- disjunct [rex| ^(?:(?{nonNull $ Just . head -> a} .)- | (?{nonNull $ Just . head -> b} ..)- | (?{nonNull $ Just . last -> c} ...))$|] =- head $ catMaybes [a, b, c]---- disjunct "a" == 'a'--- disjunct "ab" == 'a'--- disjunct "abc" == 'c'
+ README.md view
@@ -0,0 +1,90 @@+http://hackage.haskell.org/package/rex++Provides a quasi-quoter for regular expressions which yields a tuple, of +appropriate arity and types, representing the results of the captures. Allows +the user to specify parsers for captures as inline Haskell. Can also be used to+provide typeful pattern matching in function definitions and case patterns.++To build / install:++```+./Setup.hs configure --user+./Setup.hs build+./Setup.hs install+```++See the haddock or `Text/Regex/PCRE/Rex.hs` for documentation.++Some examples (verbatim from Test.hs):++```haskell+math x = mathl x 0++mathl [] x = x+mathl [rex|^ \s*(?{ read -> y }\d+)\s*(?{ s }.*)$|] x = mathl s y+mathl [rex|^\+\s*(?{ read -> y }\d+)\s*(?{ s }.*)$|] x = mathl s $ x + y+mathl [rex|^ -\s*(?{ read -> y }\d+)\s*(?{ s }.*)$|] x = mathl s $ x - y+mathl [rex|^\*\s*(?{ read -> y }\d+)\s*(?{ s }.*)$|] x = mathl s $ x * y+mathl [rex|^ /\s*(?{ read -> y }\d+)\s*(?{ s }.*)$|] x = mathl s $ x / y+mathl str x = error str++-- math "1 + 3" == 4.0+-- math "3 * 2 + 100" == 106.0+-- math "20 / 3 + 100 * 2" == 213.33333333333334+```+++```haskell+peano :: String -> Maybe Int+peano = [rex|^(?{ length . filter (=='S') } \s* (?:S\s+)*Z)\s*$|]++-- peano "S Z" == Just 1+-- peano "S S S S Z" == Just 4+-- peano "S S Z" == Just 2+```++```haskell+parsePair :: String -> Maybe (String, String)+parsePair = [rex|^<\s* (?{ }[^\s,>]+) \s*,\s* (?{ }[^\s,>]+) \s*>$|]++-- parsePair "<-1, 3>" == Just ("-1","3")+-- parsePair "<-4,3b0>" == Just ("-4","3b0")+-- parsePair "< a, -30 >" == Just ("a","-30")+-- parsePair "< a, other>" == Just ("a","other")+```+++```haskell+-- From http://www.regular-expressions.info/dates.html+parseDate :: String -> Maybe (Int, Int, Int)+parseDate [rex|^(?{ read -> y }(?:19|20)\d\d)[- /.]+ (?{ read -> m }0[1-9]|1[012])[- /.]+ (?{ read -> d }0[1-9]|[12][0-9]|3[01])$|]+ | (d > 30 && (m `elem` [4, 6, 9, 11]))+ || (m == 2 &&+ (d ==29 && not (mod y 4 == 0 && (mod y 100 /= 0 || mod y 400 == 0)))+ || (d > 29)) = Nothing+ | otherwise = Just (y, m, d)+parseDate _ = Nothing++-- parseDate "1993.8.10" == Nothing+-- parseDate "1993.08.10" == Just (1993,8,10)+-- parseDate "2003.02.28" == Just (2003,2,28)+-- parseDate "2003.02.27" == Just (2003,2,27)+```++```haskell+onNull a f [] = a+onNull _ f xs = f xs++nonNull = onNull Nothing++disjunct [rex| ^(?:(?{nonNull $ Just . head -> a} .)+ | (?{nonNull $ Just . head -> b} ..)+ | (?{nonNull $ Just . last -> c} ...))$|] =+ head $ catMaybes [a, b, c]++-- disjunct "a" == 'a'+-- disjunct "ab" == 'a'+-- disjunct "abc" == 'c'+```
− Test.hs
@@ -1,70 +0,0 @@-{-# LANGUAGE TemplateHaskell, QuasiQuotes, ViewPatterns #-}--module Test where--import Text.Regex.PCRE.Rex--import qualified Data.ByteString.Char8 as B-import Data.Maybe (catMaybes, isJust)--demo =- do demonstrate "math" math "1 + 3"- demonstrate "math" math "3 * 2 + 100"- demonstrate "math" math "20 / 3 + 100 * 2"- demonstrate "peano" peano "S Z"- demonstrate "peano" peano "S S S S Z"- demonstrate "peano" peano "S S Z"- demonstrate "parsePair" parsePair "<-1, 3>"- demonstrate "parsePair" parsePair "<-4,3b0>"- demonstrate "parsePair" parsePair "< a, -30 >"- demonstrate "parsePair" parsePair "< a, other>"- demonstrate "parseDate" parseDate "1993.8.10"- demonstrate "parseDate" parseDate "1993.08.10"- demonstrate "parseDate" parseDate "2003.02.28"- demonstrate "parseDate" parseDate "2003.02.27"- demonstrate "disjunct" disjunct "a"- demonstrate "disjunct" disjunct "ab"- demonstrate "disjunct" disjunct "abc"- print $ "btest: " ++ show btest--demonstrate n f input = putStrLn $ n ++ " \"" ++ input ++ "\" == " ++ show (f input)--math x = mathl x 0--mathl [] x = x-mathl [rex|^ \s*(?{ read -> y }\d+)\s*(?{ s }.*)$|] x = mathl s y-mathl [rex|^\+\s*(?{ read -> y }\d+)\s*(?{ s }.*)$|] x = mathl s $ x + y-mathl [rex|^ -\s*(?{ read -> y }\d+)\s*(?{ s }.*)$|] x = mathl s $ x - y-mathl [rex|^\*\s*(?{ read -> y }\d+)\s*(?{ s }.*)$|] x = mathl s $ x * y-mathl [rex|^ /\s*(?{ read -> y }\d+)\s*(?{ s }.*)$|] x = mathl s $ x / y-mathl str x = error str--peano :: String -> Maybe Int-peano = [rex|^(?{ length . filter (=='S') } \s* (?:S\s+)*Z)\s*$|]--parsePair :: String -> Maybe (String, String)-parsePair = [rex|^<\s* (?{ }[^\s,>]+) \s*,\s* (?{ }[^\s,>]+) \s*>$|]---- From http://www.regular-expressions.info/dates.html-parseDate :: String -> Maybe (Int, Int, Int)-parseDate [rex|^(?{ read -> y }(?:19|20)\d\d)[- /.]- (?{ read -> m }0[1-9]|1[012])[- /.]- (?{ read -> d }0[1-9]|[12][0-9]|3[01])$|]- | (d > 30 && (m `elem` [4, 6, 9, 11]))- || (m == 2 &&- (d ==29 && not (mod y 4 == 0 && (mod y 100 /= 0 || mod y 400 == 0)))- || (d > 29)) = Nothing- | otherwise = Just (y, m, d)-parseDate _ = Nothing--onNull a f [] = a-onNull _ f xs = f xs--nonNull = onNull Nothing--disjunct [rex| ^(?:(?{nonNull $ Just . head -> a} .)- | (?{nonNull $ Just . head -> b} ..)- | (?{nonNull $ Just . last -> c} ...))$|] =- head $ catMaybes [a, b, c]--btest = [brex|(?{}hello)|] (B.pack "hello") == Just (B.pack "hello")
Text/Regex/PCRE/Precompile.hs view
@@ -1,5 +1,4 @@-{-# LANGUAGE TupleSections #-}-+{-# LANGUAGE MagicHash, BangPatterns #-} ----------------------------------------------------------------------------- -- | -- Module : Text.Regex.PCRE.Rex@@ -18,44 +17,41 @@ module Text.Regex.PCRE.Precompile where -import Control.Monad (liftM)--import qualified Data.ByteString.Char8 as B-import Data.ByteString.Unsafe (unsafePackCStringLen, unsafeUseAsCString)--import Foreign.ForeignPtr (withForeignPtr, newForeignPtr_)-import Foreign.Ptr (nullPtr, castPtr)-import Foreign.Marshal (alloca)-import Foreign.Storable (peek)-import Foreign.C.Types (CInt)-+import Control.Monad (liftM)+import Data.ByteString.Char8 (ByteString, packCStringLen)+import Data.ByteString.Internal (toForeignPtr)+import Foreign.C.Types (CSize)+import Foreign.ForeignPtr (withForeignPtr)+import Foreign.Ptr (nullPtr, castPtr)+import Foreign.Marshal (alloca)+import Foreign.Storable (peek)+import GHC.Exts (Int(..), plusAddr#)+import GHC.ForeignPtr (ForeignPtr(..)) import Text.Regex.PCRE.Light import Text.Regex.PCRE.Light.Base -import Debug.Trace---- | A type synonym indicating which ByteStrings represent PCRE-format compiled--- data.-type CompiledBytes = B.ByteString+-- | A synonym indicating which ByteStrings represent PCRE-format compiled data.+type CompiledBytes = ByteString --- | Compiles the given regular expression, and assuming nothing bad--- happens, yields the bytestring filled with PCRE's compiled--- representation.-precompile :: B.ByteString -> [PCREOption] -> IO (Maybe CompiledBytes)+-- | Compiles the given regular expression, and assuming nothing bad happens,+-- yields the bytestring filled with PCRE's compiled representation.+precompile :: ByteString -> [PCREOption] -> IO (Maybe CompiledBytes) precompile pat opts = regexToTable $ compile pat opts --- | Takes a compiled regular expression, and yields .+-- | Takes a compiled regular expression, and if successful, yields the+-- compiled representation. regexToTable :: Regex -> IO (Maybe CompiledBytes) regexToTable (Regex p _) = withForeignPtr p $ \pcre -> alloca $ \res -> do success <- c_pcre_fullinfo pcre nullPtr info_size res- len <- return . fromIntegral =<< (peek res :: IO CInt)+ len <- return . fromIntegral =<< (peek res :: IO CSize) if success >= 0 - then withForeignPtr p (liftM Just . unsafePackCStringLen . (, len) . castPtr)+ then liftM Just $ packCStringLen (castPtr pcre, len) else return Nothing -- | Creates a regular expression from the compiled representation. regexFromTable :: CompiledBytes -> IO Regex-regexFromTable bytes = unsafeUseAsCString bytes $ \cstr -> do- ptr <- newForeignPtr_ (castPtr cstr)- return $ Regex ptr bytes+regexFromTable bytes =+ return $ Regex (ForeignPtr (plusAddr# addr offset) content) bytes+ where+ !(ForeignPtr addr content, I# offset, _) = toForeignPtr bytes
Text/Regex/PCRE/Rex.hs view
@@ -20,13 +20,13 @@ -- 3) Allows for the inline interpolation of mapping functions :: String -> a. -- -- 4) Precompiles the regular expression at compile time, by calling into the--- PCRE library and storing a 'B.ByteString' literal representation of its state.+-- PCRE library and storing a 'ByteString' literal representation of its state. -- -- NOTE: for some unknown reason this feature is currently broken, and so off by -- default. -- -- 5) Compile-time configurable to use different PCRE options, turn off--- precompilation, use 'B.ByteString's, or set a default mapping expression.+-- precompilation, use 'ByteString's, or set a default mapping expression. -- -- Since this is a quasiquoter library that generates code using view patterns, -- the following extensions are required:@@ -102,12 +102,6 @@ -- a single variable / pattern, preferring the first non-empty option. The -- general logic for this is a bit complicated, and postponed for a later -- release.--- --- 3) The following error currently sometimes happens when using precompiled--- regular expressions. This 'feature' is now off by default until this is--- fixed.--- --- > <interactive>: out of memory (requested 17584491593728 bytes) -- -- Since pcre-light is a wrapper over a C API, the most efficient interface is -- ByteStrings, as it does not natively speak Haskell lists. The [rex| ... ]@@ -116,10 +110,13 @@ -- 'QuasiQuoter' is provided for this purpose. You can also define your own -- 'QuasiQuoter' - the definitions of the default configurations are as follows: ----- > rex = rexConf False True "id" rexPCREOptions []--- > brex = rexConf True True "id" rexPCREOptions []+-- > rex = rexWithConf $ defaultRexConf+-- > brex = rexWithConf $ defaultRexConf { rexByteString = True } +-- >+-- > defaultRexConf = RexConf False True "id" [PCRE.extended] [] ----- As mentioned, the other Bool determines whether precompilation is used. The+-- The first @False@ specifies to use @String@ rather than 'ByteString'. The+-- @True@ argument specifies to use precompilation. -- The -- string following is the default mapping expression, used when omitted. -- Due to GHC staging restrictions, your configuration will need to be in a -- different module than its usage.@@ -132,49 +129,55 @@ -- ----------------------------------------------------------------------------- -module Text.Regex.PCRE.Rex (- rex, brex, rexConf, rexPCREOptions,- maybeRead, padRight, makeQuasiMultiline) where+module Text.Regex.PCRE.Rex+ (+ -- * Quasiquoters+ rex, brex+ -- * Configurable QuasiQuoter+ , rexWithConf, RexConf(..), defaultRexConf+ -- * Utility+ , makeQuasiMultiline+ -- * Used by Generated Code+ , maybeRead, padRight+ ) where -import qualified Text.Regex.PCRE.Light as PCRE-import qualified Text.Regex.PCRE.Light.Base as PCRE import Text.Regex.PCRE.Precompile -import Control.Arrow (first)-import Control.Monad (liftM)+import qualified Text.Regex.PCRE.Light as PCRE -import qualified Data.ByteString.Char8 as B-import Data.Either.Utils (forceEitherMsg)-import Data.List (find)-import Data.List.Split (split, onSublist)-import Data.Maybe (catMaybes, listToMaybe, fromJust, isJust)-import Data.Maybe.Utils (forceMaybeMsg)-import Data.Char (isSpace)+import Control.Applicative ( (<$>) )+import Control.Arrow ( first )+import Control.Monad ( liftM )+import Data.ByteString.Char8 ( pack, unpack, empty )+import Data.List ( find )+import Data.List.Split ( split, onSublist )+import Data.Maybe ( catMaybes, listToMaybe, fromJust, isJust )+import Data.Char ( isSpace )+import System.IO.Unsafe ( unsafePerformIO ) import Language.Haskell.TH import Language.Haskell.TH.Quote import Language.Haskell.Meta.Parse -import System.IO.Unsafe (unsafePerformIO)- {- TODO:- * Fix mentioned caveats+ * Benchmark * Target Text.Regex.Base ? - * Provide a variant which allows splicing in an expression that evaluates to- regex string. * Add unit tests- * Consider allowing passing arity lists in to configuration -} -type ParseChunk = Either String (Int, String)-type ParseChunks = (Int, String, [(Int, String)])-type Config = (Bool, Bool, String, [PCRE.PCREOption], [PCRE.PCREExecOption])+data RexConf = RexConf+ { rexByteString :: Bool+ , rexCompiled :: Bool+ , rexView :: String + , rexPCREOpts :: [PCRE.PCREOption]+ , rexPCREExecOpts :: [PCRE.PCREExecOption]+ } --- | Default regular expression quasiquoter for 'String's and 'B.ByteString's,+-- | Default regular expression quasiquoter for 'String's and 'ByteString's, -- respectively. rex, brex :: QuasiQuoter-rex = rexConf False False "id" rexPCREOptions []-brex = rexConf True False "id" rexPCREOptions []+rex = rexWithConf $ defaultRexConf+brex = rexWithConf $ defaultRexConf { rexByteString = True } -- | This is a 'QuasiQuoter' transformer, which allows for a whitespace-sensitive -- quasi-quoter to be broken over multiple lines. The default 'rex' and@@ -183,30 +186,28 @@ -- parameter, then this could be useful. The leading space of each line is -- ignored, and all newlines removed. makeQuasiMultiline :: QuasiQuoter -> QuasiQuoter-makeQuasiMultiline (QuasiQuoter a b c d) =- QuasiQuoter (a . pre) (b . pre) (c . pre) (d . pre)+makeQuasiMultiline (QuasiQuoter a b c d)+ = QuasiQuoter (a . pre) (b . pre) (c . pre) (d . pre) where pre = concat . (\(x:xs) -> x : map (dropWhile isSpace) xs) . lines --- | Default compilation time PCRE options. The default is 'PCRE.extended',--- which causes whitespace to be nonsemantic, and ignores # comments.-rexPCREOptions :: [PCRE.PCREOption]-rexPCREOptions = [ PCRE.extended ]- -- , dotall, caseless, utf8- -- , newline_any, PCRE.newline_crlf ]+-- | Default rex configuration, which specifies that the regexes operate on+-- strings, don't postprocess the matched patterns, and use 'PCRE.extended'.+-- This setting causes whitespace to be nonsemantic, and ignores # comments.+defaultRexConf :: RexConf+defaultRexConf = RexConf False False "id" [PCRE.extended] [] -- | A configureable regular-expression QuasiQuoter. Takes the options to pass--- to the PCRE engine, along with 'Bool's to flag 'B.ByteString' usage and+-- to the PCRE engine, along with 'Bool's to flag 'ByteString' usage and -- non-compilation respecively. The provided 'String' indicates which mapping -- function to use, when one is omitted - \"(?{} ...)\".-rexConf :: Bool -> Bool -> String -> [PCRE.PCREOption] -> [PCRE.PCREExecOption]- -> QuasiQuoter-rexConf bs pc d os eos = QuasiQuoter- (makeExp conf . parseIt)- (makePat conf . parseIt)- undefined undefined- where- conf = (bs, pc, d, [PCRE.combineOptions os], [PCRE.combineExecOptions eos])+rexWithConf :: RexConf -> QuasiQuoter+rexWithConf conf+ = QuasiQuoter+ (makeExp conf . parseIt)+ (makePat conf . parseIt)+ undefined+ undefined -- Template Haskell Code Generation -------------------------------------------------------------------------------@@ -215,10 +216,10 @@ -- regex. This particular code mainly just handles making "read" the -- default for captures which lack a parser definition, and defaulting to making -- the parser that doesn't exist-makeExp :: Config -> ParseChunks -> ExpQ+makeExp :: RexConf -> ParseChunks -> ExpQ makeExp conf (cnt, pat, exs) = buildExp conf cnt pat exs' where- exs' = map (\ix -> liftM (processExp conf . snd) $ find ((==ix).fst) exs) [0..cnt]+ exs' = map (\ix -> liftM (processExp conf . snd) $ find ((==ix) . fst) exs) [0..cnt] -- Creates the template haskell Pat which corresponds to the parsed interpolated -- regex. As well as handling the aforementioned defaulting considerations, this@@ -226,7 +227,7 @@ -- -- E.g. [reg| ... (?{e1 -> v1} ...) ... (?{e2 -> v2} ...) ... |] becomes -- [reg| ... (?{e1} ...) ... (?{e2} ...) ... |] -> (v1, v2)-makePat :: Config -> ParseChunks -> PatQ+makePat :: RexConf -> ParseChunks -> PatQ makePat conf (cnt, pat, exs) = do viewExp <- buildExp conf cnt pat $ map (liftM fst) views return . ViewP viewExp@@ -249,29 +250,29 @@ -- Here's where the main meat of the template haskell is generated. Given the -- number of captures, the pattern string, and a list of capture expressions, -- yields the template Haskell Exp which parses a string into a tuple.-buildExp :: Config -> Int -> String -> [Maybe Exp] -> ExpQ-buildExp (bs, nc, _, pcreOpts, pcreExecOpts) cnt pat xs =+buildExp :: RexConf -> Int -> String -> [Maybe Exp] -> ExpQ+buildExp conf cnt pat xs = [| let r = $(get_regex) in- $(process) . (flip $ PCRE.match r) $(liftRS pcreExecOpts)- . $(if bs then [| id |] else [| B.pack |]) |]+ $(process) . (flip $ PCRE.match r) $(liftRS $ rexPCREExecOpts conf)+ . $(if rexByteString conf then [| id |] else [| pack |]) |] where- liftRS x = [| read shown |]- where shown = show x+ liftRS x = [| read shown |] where shown = show x --TODO: make sure this takes advantage of bytestring fusion stuff - is -- the right pack / unpack. Or use XOverloadedStrings- get_regex = if nc- then [| unsafePerformIO (regexFromTable $! $(table_bytes)) |]- else [| PCRE.compile (B.pack pat) $(liftRS pcreOpts)|]- table_bytes = [| B.pack $(runIO table_string) |]- table_string = precompile (B.pack pat) pcreOpts >>= - return . LitE . StringL . B.unpack . - forceMaybeMsg "Error while getting PCRE compiled representation\n"+ get_regex + | rexCompiled conf = [| unsafePerformIO (regexFromTable $! $(table_bytes)) |]+ | otherwise = [| PCRE.compile (pack pat) $(liftRS pcreOpts) |]+ table_bytes = [| pack $(LitE . StringL . unpack <$> runIO table_string) |]+ table_string+ = forceMaybeMsg "Error while getting PCRE compiled representation\n"+ <$> precompile (pack pat) pcreOpts+ pcreOpts = rexPCREOpts conf - process = case (null vs, bs) of+ process = case (null vs, rexByteString conf) of (True, _) -> [| liftM ( const () ) |]- (_, False) -> [| liftM (($(return maps)) . padRight "" pad . map B.unpack) |]- (_, True) -> [| liftM (($(return maps)) . padRight B.empty pad) |]+ (_, False) -> [| liftM ($(return maps) . padRight "" pad . map unpack) |]+ (_, True) -> [| liftM ($(return maps) . padRight empty pad) |] pad = cnt + 2 maps = LamE [ListP . (WildP:) $ map VarP vs] . TupE . map (uncurry AppE)@@ -282,10 +283,10 @@ vs = [mkName $ "v" ++ show i | i <- [0..cnt]] -- Parse a Haskell expression into a template Haskell Exp-processExp :: Config -> String -> Exp-processExp (_, _, d, _, _) xs+processExp :: RexConf -> String -> Exp+processExp conf xs = forceEitherMsg ("Error while parsing capture mapper `" ++ xs ++ "'")- . parseExp $ onSpace xs d id+ . parseExp $ onSpace xs (rexView conf) id -- Parse a Haskell pattern match into a template Haskell Pat, yielding Nothing -- for patterns which consist of just whitespace.@@ -297,6 +298,9 @@ -- Parsing ------------------------------------------------------------------------------- +type ParseChunk = Either String (Int, String)+type ParseChunks = (Int, String, [(Int, String)])+ -- Postprocesses the results of the chunk-wise parse output, into the pattern to -- be pased to the regex engine, and the interpolated parseIt :: String -> ParseChunks@@ -371,3 +375,16 @@ mapSnd :: (t -> t2) -> (t1, t) -> (t1, t2) mapSnd f (x, y) = (x, f y)++-- From MissingH++{- | Like 'forceMaybe', but lets you customize the error message raised if+Nothing is supplied. -}+forceMaybeMsg :: String -> Maybe a -> a+forceMaybeMsg msg Nothing = error msg+forceMaybeMsg _ (Just x) = x++{- | Like 'forceEither', but can raise a specific message with the error. -}+forceEitherMsg :: Show e => String -> Either e a -> a+forceEitherMsg msg (Left x) = error $ msg ++ ": " ++ show x+forceEitherMsg _ (Right x) = x
rex.cabal view
@@ -1,5 +1,5 @@ Name: rex-Version: 0.3.1+Version: 0.4.1 Synopsis: A quasi-quoter for typeful results of regex captures. Description: Provides a quasi-quoter for regular expressions which yields a tuple, of appropriate arity and types,@@ -18,8 +18,9 @@ Category: Control Build-type: Simple Stability: experimental-Extra-source-files: Test.hs, Bench.hs-Data-files: README+Extra-source-files: Demo.hs, Bench.hs+Data-files: README.md+ CHANGES.md Cabal-version: >=1.6 Bug-Reports: http://github.com/mgsloan/rex/issues Source-Repository head@@ -30,6 +31,6 @@ Extensions: TemplateHaskell, QuasiQuotes, TupleSections, ViewPatterns Exposed-modules: Text.Regex.PCRE.Rex, Text.Regex.PCRE.Precompile Build-depends: base >= 3.0 && < 6, bytestring, containers, ghc >= 7.0,- haskell-src-meta, MissingH, pcre-light, split,+ haskell-src-meta >= 0.5, pcre-light, split, template-haskell >= 2.5.0.0 Ghc-options: -Wall