ddc-source-tetra-0.4.3.1: DDC/Source/Tetra/Transform/Freshen/State.hs
{-# LANGUAGE TypeFamilies, OverloadedStrings #-}
module DDC.Source.Tetra.Transform.Freshen.State
( type S
, State (..), stateZero
, Env (..), envZero
, evalState
, newName
, withModifiedEnvT
, withModifiedEnvX
, mapFreshBinds)
where
import DDC.Source.Tetra.Exp
import Data.Monoid
import Data.Set (Set)
import Data.Map.Strict (Map)
import Data.Text (Text)
import qualified Control.Monad.State as S
import qualified Data.Text as Text
import qualified Data.Set as Set
import qualified Data.Map.Strict as Map
-------------------------------------------------------------------------------
-- | State holding a variable name prefix and counter to
-- create fresh variable names.
type S = S.State State
data State
= State
{ -- | Prefix for creating fresh variables.
stateVarPrefix :: Text
-- | Current counter for creating fresh variables.
, stateVarCount :: Int
-- | Environment for type level names.
, stateEnvT :: Env
-- | Environment for value level names.
, stateEnvX :: Env
}
-- | Information about a current environemnt.
data Env
= Env
{ -- | Stack of names of anonymous binders.
envStack :: [Name]
-- | Length of the above sack.
, envStackLen :: Int
-- | Names currently in scope.
, envNames :: Set Name
-- | Names currently being rewritten.
, envRename :: Map Name Name }
-- | The empty environmenet.
envZero :: Env
envZero
= Env
{ envStack = []
, envStackLen = 0
, envNames = Set.empty
, envRename = Map.empty }
-- | The starting state.
stateZero :: Text -> State
stateZero prefix
= State
{ stateVarPrefix = prefix
, stateVarCount = 0
, stateEnvT = envZero
, stateEnvX = envZero }
-- | Evaluate a desguaring computation,
-- using the given prefix for freshly introduced variables.
evalState :: Text -> S a -> a
evalState prefix c
= S.evalState c (stateZero prefix)
-- | Allocate a new name.
newName :: Text -> S Name
newName pre
= do prefix <- S.gets stateVarPrefix
count <- S.gets stateVarCount
let name = pre <> "$" <> prefix <> Text.pack (show count)
S.modify $ \s -> s { stateVarCount = count + 1 }
return name
-- | Run a computation in a modified EnvT,
-- restoring the original environment after it's done.
withModifiedEnvT :: (Env -> Env) -> S a -> S a
withModifiedEnvT modEnvT cont
= do
state <- S.get
let envT = stateEnvT state
let envT' = modEnvT envT
S.put state { stateEnvT = envT' }
result <- cont
state' <- S.get
S.put state' { stateEnvT = envT }
return result
-- | Run a computation in a modified EnvX,
-- restoring the original environment after it's done.
withModifiedEnvX :: (Env -> Env) -> S a -> S a
withModifiedEnvX modEnvX cont
= do
state <- S.get
let envX = stateEnvX state
let envX' = modEnvX envX
S.put state { stateEnvX = envX' }
result <- cont
state' <- S.get
S.put state' { stateEnvX = envX }
return result
-- | Given a function that binds and freshens a single thing,
-- binds and freshens a list of things in sequence.
mapFreshBinds
:: (a -> ( a -> S b) -> S b)
-> [a] -> ([a] -> S b) -> S b
mapFreshBinds freshBind as0 cont
= go [] as0
where
go asAcc []
= cont (reverse asAcc)
go asAcc (a : as)
= freshBind a $ \a' -> go (a' : asAcc) as