packages feed

trace-embrace-1.1.0: src/Debug/TraceEmbrace/TH.hs

{-# OPTIONS_HADDOCK hide, prune #-}

-- | Tracing with TH
module Debug.TraceEmbrace.TH (tr, tw, tw', trIo, u, a, s_, tg, tg', underbar, trFunMarker, trIoFunMarker) where

import Debug.Trace
import Debug.TraceEmbrace.Config
import Debug.TraceEmbrace.FileIndex (FunName (..))
import Debug.TraceEmbrace.Internal.Rewrap
import Debug.TraceEmbrace.Internal.TH qualified as I
import Haddock.UseRefs
import Language.Haskell.TH
import Language.Haskell.TH.Syntax

countDocRefs

-- | TH version of 'trace' and 'traceEvent'
-- The message is formatted according to 'TraceMessageFormat'.
-- The generated expression has type @forall r (a :: TYPE r) b a. 'Rewrap' a b => a -> a@.
-- 'id' is generated if effective trace level is lower than trace level threshold.
-- Example:
--
-- > foo x = $(tr "get/x") x
--
-- Output:
--
-- > Module::foo get; x : 132
tr :: String -> Q Exp
tr tm = I.tr [| \x -> x |] tm

-- | TH version of 'traceWith' and 'traceEventWith'
-- The message is formatted according to 'TraceMessageFormat'.
-- The generated expression has type @forall r (a :: TYPE r) b a. (Show a, Rewrap a b) => a -> a@.
-- 'id' is generated if effective trace level is lower than trace level threshold.
-- Example:
--
-- > foo x = $(tw "get/x") (x + 1)
--
-- Output:
--
-- > Module::foo get; x : 132 => 133
tw :: String -> Q Exp
tw = I.tw [| \x -> x |]

-- | Like 'tw' but return value is wrapped with 'ShowTrace'.
tw' :: String -> Q Exp
tw' = I.tw' [| \x -> x |]

-- | TH version of 'traceIO' and 'traceEventIO'
-- The message is formatted according to 'TraceMessageFormat'.
-- Example:
--
-- > foo x = $(trIo "get/x") >> pure x
--
-- Output:
--
-- > Module::foo get; x : 132
trIo :: String -> Q Exp
trIo = I.trIo [| pure () |]

-- | TH version of 'traceMarker' where module and function
-- are used as a marker. Trace level is used.
trFunMarker :: Q Exp
trFunMarker = I.trFunMarker [| \x -> x |]

-- | TH version of 'traceMarkerIO' where module and function
-- are used as a marker. Trace level is not used.
trIoFunMarker :: Q Exp
trIoFunMarker = I.trIoFunMarker [| pure () |]

data ArgPatCounter
  = ArgPatCounter
    { funName :: FunName
    , argNames :: [Name]
    } deriving (Show)

data Undebar = Undebar

instance Show Undebar where
  show _ = "_"

underbar :: Undebar
underbar = Undebar
-- | Generates consequent pattern variable for tracing arguments of a guarded function.
-- It is assumed that 'a' is used together with 'tg' and 'u'.
--
-- > foo $a $a $a | $tg = $u
-- > foo 0  _  _ = 0
-- > foo x y z = x + y + z
--
a :: Q Pat
a = do
  cfn <- I.currentFunName
  getQ >>= \case
    Nothing -> reset cfn
    Just (ArgPatCounter fn aNames)
      | fn == cfn -> go aNames cfn
      | otherwise -> reset cfn
  where
    reset cfn = go [] cfn
    go ans cfn = do
      nn <- newName $ "traceEmbracePatArg" <> (show $ length ans)
      putQ (ArgPatCounter cfn $ nn : ans)
      varP nn

-- | Similar to 'a', but argument is not included in trace message.
s_ :: Q Pat
s_ = do
  cfn <- I.currentFunName
  getQ >>= \case
    Nothing -> reset cfn
    Just (ArgPatCounter fn aNames)
      | fn == cfn -> go aNames cfn
      | otherwise -> reset cfn
  where
    reset cfn = go [] cfn
    go ans cfn = putQ (ArgPatCounter cfn $ 'underbar : ans) >> [p|_|]

-- | Expands to @$(tr "/a b c d...") False@
tg :: Q Exp
tg = tg' ""

-- | Similar to 'tg' with message prefix with the argument.
tg' :: String -> Q Exp
tg' msgPrefix = do
  cfn <- I.currentFunName
  getQ >>= \case
    Nothing -> er
    Just (ArgPatCounter fn aNames)
      | fn == cfn -> [|$(I.tr' [| \x -> x |] msgPrefix $ reverse aNames) False|]
      | otherwise -> er
  where
    er = fail "Use 'Debug.TraceEmbrace.TH.a' macro to capture function arguments before calling 'tg'"

-- | Shortcut for 'undefined'
u :: Q Exp
u = [| undefined |]