packages feed

pcre-light 0.3.1.1 → 0.4.1.3

raw patch · 13 files changed

Files

+ ChangeLog.md view
@@ -0,0 +1,15 @@+# Changelog for pcre-light++## 0.4.1.3+- Remove old-base flag.+- Fix [#18](https://codeberg.org/daniel-casanueva/pcre-light/issues/18).+- Metadata update.++## 0.4.1.2+- Replace finalizerFree with c_pcre_free. ([PR 17](https://gitlab.com/daniel-casanueva/haskell/pcre-light/-/merge_requests/17))++## 0.4.1.1+- Bugfix where ByteString.empty was treated differently than the empty ByteString `""`++## 0.4.1.0+- Add `captureCount` and `captureNames` for working with named captures/groups
+ README.md view
@@ -0,0 +1,11 @@++# pcre-light library++A small, efficient and portable regex library for Perl 5 compatible regular expressions++The PCRE library is a set of functions that implement regular+expression pattern matching using the same syntax and semantics as Perl 5.++## Authorship++This library was originally written by Donald Stewart.
− TODO
@@ -1,2 +0,0 @@-* backwards compatibility with older libpcres (e.g. pcre3 on debian)-* some tests fail for strange reasons
Text/Regex/PCRE/Light.hs view
@@ -10,7 +10,7 @@ -- Portability: H98 + CPP -- ----------------------------------------------------------------------- +-- -- A simple, portable binding to perl-compatible regular expressions -- (PCRE) via strict ByteStrings. --@@ -23,6 +23,8 @@         -- * ByteString interface         , compile, compileM         , match+        , captureCount+        , captureNames          -- * Regex types and constructors externally visible @@ -80,8 +82,12 @@ import qualified Data.ByteString.Base     as S #endif +import System.IO.Unsafe (unsafePerformIO)+import Data.List (sortBy)+import Data.Function (on)+ -- Foreigns-import Foreign+import Foreign (newForeignPtr, withForeignPtr) import Foreign.Ptr import Foreign.C.Types import Foreign.C.String@@ -102,7 +108,7 @@ -- > let r = compile "^(b+|a){1,2}?bc" [] -- -- If the regular expression is invalid, an exception is thrown.--- If this is unsuitable, 'compileM' is availlable, which returns failure +-- If this is unsuitable, 'compileM' is availlable, which returns failure -- in a monad. -- -- To do case insentive matching,@@ -116,7 +122,7 @@ -- -- The arguments are: ----- * 'pat': A ByteString containing the regular expression to be compiled. +-- * 'pat': A ByteString containing the regular expression to be compiled. -- -- * 'flags', optional bit flags. If 'Nothing' is provided, defaults are used. --@@ -195,15 +201,15 @@ compileM str os = unsafePerformIO $   S.useAsCString str $ \pattern -> do     alloca $ \errptr       -> do-    alloca $ \erroffset    -> do-        pcre_ptr <- c_pcre_compile pattern (combineOptions os) errptr erroffset nullPtr-        if pcre_ptr == nullPtr-            then do-                err <- peekCString =<< peek errptr-                return (Left err)-            else do-                reg <- newForeignPtr finalizerFree pcre_ptr -- release with free()-                return (Right (Regex reg str))+      alloca $ \erroffset    -> do+          pcre_ptr <- c_pcre_compile pattern (combineOptions os) errptr erroffset nullPtr+          if pcre_ptr == nullPtr+              then do+                  err <- peekCString =<< peek errptr+                  return (Left err)+              else do+                  reg <- newForeignPtr c_pcre_free pcre_ptr+                  return (Right (Regex reg str))  -- Possible improvements: an 'IsString' instance could be defined -- for 'Regex', which would allow the compiler to insert calls to@@ -264,7 +270,7 @@ match :: Regex -> S.ByteString -> [PCREExecOption] -> Maybe [S.ByteString] match (Regex pcre_fp _) subject os = unsafePerformIO $ do   withForeignPtr pcre_fp $ \pcre_ptr -> do-    n_capt <- capturedCount pcre_ptr+    n_capt <- fullInfoInt pcre_ptr info_capturecount      -- The smallest  size  for ovector that will allow for n captured     -- substrings, in addition to the offsets  of  the  substring@@ -277,16 +283,24 @@          let (str_fp, off, len) = S.toForeignPtr subject         withForeignPtr str_fp $ \cstr -> do-            r <- c_pcre_exec+            let exec csub clen = c_pcre_exec                          pcre_ptr                          nullPtr-                         (cstr `plusPtr` off) -- may contain binary zero bytes.-                         (fromIntegral len)+                         csub -- may contain binary zero bytes.+                         (fromIntegral clen)                          0                          (combineExecOptions os)                          ovec                          (fromIntegral ovec_size) +            -- An empty ByteString may be represented with a nullPtr.  Passing+            -- a nullPtr to pcre_exec will cause it to return an error, even if+            -- the pattern could succesfully match an empty subject. As a+            -- workaround, allocate a small buffer and pass that to pcre_exec.+            r <- if cstr == nullPtr+                then allocaBytes 1 $ \buf -> exec buf 0+                else exec (cstr `plusPtr` off) len+             if r < 0 -- errors, or error_no_match                 then return Nothing                 else let loop n o acc =@@ -308,7 +322,7 @@     -- pair is used for the first capturing subpattern,  and  so on.  The     -- value returned  by pcre_exec() is one more than the highest num- bered     -- pair that has been set. For  example,  if  two  sub- strings  have been-    -- captured, the returned value is 3. +    -- captured, the returned value is 3.    where     -- The first element of a pair is set  to  the offset of the first@@ -321,9 +335,63 @@             start = S.unsafeDrop (fromIntegral a) s             end   = S.unsafeTake (fromIntegral (b-a)) start -    -- use pcre_info to work out how many substrings to reserve space for-    capturedCount :: Ptr PCRE -> IO Int-    capturedCount regex_ptr =-        alloca $ \n_ptr -> do -- (st :: Ptr CInt)-             c_pcre_fullinfo regex_ptr nullPtr info_capturecount n_ptr-             return . fromIntegral =<< peek (n_ptr :: Ptr CInt)++-- Wrapper around c_pcre_fullinfo for integer values+fullInfoInt pcre_ptr what =+  alloca $ \n_ptr -> do+    c_pcre_fullinfo pcre_ptr nullPtr what n_ptr+    return . fromIntegral =<< peek (n_ptr :: Ptr CInt)+++-- | 'captureCount'+--+-- Returns the number of captures in a 'Regex'. Correctly ignores non-capturing groups+-- like @(?:abc)@.+--+-- >>> captureCount (compile "(?<one>abc) (def) (?:non-captured) (?<three>ghi)" [])+-- 3+captureCount :: Regex -> Int+captureCount (Regex pcre_fp _) = unsafePerformIO $+  withForeignPtr pcre_fp $ \pcre_ptr ->+    fullInfoInt pcre_ptr info_capturecount+++-- | 'captureNames'+--+-- Returns the names and numbers of all named subpatterns in the regular+-- expression. Groups are zero-indexed. Unnamed groups are counted, but don't appear in the+-- result list.+--+-- >>> captureNames (compile "(?<one>abc) (def) (?<three>ghi)")+-- [("one", 0), ("three", 2)]+captureNames :: Regex -> [(S.ByteString, Int)]+captureNames (Regex pcre_fp _) = unsafePerformIO $+  withForeignPtr pcre_fp $ \pcre_ptr -> do+    count     <- fullInfoInt pcre_ptr info_namecount+    entrysize <- fullInfoInt pcre_ptr info_nameentrysize++    buf <- alloca $ \n_ptr -> do+      c_pcre_fullinfo pcre_ptr nullPtr info_nametable n_ptr+      buf <- peek n_ptr+      S.packCStringLen (buf, count*entrysize)++    let results = split entrysize buf+        zeroIndexed = fmap (subtract 1) <$> results+        sorted = sortBy (compare `on` snd) zeroIndexed+    return sorted++  where+    -- Split the nametable buffer into entries. Each entry has a fixed size in+    -- bytes. The first two bytes in each entry store the pattern number in+    -- big-endian format, the bytes following that contain the nul-terminated+    -- name of the subpattern.+    split :: Int -> S.ByteString -> [(S.ByteString, Int)]+    split entrysize buf+      | S.null buf = []+      | otherwise =+        let+          (entry, tail) = S.splitAt entrysize buf+          idx = fromIntegral . S.index entry+          num = idx 0 * 256 + idx 1+          name = S.takeWhile (/= 0) $ S.drop 2 entry+        in (name, num) : split entrysize tail
Text/Regex/PCRE/Light/Base.hsc view
@@ -1,4 +1,11 @@-{-# LANGUAGE CPP, ForeignFunctionInterface, GeneralizedNewtypeDeriving #-}+{-# LANGUAGE CPP, ForeignFunctionInterface, CApiFFI #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}++#if __GLASGOW_HASKELL__ < 910+-- Disable dodgy-foreign-imports warnings.+--   See https://gitlab.com/daniel-casanueva/pcre-light/-/merge_requests/17+{-# OPTIONS_GHC -Wno-dodgy-foreign-imports #-}+#endif -------------------------------------------------------------------- -- | -- Module   : Text.Regex.PCRE.Light.Base@@ -25,6 +32,7 @@         , c_pcre_compile         , c_pcre_exec         , c_pcre_fullinfo+        , c_pcre_free          ------------------------------------------------------------------------ @@ -823,4 +831,8 @@                     -> PCREInfo                     -> Ptr a                     -> IO CInt++-- | Return pcre_free finalizer+foreign import capi "pcre.h value pcre_free"+    c_pcre_free :: FinalizerPtr a 
Text/Regex/PCRE/Light/Char8.hs view
@@ -9,7 +9,7 @@ -- Portability: H98 + FFI -- ----------------------------------------------------------------------- +-- -- A simple, portable binding to perl-compatible regular expressions -- (PCRE) via 8-bit latin1 Strings. --@@ -22,6 +22,8 @@         -- * String interface         , compile, compileM         , match+        , S.captureCount+        , captureNames          -- * Regex types and constructors externally visible @@ -69,7 +71,7 @@  import qualified Data.ByteString.Char8 as S import qualified Text.Regex.PCRE.Light as S-import Text.Regex.PCRE.Light hiding (match, compile, compileM)+import Text.Regex.PCRE.Light hiding (match, compile, compileM, captureNames)  -- | 'compile' --@@ -77,7 +79,7 @@ -- The arguments are: -- -- * 'pat': A ByteString, which may or may not be zero-terminated,--- containing the regular expression to be compiled. +-- containing the regular expression to be compiled. -- -- * 'flags', optional bit flags. If 'Nothing' is provided, defaults are used. --@@ -125,15 +127,15 @@ -- -- * 'no_utf8_check'   - Do not check the pattern for UTF-8 validity ----- If compilation of the pattern fails, the 'Left' constructor is +-- If compilation of the pattern fails, the 'Left' constructor is -- returned with the error string. Otherwise an abstract type -- representing the compiled regular expression is returned. -- The regex is allocated via malloc on the C side, and will be -- deallocated by the runtime when the Haskell value representing it -- goes out of scope. ----- As regexes are often defined statically, GHC will compile them --- to null-terminated, strict C strings, enabling compilation of the +-- As regexes are often defined statically, GHC will compile them+-- to null-terminated, strict C strings, enabling compilation of the -- pattern without copying. This may be useful for very large patterns. -- -- See man pcreapi for more details.@@ -203,3 +205,15 @@            Nothing -> Nothing            Just x  -> Just (map S.unpack x) {-# INLINE match #-}+++-- | 'captureNames'+--+-- Returns the names and numbers of all named subpatterns in the regular+-- expression. Groups are zero-indexed. Unnamed groups are counted, but don't appear in the+-- result list.+--+-- >>> captureNames (compile "(?<one>abc) (def) (?<three>ghi)")+-- [("one", 0), ("three", 2)]+captureNames :: Regex -> [(String, Int)]+captureNames r = map (\(n,i) -> (S.unpack n, i)) $ S.captureNames r
− configure
@@ -1,9 +0,0 @@-#!/bin/sh-#--# subst standard header path variables-if test -n "$CPPFLAGS" ; then-    echo "Found CPPFLAGS in environment: '$CPPFLAGS'"-    sed 's,@CPPFLAGS@,'"$CPPFLAGS"',g;s,@LDFLAGS@,'"$LDFLAGS"',g'  \-        < pcre-light.buildinfo.in > pcre-light.buildinfo-fi
− pcre-light.buildinfo.in
@@ -1,3 +0,0 @@-ghc-options: -optc@CPPFLAGS@-cc-options:  @CPPFLAGS@-ld-options:  @LDFLAGS@
pcre-light.cabal view
@@ -1,43 +1,55 @@-name:            pcre-light-version:         0.3.1.1-homepage:        http://code.haskell.org/~dons/code/pcre-light-synopsis:        A small, efficient and portable regex library for Perl 5 compatible regular expressions+name: pcre-light+version: 0.4.1.3+homepage: https://gitlab.com/daniel-casanueva/haskell/pcre-light+synopsis: Portable regex library for Perl 5 compatible regular expressions description:-    A small, efficient and portable regex library for Perl 5 compatible regular expressions+    A small, efficient and portable regex library for Perl 5 compatible regular expressions.     .     The PCRE library is a set of functions that implement regular     expression pattern matching using the same syntax and semantics as Perl 5.     .-    Test coverage data for this library is available at:-        <http://code.haskell.org/~dons/tests/pcre-light/hpc_index.html>+    If installation fails with missing pcre/pkg-config, try installing+    the @libpcre3-dev@ package (linux) or running @brew install pcre pkg-config@ (macOS).     .-category:        Text-license:         BSD3-license-file:    LICENSE-copyright:       (c) 2007-2010. Don Stewart <dons@galois.com>-author:          Don Stewart-maintainer:      Don Stewart <dons@galois.com>-cabal-version: >= 1.2.0-build-type:      Configure-tested-with:     GHC ==6.8.2, GHC ==6.6.1, GHC ==6.12.1, Hugs ==2005-extra-source-files: configure, pcre-light.buildinfo.in-extra-tmp-files:    pcre-light.buildinfo+category:         Text+license:          BSD3+license-file:     LICENSE+copyright:        (c) 2007-2010. Don Stewart <dons@galois.com>+author:           Don Stewart+maintainer:       Daniel Casanueva <coding `at` danielcasanueva.eu>+bug-reports:      https://codeberg.org/daniel-casanueva/pcre-light+cabal-version:    1.18+build-type:       Simple+extra-doc-files:  README.md, ChangeLog.md -flag small_base-  description: Build with new smaller base library-  default:     False+flag use-pkg-config+  default: False+  manual:  True  library     exposed-modules: Text.Regex.PCRE.Light                      Text.Regex.PCRE.Light.Char8                      Text.Regex.PCRE.Light.Base -    extensions:      CPP, ForeignFunctionInterface+    default-language: Haskell2010+    default-extensions: CPP, ForeignFunctionInterface -    if flag(small_base)-        build-depends: base >= 3 && <= 5, bytestring >= 0.9-    else-        build-depends: base < 3+    build-depends: base >= 3 && <= 5, bytestring >= 0.9 -    extra-libraries: pcre+    if flag(use-pkg-config)+      pkgconfig-depends: libpcre+    else+      extra-Libraries: pcre +test-suite unit+    type:                exitcode-stdio-1.0+    default-language:    Haskell2010+    hs-source-dirs:      tests+    main-is:             Unit.hs+    build-depends: +        base >= 3 && <= 5+      , bytestring >= 0.9+      , containers >= 0.5.5.1+      , HUnit >= 1.2.5.2+      , mtl >= 2.1.3.2+      , pcre-light
− tests/Makefile
@@ -1,11 +0,0 @@-all:-	ghc -Onot -fasm Unit.hs --make && ./Unit-#	runhaskell Unit.hs && echo $$?--test::-	ghc -ddump-simpl-stats -lpcre -fhpc --make -i.. -i../dist/build/ Unit.hs -o Unit -O2 -no-recomp-	rm -f *.tix-	./Unit -	hpc report --decl-list       --exclude=Main Unit-	hpc markup --fun-entry-count --exclude=Main Unit-
− tests/Parse.hs
@@ -1,75 +0,0 @@------ A script to translate the pcre.c testsuite into Haskell-----import System.Environment-import System.IO-import Data.Char-import Data.List-import Text.PrettyPrint.HughesPJ--data Test = Test String [String] [Maybe [String]]-       deriving (Eq,Show,Read)--main = do-    [f,g] <- getArgs-    inf   <- readFile f-    outf  <- readFile g--    let in_str  = lines inf-        out_str = lines outf--    let loop [] []     = []-        loop i_xs o_xs = Test r subj results   : loop (dropWhile (=="") i_ys)-                                                      (dropWhile (=="") o_ys)-           where-             ((r:subj),    i_ys) = break (== "") i_xs--             ((_:results'),o_ys) = break (== "") o_xs-             results= [ if s == "No match"-                           then Nothing-                           else Just [s]-                      | s <- filter (not . all isSpace . take 2) results'-                      ]--    print . vcat . intersperse (char ',') . map ppr . loop in_str $ out_str--breakReg ('/':rest) = -  let s = reverse . dropWhile (/= '/') . reverse $ rest--      t = case head (reverse rest) of-               'i' -> ["caseless"]-               '/' -> []-               _   -> ["ERROR"]--  in if s == "" then ("ERROR", [])-                else (init s, t)---breakReg ('"':rest) =-  let s = reverse . dropWhile (/= '"') . reverse $ rest--      t = case head (reverse rest) of-               'i' -> ["caseless"]-               '/' -> []-               _   -> ["ERROR"]--  in if s == "" then ("ERROR", [])-                else (init s, t)--breakReg s          = ("ERROR", [])--ppr :: Test -> Doc-ppr (Test r subjs res) =-    hang (empty <+> text "testRegex" <+> text-       (show (fst $ breakReg r)) <+> -               bracket (case snd (breakReg r) of-                                    [] -> empty-                                    [x] -> text x-                       ))-         4 $-         (bracket $ vcat $ punctuate (char ',') (map (text.show) subjs))-            $+$-         (bracket $ vcat $ punctuate (char ',') (map (text.show) res))--bracket x = char '[' <> x <> char ']'
tests/Unit.hs view
@@ -1,8 +1,8 @@ {-# LANGUAGE OverloadedStrings #-} -import Text.Regex.PCRE.Light (compile,compileM,match)+import Text.Regex.PCRE.Light (compile,compileM,match,captureNames,captureCount) import qualified Text.Regex.PCRE.Light.Char8 as String (compile,compileM,match)-import Text.Regex.PCRE.Light.Base +import Text.Regex.PCRE.Light.Base  import qualified Data.ByteString.Char8 as S import System.IO@@ -16,17 +16,17 @@  import System.IO.Unsafe import Control.Exception-import Control.Monad.Error+import Control.Monad.Except +assertBool' :: S.ByteString -> Bool -> Assertion assertBool'  s = assertBool  (S.unpack s)++assertEqual' :: (Eq a, Show a) => S.ByteString -> a -> a -> Assertion assertEqual' s = assertEqual (S.unpack s) +testLabel :: S.ByteString -> Test -> Test testLabel  s = TestLabel (S.unpack s) -instance Error S.ByteString where-    noMsg = S.empty-    strMsg = S.pack- testRegex :: S.ByteString      -> [PCREOption]      -> [S.ByteString]@@ -67,9 +67,23 @@                 | (i,o) <- zip (map (S.unpack) inputs)                                (map (fmap (map S.unpack)) outputs) ] +testCaptures :: Int+     -> S.ByteString+     -> [(S.ByteString, Int)]+     -> Test+testCaptures expectedTotalCaptures regex expectedCaptureNames = testLabel regex $+    TestCase $ do+        r <- case compileM regex [] of+            Left s -> assertFailure ("ERROR in ByteString in compileM " ++ s)+            Right r -> return r+        assertEqual' "Capture group name mismatch" expectedCaptureNames (captureNames r)+        assertEqual' "Capture count mismatch" expectedTotalCaptures (captureCount r)++main :: IO () main = do counts <- runTestTT tests           when (errors counts > 0 || failures counts > 0) exitFailure +tests :: Test tests = TestList      [ testRegex "the quick brown fox" []@@ -89,12 +103,11 @@                 Left ("nothing to repeat" ) == compileM "*" [])      , testLabel "compile failure" $-            TestCase $ (assertBool' "compile failure" =<< (return $-                    (Just ("Text.Regex.PCRE.Light: Error in regex: nothing to repeat"))-                    ==-                    (unsafePerformIO $ do-                        handle (\e -> return (Just (S.pack $ show e)))-                               (compile "*" [] `seq` return Nothing))))+            TestCase $ (assertEqual' "compile failure"+                            (Just ("Text.Regex.PCRE.Light: Error in regex: nothing to repeat"))+                            (fmap (head . S.lines) . unsafePerformIO $ do+                                handle (\e -> return (Just (S.pack $ displayException (e :: SomeException))))+                                    (compile "*" [] `seq` return Nothing)))  --  , testRegex "\0*" [] -- the embedded null in the pattern seems to be a problem --      ["\0\0\0\0"]@@ -1530,6 +1543,7 @@          Nothing,          Nothing] +{-     , testRegex "^(a\\1?){4}$" []         ["a",          "aa",@@ -1561,6 +1575,7 @@          Nothing,          Nothing,          Nothing]+-}      , testRegex "abc" []         ["abc",@@ -3588,7 +3603,7 @@         [Just ["b", "b"]]     , -+{-     testRegex "(?(1)a|b)" []         []         []@@ -3600,6 +3615,7 @@         [Just ["a"]]     , +-}      testRegex "(x)?(?(1)a|b)" []         ["*** Failers",@@ -3655,7 +3671,7 @@          "*** Failers",          "blah)",          "(blah"]-        [Just ["(blah)", "(", ")"], +        [Just ["(blah)", "(", ")"],          Just ["blah"],          Nothing,          Nothing,@@ -3891,21 +3907,6 @@         [Just ["", ""]]     , ---    testRegex "^[a-\\d]" []-        ["abcde",-         "-things",-         "0digit",-         "*** Failers",-         "bcdef    "]-        [Just ["a"],-         Just ["-"],-         Just ["0"],-         Nothing,-         Nothing]-    ,-     testRegex "\\Qabc\\$xyz\\E" []         ["abc\\$xyz"]         [Just ["abc\\$xyz"]]@@ -4450,5 +4451,50 @@          Nothing,          Nothing] +    , -- Regression test; ensure "" and empty are treated the same.+    testRegex ".*" []+      [ ""+      , S.empty]+      [Just [""]+      ,Just [""]]++    -- named capture group tests+    , -- Handles cases with no capture groups+    testCaptures 0 "no capture groups" []++    , -- Properly labels capture groups+    testCaptures 1 "(?<first>first capture group)" [("first", 0)]++    , -- Doesn't return labels for unnamed groups+    testCaptures 1 "(doesn't return unnamed groups)" []++    , -- Counts but doesn't return unnamed groups+    testCaptures 3 "(?<one>abc) (def) (?<three>ghi)" [("one", 0), ("three", 2)]++    , -- Doesn't count non-capturing groups+    testCaptures 1 "(?:abc) (?<named>def)" [("named", 0)]++    , -- Doesn't count named back-references+    testCaptures 2 "(?<first>abc) (?P=first) (?<second>def)" [("first", 0), ("second", 1)]++    , -- Test alternate group naming syntaxes+    testCaptures 3 "(?'first'abc) (?P<second>def) (?<third>ghi)" [("first", 0), ("second", 1), ("third", 2)]++    , -- Groups are returned in numeric order+    testCaptures 3 "(?'c'0) (?P<b>1) (?<a>2)" [("c", 0), ("b", 1), ("a", 2)]++    , -- Handles alternation between groups+    testCaptures 2 "(?<optionA>abc)|(?<optionB>def)" [("optionA", 0), ("optionB", 1)]++    , -- Labels optional groups+    testCaptures 2 "(?<named>abc)(?<optional>def)?" [("named", 0), ("optional", 1)]++    , -- Handles nested named groups+    testCaptures 3 "(?<named>a(?<nested>b)c) (?<unnested>d)" [("named", 0), ("nested", 1), ("unnested", 2)]++    , testLabel "compile failure on duplicate named groups" $+            TestCase $ (assertEqual' "compile failure on duplicate named groups"+                         (Left ("two named subpatterns have the same name"))+                         (compileM "(?<dup>abc) (?<dup>def)" []))   ]
− tests/failure1.hs
@@ -1,5 +0,0 @@-import Text.Regex.PCRE.Light.Char8--main = do-    let r = compile "(a|)*\\d" []-    print (match r "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" [])