    \begin{code}
    {-# LANGUAGE NoImplicitPrelude          #-}
    {-# LANGUAGE RecordWildCards            #-}
    {-# LANGUAGE FlexibleContexts           #-}
    {-# LANGUAGE CPP                        #-}

    module Text.RE.Tools.Grep
      (
      -- Grep
      -- $tutorial
        grep
      , Verbosity(..)
      , Line(..)
      , grepLines
      , grepFilter
      , GrepScript
      , grepWithScript
      , report
      , linesMatched
      -- * IsRegex
      , IsRegex(..)
      , SearchReplace(..)
      , searchReplaceAll
      , searchReplaceFirst
      -- * LineNo
      , LineNo(..)
      , firstLine
      , getLineNo
      , lineNo
      -- * Replace
      , module Text.RE.Replace
      ) where

    import qualified Data.ByteString.Lazy.Char8               as LBS
    import           Prelude.Compat
    import           Text.Printf
    import           Text.RE.Replace
    import           Text.RE.Tools.IsRegex
    import           Text.RE.ZeInternals.Types.LineNo
    \end{code}

    \begin{code}
    -- | operates a bit like classic @grep@ printing out the lines matched
    grep :: IsRegex re LBS.ByteString => Verbosity -> re -> FilePath -> IO ()
    grep :: forall re.
    IsRegex re ByteString =>
    Verbosity -> re -> String -> IO ()
    grep Verbosity
    v re
    rex String
    fp = re -> String -> IO [Line ByteString]
    forall re.
    IsRegex re ByteString =>
    re -> String -> IO [Line ByteString]
    grepLines re
    rex String
    fp IO [Line ByteString] -> ([Line ByteString] -> IO ()) -> IO ()
    forall a b. IO a -> (a -> IO b) -> IO b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= String -> IO ()
    putStr (String -> IO ())
    -> ([Line ByteString] -> String) -> [Line ByteString] -> IO ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Verbosity -> [Line ByteString] -> String
    report Verbosity
    v
    \end{code}

    \begin{code}
    -- | specifies whether to return the lines matched or missed
    data Verbosity
      = LinesMatched
      | LinesNotMatched
      deriving (Int -> Verbosity -> ShowS
    [Verbosity] -> ShowS
    Verbosity -> String
    (Int -> Verbosity -> ShowS)
    -> (Verbosity -> String)
    -> ([Verbosity] -> ShowS)
    -> Show Verbosity
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> Verbosity -> ShowS
    showsPrec :: Int -> Verbosity -> ShowS
    $cshow :: Verbosity -> String
    show :: Verbosity -> String
    $cshowList :: [Verbosity] -> ShowS
    showList :: [Verbosity] -> ShowS
    Show,Verbosity -> Verbosity -> Bool
    (Verbosity -> Verbosity -> Bool)
    -> (Verbosity -> Verbosity -> Bool) -> Eq Verbosity
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    $c== :: Verbosity -> Verbosity -> Bool
    == :: Verbosity -> Verbosity -> Bool
    $c/= :: Verbosity -> Verbosity -> Bool
    /= :: Verbosity -> Verbosity -> Bool
    Eq,Eq Verbosity
    Eq Verbosity =>
    (Verbosity -> Verbosity -> Ordering)
    -> (Verbosity -> Verbosity -> Bool)
    -> (Verbosity -> Verbosity -> Bool)
    -> (Verbosity -> Verbosity -> Bool)
    -> (Verbosity -> Verbosity -> Bool)
    -> (Verbosity -> Verbosity -> Verbosity)
    -> (Verbosity -> Verbosity -> Verbosity)
    -> Ord Verbosity
    Verbosity -> Verbosity -> Bool
    Verbosity -> Verbosity -> Ordering
    Verbosity -> Verbosity -> Verbosity
    forall a.
    Eq a =>
    (a -> a -> Ordering)
    -> (a -> a -> Bool)
    -> (a -> a -> Bool)
    -> (a -> a -> Bool)
    -> (a -> a -> Bool)
    -> (a -> a -> a)
    -> (a -> a -> a)
    -> Ord a
    $ccompare :: Verbosity -> Verbosity -> Ordering
    compare :: Verbosity -> Verbosity -> Ordering
    $c< :: Verbosity -> Verbosity -> Bool
    < :: Verbosity -> Verbosity -> Bool
    $c<= :: Verbosity -> Verbosity -> Bool
    <= :: Verbosity -> Verbosity -> Bool
    $c> :: Verbosity -> Verbosity -> Bool
    > :: Verbosity -> Verbosity -> Bool
    $c>= :: Verbosity -> Verbosity -> Bool
    >= :: Verbosity -> Verbosity -> Bool
    $cmax :: Verbosity -> Verbosity -> Verbosity
    max :: Verbosity -> Verbosity -> Verbosity
    $cmin :: Verbosity -> Verbosity -> Verbosity
    min :: Verbosity -> Verbosity -> Verbosity
    Ord)
    \end{code}

    \begin{code}
    -- | 'grepLines' returns a 'Line' for each line in the file, listing all
    -- of the 'Matches' for that line
    data Line s =
      Line
        { forall s. Line s -> LineNo
    getLineNumber  :: LineNo    -- ^ the 'LineNo' for this line
        , forall s. Line s -> Matches s
    getLineMatches :: Matches s -- ^ all the 'Matches' of the RE on this line
        }
      deriving (Int -> Line s -> ShowS
    [Line s] -> ShowS
    Line s -> String
    (Int -> Line s -> ShowS)
    -> (Line s -> String) -> ([Line s] -> ShowS) -> Show (Line s)
    forall s. Show s => Int -> Line s -> ShowS
    forall s. Show s => [Line s] -> ShowS
    forall s. Show s => Line s -> String
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: forall s. Show s => Int -> Line s -> ShowS
    showsPrec :: Int -> Line s -> ShowS
    $cshow :: forall s. Show s => Line s -> String
    show :: Line s -> String
    $cshowList :: forall s. Show s => [Line s] -> ShowS
    showList :: [Line s] -> ShowS
    Show)
    \end{code}

    \begin{code}
    -- | returns a 'Line' for each line in the file, enumerating all of the
    -- matches for that line
    grepLines :: IsRegex re LBS.ByteString
              => re
              -> FilePath
              -> IO [Line LBS.ByteString]
    grepLines :: forall re.
    IsRegex re ByteString =>
    re -> String -> IO [Line ByteString]
    grepLines re
    rex String
    fp = re -> ByteString -> [Line ByteString]
    forall re s. IsRegex re s => re -> s -> [Line s]
    grepFilter re
    rex (ByteString -> [Line ByteString])
    -> IO ByteString -> IO [Line ByteString]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> String -> IO ByteString
    LBS.readFile String
    fp
    \end{code}

    \begin{code}
    -- | returns a 'Line' for each line in the argument text, enumerating
    -- all of the matches for that line
    grepFilter :: IsRegex re s => re -> s -> [Line s]
    grepFilter :: forall re s. IsRegex re s => re -> s -> [Line s]
    grepFilter re
    rex = GrepScript re s (Line s) -> [s] -> [Line s]
    forall re s t. IsRegex re s => GrepScript re s t -> [s] -> [t]
    grepWithScript [(re
    rex,LineNo -> Matches s -> Maybe (Line s)
    forall {s}. LineNo -> Matches s -> Maybe (Line s)
    mk)] ([s] -> [Line s]) -> (s -> [s]) -> s -> [Line s]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . s -> [s]
    forall a. Replace a => a -> [a]
    linesR
      where
        mk :: LineNo -> Matches s -> Maybe (Line s)
    mk LineNo
    i Matches s
    mtchs = Line s -> Maybe (Line s)
    forall a. a -> Maybe a
    Just (Line s -> Maybe (Line s)) -> Line s -> Maybe (Line s)
    forall a b. (a -> b) -> a -> b
    $ LineNo -> Matches s -> Line s
    forall s. LineNo -> Matches s -> Line s
    Line LineNo
    i Matches s
    mtchs
    \end{code}

    \begin{code}
    -- | a GrepScript lists RE-action associations, with the first RE to match
    -- a line selecting the action to be executed on each line in the file
    type GrepScript re s t = [(re,LineNo -> Matches s -> Maybe t)]

    -- | given a list of lines, apply the 'GrepScript' to each line of the file
    grepWithScript :: IsRegex re s => GrepScript re s t -> [s] -> [t]
    grepWithScript :: forall re s t. IsRegex re s => GrepScript re s t -> [s] -> [t]
    grepWithScript GrepScript re s t
    scr = LineNo -> [s] -> [t]
    loop LineNo
    firstLine
      where
        loop :: LineNo -> [s] -> [t]
    loop LineNo
    _ []       = []
        loop LineNo
    i (s
    ln:[s]
    lns) = LineNo -> [t] -> [t]
    forall a b. a -> b -> b
    seq LineNo
    i ([t] -> [t]) -> [t] -> [t]
    forall a b. (a -> b) -> a -> b
    $ LineNo -> s -> [s] -> GrepScript re s t -> [t]
    choose LineNo
    i s
    ln [s]
    lns GrepScript re s t
    scr

        choose :: LineNo -> s -> [s] -> GrepScript re s t -> [t]
    choose LineNo
    i s
    _  [s]
    lns []             = LineNo -> [s] -> [t]
    loop (LineNo -> LineNo
    forall a. Enum a => a -> a
    succ LineNo
    i) [s]
    lns
        choose LineNo
    i s
    ln [s]
    lns ((re
    rex,LineNo -> Matches s -> Maybe t
    f):GrepScript re s t
    scr') = case LineNo -> Matches s -> Maybe t
    f LineNo
    i (Matches s -> Maybe t) -> Matches s -> Maybe t
    forall a b. (a -> b) -> a -> b
    $ re -> s -> Matches s
    forall re s. IsRegex re s => re -> s -> Matches s
    matchMany re
    rex s
    ln of
          Maybe t
    Nothing -> LineNo -> s -> [s] -> GrepScript re s t -> [t]
    choose LineNo
    i s
    ln [s]
    lns GrepScript re s t
    scr'
          Just t
    t  -> t
    t t -> [t] -> [t]
    forall a. a -> [a] -> [a]
    : LineNo -> [s] -> [t]
    loop (LineNo -> LineNo
    forall a. Enum a => a -> a
    succ LineNo
    i) [s]
    lns

    -- | generate a grep report from a list of 'Line'
    report :: Verbosity -> [Line LBS.ByteString] -> String
    report :: Verbosity -> [Line ByteString] -> String
    report Verbosity
    v = [String] -> String
    unlines ([String] -> String)
    -> ([Line ByteString] -> [String]) -> [Line ByteString] -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Line ByteString -> String) -> [Line ByteString] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map Line ByteString -> String
    forall {b}. PrintfType b => Line ByteString -> b
    fmt ([Line ByteString] -> [String])
    -> ([Line ByteString] -> [Line ByteString])
    -> [Line ByteString]
    -> [String]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Verbosity -> [Line ByteString] -> [Line ByteString]
    forall s. Verbosity -> [Line s] -> [Line s]
    linesMatched Verbosity
    v
      where
        fmt :: Line ByteString -> b
    fmt Line{LineNo
    Matches ByteString
    getLineNumber :: forall s. Line s -> LineNo
    getLineMatches :: forall s. Line s -> Matches s
    getLineNumber :: LineNo
    getLineMatches :: Matches ByteString
    ..} =
          String -> Int -> String -> b
    forall r. PrintfType r => String -> r
    printf String
    "%05d %s" (LineNo -> Int
    getLineNo LineNo
    getLineNumber) (String -> b) -> String -> b
    forall a b. (a -> b) -> a -> b
    $
              ByteString -> String
    LBS.unpack (ByteString -> String) -> ByteString -> String
    forall a b. (a -> b) -> a -> b
    $ Matches ByteString -> ByteString
    forall a. Matches a -> a
    matchesSource Matches ByteString
    getLineMatches

    -- | given a 'velocity' flag filter out either the lines matched or not
    -- matched
    linesMatched :: Verbosity -> [Line s] -> [Line s]
    linesMatched :: forall s. Verbosity -> [Line s] -> [Line s]
    linesMatched Verbosity
    v = (Line s -> Bool) -> [Line s] -> [Line s]
    forall a. (a -> Bool) -> [a] -> [a]
    filter ((Line s -> Bool) -> [Line s] -> [Line s])
    -> (Line s -> Bool) -> [Line s] -> [Line s]
    forall a b. (a -> b) -> a -> b
    $ Bool -> Bool
    f (Bool -> Bool) -> (Line s -> Bool) -> Line s -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Matches s -> Bool
    forall a. Matches a -> Bool
    anyMatches (Matches s -> Bool) -> (Line s -> Matches s) -> Line s -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Line s -> Matches s
    forall s. Line s -> Matches s
    getLineMatches
      where
        f :: Bool -> Bool
    f = case Verbosity
    v of
          Verbosity
    LinesMatched    -> Bool -> Bool
    forall a. a -> a
    id
          Verbosity
    LinesNotMatched -> Bool -> Bool
    not
    \end{code}

    \begin{code}
    -- $tutorial
    -- The Grep toolkit matches REs against each line of a text.
    --
    -- See the Regex Tools tutorial at http://re-tutorial-tools.regex.uk
    \end{code}