pcre2 1.1.0 → 1.1.1
raw patch · 7 files changed
+67/−47 lines, 7 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- ChangeLog.md +4/−0
- README.md +1/−1
- pcre2.cabal +2/−2
- src/hs/Text/Regex/Pcre2/Foreign.hs +1/−1
- src/hs/Text/Regex/Pcre2/Internal.hs +51/−42
- src/hs/Text/Regex/Pcre2/TH.hs +1/−1
- test/Spec.hs +7/−0
ChangeLog.md view
@@ -1,5 +1,9 @@ # Changelog and Acknowledgements +## 1.1.1+* Fixed [#12](https://github.com/sjshuck/pcre2/4), where some functions returned+ too many match results.+ ## 1.1.0 * Added global matching. * New functions `matchAll`, `matchAllOpt`, `capturesAll`, `capturesAllOpt`.
README.md view
@@ -48,7 +48,7 @@ Both are first-class. * Vast presentation of PCRE2 functionality. We can even register Haskell callbacks to run during matching!-* No dependencies that aren't distributed with GHC.+* Few dependencies. * Bundled, statically-linked UTF-16 build of ~~up-to-date~~ PCRE2 (version 10.35), with a complete, exposed Haskell binding.
pcre2.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 0a7a573e61659cfbca9092c36ae2549d63b0c6745b4bc3953f29dfc97aebd1b6+-- hash: d8aba62e213c9b15ec9218a159e7922eb4ebb298385cd3ff0e1acd1f15fd6506 name: pcre2-version: 1.1.0+version: 1.1.1 synopsis: Regular expressions via the PCRE2 C library (included) description: Please see the README on GitHub at <https://github.com/sjshuck/hs-pcre2> category: Text
src/hs/Text/Regex/Pcre2/Foreign.hs view
@@ -5,7 +5,7 @@ -- -- Items here are named identically to their C counterparts. Therefore, -- documentation will be sparse;--- [the official PCRE2 API docs](https://pcre.org/current/doc/html/pcre2api.html#SEC36)+-- [the official PCRE2 API docs](https://pcre.org/current/doc/html/pcre2api.html) -- should suffice. module Text.Regex.Pcre2.Foreign where
src/hs/Text/Regex/Pcre2/Internal.hs view
@@ -150,10 +150,11 @@ -- * Assembling inputs into @Matcher@s and @Subber@s -- | A matching function where all inputs and auxilliary data structures have--- been \"compiled\". It takes a subject and produces a list of match data--- objects, representing a global match, to be passed to further validation and--- inspection.-type Matcher = Text -> IO [MatchData]+-- been \"compiled\". It takes a subject and a whitelist of capture indexes+-- to extract, and produces lists of capture offsets representing a global+-- match. The whitelist is @[]@ rather than @NonEmpty@ to facilitate writing+-- literals.+type Matcher = Text -> [Int] -> IO [NonEmpty SliceRange] -- | A substitution function. It takes a subject and produces the number of -- substitutions performed (0 or 1, or more in the presence of `SubGlobal`)@@ -658,12 +659,12 @@ return $ MatchEnv {..} -- | Helper for @assemble*@ functions. Basically, extract all options and help--- produce a function that takes a `Text` subject and does something.+-- produce a function that takes a `Text` subject. assembleSubjFun- :: (MatchEnv -> Text -> IO a)+ :: (MatchEnv -> Text -> a) -> Option -> Text- -> IO (Text -> IO a)+ -> IO (Text -> a) assembleSubjFun mkSubjFun option patt = runStateT extractAll (applyOption option) <&> \case (subjFun, []) -> subjFun@@ -684,27 +685,29 @@ -> IO Matcher assembleMatcher = assembleSubjFun $ \matchEnv@(MatchEnv {..}) -> let matchTempEnvWithSubj = mkMatchTempEnv matchEnv- in \subject ->+ in \subject whitelist -> withForeignPtr matchEnvCode $ \codePtr -> Text.useAsPtr subject $ \subjPtr subjCUs -> do MatchTempEnv {..} <- matchTempEnvWithSubj subject - -- Loop over the subject, emitting MatchData's until stopping.- execWriterT $ runMaybeT $ fix1 0 $ \continue curOff -> do- -- TODO Can one of these be reused for a whole global match?- matchData <- liftIO $ mkForeignPtr pcre2_match_data_free $- pcre2_match_data_create_from_pattern codePtr nullPtr+ -- These need to happen one after the other. We need the+ -- ForeignPtr to touch later, to allow cleanup after lazy matching.+ matchDataPtr <- pcre2_match_data_create_from_pattern codePtr nullPtr+ matchData <-+ Conc.newForeignPtr <*> pcre2_match_data_free $ matchDataPtr + -- Loop over the subject, emitting slice lists until stopping.+ lists <- execWriterT $ runMaybeT $ fix1 0 $ \continue curOff -> do result <- liftIO $ withForeignOrNullPtr matchTempEnvCtx $ \ctxPtr ->- withForeignPtr matchData $ \matchDataPtr -> pcre2_match- codePtr- (castCUs subjPtr)- (fromIntegral subjCUs)- curOff- matchEnvOpts- matchDataPtr- ctxPtr+ pcre2_match+ codePtr+ (castCUs subjPtr)+ (fromIntegral subjCUs)+ curOff+ matchEnvOpts+ matchDataPtr+ ctxPtr -- Handle no match and errors when (result == pcre2_ERROR_NOMATCH) stop@@ -712,22 +715,30 @@ liftIO $ maybeRethrow matchTempEnvRef liftIO $ check (> 0) result - emit matchData+ slices <- liftIO $ unsafeInterleaveIO $+ getOvecEntriesAt whitelist matchDataPtr + emit slices+ lazy $ do -- Determine next starting offset- nextOff <- liftIO $- withForeignPtr matchData $ \matchDataPtr -> do- ovecPtr <- pcre2_get_ovector_pointer matchDataPtr- curOffEnd <- peekElemOff ovecPtr 1- -- Prevent infinite loop upon empty match- return $ max curOffEnd (curOff + 1)+ nextOff <- liftIO $ do+ ovecPtr <- pcre2_get_ovector_pointer matchDataPtr+ curOffEnd <- peekElemOff ovecPtr 1+ -- Prevent infinite loop upon empty match+ return $ max curOffEnd (curOff + 1) -- Handle end of subject when (nextOff > fromIntegral subjCUs) stop - continue nextOff+ -- Force slices out of the current match_data state before+ -- clobbering it with the next match+ slices `seq` continue nextOff + touchForeignPtr matchData++ return lists+ where stop = MaybeT $ return Nothing emit x = tell [x]@@ -1028,17 +1039,15 @@ -- Internal only! Users should not (have to) know about `Matcher`. _capturesInternal :: Matcher -> [Int] -> Traversal' Text (NonEmpty Text) _capturesInternal matcher whitelist f subject = traverse f css <&> \css' ->- -- Swag foldl-as-foldr to create only as many segments as we need to- -- stitch back together and no more.+ -- Swag foldl-as-foldr to create only as many segments as we need to stitch+ -- back together and no more. let triples = zip3 (flatten ovecEntries) (flatten css) (flatten css') in Text.concat $ foldr mkSegments termSegments triples 0 where flatten = concatMap NE.toList- ovecEntries = do- matchData <- unsafePerformIO $ matcher subject- return $ unsafePerformIO $ getOvecEntriesAt whitelist matchData+ ovecEntries = unsafePerformIO $ matcher subject whitelist css = do ovecEntry <- ovecEntries return $ unsafePerformIO $ mapM (evaluate . slice subject) ovecEntry@@ -1061,12 +1070,12 @@ -- | Strictly read specifically indexed captures' offsets from match results. -- Truncate indexes when they go out of bounds, to support infinite lists.-getOvecEntriesAt :: [Int] -> MatchData -> IO (NonEmpty SliceRange)-getOvecEntriesAt ns matchData = withForeignPtr matchData $ \matchDataPtr -> do+getOvecEntriesAt :: [Int] -> Ptr Pcre2_match_data -> IO (NonEmpty SliceRange)+getOvecEntriesAt whitelist matchDataPtr = do count <- fromIntegral <$> pcre2_get_ovector_count matchDataPtr - case NE.nonEmpty $ takeWhile (< count) ns of- Nothing -> error "BUG! Tried to get empty list of captures"+ case NE.nonEmpty $ takeWhile (< count) whitelist of+ Nothing -> error "BUG! Empty whitelist" Just ns -> do ovecPtr <- pcre2_get_ovector_pointer matchDataPtr @@ -1089,7 +1098,8 @@ -- | @capturesOpt mempty = captures@ capturesOpt :: Option -> Text -> Text -> [Text]-capturesOpt option patt = view $ _capturesOpt option patt . to NE.toList+capturesOpt option patt =+ maybe [] NE.toList . preview (_capturesOpt option patt) -- | Match a pattern to a subject once and return a non-empty list of captures -- in an `Alternative`, or `empty` if no match. The non-empty list constructor@@ -1108,9 +1118,8 @@ capturesAOpt :: (Alternative f) => Option -> Text -> Text -> f (NonEmpty Text) capturesAOpt option patt = toAlternativeOf $ _capturesOpt option patt --- | Match a pattern to a subject and lazily return zero or more non-empty lists--- of captures corresponding to every non-overlapping place in the subject the--- pattern matched.+-- | Match a pattern to a subject and lazily a list of all non-overlapping+-- portions, with all capture groups, that matched. -- -- @since 1.1.0 capturesAll :: Text -> Text -> [NonEmpty Text]
src/hs/Text/Regex/Pcre2/TH.hs view
@@ -126,7 +126,7 @@ (nums, names) = unzip numberedNames e = [e| let _cs = _capturesInternal $(matcherQ s) $(liftData nums)- in view $ _cs . to NE.toList |]+ in maybe [] NE.toList . preview _cs |] p = listP $ map (varP . mkName . Text.unpack) names, quoteType = const $ fail "regex: cannot produce a type",
test/Spec.hs view
@@ -215,6 +215,13 @@ f "a" `shouldReturn` "" f "b" `shouldReturn` "b" + issue 12 $ do+ captures ".a." "foo bar baz" `shouldBe` ["bar"]+ case "foo bar baz" of+ [regex|.a(?<x>.)|] -> return ()+ _ ->+ expectationFailure "quasi-quoted pattern didn't match"+ where issue :: Int -> Expectation -> Spec issue n = it $ "https://github.com/sjshuck/issues/" ++ show n