pcre2 2.0.1 → 2.0.2
raw patch · 6 files changed
+132/−104 lines, 6 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- ChangeLog.md +5/−1
- pcre2.cabal +2/−2
- src/hs/Text/Regex/Pcre2.hs +19/−19
- src/hs/Text/Regex/Pcre2/Internal.hs +79/−52
- src/hs/Text/Regex/Pcre2/TH.hs +26/−28
- src/hs/Text/Regex/Pcre2/Unsafe.hs +1/−2
ChangeLog.md view
@@ -1,8 +1,12 @@ # Changelog and Acknowledgements +## 2.0.2+* Fixed a minor issue where the caret indicating pattern location of a+ `Pcre2CompileException` was misplaced if the pattern contained a newline.+ ## 2.0.1 * Added `microlens` as a dependency to improve Haddock docs (`Traversal'` _et- al_ are clickable) and relieve maintenance burden somewhat.+ al._ are clickable) and relieve maintenance burden somewhat. * Moderate refactoring of internals. ## 2.0.0
pcre2.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: pcre2-version: 2.0.1+version: 2.0.2 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@@ -108,7 +108,7 @@ Paths_pcre2 hs-source-dirs: src/hs- ghc-options: -optc=-DPCRE2_CODE_UNIT_WIDTH=16 -optc=-DPCRE2_STATIC=1 -optc=-DHAVE_INTTYPES_H=1 -optc=-DHAVE_LIMITS_H=1 -optc=-DHAVE_MEMMOVE_H=1 -optc=-DHAVE_STDINT_H=1 -optc=-DHAVE_STDLIB_H=1 -optc=-DHAVE_STRERROR_H=1 -optc=-DHAVE_STRING_H=1 -optc=-DSUPPORT_JIT=1 -optc=-DSUPPORT_PCRE2_16=1 -optc=-DSUPPORT_UNICODE=1 -optc=-Wno-discarded-qualifiers -optc=-Wno-incompatible-pointer-types+ ghc-options: -W -optc=-DPCRE2_CODE_UNIT_WIDTH=16 -optc=-DPCRE2_STATIC=1 -optc=-DHAVE_INTTYPES_H=1 -optc=-DHAVE_LIMITS_H=1 -optc=-DHAVE_MEMMOVE_H=1 -optc=-DHAVE_STDINT_H=1 -optc=-DHAVE_STDLIB_H=1 -optc=-DHAVE_STRERROR_H=1 -optc=-DHAVE_STRING_H=1 -optc=-DSUPPORT_JIT=1 -optc=-DSUPPORT_PCRE2_16=1 -optc=-DSUPPORT_UNICODE=1 -optc=-Wno-discarded-qualifiers -optc=-Wno-incompatible-pointer-types cc-options: -DHAVE_CONFIG_H -DPCRE2_CODE_UNIT_WIDTH=16 include-dirs: src/c
src/hs/Text/Regex/Pcre2.hs view
@@ -6,8 +6,8 @@ Atop the low-level binding to the C API, we present a high-level interface to add regular expressions to Haskell programs. - All input and output strings are strict `Text`, which maps directly to how- PCRE2 operates on strings of 16-bit-wide code units.+ All input and output strings are strict `Data.Text.Text`, which maps+ directly to how PCRE2 operates on strings of 16-bit-wide code units. The C API requires pattern strings to be compiled and the compiled patterns to be executed on subject strings in discrete steps. We hide this@@ -52,11 +52,11 @@ [Subject]: The string the compiled regular expression is executed on. - [Regex]: A function of the form @`Text` -> result@, where the argument is- the subject. It is \"compiled\" via partial application as discussed above.- (Lens users: A regex has the more abstract form+ [Regex]: A function of the form @`Data.Text.Text` -> result@, where the+ argument is the subject. It is \"compiled\" via partial application as+ discussed above. (Lens users: A regex has the more abstract form @[Traversal\'](https://hackage.haskell.org/package/microlens/docs/Lens-Micro.html#t:Traversal-39-)- `Text` result@, but the concept is the same.)+ `Data.Text.Text` result@, but the concept is the same.) [Capture (or capture group)]: Any substrings of the subject matched by the pattern, meaning the whole pattern and any parenthesized groupings. The@@ -68,8 +68,9 @@ [Unset capture]: A capture considered unset as distinct from empty. This can arise from matching the pattern @(a)?@ to an empty subject—the 0th capture will be set as empty, but the 1st will be unset altogether. We- represent both as empty `Text` for simplicity. See below for discussions- about how unset captures may be detected or substituted using this library.+ represent both as empty `Data.Text.Text` for simplicity. See below for+ discussions about how unset captures may be detected or substituted using+ this library. [Named capture]: A parenthesized capture can be named like this: @(?\<foo\>...)@. Whether they have names or not, captures are always@@ -100,9 +101,9 @@ freely inlined; see below. Also of note is the optimization that, for each capture that\'s more than- half the length of the subject, a zero-copy `Text` is produced in constant- time and space. This can yield a large performance boost in many cases,- for example when splitting lines into key-value pairs as in+ half the length of the subject, a zero-copy `Data.Text.Text` is produced in+ constant time and space. This can yield a large performance boost in many+ cases, for example when splitting lines into key-value pairs as in the [teaser](https://github.com/sjshuck/hs-pcre2#teasers). A downside, however, is that retaining these slices in memory will carry the overhead of the dead portions of the subject (still guaranteed to be less than the@@ -114,9 +115,9 @@ [APIs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/matchAll)—including previous versions of [this library](https://github.com/sjshuck/hs-pcre2/issues/17)—where there are separate functions to request single versus global matching, we- accomplish this /(since 2.0.0)/ in a unified fashion using the `Alternative`- typeclass. Typically the user will choose from two instances, `Maybe` and- `[]`:+ accomplish this /(since 2.0.0)/ in a unified fashion using the+ `Control.Applicative.Alternative` typeclass. Typically the user will+ choose from two instances, `Maybe` and `[]`: > b2 :: (Alternative f) => Text -> f Text > b2 = match "b.."@@ -155,7 +156,7 @@ >>> handle @SomePcre2Exception (\_ -> return Nothing) $ evaluate $ broken "foo" Nothing - Or simply select `IO` as the `Alternative` instance:+ Or simply select `IO` as the `Control.Applicative.Alternative` instance: >>> handle @SomePcre2Exception (\_ -> return "broken") $ broken "foo" "broken"@@ -190,8 +191,9 @@ -- @[packed](https://hackage.haskell.org/package/microlens-platform/docs/Lens-Micro-Platform.html#v:packed)@ -- and -- @[unpacked](https://hackage.haskell.org/package/microlens-platform/docs/Lens-Micro-Platform.html#v:packed)@- -- are included for working with `Text`, and it is upwards-compatible with- -- the full [lens](https://hackage.haskell.org/package/lens) library.+ -- are included for working with `Data.Text.Text`, and it is+ -- upwards-compatible with the+ -- full [lens](https://hackage.haskell.org/package/lens) library. -- -- We expose a set of traversals that focus on matched substrings within a -- subject. Like the basic functional regexes, they should be \"compiled\"@@ -347,7 +349,5 @@ pcreVersion) where -import Control.Applicative (Alternative(..))-import Data.Text (Text) import Text.Regex.Pcre2.Internal import Text.Regex.Pcre2.TH
src/hs/Text/Regex/Pcre2/Internal.hs view
@@ -2,6 +2,7 @@ {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiWayIf #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeApplications #-}@@ -12,14 +13,13 @@ import Control.Exception import Control.Monad.State.Strict import Data.Either (partitionEithers)-import Data.Foldable (toList)+import Data.Foldable (foldl', toList) import Data.Functor.Identity (Identity(..)) import Data.IORef import Data.IntMap.Strict (IntMap, (!?)) import qualified Data.IntMap.Strict as IM-import Data.List (foldl', intercalate) import Data.List.NonEmpty (NonEmpty(..))-import qualified Data.List.NonEmpty as NE+import Data.Maybe (fromMaybe) import Data.Monoid (Alt(..), First) import Data.Proxy (Proxy(..)) import Data.Text (Text)@@ -58,6 +58,12 @@ bitOr :: (Foldable t, Bits a) => t a -> a bitOr = foldl' (.|.) zeroBits +-- | Like `lines`, but don\'t remove any characters.+unchompedLines :: String -> [String]+unchompedLines s = case break (== '\n') s of+ (line, _ : rest) -> (line ++ "\n") : unchompedLines rest+ (lastLine, "") -> [lastLine | not $ null lastLine]+ -- | Equivalent to @flip fix@. -- -- Used to express a recursive function of one argument that is called only once@@ -88,20 +94,29 @@ -- | Zero-copy slice a 'Text'. An unset capture is represented by a -- `pcre2_UNSET` range and is interpreted in this library as `Text.empty`. thinSlice :: Text -> Slice -> Text-thinSlice text (Slice off offEnd)- | off == fromIntegral pcre2_UNSET = Text.empty- | otherwise = text+thinSlice text slice = fromMaybe Text.empty $ maybeThinSlice text slice++-- | Like `thinSlice`, but encode `pcre2_UNSET` as `Nothing`.+maybeThinSlice :: Text -> Slice -> Maybe Text+maybeThinSlice text (Slice off offEnd)+ | off == fromIntegral pcre2_UNSET = Nothing+ | otherwise = Just $ text & Text.takeWord16 offEnd & Text.dropWord16 off --- | Slice a 'Text', copying if it\'s less than half of the original.+-- | Slice a 'Text', copying if it\'s less than half of the original. Note this+-- is a lazy, pure operation. smartSlice :: Text -> Slice -> Text-smartSlice text slice =- let substring = thinSlice text slice- in if Text.length substring > Text.length text `div` 2- then substring- else Text.copy substring+smartSlice text slice = fromMaybe Text.empty $ maybeSmartSlice text slice +-- | Like `smartSlice`, but only produce a result when not `pcre2_UNSET`. It is+-- forced when the outer `Maybe` constructor is forced.+maybeSmartSlice :: Text -> Slice -> Maybe Text+maybeSmartSlice text slice = f <$!> maybeThinSlice text slice where+ f substring+ | Text.length substring > Text.length text `div` 2 = substring+ | otherwise = Text.copy substring+ -- | Probably unnecessary, but unrestricted 'castPtr' feels dangerous. class CastCUs a b | a -> b where castCUs :: Ptr a -> Ptr b@@ -730,8 +745,8 @@ -- | Helper to generate public matching functions. pureUserMatcher :: Option -> Text -> Matcher-pureUserMatcher option patt = matcherWithEnv matchEnv where- matchEnv = unsafePerformIO $ userMatchEnv option patt+pureUserMatcher option patt =+ matcherWithEnv $ unsafePerformIO $ userMatchEnv option patt -- | A `Subber` works by first writing results to a reasonably-sized buffer. If -- we run out of room, PCRE2 allows us to simulate the rest of the substitution@@ -939,11 +954,9 @@ top <- pcre2_callout_block_capture_top blockPtr forM (0 :| [1 .. fromIntegral top - 1]) $ \n -> do [start, end] <- forM [0, 1] $ \i -> peekElemOff ovecPtr $ n * 2 + i- return $ if start == pcre2_UNSET- then Nothing- else Just $ smartSlice calloutSubject $ Slice- (fromIntegral start)- (fromIntegral end)+ evaluate $ maybeSmartSlice calloutSubject $ Slice+ (fromIntegral start)+ (fromIntegral end) calloutMark <- do ptr <- pcre2_callout_block_mark blockPtr@@ -978,11 +991,9 @@ ovecCount <- pcre2_substitute_callout_block_oveccount blockPtr forM (0 :| [1 .. fromIntegral ovecCount - 1]) $ \n -> do [start, end] <- forM [0, 1] $ \i -> peekElemOff ovecPtr $ n * 2 + i- return $ if start == pcre2_UNSET- then Nothing- else Just $ smartSlice subCalloutSubject $ Slice- (fromIntegral start)- (fromIntegral end)+ evaluate $ maybeSmartSlice subCalloutSubject $ Slice+ (fromIntegral start)+ (fromIntegral end) subCalloutReplacement <- do outPtr <- pcre2_substitute_callout_block_output blockPtr@@ -1002,23 +1013,25 @@ -- | The most general form of a matching function, which can also be used as a -- @Setter'@ to perform substitutions at the Haskell level.-_capturesInternal :: (Traversable t) =>+_gcaptures :: (Traversable t) => Matcher -> FromMatch t -> Traversal' Text (t Text)-_capturesInternal matcher fromMatch f subject =- traverse f captureTs <&> \captureTs' ->- -- Swag foldl-as-foldr to create only as many segments as we need to- -- stitch back together and no more.- let triples = concat $ zipWith3 zip3- (map toList sliceTs)- (map toList captureTs)- (map toList captureTs')- in Text.concat $ foldr mkSegments termSegments triples 0+_gcaptures matcher fromMatch f subject = traverse f captureTs <&> \captureTs' ->+ -- Swag foldl-as-foldr to create only as many segments as we need to stitch+ -- back together and no more.+ let beforeAndAfter = zip+ (concatMap toList sliceAndCaptureTs)+ (concatMap toList captureTs')+ in Text.concat $ foldr mkSegments termSegments beforeAndAfter 0 where- sliceTs = unsafeLazyStreamToList $ mapMS fromMatch $ matcher subject- captureTs = map (smartSlice subject <$>) sliceTs+ sliceAndCaptureTs = unsafeLazyStreamToList $+ mapMS (fromMatch >=> mapM enrichWithCapture) $ matcher subject+ enrichWithCapture slice = do+ capture <- evaluate $ smartSlice subject slice+ return (slice, capture)+ captureTs = map (snd <$>) sliceAndCaptureTs - mkSegments (Slice off offEnd, c, c') r prevOffEnd+ mkSegments ((Slice off offEnd, c), c') r prevOffEnd | off == fromIntegral pcre2_UNSET || c == c' = -- This substring is unset or unchanged. Keep going without making -- cuts.@@ -1035,9 +1048,9 @@ in [thinSlice subject (Slice off offEnd) | off /= offEnd] -- | A function that takes a C match result and extracts captures into a--- container. We need to pass this effectful callback to `_capturesInternal`--- because of the latter\'s imperative loop that reuses the same--- @pcre2_match_data@ block.+-- container. We need to pass this effectful callback to `_gcaptures` because+-- of the latter\'s imperative loop that reuses the same @pcre2_match_data@+-- block. -- -- The container type is polymorphic and in practice carries a `Traversable` -- constraint. Currently the following containers are used:@@ -1101,8 +1114,8 @@ -- | @matchesOpt mempty = matches@ matchesOpt :: Option -> Text -> Text -> Bool-matchesOpt option patt = has $ _capturesInternal matcher getNoSlices where- matcher = pureUserMatcher option patt+matchesOpt option patt = has $+ _gcaptures (pureUserMatcher option patt) getNoSlices -- | Match a pattern to a subject and return the portion(s) that matched in an -- `Alternative`, or `empty` if no match.@@ -1112,6 +1125,8 @@ match = matchOpt mempty -- | @matchOpt mempty = match@+--+-- @since 2.0.0 matchOpt :: (Alternative f) => Option -> Text -> Text -> f Text matchOpt option patt = toAlternativeOf $ _matchOpt option patt @@ -1140,8 +1155,8 @@ -- subOpt SubGlobal = gsub -- @ subOpt :: Option -> Text -> Text -> Text -> Text-subOpt option patt replacement = snd . unsafePerformIO . subber where- subber = pureUserSubber option patt replacement+subOpt option patt replacement =+ snd . unsafePerformIO . pureUserSubber option patt replacement -- | Given a pattern, produce a traversal (0 or more targets) that focuses from -- a subject to each non-empty list of captures that pattern matches.@@ -1184,8 +1199,7 @@ -- | @_capturesOpt mempty = _captures@ _capturesOpt :: Option -> Text -> Traversal' Text (NonEmpty Text)-_capturesOpt option patt = _capturesInternal matcher getAllSlices where- matcher = pureUserMatcher option patt+_capturesOpt option patt = _gcaptures (pureUserMatcher option patt) getAllSlices -- | Given a pattern, produce a traversal (0 or more targets) that focuses from -- a subject to the non-overlapping portions of it that match.@@ -1196,8 +1210,8 @@ -- | @_matchOpt mempty = _match@ _matchOpt :: Option -> Text -> Traversal' Text Text-_matchOpt option patt = _capturesInternal matcher get0thSlice . _Identity where- matcher = pureUserMatcher option patt+_matchOpt option patt =+ _gcaptures (pureUserMatcher option patt) get0thSlice . _Identity -- * Support for Template Haskell compile-time regex analysis @@ -1262,13 +1276,26 @@ -- last character). data Pcre2CompileException = Pcre2CompileException !CInt !Text !PCRE2_SIZE instance Show Pcre2CompileException where- show (Pcre2CompileException x patt offset) = intercalate "\n" $ [- "pcre2_compile: " ++ Text.unpack (getErrorMessage x),- replicate tab ' ' ++ Text.unpack patt] ++- [replicate (tab + offset') ' ' ++ "^" | offset' < Text.length patt]+ show (Pcre2CompileException x patt offset) =+ "pcre2_compile: " ++ Text.unpack (getErrorMessage x) ++ "\n" +++ concatMap (replicate tab ' ' ++) pattLinesMaybeWithCaret where tab = 20+ pattLinesMaybeWithCaret+ | offset' == Text.length patt = pattLines+ | otherwise = insertCaretLine numberedPattLines offset' = fromIntegral offset+ pattLines = unchompedLines $ Text.unpack patt+ numberedPattLines = zip [0 ..] pattLines+ (caretRow, caretCol) = (!! offset') $ do+ (row, line) <- numberedPattLines+ (col, _) <- zip [0 ..] line+ return (row, col)+ insertCaretLine = concatMap $ \(n, line) -> if+ | n /= caretRow -> [line]+ | last line == '\n' -> [line, caretLine ++ "\n"]+ | otherwise -> [line ++ "\n", caretLine]+ caretLine = replicate caretCol ' ' ++ "^" instance Exception Pcre2CompileException where toException = toException . SomePcre2Exception fromException = fromException >=> \(SomePcre2Exception e) -> cast e
src/hs/Text/Regex/Pcre2/TH.hs view
@@ -15,7 +15,6 @@ import Control.Applicative (Alternative(..)) import Data.IORef import Data.List.NonEmpty (NonEmpty(..))-import qualified Data.List.NonEmpty as NE import Data.Map.Lazy (Map) import qualified Data.Map.Lazy as Map import Data.Proxy (Proxy(..))@@ -47,6 +46,9 @@ -- be written with the help of @{-\# LANGUAGE PartialTypeSignatures \#-}@. newtype Captures (info :: CapturesInfo) = Captures (NonEmpty Text) +captured :: Lens' (NonEmpty Text) (Captures info)+captured f cs = f (Captures cs) <&> \(Captures cs') -> cs'+ -- | The kind of `Captures`\'s @info@. The first number is the total number of -- parenthesized captures, and the list is a lookup table from capture names to -- numbers.@@ -98,9 +100,8 @@ -- | Like `capture` but focus from a `Captures` to a capture. _capture :: forall i info num. (CaptNum i info ~ num, KnownNat num) => Lens' (Captures info) Text-_capture f (Captures cs) =- let (ls, c : rs) = NE.splitAt (fromInteger $ natVal @num Proxy) cs- in f c <&> \c' -> Captures $ NE.fromList $ ls ++ c' : rs+_capture = _Captures . singular (ix $ fromInteger $ natVal @num Proxy) where+ _Captures f (Captures cs) = Captures <$> f cs -- | Unexported, top-level `IORef` that\'s created upon the first runtime -- evaluation of a Template Haskell `Matcher`.@@ -134,13 +135,13 @@ [] -> return Nothing -- One or more parenthesized captures. Present- -- [Just "foo", Nothing, Nothing, Just "bar"]+ -- [Just "foo", Nothing, Nothing, Just "bar", Nothing] -- as- -- '(4, '[ '("foo", 1), '("bar", 4)]).+ -- '(5, '[ '("foo", 1), '("bar", 4)]). captureNames -> Just <$> promotedTupleT 2 `appT` hiQ `appT` kvsQ where- -- 4+ -- 5 hiQ = litT $ numTyLit $ fromIntegral $ length captureNames- -- '[ '("foo", 1), '("bar", 4), '("", 0)]+ -- '[ '("foo", 1), '("bar", 4)] kvsQ = case toKVs captureNames of -- We can't splice '[] because it doesn't kind-check. See above. [] -> [t| NoNamedCaptures |]@@ -154,32 +155,29 @@ -- | Helper for `regex` with no parenthesized captures. matchTH :: (Alternative f) => Text -> Text -> f Text matchTH patt = toAlternativeOf $- _capturesInternal (memoMatcher patt) get0thSlice . _Identity+ _gcaptures (memoMatcher patt) get0thSlice . _Identity -- | Helper for `regex` with parenthesized captures. capturesTH :: (Alternative f) => Text -> Proxy info -> Text -> f (Captures info) capturesTH patt _ = toAlternativeOf $- _capturesInternal (memoMatcher patt) getAllSlices . to Captures+ _gcaptures (memoMatcher patt) getAllSlices . captured -- | Helper for `regex` as a guard pattern. matchesTH :: Text -> Text -> Bool-matchesTH patt = has $ _capturesInternal (memoMatcher patt) getNoSlices+matchesTH patt = has $ _gcaptures (memoMatcher patt) getNoSlices -- | Helper for `regex` as a pattern that binds local variables.-capturesNumberedTH :: Text -> NonEmpty Int -> Text -> [Text]-capturesNumberedTH patt numbers = concatMap NE.toList . toListOf _cs where- _cs = _capturesInternal (memoMatcher patt) fromMatch- fromMatch = getWhitelistedSlices numbers+capturesNumberedTH :: Text -> [Int] -> Text -> [Text]+capturesNumberedTH patt numbers = view $+ _gcaptures (memoMatcher patt) (getWhitelistedSlices numbers) -- | Helper for `_regex` with no parenthesized captures. _matchTH :: Text -> Traversal' Text Text-_matchTH patt = _capturesInternal (memoMatcher patt) get0thSlice . _Identity+_matchTH patt = _gcaptures (memoMatcher patt) get0thSlice . _Identity -- | Helper for `_regex` with parenthesized captures. _capturesTH :: Text -> Proxy info -> Traversal' Text (Captures info)-_capturesTH patt _ = _cs . wrapped where- _cs = _capturesInternal (memoMatcher patt) getAllSlices- wrapped f cs = f (Captures cs) <&> \(Captures cs') -> cs'+_capturesTH patt _ = _gcaptures (memoMatcher patt) getAllSlices . captured -- | === As an expression --@@ -231,22 +229,22 @@ quotePat = \s -> do captureNames <- predictCaptureNamesQ s - case NE.nonEmpty $ toKVs captureNames of+ case toKVs captureNames of -- No named captures. Test whether the string matches without -- creating any new Text values.- Nothing -> viewP+ [] -> viewP [e| matchesTH (Text.pack $(stringE s)) |] [p| True |] -- One or more named captures. Attempt to bind only those to local -- variables of the same names.- Just numberedNames -> viewP e p where- (numbers, names) = NE.unzip numberedNames+ numberedNames -> viewP e p where+ (numbers, names) = unzip numberedNames e = [e| capturesNumberedTH (Text.pack $(stringE s)) $(liftData numbers) |]- p = foldr f wildP names where- f name r = conP '(:) [varP $ mkName $ Text.unpack name, r],+ p = foldr f wildP names+ f name r = conP '(:) [varP $ mkName $ Text.unpack name, r], quoteType = const $ fail "regex: cannot produce a type", @@ -262,11 +260,11 @@ -- > import Control.Lens -- > import Data.Text.Lens -- >--- > embeddedNumber :: Traversal' String Int--- > embeddedNumber = packed . [_regex|\d+|] . unpacked . _Show+-- > embeddedNumbers :: Traversal' String Int+-- > embeddedNumbers = packed . [_regex|\d+|] . unpacked . _Show -- > -- > main :: IO ()--- > main = putStrLn $ "There are 14 competing standards" & embeddedNumber %~ (+ 1)+-- > main = putStrLn $ "There are 14 competing standards" & embeddedNumbers %~ (+ 1) -- > -- > -- There are 15 competing standards _regex :: QuasiQuoter
src/hs/Text/Regex/Pcre2/Unsafe.hs view
@@ -8,7 +8,7 @@ -- them here for completeness and use them to implement unit tests for this -- library; for ordinary use, however, seek other means to accomplish whatever -- is needed (such as accreting effects with optics), since they carry all the--- problems of `unsafePerformIO`. See+-- problems of `System.IO.Unsafe.unsafePerformIO`. See -- the [C API docs](https://pcre.org/current/doc/html/pcre2callout.html) -- for more information. module Text.Regex.Pcre2.Unsafe (@@ -28,5 +28,4 @@ SubCalloutResult(..)) where -import System.IO.Unsafe (unsafePerformIO) import Text.Regex.Pcre2.Internal