module FreeC.IR.Subterm
(
childTerms
, replaceChildTerms
, replaceChildTerms'
, Pos(..)
, rootPos
, consPos
, parentPos
, parentPos'
, ancestorPos
, allPos
, above
, below
, leftOf
, rightOf
, selectSubterm
, selectSubterm'
, replaceSubterm
, replaceSubterm'
, replaceSubterms
, replaceSubterms'
, findSubtermPos
, findSubtermWithPos
, findSubterms
, findFirstSubterm
, mapSubterms
, mapSubtermsM
, boundVarsOf
, boundVarsWithTypeOf
, boundVarsAt
, boundVarsWithTypeAt
) where
import Control.Monad ( foldM )
import Data.Composition ( (.:) )
import Data.Functor.Identity ( runIdentity )
import Data.List ( inits, intersperse, isPrefixOf )
import Data.Map.Strict ( Map )
import qualified Data.Map.Strict as Map
import Data.Maybe ( fromMaybe, listToMaybe )
import Data.Set ( Set )
import Data.Tuple.Extra ( (&&&) )
import qualified FreeC.IR.Syntax as IR
import FreeC.Pretty
checkArity :: Int -> ([a] -> b) -> [a] -> Maybe b
checkArity n f xs | length xs == n = Just (f xs)
| otherwise = Nothing
nullary :: b -> [a] -> Maybe b
nullary y xs | null xs = Just y
| otherwise = Nothing
missingPosError :: Subterm a => String -> a -> Pos -> a
missingPosError funcName term pos = error
$ funcName
++ ": The subterm at position "
++ showPretty pos
++ "in term "
++ showPretty term
++ " does not exists."
class Pretty a => Subterm a where
childTerms :: a -> [a]
replaceChildTerms :: a -> [a] -> Maybe a
replaceChildTerms' :: Subterm a => a -> [a] -> a
replaceChildTerms' term children' = fromMaybe argCountError
(replaceChildTerms term children')
where
argCountError = error
$ "replaceChildTerms: Wrong number of child terms. Got "
++ show (length children')
++ " but expected "
++ show (length (childTerms term))
++ "!"
instance Subterm IR.Expr where
childTerms (IR.App _ e1 e2 _) = [e1, e2]
childTerms (IR.TypeAppExpr _ expr _ _) = [expr]
childTerms (IR.If _ e1 e2 e3 _) = [e1, e2, e3]
childTerms (IR.Case _ expr alts _) = expr : map IR.altRhs alts
childTerms (IR.Lambda _ _ expr _) = [expr]
childTerms (IR.Con _ _ _) = []
childTerms (IR.Var _ _ _) = []
childTerms (IR.Undefined _ _) = []
childTerms (IR.ErrorExpr _ _ _) = []
childTerms (IR.Trace _ _ e _) = [e]
childTerms (IR.IntLiteral _ _ _) = []
childTerms (IR.Let _ binds e _) = e : map IR.bindExpr binds
replaceChildTerms (IR.App srcSpan _ _ exprType)
= checkArity 2 $ \[e1', e2'] -> IR.App srcSpan e1' e2' exprType
replaceChildTerms (IR.TypeAppExpr srcSpan _ typeExpr exprType)
= checkArity 1 $ \[expr'] -> IR.TypeAppExpr srcSpan expr' typeExpr exprType
replaceChildTerms (IR.If srcSpan _ _ _ exprType)
= checkArity 3 $ \[e1', e2', e3'] -> IR.If srcSpan e1' e2' e3' exprType
replaceChildTerms (IR.Case srcSpan _ alts exprType)
= checkArity (length alts + 1) $ \(expr' : altChildren') -> IR.Case srcSpan
expr' (zipWith replaceAltChildExpr alts altChildren') exprType
where
replaceAltChildExpr :: IR.Alt -> IR.Expr -> IR.Alt
replaceAltChildExpr alt rhs' = alt { IR.altRhs = rhs' }
replaceChildTerms (IR.Lambda srcSpan args _ exprType)
= checkArity 1 $ \[expr'] -> IR.Lambda srcSpan args expr' exprType
replaceChildTerms (IR.Let srcSpan binds _ exprType)
= checkArity (length binds + 1) $ \(expr' : bindChildren') -> IR.Let srcSpan
(zipWith replaceBindChildExpr binds bindChildren') expr' exprType
where
replaceBindChildExpr :: IR.Bind -> IR.Expr -> IR.Bind
replaceBindChildExpr b expr = b { IR.bindExpr = expr }
replaceChildTerms expr@(IR.Con _ _ _) = nullary expr
replaceChildTerms expr@(IR.Var _ _ _) = nullary expr
replaceChildTerms expr@(IR.Undefined _ _) = nullary expr
replaceChildTerms expr@(IR.ErrorExpr _ _ _) = nullary expr
replaceChildTerms (IR.Trace srcSpan msg _ exprType)
= checkArity 1 $ \[e'] -> IR.Trace srcSpan msg e' exprType
replaceChildTerms expr@(IR.IntLiteral _ _ _) = nullary expr
instance Subterm IR.Type where
childTerms (IR.TypeVar _ _) = []
childTerms (IR.TypeCon _ _) = []
childTerms (IR.TypeApp _ t1 t2) = [t1, t2]
childTerms (IR.FuncType _ t1 t2) = [t1, t2]
replaceChildTerms typeExpr@(IR.TypeVar _ _) = nullary typeExpr
replaceChildTerms typeExpr@(IR.TypeCon _ _) = nullary typeExpr
replaceChildTerms (IR.TypeApp srcSpan _ _)
= checkArity 2 $ \[t1', t2'] -> IR.TypeApp srcSpan t1' t2'
replaceChildTerms (IR.FuncType srcSpan _ _)
= checkArity 2 $ \[t1', t2'] -> IR.FuncType srcSpan t1' t2'
newtype Pos = Pos [Int]
deriving ( Eq, Show )
instance Pretty Pos where
pretty (Pos xs)
= char '<' <> hcat (intersperse (char '.') (map int xs)) <> char '>'
rootPos :: Pos
rootPos = Pos []
consPos :: Int -> Pos -> Pos
consPos p (Pos ps) = Pos (p : ps)
parentPos' :: Pos -> Maybe (Int, Pos)
parentPos' (Pos []) = Nothing
parentPos' (Pos ps) = Just (last ps, Pos (init ps))
parentPos :: Pos -> Maybe Pos
parentPos (Pos ps) | null ps = Nothing
| otherwise = Just (Pos (init ps))
ancestorPos :: Pos -> [Pos]
ancestorPos (Pos ps) = map Pos (inits ps)
allPos :: Subterm a => a -> [Pos]
allPos term = rootPos
: [consPos p childPos
| (p, child) <- zip [1 ..] (childTerms term)
, childPos <- allPos child
]
above :: Pos -> Pos -> Bool
above (Pos ps1) (Pos ps2) = ps1 `isPrefixOf` ps2
below :: Pos -> Pos -> Bool
below = flip above
leftOf :: Pos -> Pos -> Bool
leftOf (Pos []) _ = False
leftOf _ (Pos []) = False
leftOf (Pos (p1 : ps1)) (Pos (p2 : ps2)) = case compare p1 p2 of
LT -> True
EQ -> leftOf (Pos ps1) (Pos ps2)
GT -> False
rightOf :: Pos -> Pos -> Bool
rightOf = flip leftOf
selectSubterm :: Subterm a => a -> Pos -> Maybe a
selectSubterm term (Pos []) = Just term
selectSubterm term (Pos (p : ps))
| p <= 0 || p > length children = Nothing
| otherwise = selectSubterm (children !! (p - 1)) (Pos ps)
where
children = childTerms term
selectSubterm' :: Subterm a => a -> Pos -> a
selectSubterm' term pos = fromMaybe (missingPosError "selectSubterm" term pos)
(selectSubterm term pos)
replaceSubterm
:: Subterm a
=> a
-> Pos
-> a
-> Maybe a
replaceSubterm _ (Pos []) term' = Just term'
replaceSubterm term (Pos (p : ps)) term'
| p <= 0 || p > length children = Nothing
| otherwise = do
let (before, child : after) = splitAt (p - 1) children
child' <- replaceSubterm child (Pos ps) term'
replaceChildTerms term (before ++ child' : after)
where
children = childTerms term
replaceSubterm' :: Subterm a => a -> Pos -> a -> a
replaceSubterm' term pos term' = fromMaybe
(missingPosError "replaceSubterm" term pos) (replaceSubterm term pos term')
replaceSubterms :: Subterm a => a -> [(Pos, a)] -> Maybe a
replaceSubterms = foldM (\term (pos, term') -> replaceSubterm term pos term')
replaceSubterms' :: Subterm a => a -> [(Pos, a)] -> a
replaceSubterms' = foldl (\term (pos, term') -> replaceSubterm' term pos term')
findSubtermPos :: Subterm a => (a -> Bool) -> a -> [Pos]
findSubtermPos predicate = map snd
. findSubtermWithPos (flip (const predicate))
findSubtermWithPos :: Subterm a => (a -> Pos -> Bool) -> a -> [(a, Pos)]
findSubtermWithPos predicate term = filter (uncurry predicate)
(map (selectSubterm' term &&& id) (allPos term))
findSubterms :: Subterm a => (a -> Bool) -> a -> [a]
findSubterms predicate = map fst . findSubtermWithPos (flip (const predicate))
findFirstSubterm :: Subterm a => (a -> Bool) -> a -> Maybe a
findFirstSubterm = listToMaybe .: findSubterms
mapSubterms :: Subterm a => (a -> a) -> a -> a
mapSubterms f = runIdentity . mapSubtermsM (return . f)
mapSubtermsM :: (Subterm a, Monad m) => (a -> m a) -> a -> m a
mapSubtermsM f term = do
term' <- f term
children' <- mapM (mapSubtermsM f) (childTerms term')
return (replaceChildTerms' term' children')
boundVarsOf :: IR.Expr -> Int -> Set IR.QName
boundVarsOf = Map.keysSet .: boundVarsWithTypeOf
boundVarsWithTypeOf :: IR.Expr -> Int -> Map IR.QName (Maybe IR.Type)
boundVarsWithTypeOf expr i = case expr of
IR.Lambda _ args _ _ -> fromVarPats args
IR.Let _ binds _ _ -> fromVarPats (map IR.bindVarPat binds)
IR.Case _ _ alts _ | i >= 1 && i <= length alts -> fromVarPats
(IR.altVarPats (alts !! (i - 1)))
| otherwise -> Map.empty
IR.Con _ _ _ -> Map.empty
IR.Var _ _ _ -> Map.empty
IR.App _ _ _ _ -> Map.empty
IR.TypeAppExpr _ _ _ _ -> Map.empty
IR.If _ _ _ _ _ -> Map.empty
IR.Undefined _ _ -> Map.empty
IR.ErrorExpr _ _ _ -> Map.empty
IR.Trace _ _ _ _ -> Map.empty
IR.IntLiteral _ _ _ -> Map.empty
where
fromVarPats :: [IR.VarPat] -> Map IR.QName (Maybe IR.Type)
fromVarPats = Map.fromList . map (IR.varPatQName &&& IR.varPatType)
boundVarsAt :: IR.Expr -> Pos -> Set IR.QName
boundVarsAt = Map.keysSet .: boundVarsWithTypeAt
boundVarsWithTypeAt :: IR.Expr -> Pos -> Map IR.QName (Maybe IR.Type)
boundVarsWithTypeAt = fromMaybe Map.empty .: boundVarsWithTypeAt'
where
boundVarsWithTypeAt'
:: IR.Expr -> Pos -> Maybe (Map IR.QName (Maybe IR.Type))
boundVarsWithTypeAt' _ (Pos []) = return Map.empty
boundVarsWithTypeAt' expr (Pos (p : ps)) = do
child <- selectSubterm expr (Pos [p])
boundInChild <- boundVarsWithTypeAt' child (Pos ps)
let boundLocally = boundVarsWithTypeOf expr (p - 1)
return (boundInChild `Map.union` boundLocally)