packages feed

ghc-stack-profiler-0.5.0.0: src/GHC/Stack/Profiler/Internal/Util.hs

module GHC.Stack.Profiler.Internal.Util (
  castPtrToWord64,

  -- * Glob Patterns
  Glob,
  matches,

  -- * DList
  DList,

  -- * WriterT
  WriterT,
  tell,
  runWriterT,
) where

import Control.Monad.IO.Class (MonadIO (..))
import Data.String (IsString (..))
import Data.Word
import Foreign.Ptr
import GHC.IsList (IsList (..))

castPtrToWord64 :: Ptr a -> Word64
castPtrToWord64 ptr = case ptrToWordPtr ptr of
  WordPtr w -> fromIntegral w -- On platforms that use 32-bit systems, the key is still Word64

-------------------------------------------------------------------------------
-- Glob
-------------------------------------------------------------------------------

-- NOTE: The `Glob` type (but not its implementation) is part of the public API.

-- | A glob pattern.
--
--   Use `fromString` to construct glob patterns from strings.
--
--   A @*@ matches any string, including the empty string.
--
--   One can remove the special meaning of @*@ by preceding it with a backslash.
--
--  @since 0.5.0.0
newtype Glob = Glob [GlobPart]

data GlobPart = Wildcard | Literal String

instance IsString Glob where
  fromString :: String -> Glob
  fromString = Glob . go
   where
    go :: String -> [GlobPart]
    go [] = []
    go ('*' : pat) = Wildcard : go pat
    go ('\\' : '*' : pat) = literal ['*'] (go pat)
    go (c : pat) = literal [c] (go pat)

    literal :: String -> [GlobPart] -> [GlobPart]
    literal lit (Literal lit' : pat) = Literal (lit <> lit') : pat
    literal lit pat = Literal lit : pat

instance Show Glob where
  showsPrec :: Int -> Glob -> ShowS
  showsPrec p (Glob pat) = showsPrec p (go pat)
   where
    go [] = []
    go (Wildcard : pat') = '*' : go pat'
    go (Literal lit : pat') = escape lit <> go pat'

    escape :: String -> String
    escape [] = []
    escape ('*' : str) = '\\' : '*' : escape str
    escape (c : str) = c : escape str

-- NOTE: The `matches` function is part of the public API.

-- | Test if the given `Glob` pattern matches the given `String`.
--
--   @since 0.5.0.0
matches :: Glob -> String -> Bool
matches (Glob parts) = go parts
 where
  go [] _str = True
  go [Wildcard] _str = True
  go (Wildcard : pat'@(Wildcard : _)) str = go pat' str
  go (Wildcard : Literal lit : pat') str = any (go pat') (skipWildcardLiteral lit str)
  go (Literal lit : pat') str = maybe False (go pat') (skipPrefix lit str)

  -- Stream the possible remainders after matching a wildcard followed by a literal.
  --
  -- NOTE: O( n * m ) where n = length str and m = length lit.
  skipWildcardLiteral :: String -> String -> [String]
  skipWildcardLiteral _lit [] = []
  skipWildcardLiteral lit str@(_c : str')
    -- NOTE: yield suff, but continue searching from str', in case of overlaps.
    | Just suff <- skipPrefix lit str = suff : skipWildcardLiteral lit str'
    | otherwise = skipWildcardLiteral lit str'

  -- Stream the possible remainders after matching a literal.
  --
  -- NOTE: O( m ) where m = length lit
  skipPrefix :: String -> String -> Maybe String
  skipPrefix [] str = Just str
  skipPrefix (_ : _) [] = Nothing
  skipPrefix (l : lit') (c : str') = if l == c then skipPrefix lit' str' else Nothing

-------------------------------------------------------------------------------
-- DList
-------------------------------------------------------------------------------

newtype DList a = MkDList {unDList :: [a] -> [a]}

instance Semigroup (DList a) where
  (<>) :: DList a -> DList a -> DList a
  MkDList xs <> MkDList ys = MkDList (xs . ys)
  {-# INLINE (<>) #-}

instance Monoid (DList a) where
  mempty :: DList a
  mempty = MkDList id
  {-# INLINE mempty #-}

instance IsList (DList a) where
  type Item (DList a) = a

  toList :: DList a -> [a]
  toList = ($ []) . unDList
  {-# INLINE toList #-}

  fromList :: [a] -> DList a
  fromList = MkDList . (++)
  {-# INLINE fromList #-}

-------------------------------------------------------------------------------
-- WriterT
-------------------------------------------------------------------------------

newtype WriterT w m a = WriterT {unWriterT :: w -> m (a, w)}

instance (Functor m) => Functor (WriterT w m) where
  fmap :: (Functor m) => (a -> b) -> WriterT w m a -> WriterT w m b
  fmap f m = WriterT $ \w -> (\(a, w') -> (f a, w')) <$> unWriterT m w
  {-# INLINE fmap #-}

instance (Functor m, Monad m) => Applicative (WriterT w m) where
  pure ::
    (Functor m, Monad m) =>
    a -> WriterT w m a
  pure a = WriterT $ \w -> return (a, w)
  {-# INLINE pure #-}

  (<*>) ::
    (Functor m, Monad m) =>
    WriterT w m (a -> b) -> WriterT w m a -> WriterT w m b
  WriterT mf <*> WriterT mx = WriterT $ \w -> do
    (f, w') <- mf w
    (x, w'') <- mx w'
    return (f x, w'')
  {-# INLINE (<*>) #-}

instance (Monad m) => Monad (WriterT w m) where
  (>>=) ::
    (Monad m) =>
    WriterT w m a -> (a -> WriterT w m b) -> WriterT w m b
  m >>= k = WriterT $ \w -> do
    (a, w') <- unWriterT m w
    unWriterT (k a) w'
  {-# INLINE (>>=) #-}

writer :: (Monoid w, Monad m) => (a, w) -> WriterT w m a
writer (a, w') = WriterT $ \w ->
  let wt = w `mappend` w' in wt `seq` return (a, wt)
{-# INLINE writer #-}

tell :: (Monoid w, Monad m) => w -> WriterT w m ()
tell w = writer ((), w)
{-# INLINE tell #-}

runWriterT :: (Monoid w) => WriterT w m a -> m (a, w)
runWriterT m = unWriterT m mempty
{-# INLINE runWriterT #-}

lift :: (Monad m) => m a -> WriterT w m a
lift m = WriterT $ \w -> do
  a <- m
  return (a, w)
{-# INLINE lift #-}

instance (MonadIO m) => MonadIO (WriterT w m) where
  liftIO :: (MonadIO m) => IO a -> WriterT w m a
  liftIO = lift . liftIO
  {-# INLINE liftIO #-}