Retrie/Subst.hs (128 lines of code) (raw):
-- Copyright (c) Facebook, Inc. and its affiliates.
--
-- This source code is licensed under the MIT license found in the
-- LICENSE file in the root directory of this source tree.
--
{-# LANGUAGE CPP #-}
{-# LANGUAGE ViewPatterns #-}
module Retrie.Subst (subst) where
import Control.Monad.Writer.Strict
import Data.Generics
import Retrie.Context
import Retrie.ExactPrint
import Retrie.Expr
import Retrie.GHC
import Retrie.Substitution
import Retrie.SYB
import Retrie.Types
import Retrie.Util
------------------------------------------------------------------------
-- | Perform the given 'Substitution' on an AST, avoiding variable capture
-- by alpha-renaming binders as needed.
subst
:: (MonadIO m, Data ast)
=> Substitution
-> Context
-> ast
-> TransformT m ast
subst sub ctxt =
everywhereMWithContextBut bottomUp (const False) updateContext f ctxt'
where
ctxt' = ctxt { ctxtSubst = Just sub }
f c =
mkM (substExpr c)
`extM` substPat c
`extM` substType c
`extM` substHsMatchContext c
`extM` substBind c
lookupHoleVar :: RdrName -> Context -> Maybe HoleVal
lookupHoleVar rdr ctxt = do
sub <- ctxtSubst ctxt
lookupSubst (rdrFS rdr) sub
substExpr
:: MonadIO m
=> Context
-> LHsExpr GhcPs
-> TransformT m (LHsExpr GhcPs)
substExpr ctxt e@(L l1 (HsVar x (L l2 v))) =
case lookupHoleVar v ctxt of
Just (HoleExpr eA) -> do
-- lift $ liftIO $ debugPrint Loud "substExpr:HoleExpr:e" [showAst e]
-- lift $ liftIO $ debugPrint Loud "substExpr:HoleExpr:eA" [showAst eA]
e0 <- graftA (unparen <$> eA)
let comments = hasComments e0
-- unless comments $ transferEntryDPT e e'
e1 <- if comments
then return e0
else transferEntryDP e e0
e2 <- transferAnnsT isComma e e1
-- let e'' = setEntryDP e' (SameLine 1)
-- lift $ liftIO $ debugPrint Loud "substExpr:HoleExpr:e2" [showAst e2]
parenify ctxt e2
Just (HoleRdr rdr) ->
return $ L l1 $ HsVar x $ L l2 rdr
_ -> return e
substExpr _ e = return e
substPat
:: MonadIO m
=> Context
-> LPat GhcPs
-> TransformT m (LPat GhcPs)
substPat ctxt (dLPat -> Just p@(L l1 (VarPat x _vl@(L l2 v)))) = fmap cLPat $
case lookupHoleVar v ctxt of
Just (HolePat pA) -> do
-- lift $ liftIO $ debugPrint Loud "substPat:HolePat:p" [showAst p]
-- lift $ liftIO $ debugPrint Loud "substPat:HolePat:pA" [showAst pA]
p' <- graftA (unparenP <$> pA)
p0 <- transferEntryAnnsT isComma p p'
-- the relevant entry delta is sometimes attached to
-- the OccName and not to the VarPat.
-- This seems to be the case only when the pattern comes from a lhs,
-- whereas it has no annotations in patterns found in rhs's.
-- tryTransferEntryDPT vl p'
parenifyP ctxt p0
Just (HoleRdr rdr) ->
return $ L l1 $ VarPat x $ L l2 rdr
_ -> return p
substPat _ p = return p
substType
:: MonadIO m
=> Context
-> LHsType GhcPs
-> TransformT m (LHsType GhcPs)
substType ctxt ty
| Just (L _ v) <- tyvarRdrName (unLoc ty)
, Just (HoleType tyA) <- lookupHoleVar v ctxt = do
-- lift $ liftIO $ debugPrint Loud "substType:HoleType:ty" [showAst ty]
-- lift $ liftIO $ debugPrint Loud "substType:HoleType:tyA" [showAst tyA]
ty' <- graftA (unparenT <$> tyA)
ty0 <- transferEntryAnnsT isComma ty ty'
parenifyT ctxt ty0
substType _ ty = return ty
-- You might reasonably think that we would replace the RdrName in FunBind...
-- but no, exactprint only cares about the RdrName in the MatchGroup matches,
-- which are here. In case that changes in the future, we define substBind too.
substHsMatchContext
:: Monad m
=> Context
#if __GLASGOW_HASKELL__ < 900
-> HsMatchContext RdrName
-> TransformT m (HsMatchContext RdrName)
#else
-> HsMatchContext GhcPs
-> TransformT m (HsMatchContext GhcPs)
#endif
substHsMatchContext ctxt (FunRhs (L l v) f s)
| Just (HoleRdr rdr) <- lookupHoleVar v ctxt =
return $ FunRhs (L l rdr) f s
substHsMatchContext _ other = return other
substBind
:: Monad m
=> Context
-> HsBind GhcPs
-> TransformT m (HsBind GhcPs)
substBind ctxt fb@FunBind{}
| L l v <- fun_id fb
, Just (HoleRdr rdr) <- lookupHoleVar v ctxt =
return fb { fun_id = L l rdr }
substBind _ other = return other