{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
module FreeC.IR.Subst
(
Subst
, identitySubst
, singleSubst
, singleSubst'
, mkVarSubst
, mkTypeVarSubst
, composeSubst
, composeSubsts
, filterSubst
, substWithout
, ApplySubst(..)
, renameTypeArgsSubst
, renameTypeArgs
, renameArgsSubst
, renameArgs
) where
import Data.Composition ( (.:) )
import Data.Map.Strict ( Map )
import qualified Data.Map.Strict as Map
import Data.Maybe ( mapMaybe )
import Data.Set ( Set )
import qualified Data.Set as Set
import FreeC.Environment.Fresh
import FreeC.IR.Reference
import FreeC.IR.SrcSpan
import qualified FreeC.IR.Syntax as IR
import FreeC.Monad.Converter
import FreeC.Pretty
newtype Subst a = Subst (Map IR.QName (SrcSpan -> a))
instance Pretty a => Pretty (Subst a) where
pretty (Subst m) = braces
$ prettySeparated (comma <> space)
$ flip map (Map.assocs m)
$ \(v, f) -> pretty v <+> prettyString "↦" <+> pretty (f NoSrcSpan)
identitySubst :: Subst a
identitySubst = Subst Map.empty
singleSubst :: IR.QName -> a -> Subst a
singleSubst = flip (flip singleSubst' . const)
singleSubst' :: IR.QName -> (SrcSpan -> a) -> Subst a
singleSubst' = Subst .: Map.singleton
mkVarSubst :: IR.QName -> IR.QName -> Subst IR.Expr
mkVarSubst v1 v2 = singleSubst' v1 (flip IR.untypedVar v2)
mkTypeVarSubst :: String -> String -> Subst IR.Type
mkTypeVarSubst v1 v2 = singleSubst' (IR.UnQual (IR.Ident v1))
(flip IR.TypeVar v2)
composeSubst :: ApplySubst a a => Subst a -> Subst a -> Subst a
composeSubst s2@(Subst m2) (Subst m1)
= let m1' = fmap (\f srcSpan -> applySubst s2 (f srcSpan)) m1
m2' = Map.filterWithKey (const . (`Map.notMember` m1)) m2
in Subst (m2' `Map.union` m1')
composeSubsts :: ApplySubst a a => [Subst a] -> Subst a
composeSubsts = foldl composeSubst identitySubst
filterSubst :: (IR.QName -> Bool) -> Subst a -> Subst a
filterSubst p (Subst m) = Subst (Map.filterWithKey (const . p) m)
substWithout :: Subst a -> [IR.QName] -> Subst a
substWithout subst names = filterSubst (`notElem` names) subst
class ApplySubst a b where
applySubst :: Subst a -> b -> b
instance (ApplySubst a b, Functor f) => ApplySubst a (f b) where
applySubst = fmap . applySubst
instance ApplySubst IR.Expr IR.Expr where
applySubst subst@(Subst substMap) = applySubst'
where
applySubst' :: IR.Expr -> IR.Expr
applySubst' var@(IR.Var srcSpan name _) = maybe var ($ srcSpan)
(Map.lookup name substMap)
applySubst' (IR.App srcSpan e1 e2 exprType)
= let e1' = applySubst' e1
e2' = applySubst' e2
in IR.App srcSpan e1' e2' exprType
applySubst' (IR.TypeAppExpr srcSpan expr typeExpr exprType)
= let expr' = applySubst' expr
in IR.TypeAppExpr srcSpan expr' typeExpr exprType
applySubst' (IR.If srcSpan e1 e2 e3 exprType)
= let e1' = applySubst' e1
e2' = applySubst' e2
e3' = applySubst' e3
in IR.If srcSpan e1' e2' e3' exprType
applySubst' (IR.Case srcSpan expr alts exprType)
= let expr' = applySubst' expr
alts' = map (applySubst subst) alts
in IR.Case srcSpan expr' alts' exprType
applySubst' (IR.Lambda srcSpan args expr exprType)
= let (subst', args') = newRenameArgs subst args
expr' = applySubst subst' expr
in IR.Lambda srcSpan args' expr' exprType
applySubst' (IR.Let srcSpan binds expr exprType)
= let (subst', varpats') = newRenameArgs subst (map IR.bindVarPat binds)
binds' = zipWith (\v (IR.Bind s _ e) -> IR.Bind s v e)
varpats' binds
binds'' = map (applySubst subst') binds'
expr' = applySubst subst' expr
in IR.Let srcSpan binds'' expr' exprType
applySubst' (IR.Trace srcSpan msg expr exprType)
= let expr' = applySubst' expr
in IR.Trace srcSpan msg expr' exprType
applySubst' expr@(IR.Con _ _ _) = expr
applySubst' expr@(IR.Undefined _ _) = expr
applySubst' expr@(IR.ErrorExpr _ _ _) = expr
applySubst' expr@(IR.IntLiteral _ _ _) = expr
instance ApplySubst IR.Expr IR.Alt where
applySubst subst (IR.Alt srcSpan conPat varPats expr)
= let (subst', varPats') = newRenameArgs subst varPats
expr' = applySubst subst' expr
in IR.Alt srcSpan conPat varPats' expr'
instance ApplySubst IR.Expr IR.Bind where
applySubst subst (IR.Bind srcSpan varPat expr)
= let expr' = applySubst subst expr
in IR.Bind srcSpan varPat expr'
instance ApplySubst IR.Type IR.Expr where
applySubst subst = applySubst'
where
applySubst' :: IR.Expr -> IR.Expr
applySubst' (IR.Con srcSpan conName exprType)
= let exprType' = applySubst subst exprType
in IR.Con srcSpan conName exprType'
applySubst' (IR.Var srcSpan varName exprType)
= let exprType' = applySubst subst exprType
in IR.Var srcSpan varName exprType'
applySubst' (IR.App srcSpan e1 e2 exprType)
= let e1' = applySubst' e1
e2' = applySubst' e2
exprType' = applySubst subst exprType
in IR.App srcSpan e1' e2' exprType'
applySubst' (IR.TypeAppExpr srcSpan expr typeExpr exprType)
= let expr' = applySubst' expr
typeExpr' = applySubst subst typeExpr
exprType' = applySubst subst exprType
in IR.TypeAppExpr srcSpan expr' typeExpr' exprType'
applySubst' (IR.If srcSpan e1 e2 e3 exprType)
= let e1' = applySubst' e1
e2' = applySubst' e2
e3' = applySubst' e3
exprType' = applySubst subst exprType
in IR.If srcSpan e1' e2' e3' exprType'
applySubst' (IR.Case srcSpan expr alts exprType)
= let expr' = applySubst' expr
alts' = applySubst subst alts
exprType' = applySubst subst exprType
in IR.Case srcSpan expr' alts' exprType'
applySubst' (IR.Undefined srcSpan exprType)
= let exprType' = applySubst subst exprType
in IR.Undefined srcSpan exprType'
applySubst' (IR.ErrorExpr srcSpan msg exprType)
= let exprType' = applySubst subst exprType
in IR.ErrorExpr srcSpan msg exprType'
applySubst' (IR.Trace srcSpan msg expr exprType)
= let expr' = applySubst subst expr
exprType' = applySubst subst exprType
in IR.Trace srcSpan msg expr' exprType'
applySubst' (IR.IntLiteral srcSpan value exprType)
= let exprType' = applySubst subst exprType
in IR.IntLiteral srcSpan value exprType'
applySubst' (IR.Lambda srcSpan args expr exprType)
= let args' = applySubst subst args
expr' = applySubst' expr
exprType' = applySubst subst exprType
in IR.Lambda srcSpan args' expr' exprType'
applySubst' (IR.Let srcSpan binds expr exprType)
= let binds' = applySubst subst binds
expr' = applySubst subst expr
exprType' = applySubst subst exprType
in IR.Let srcSpan binds' expr' exprType'
instance ApplySubst IR.Type IR.Alt where
applySubst subst (IR.Alt srcSpan conPat varPats expr)
= let varPats' = applySubst subst varPats
expr' = applySubst subst expr
in IR.Alt srcSpan conPat varPats' expr'
instance ApplySubst IR.Type IR.VarPat where
applySubst subst (IR.VarPat srcSpan varIdent maybeVarType isStrict)
= let maybeVarType' = applySubst subst maybeVarType
in IR.VarPat srcSpan varIdent maybeVarType' isStrict
instance ApplySubst IR.Type IR.Bind where
applySubst subst (IR.Bind srcSpan varPat expr)
= let varPat' = applySubst subst varPat
expr' = applySubst subst expr
in IR.Bind srcSpan varPat' expr'
instance ApplySubst IR.Expr IR.FuncDecl where
applySubst subst
(IR.FuncDecl srcSpan declIdent typeArgs args maybeRetType rhs)
= let (subst', args') = newRenameArgs subst args
rhs' = applySubst subst' rhs
in IR.FuncDecl srcSpan declIdent typeArgs args' maybeRetType rhs'
instance ApplySubst IR.Type IR.FuncDecl where
applySubst subst
(IR.FuncDecl srcSpan declIdent typeArgs args maybeRetType rhs)
= let args' = applySubst subst args
rhs' = applySubst subst rhs
maybeRetType' = applySubst subst maybeRetType
in IR.FuncDecl srcSpan declIdent typeArgs args' maybeRetType' rhs'
instance ApplySubst IR.Type IR.Type where
applySubst (Subst substMap) = applySubst'
where
applySubst' :: IR.Type -> IR.Type
applySubst' typeCon@(IR.TypeCon _ _) = typeCon
applySubst' typeVar@(IR.TypeVar srcSpan ident) = maybe typeVar ($ srcSpan)
(Map.lookup (IR.UnQual (IR.Ident ident)) substMap)
applySubst' (IR.TypeApp srcSpan t1 t2)
= let t1' = applySubst' t1
t2' = applySubst' t2
in IR.TypeApp srcSpan t1' t2'
applySubst' (IR.FuncType srcSpan t1 t2)
= let t1' = applySubst' t1
t2' = applySubst' t2
in IR.FuncType srcSpan t1' t2'
instance ApplySubst IR.Type IR.TypeScheme where
applySubst subst (IR.TypeScheme srcSpan typeArgs typeExpr)
= let (subst', typeArgs') = newRenameArgs subst typeArgs
typeExpr' = applySubst subst' typeExpr
in IR.TypeScheme srcSpan typeArgs' typeExpr'
class Renamable arg expr | arg -> expr, expr -> arg where
getIdent :: arg -> String
setIdent :: arg -> String -> arg
getScope :: arg -> IR.Scope
toExpr :: arg -> SrcSpan -> expr
instance Renamable IR.TypeVarDecl IR.Type where
getIdent = IR.typeVarDeclIdent
setIdent typeArg ident' = typeArg { IR.typeVarDeclIdent = ident' }
getScope = const IR.TypeScope
toExpr = flip IR.TypeVar . getIdent
instance Renamable IR.VarPat IR.Expr where
getIdent = IR.varPatIdent
setIdent varPat ident' = varPat { IR.varPatIdent = ident' }
getScope = const IR.ValueScope
toExpr = flip IR.untypedVar . IR.UnQual . IR.Ident . getIdent
newRenameArgs :: (HasRefs expr, Renamable arg expr, ApplySubst expr expr)
=> Subst expr
-> [arg]
-> (Subst expr, [arg])
newRenameArgs subst args
= let subst' = subst
`substWithout` map (IR.UnQual . IR.Ident . getIdent) args
in newRenameArgs' subst' args
newRenameArgs' :: (HasRefs expr, Renamable arg expr, ApplySubst expr expr)
=> Subst expr
-> [arg]
-> (Subst expr, [arg])
newRenameArgs' subst [] = (subst, [])
newRenameArgs' subst (arg : args) = (arg' :) <$> newRenameArgs' subst' args
where
argIdent :: String
argIdent = getIdent arg
argName :: IR.QName
argName = IR.UnQual (IR.Ident argIdent)
argIdent' :: String
argIdent' = head (filter isNotFree (freshIdentsWithPrefix argIdent))
isNotFree :: String -> Bool
isNotFree = flip Set.notMember (freeSubstIdents (getScope arg) subst)
subst' = subst `composeSubst` singleSubst' argName (toExpr arg')
arg' = setIdent arg argIdent'
freeSubstIdents :: HasRefs a => IR.Scope -> Subst a -> Set String
freeSubstIdents scope (Subst substMap) = Set.fromList
$ mapMaybe (IR.identFromQName . refName)
$ filter ((== scope) . refScope)
$ concatMap (refs . ($ NoSrcSpan))
$ Map.elems substMap
freshIdentsWithPrefix :: String -> [String]
freshIdentsWithPrefix prefix = map (prefix ++) ("" : map show [0 :: Int ..])
renameTypeArgsSubst
:: [IR.TypeVarDecl] -> Converter ([IR.TypeVarDecl], Subst IR.Type)
renameTypeArgsSubst typeArgDecls = do
typeArgDecls' <- mapM freshTypeArgDecl typeArgDecls
let typeArgNames = map IR.typeVarDeclQName typeArgDecls
typeArgs' = map (flip IR.TypeVar . IR.typeVarDeclIdent) typeArgDecls'
subst = composeSubsts (zipWith singleSubst' typeArgNames typeArgs')
return (typeArgDecls', subst)
where
freshTypeArgDecl :: IR.TypeVarDecl -> Converter IR.TypeVarDecl
freshTypeArgDecl (IR.TypeVarDecl srcSpan ident) = do
ident' <- freshHaskellIdent ident
return (IR.TypeVarDecl srcSpan ident')
renameTypeArgs :: ApplySubst IR.Type a
=> [IR.TypeVarDecl]
-> a
-> Converter ([IR.TypeVarDecl], a)
renameTypeArgs typeArgDecls x = do
(typeArgDecls', subst) <- renameTypeArgsSubst typeArgDecls
return (typeArgDecls', applySubst subst x)
renameArgsSubst :: [IR.VarPat] -> Converter ([IR.VarPat], Subst IR.Expr)
renameArgsSubst args = do
args' <- mapM freshVarPat args
let argNames = map IR.varPatQName args
argVars' = map (flip IR.untypedVar . IR.varPatQName) args'
argSubst = composeSubsts (zipWith singleSubst' argNames argVars')
return (args', argSubst)
where
freshVarPat :: IR.VarPat -> Converter IR.VarPat
freshVarPat (IR.VarPat srcSpan varIdent maybeVarType isStrict) = do
varIdent' <- freshHaskellIdent varIdent
return (IR.VarPat srcSpan varIdent' maybeVarType isStrict)
renameArgs
:: ApplySubst IR.Expr a => [IR.VarPat] -> a -> Converter ([IR.VarPat], a)
renameArgs args x = do
(args', subst) <- renameArgsSubst args
return (args', applySubst subst x)