packages feed

substring-parser-0.4.1.0: src/Text/Parser/Substring.hs

module Text.Parser.Substring
  ( matchAll
  , replaceFileOnceWithParser
  , replaceOnceWithParser
  , onceReplacify
  , takeMatch
  ) where

import           Control.Applicative ((<|>), optional)
import qualified Data.Attoparsec.Text as AT
import qualified Data.DList as DList
import           Data.Maybe (isJust)
import           Data.Monoid ((<>))
import           Data.Text (Text)
import qualified Data.Text.Lazy as Text
import qualified Data.Text.Lazy.Builder as TextBuilder
import           Data.Text.Lazy.Builder (Builder)
import qualified Data.Text.IO as Text
import           Text.Parser.Char (CharParsing, anyChar)
import           Text.Parser.Combinators (eof)

import           Debug.NoTrace (trace, traceM)


replaceFileOnceWithParser :: AT.Parser Text -> FilePath -> IO ()
replaceFileOnceWithParser p filePath =
  Text.writeFile filePath =<< replaceOnceWithParser p <$> Text.readFile filePath


replaceOnceWithParser :: AT.Parser Text -> Text -> Text
replaceOnceWithParser p t =
  maybe t (Text.toStrict . TextBuilder.toLazyText)
    $ AT.maybeResult
    $ traceId "fed"
    $ flip AT.feed ""
    $ traceId "parsed"
    $ AT.parse (onceReplacify p) t


-- | ref: https://stackoverflow.com/questions/29549435/parsec-how-to-find-matches-within-a-string
{-# INLINE matchAll #-}
matchAll :: (Monad m, CharParsing m) => m a -> m [a]
matchAll p = DList.toList <$> loop DList.empty
  where
    loop xs = do
      mMatched <- optional p
      case mMatched of
          Just matched ->
            loop (xs `DList.snoc` matched)
          _ ->
            (anyChar *> loop xs) <|> pure xs


onceReplacify :: (Monad m, CharParsing m) => m Text -> m Builder
onceReplacify p =
  let firstToMatched =
        (TextBuilder.fromText <$> p)
          <|> ((<>) <$> (TextBuilder.singleton <$> anyChar) <*> firstToMatched)
  in
    (<>) <$> firstToMatched <*> takeBuilder


{-# INLINE takeBuilder #-}
takeBuilder :: (Monad m, CharParsing m) => m Builder
takeBuilder = loop mempty
 where
  loop b = (eof *> pure b) <|> ( do
      c <- anyChar
      loop (b <> TextBuilder.singleton c)
    )


{-# INLINE takeMatch #-}
takeMatch :: (Monad m, CharParsing m) => m a -> m (Text, a)
takeMatch p = loop mempty
  where
    loop taken = do
      mMatched <- optional p
      traceM $ "isJust mMatched: " ++ show (isJust mMatched)
      case mMatched of
          Just matched ->
            return (Text.toStrict $ TextBuilder.toLazyText taken, matched)
          _ -> do
            newChar <- (TextBuilder.singleton <$> anyChar) <|> pure mempty
            traceM $ "newChar: " ++ show newChar
            loop (taken <> newChar)


{-# INLINE traceIdVia #-}
traceIdVia :: Show b => (a -> b) -> String -> a -> a
traceIdVia via prefix x = trace (prefix ++ ": " ++ show (via x)) x


{-# INLINE traceId #-}
traceId :: Show a => String -> a -> a
traceId = traceIdVia id