{-# LANGUAGE ViewPatterns #-} module Comprehension.Plugin (plugin) where import Prelude hiding (init, last) import Data.Bool import Data.Data import Data.Generics as SYB import Data.String (fromString) import Type.Reflection as R import qualified GhcPlugins as GHC import HsExtension (GhcPs, NoExt (..)) import HsSyn import qualified HsTypes import Outputable (SDoc, ppr) import qualified Outputable as Ppr import SrcLoc plugin :: GHC.Plugin plugin = GHC.defaultPlugin { GHC.parsedResultAction = \ _ -> go } where go :: GHC.ModSummary -> GHC.HsParsedModule -> GHC.Hsc GHC.HsParsedModule go _ m = [m { GHC.hpm_module = hpm_module' } | dflags <- GHC.getDynFlags , hpm_module' <- transform dflags (GHC.hpm_module m)] transform :: GHC.DynFlags -> GHC.Located (HsModule GhcPs) -> GHC.Hsc (GHC.Located (HsModule GhcPs)) transform _dflags = Ppr.pprTraceDebug "comprehensions-ghc:Comprehension.Plugin:" . gppr' <*> SYB.everywhereM (SYB.mkM transform') where transform' :: LHsExpr GhcPs -> GHC.Hsc (LHsExpr GhcPs) transform' = pure . \ case L l (HsDo _ comp (L l' (reverse -> L l'' (LastStmt _ body _ x):ss))) | case comp of ListComp -> True MonadComp -> True _ -> False -> L l . HsDo NoExt DoExpr . L l' . reverse $ L l'' (BodyStmt NoExt (noLoc $ HsApp NoExt pureExpr body) x x):ss xl -> xl pureExpr :: GenLocated SrcSpan (HsExpr GhcPs) pureExpr = noLoc . HsVar NoExt . noLoc . GHC.mkOrig (GHC.Module (GHC.stringToUnitId "base") $ GHC.mkModuleName "GHC.Base") . GHC.mkVarOcc $ "pure" gppr' :: Data a => a -> Ppr.SDoc gppr' = gpprPrec' 0 gpprPrec' :: Data a => Rational -> a -> SDoc gpprPrec' prec t | headTyConOf (L () ()) == headTyConOf t = gmapQi 1 (gpprPrec' prec) t | headTyConOf [()] == headTyConOf t = pprList $ asList t | Just NoExt <- cast t = Ppr.empty | Just _ <- cast t :: Maybe GHC.SourceText = Ppr.empty | Just _ <- cast t :: Maybe HsTypes.Promoted = Ppr.empty | Just l <- cast t :: Maybe (HsLit GhcPs) = ppr l | Just n <- cast t :: Maybe GHC.RdrName = Ppr.doubleQuotes $ ppr n | otherwise = Ppr.sdocWithDynFlags $ \ dflags -> let sdocs = filter (not . Ppr.isEmpty dflags) $ (:) <$> fromString . showConstr . toConstr <*> gmapQ (gpprPrec' (prec + 1)) $ t in bool id Ppr.parens (prec > 0 && length sdocs > 1) $ Ppr.fsep sdocs where pprList = Ppr.brackets . Ppr.pprWithCommas id headTyConOf :: Typeable a => a -> TyCon headTyConOf = fst . splitApps . R.typeOf asList :: Data a => a -> [SDoc] asList t | [] <- gmapQ (pure ()) t = [] | otherwise = gmapQi 0 gppr' t : gmapQi 1 asList t