----------------------------------------------------------------------------- -- | -- Module : RefacAsPatterns -- Copyright : (c) Christopher Brown 2007 -- -- Maintainer : cmb21@kent.ac.uk -- Stability : provisional -- Portability : portable -- -- This module contains a transformation for HaRe. -- Convert all patterns into AS patterns and -- change all reference to the pattern on the RHS to references -- to the AS name. -- ----------------------------------------------------------------------------- module RefacAsPatterns where import System.IO.Unsafe import PrettyPrint import RefacTypeSyn import RefacLocUtils import Data.Char import GHC.Unicode import AbstractIO import Maybe import List import RefacUtils data Patt = Match HsMatchP | MyAlt HsAltP | MyDo HsStmtP | MyListComp HsExpP deriving (Show) refacAsPatterns args = do let fileName = args!!0 name = args!!1 beginRow = read (args!!2)::Int beginCol = read (args!!3)::Int endRow = read (args!!4)::Int endCol = read (args!!5)::Int AbstractIO.putStrLn "refacAsPatterns" unless (isVarId name) $ error "The new name is invalid!\n" -- Parse the input file. modInfo@(inscps, exps, mod, tokList) <- parseSourceFile fileName -- there are two modes to this refactoring: -- either one selects a pattern, or, one selects an expression (referring to a pattern). let exp = locToExp (beginRow, beginCol) (endRow, endCol) tokList mod case exp of Exp (HsId (HsVar (PNT (PN (UnQual "unknown") (G (PlainModule "unknown") "--" (N Nothing))) Value (N Nothing)) )) -> do let (pnt, pat, match) = findPattern tokList (beginRow, beginCol) (endRow, endCol) mod ((_,m), (newToks, newMod)) <- applyRefac (changePattern name pat match) (Just (inscps, exps, mod, tokList)) fileName unless (newMod /= mod) $ AbstractIO.putStrLn "Pattern does not occur on the rhs!" writeRefactoredFiles False [((fileName, m), (newToks, newMod))] AbstractIO.putStrLn "Completed.\n" _ -> do -- all we need is the expression and the pattern to which it is imitating... let pat = getPat exp mod ((_,m), (newToks, newMod)) <- applyRefac (changePatternSimple name pat exp) (Just (inscps, exps, mod, tokList)) fileName writeRefactoredFiles False [((fileName, m), (newToks, newMod))] AbstractIO.putStrLn "Completed.\n" getPat exp t = fromMaybe (error "No Pattern is associated with the highlighted expression!") (applyTU (once_tdTU (failTU `adhocTU` worker)) t) where worker (p :: HsPatP) | rewritePats p == exp = Just p | otherwise = Nothing convertPat :: String -> HsPatP -> HsPatP convertPat _ (Pat (HsPAsPat n p)) = error "The selected pattern is already an as-pattern!" convertPat _ (Pat (HsPId _)) = error "Cannot perform to-as-patterns on a simple variable!" convertPat _ (Pat (HsPLit _ _)) = error "Cannot perform to-as-patterns on a constant value!" convertPat name (Pat (HsPParen p)) = convertPat name p convertPat name p = (Pat (HsPAsPat (nameToPNT name) p)) changePatternSimple name pat exp (_, _, t) = do inscopeNames <- hsVisibleNames exp t unless (not (name `elem` inscopeNames)) $ error ("the use of the name: " ++ name ++ " is already in scope!") -- let newPats = checkPats name pat [p] let convertedPat = convertPat name pat newExp <- checkExpr convertedPat exp newT <- update exp newExp t newT2 <- update pat convertedPat newT return newT2 changePattern name pat (Match match) (_, _,t) = do newDecl <- lookInMatches t name pat [match] newT <- update match (newDecl !! 0) t return newT changePattern name pat (MyAlt alt) (_, _, t) = do newAlt <- lookInAlt t name pat alt newT <- update alt newAlt t return newT changePattern name pat (MyDo inDo) (_,_, t) = do newDo <- lookInDo t name pat inDo newT <- update inDo newDo t return newT changePattern name pat (MyListComp inList) (_,_,t) = do newList <- lookInList t name pat inList newT <- update inList newList t return newT -- sconvertPatterns :: (Term t, MonadPlus m,Monad m) => t -> String -> HsPatP -> HsDeclP -> m HsDeclP convertPatterns t name pat dec@(Dec (HsPatBind a b (HsBody e) ds)) = do newExp <- checkExpr (Pat (HsPAsPat (nameToPNT name) pat)) e return (Dec (HsPatBind a b (HsBody newExp) ds)) convertPatterns t name pat (Dec (HsFunBind loc matches)) = do rest <- lookInMatches t name pat matches return (Dec (HsFunBind loc rest )) lookInMatches _ _ _ [] = return [] lookInMatches t name pat (match@(HsMatch s pnt (p:ps) (HsGuard g@((_,_,e):gs)) ds):ms) = do inscopeNames <- hsVisibleNames e t unless (not (name `elem` inscopeNames)) $ error ("the use of the name: " ++ name ++ " is already in scope!") let newPats = checkPats name pat (p:ps) let convertedPat = convertPat name pat newGuard <- checkGuards convertedPat g newDS <- mapM (convertPatterns t name pat) ds rest <- lookInMatches t name pat ms if newGuard == g && newDS == ds then do return (HsMatch s pnt (p:ps) (HsGuard g) ds : rest) else do return (HsMatch s pnt newPats (HsGuard newGuard) newDS : rest) lookInMatches t name pat (match@(HsMatch s pnt (p:ps) (HsBody e) ds):ms) = do inscopeNames <- hsVisibleNames e t unless (not (name `elem` inscopeNames)) $ error ("the use of the name: " ++ name ++ " is already in scope!") let newPats = checkPats name pat (p:ps) let convertedPat = convertPat name pat newExp <- checkExpr convertedPat e newDS <- mapM (convertPatterns t name pat) ds rest <- lookInMatches t name pat ms if newExp == e && newDS == ds then do return (HsMatch s pnt (p:ps) (HsBody e) ds : rest) else do return (HsMatch s pnt newPats (HsBody newExp) newDS : rest) lookInAlt t name pat (alt@(HsAlt s p (HsGuard g@((_,_,e):gs)) ds)::HsAltP) = do inscopeNames <- hsVisibleNames e t unless (not (name `elem` inscopeNames)) $ error ("the use of the name: " ++ name ++ " is already in scope!") let newPats = checkPats name pat [p] let convertedPat = convertPat name pat newGuard <- checkGuards convertedPat g newDS <- mapM (convertPatterns t name pat) ds -- rest <- lookInMatches t name pat ms if newGuard == g && newDS == ds then do return (HsAlt s p (HsGuard g) ds) else do return (HsAlt s (ghead "lookInAlt" newPats) (HsGuard newGuard) newDS) lookInAlt t name pat (alt@(HsAlt s p (HsBody e) ds)::HsAltP) = do inscopeNames <- hsVisibleNames e t unless (not (name `elem` inscopeNames)) $ error ("the use of the name: " ++ name ++ " is already in scope!") let newPats = checkPats name pat [p] let convertedPat = convertPat name pat newExp <- checkExpr convertedPat e newDS <- mapM (convertPatterns t name pat) ds -- rest <- lookInMatches t name pat ms if newExp == e && newDS == ds then do return (HsAlt s p (HsBody e) ds) else do return (HsAlt s (ghead "lookInAlt" newPats) (HsBody newExp) newDS) lookInDo t name pat (inDo@(HsGenerator s p e rest)) = do inscopeNames <- hsVisibleNames e t unless (not (name `elem` inscopeNames)) $ error ("the use of the name: " ++ name ++ " is already in scope!") let newPats = checkPats name pat [p] let convertedPat = convertPat name pat newExp <- checkExpr convertedPat e newDS <- lookInExps t name pat rest -- rest <- lookInMatches t name pat ms if newExp == e && newDS == rest then do return (HsGenerator s p e rest) else do return (HsGenerator s (ghead "lookInDo" newPats) newExp newDS) lookInList t name pat (inList@(Exp (HsListComp d))) = do res <- lookInDo t name pat d return (Exp (HsListComp res)) lookInExps t name pat (HsLast e) = do inscopeNames <- hsVisibleNames e t unless (not (name `elem` inscopeNames)) $ error ("the use of the name: " ++ name ++ " is already in scope!") let convertedPat = convertPat name pat newExp <- checkExpr convertedPat e return (HsLast newExp) lookInExps t name pat (HsGenerator s p e rest) = do inscopeNames <- hsVisibleNames e t unless (not (name `elem` inscopeNames)) $ error ("the use of the name: " ++ name ++ " is already in scope!") let newPats = checkPats name pat [p] let convertedPat = convertPat name pat newExp <- checkExpr convertedPat e newDS <- lookInExps t name pat rest -- rest <- lookInMatches t name pat ms if newExp == e && newDS == rest then do return (HsGenerator s p e rest) else do return (HsGenerator s (ghead "lookInDo" newPats) newExp newDS) lookInExps t name pat (HsQualifier e rest) = do inscopeNames <- hsVisibleNames e t unless (not (name `elem` inscopeNames)) $ error ("the use of the name: " ++ name ++ " is already in scope!") let convertedPat = convertPat name pat newExp <- checkExpr convertedPat e newDS <- lookInExps t name pat rest return (HsQualifier newExp newDS) lookInExps t name pat (HsLetStmt ds rest) = do let convertedPat = convertPat name pat newRest <- lookInExps t name pat rest newDS <- mapM (convertPatterns t name pat) ds -- rest <- lookInMatches t name pat ms if newDS == ds && rest == newRest then do return (HsLetStmt ds rest) else do return (HsLetStmt newDS newRest) checkPats :: String -> HsPatP -> [ HsPatP ] -> [ HsPatP ] checkPats _ _ [] = [] checkPats name pat (pat2@(Pat (HsPParen p)): ps) | pat == pat2 = (Pat (HsPParen (Pat (HsPAsPat (nameToPNT name) p)))) : checkPats name pat ps | otherwise = (Pat (HsPParen res)) : (checkPats name pat ps) where res = ghead "checkPats HsPParen res" $ checkPats name pat [p] checkPats name pat (pat2@(Pat (HsPApp i ps)):pss) | pat == pat2 = (Pat (HsPAsPat (nameToPNT name) pat2)) : checkPats name pat pss | otherwise = (Pat (HsPApp i (checkPats name pat ps))) : checkPats name pat pss checkPats name pat (pat2@(Pat (HsPTuple s ps)):pss) | pat == pat2 = (Pat (HsPAsPat (nameToPNT name) pat2)) : checkPats name pat pss | otherwise = (Pat (HsPTuple s (checkPats name pat ps))) : checkPats name pat pss checkPats name pat (pat2@(Pat (HsPList s ps)) : pss) | pat == pat2 = (Pat (HsPAsPat (nameToPNT name) pat2)) : checkPats name pat pss | otherwise = (Pat (HsPList s (checkPats name pat ps))) : checkPats name pat pss checkPats name pat ((Pat (HsPInfixApp p1 i p2)) : pss) = (Pat (HsPInfixApp res1 i res2)) : checkPats name pat pss where res1 = ghead "checkPats HsPInFixApp res1" $ checkPats name pat [p1] res2 = ghead "checkPats HsPInfixApp res2" $ checkPats name pat [p2] checkPats name pat (p:ps) | pat == p = (Pat (HsPAsPat (nameToPNT name) p)) : checkPats name pat ps | otherwise = p : (checkPats name pat ps) -- checkGuards [] g = return g checkGuards _ [] = return [] checkGuards pat@(Pat (HsPAsPat s p)) ((s1, e1, e2):gs) = do -- rewrite the guard newGuard <- rewriteExp s (rewritePats p) e1 pat -- newGuard' <- checkExpr ps newGuard -- rewrite the RHS of the guard rhs <- rewriteExp s (rewritePats p) e2 pat -- rhs' <- checkExpr ps rhs rest <- checkGuards pat gs return ((s1, newGuard, rhs):rest) -- checkExpr :: [ HsPatP ] -> HsExpP -> m HsExpP -- checkExpr [] e = return e checkExpr pat@(Pat (HsPAsPat s p)) e = do newExp <- rewriteExp s (rewritePats p) e pat -- error $ show (rewritePats p) -- newExp' <- checkExpr ps newExp return newExp checkExpr _ e = return e -- rewrite exp checks to see whether the given expression occurs within -- the second exp. If it does, the expression is replaced with the name of the 'as' -- pattern. -- rewriteExp :: String -> HsExpP -> HsExpP -> HsExpP rewriteExp name e1 e2 t = applyTP (full_tdTP (idTP `adhocTP` (inExp e1))) e2 where -- inExp :: HsExpP -> HsExpP -> HsExpP inExp (Exp (HsParen e1)::HsExpP) (e2::HsExpP) = do -- error $ show e2 allDef <- allDefined e2 t if (rmLocs e1) == (rmLocs e2) && (and allDef) then do return (nameToExp (pNTtoName name)) else do return e2 inExp (e1::HsExpP) (e2::HsExpP) | (rmLocs e1) == (rmLocs e2) = return (nameToExp (pNTtoName name)) | otherwise = return e2 allDefined e t = applyTU (full_tdTU (constTU [] `adhocTU` inPNT)) e where inPNT (p@(PNT pname ty _)::PNT) = return [findPN pname t] rewritePats :: HsPatP -> HsExpP rewritePats (pat1@(Pat (HsPRec x y))) = Exp (HsRecConstr loc0 x (map sortField y)) where sortField :: HsFieldI a HsPatP -> HsFieldI a HsExpP sortField (HsField i e) = (HsField i (rewritePats e)) rewritePats (pat1@(Pat (HsPLit x y))) = Exp (HsLit x y) rewritePats (pat1@(Pat (HsPAsPat i p1))) = Exp (HsAsPat i (rewritePats p1)) rewritePats (pat1@(Pat (HsPIrrPat p1))) = Exp (HsIrrPat (rewritePats p1)) rewritePats (pat1@(Pat (HsPWildCard))) = nameToExp "undefined" rewritePats (pat1@(Pat (HsPApp i p1))) = createFuncFromPat i (map rewritePats p1) rewritePats (pat1@(Pat (HsPList _ p1))) = Exp (HsList (map rewritePats p1)) rewritePats (pat1@(Pat (HsPTuple _ p1))) = Exp (HsTuple (map rewritePats p1)) rewritePats (pat1@(Pat (HsPInfixApp p1 x p2))) = Exp (HsInfixApp (rewritePats p1) (HsCon x) (rewritePats p2)) rewritePats (pat@(Pat (HsPParen p1))) = Exp (HsParen (rewritePats p1)) rewritePats (pat1@(Pat (HsPId (HsVar (PNT (PN (UnQual (i:is)) a) b c))))) | isUpper i = (Exp (HsId (HsCon (PNT (PN (UnQual (i:is)) a) b c)))) | otherwise = (Exp (HsId (HsVar (PNT (PN (UnQual (i:is)) a) b c)))) rewritePats (pat1@(Pat (HsPId (HsCon (PNT (PN (UnQual (i:is)) a) b c))))) = (Exp (HsId (HsCon (PNT (PN (UnQual (i:is)) a) b c)))) -- rewritePats p = error $ show p -- strip removes whitespace and '\n' from a given string strip :: String -> String strip [] = [] strip (' ':xs) = strip xs strip ('\n':xs) = strip xs strip (x:xs) = x : strip xs isInfixOf needle haystack = any (isPrefixOf needle) (tails haystack) --check whether the cursor points to the beginning of the datatype declaration --taken from RefacADT.hs checkCursor :: String -> Int -> Int -> HsModuleP -> Either String HsDeclP checkCursor fileName row col mod = case locToTypeDecl of Nothing -> Left ("Invalid cursor position. Please place cursor at the beginning of the definition!") Just decl -> Right decl where locToTypeDecl = find (definesPNT (locToPNT fileName (row, col) mod)) (hsModDecls mod) definesPNT pnt d@(Dec (HsPatBind loc p e ds)) = findPNT pnt d definesPNT pnt d@(Dec (HsFunBind loc ms)) = findPNT pnt d definesPNT pnt _ = False {-| Takes the position of the highlighted code and returns the function name, the list of arguments, the expression that has been highlighted by the user, and any where\/let clauses associated with the function. -} {-findPattern :: Term t => [PosToken] -- ^ The token stream for the -- file to be -- refactored. -> (Int, Int) -- ^ The beginning position of the highlighting. -> (Int, Int) -- ^ The end position of the highlighting. -> t -- ^ The abstract syntax tree. -> (SrcLoc, PNT, FunctionPats, HsExpP, WhereDecls) -- ^ A tuple of, -- (the function name, the list of arguments, -- the expression highlighted, any where\/let clauses -- associated with the function). -} findPattern toks beginPos endPos t = fromMaybe (defaultPNT, defaultPat, error "Invalid pattern selected!") (applyTU (once_tdTU (failTU `adhocTU` inMatch `adhocTU` inCase `adhocTU` inDo `adhocTU` inList )) t) where --The selected sub-expression is in the rhs of a match inMatch (match@(HsMatch loc1 pnt pats rhs ds)::HsMatchP) -- is the highlighted region selecting a pattern? | inPat pats == Nothing = Nothing | otherwise = do let pat = fromJust (inPat pats) Just (pnt, pat, Match match) inCase (alt@(HsAlt s p rhs ds)::HsAltP) | inPat [p] == Nothing = Nothing | otherwise = do let pat = fromJust (inPat [p]) Just (defaultPNT, pat, MyAlt alt) inDo (inDo@(HsGenerator s p e rest)::HsStmtP) | inPat [p] == Nothing = Nothing | otherwise = do let pat = fromJust (inPat [p]) Just (defaultPNT, pat, MyDo inDo) inDo x = Nothing inList (inlist@(Exp (HsListComp (HsGenerator s p e rest)))::HsExpP) | inPat [p] == Nothing = Nothing | otherwise = do let pat = fromJust (inPat [p]) Just (defaultPNT, pat, MyListComp inlist) inList x = Nothing inPat :: [HsPatP] -> Maybe HsPatP inPat [] = Nothing inPat (p:ps) = if p1 /= defaultPat then Just p1 else inPat ps where p1 = locToLocalPat beginPos endPos toks p -- inPat _ = Nothing