------------------------------------------------------------------------------ -- | -- Maintainer : Ralf Laemmel, Joost Visser -- Stability : experimental -- Portability : portable -- -- This module is part of 'StrategyLib', a library of functional strategy -- combinators, including combinators for generic traversal. This module -- defines generic refactoring functionality. See the paper "Towards -- Generic Refactoring" by Ralf Laemmel. See also -- generic-refactoring in the examples directory. ------------------------------------------------------------------------------ module Data.Generics.Strafunski.StrategyLib.RefactoringTheme where import Data.Generics.Strafunski.StrategyLib.StrategyPrelude import Control.Monad.Identity hiding (fail) import Data.Generics.Strafunski.StrategyLib.KeyholeTheme import Data.Generics.Strafunski.StrategyLib.NameTheme ------------------------------------------------------------------------------ -- * The abstraction interface -- | Class of abstractions class ( -- Syntactical domains Term abstr, -- Term type for abstraction Eq name, -- Names of abstraction Term [abstr], -- Lists of abstractions Term apply -- Applications ) => Abstraction abstr name tpe apply -- Dependencies between syntactical domains | abstr -> name, abstr -> tpe, abstr -> apply, apply -> name, apply -> abstr where -- Observers getAbstrName :: abstr -> Maybe name getAbstrParas :: abstr -> Maybe [(name,tpe)] getAbstrBody :: abstr -> Maybe apply getApplyName :: apply -> Maybe name getApplyParas :: apply -> Maybe [(name,tpe)] -- Constructors constrAbstr :: name -> [(name,tpe)] -> apply -> Maybe abstr constrApply :: name -> [(name,tpe)] -> Maybe apply ------------------------------------------------------------------------------ -- * Removal -- | Remove an unused abstraction eliminate :: (Term prog, Abstraction abstr name tpe apply) => TU [(name,tpe)] Identity -- ^ Identify declarations -> TU [name] Identity -- ^ Identify references -> (abstr -> Maybe abstr) -- ^ Unwrap abstraction -> prog -- ^ Input program -> Maybe prog -- ^ Output program eliminate declared referenced unwrap prog = do { abstr <- selectFocus unwrap prog; name <- getAbstrName abstr; () <- unusedAbstr name; deleteFocus unwrap prog } where -- Check if name is unused by optionally navigating to the relevant scope unusedAbstr name = maybe (notIsFree prog) notIsFree selectScope where argtype :: Monad m => (x -> y) -> x -> m x argtype _ = return selectScope = selectHost unwrap (argtype unwrap) prog notIsFree scope = do scope' <- deleteFocus unwrap scope names <- return (freeNames declared referenced scope') guard (not (elem name names)) ------------------------------------------------------------------------------ -- * Insertion -- | Insert a new abstraction introduce :: (Term prog, Abstraction abstr name tpe apply) => TU [(name,tpe)] Identity -- ^ Identify declarations -> TU [name] Identity -- ^ Identify references -> ([abstr] -> Maybe [abstr]) -- ^ Unwrap scope with abstractions -> abstr -- ^ Abstraction to be inserted -> prog -- ^ Input program -> Maybe prog -- ^ Output program introduce declared referenced unwrap abstr = replaceFocus (\abstrlist -> do abstrlist' <- unwrap abstrlist name <- getAbstrName abstr free <- return $ freeNames declared referenced abstrlist' def <- mapM getAbstrName abstrlist' guard (and [not (elem name free), not (elem name def)]) return (abstr:abstrlist') ) ------------------------------------------------------------------------------ -- * Generic extraction (say fold) -- | Extract an abstraction extract :: (Term prog, Abstraction abstr name tpe apply) => TU [(name,tpe)] Identity -- ^ Identify declarations -> TU [name] Identity -- ^ Identify references -> (apply -> Maybe apply) -- ^ Unwrap focus -> ([abstr] -> [abstr]) -- ^ Wrap host -> ([abstr] -> Maybe [abstr]) -- ^ Unwrap host -> ([(name,tpe)] -> apply -> Bool) -- ^ Check focus -> name -- ^ Name for abstraction -> prog -- ^ Input program -> Maybe prog -- ^ Output program extract declared referenced unwrap wrap unwrap' check name prog = do -- Operate on focus (bound,focus) <- boundTypedNames declared unwrap prog free <- return $ freeTypedNames declared referenced bound focus guard (check bound focus) -- Construct abstraction abstr <- constrAbstr name free focus -- Insert abstraction prog' <- markHost (maybe False (const True) . unwrap) wrap prog prog'' <- introduce declared referenced unwrap' abstr prog' -- Construct application apply <- constrApply name free -- Replace focus by application replaceFocus (maybe Nothing (const (Just apply)) . unwrap) prog'' ------------------------------------------------------------------------------