packages feed

ddc-core-0.4.3.1: DDC/Core/Env/EnvX.hs

-- | Environment of a type expression.
--
--   An environment contains the types 
--     named bound variables,
--     named primitives, 
--     and a deBruijn stack for anonymous variables.
--
module DDC.Core.Env.EnvX
        ( EnvX(..), EnvT.EnvT(..)

        -- * Construction
        , empty,        fromPrimEnvs
        , singleton
        , extendX,      extendsX
        , extendT,      extendsT
        , union,        unions

        -- * Conversion
        , fromList
        , fromListNT
        , fromTypeMap

        , kindEnvOfEnvX
        , typeEnvOfEnvX

        -- * Projections 
        , depth
        , lookupT
        , lookupX,      lookupNameX
        , memberX,      memberBindX

        -- * Primitives
        , setPrimFun
        , isPrim

        -- * Lifting
        , lift)
where
import DDC.Type.Exp
import DDC.Type.Transform.BoundT
import Data.Maybe
import DDC.Type.DataDef                 (DataDefs)
import Data.Map                         (Map)
import Prelude                          hiding (lookup)
import DDC.Core.Env.EnvT                (EnvT)
import qualified DDC.Type.Env           as Env
import qualified DDC.Type.DataDef       as DataDef
import qualified DDC.Core.Env.EnvT      as EnvT
import qualified Data.Map.Strict        as Map
import qualified Prelude                as P
import Control.Monad


-- | Environment of term expressions.
data EnvX n
        = EnvX
        { -- | Environment of type expressions.
          envxEnvT         :: EnvT n

          -- | Types of baked in, primitive names.
        , envxPrimFun      :: !(n -> Maybe (Type n))

          -- | Data type definitions.
        , envxDataDefs     :: !(DataDefs n)

          -- | Types of named variables and constructors.
        , envxMap          :: !(Map n (Type n))

          -- | Types of anonymous deBruijn variables.
        , envxStack        :: ![Type n] 
        
          -- | The length of the above stack.
        , envxStackLength  :: !Int }


-- | An empty environment.
empty :: EnvX n
empty   = EnvX
        { envxEnvT         = EnvT.empty
        , envxPrimFun      = \_ -> Nothing 
        , envxDataDefs     = DataDef.emptyDataDefs
        , envxMap          = Map.empty
        , envxStack        = [] 
        , envxStackLength  = 0 }


-- | Build an `EnvX` from prim environments.
fromPrimEnvs 
        :: Ord n 
        => Env.KindEnv n        -- ^ Primitive kind environment.
        -> Env.TypeEnv n        -- ^ Primitive type environment.
        -> DataDefs n           -- ^ Primitive data type definitions.
        -> EnvX n

fromPrimEnvs kenv tenv defs
 = let  envt    = EnvT.empty 
                { EnvT.envtPrimFun = Env.envPrimFun kenv }

        envx    = empty
                { envxEnvT         = envt
                , envxPrimFun      = Env.envPrimFun tenv 
                , envxDataDefs     = defs }
   in   envx


-- | Construct a singleton type environment.
singleton :: Ord n => Bind n -> EnvX n
singleton b
        = extendX b empty


-------------------------------------------------------------------------------
-- | Extend an environment with a new binding.
--   Replaces bindings with the same name already in the environment.
extendX :: Ord n => Bind n -> EnvX n -> EnvX n
extendX bb env
 = case bb of
         BName n k -> env { envxMap         = Map.insert n k (envxMap env) }
         BAnon   k -> env { envxStack       = k : envxStack env 
                          , envxStackLength = envxStackLength env + 1 }
         BNone{}   -> env


-- | Extend an environment with a list of new bindings.
--   Replaces bindings with the same name already in the environment.
extendsX :: Ord n => [Bind n] -> EnvX n -> EnvX n
extendsX bs env
        = foldl (flip extendX) env bs


-- | Extend the environment with the kind of a new type variable.
extendT :: Ord n => Bind n -> EnvX n -> EnvX n
extendT bb envx
 = envx { envxEnvT = EnvT.extend bb (envxEnvT envx) }


-- | Extend the environment with some new type bindings.
extendsT :: Ord n => [Bind n] -> EnvX n -> EnvX n
extendsT bs env
        = foldl (flip extendT) env bs


-------------------------------------------------------------------------------
-- | Set the function that knows the types of primitive things.
setPrimFun :: (n -> Maybe (Type n)) -> EnvX n -> EnvX n
setPrimFun f env
        = env { envxPrimFun = f }


-- | Check if the type of a name is defined by the `envPrimFun`.
isPrim :: EnvX n -> n -> Bool
isPrim env n
        = isJust $ envxPrimFun env n


-- | Convert a list of `Bind`s to an environment.
fromList :: Ord n => [Bind n] -> EnvX n
fromList bs
        = foldr extendX empty bs


-- | Convert a list of name and types into an environment
fromListNT :: Ord n => [(n, Type n)] -> EnvX n
fromListNT nts
        = fromList [BName n t | (n, t) <- nts]


-- | Convert a map of names to types to a environment.
fromTypeMap :: Map n (Type n) -> EnvX n
fromTypeMap m
        = empty { envxMap = m }


-- | Extract a `KindEnv` from an EnvX.
kindEnvOfEnvX :: Ord n => EnvX n -> Env.KindEnv n
kindEnvOfEnvX env 
        = EnvT.kindEnvOfEnvT $ envxEnvT env


-- | Extract `TypeEnv` from an `EnvX.
typeEnvOfEnvX :: Ord n => EnvX n -> Env.TypeEnv n
typeEnvOfEnvX env
        = Env.empty
        { Env.envMap       = envxMap env
        , Env.envPrimFun   = \n -> envxPrimFun env n }


-- | Combine two environments.
--   If both environments have a binding with the same name,
--   then the one in the second environment takes preference.
union :: Ord n => EnvX n -> EnvX n -> EnvX n
union env1 env2
        = EnvX       
        { envxEnvT         = envxEnvT          env1 `EnvT.union` envxEnvT        env2
        , envxPrimFun      = \n -> envxPrimFun env2 n `mplus`   envxPrimFun env1 n 
        , envxDataDefs     = envxDataDefs      env1 `mappend`   envxDataDefs     env2
        , envxMap          = envxMap           env1 `Map.union` envxMap          env2
        , envxStack        = envxStack         env2  ++ envxStack                env1
        , envxStackLength  = envxStackLength   env2  +  envxStackLength          env1 }


-- | Combine multiple environments,
--   with the latter ones taking preference.
unions :: Ord n => [EnvX n] -> EnvX n
unions envs
        = foldr union empty envs


-- | Check whether a bound variable is present in an environment.
memberX :: Ord n => Bound n -> EnvX n -> Bool
memberX uu env
        = isJust $ lookupX uu env


-- | Check whether a binder is already present in the an environment.
--   This can only return True for named binders, not anonymous or primitive ones.
memberBindX :: Ord n => Bind n -> EnvX n -> Bool
memberBindX uu env
 = case uu of
        BName n _ -> Map.member n (envxMap env)
        _         -> False


-- | Lookup a bound variable from an environment.
lookupT :: Ord n => Bound n -> EnvX n -> Maybe (Kind n)
lookupT uu env
 = EnvT.lookup uu (envxEnvT env) 


-- | Lookup a bound variable from an environment.
lookupX :: Ord n => Bound n -> EnvX n -> Maybe (Type n)
lookupX uu env
 = case uu of
        UName n 
         ->      Map.lookup n (envxMap env) 
         `mplus` envxPrimFun env n

        UIx i     -> P.lookup i (zip [0..] (envxStack env))
        UPrim n _ -> envxPrimFun env n


-- | Lookup a bound name from an environment.
lookupNameX :: Ord n => n -> EnvX n -> Maybe (Type n)
lookupNameX n env
          = Map.lookup n (envxMap env)


-- | Yield the total depth of the deBruijn stack.
depth :: EnvX n -> Int
depth env = envxStackLength env


-- | Lift all free deBruijn indices in the environment by the given number of steps.
---
--  ISSUE #276: Delay lifting of indices in type environments.
--      The 'lift' function on type environments applies to every member of
--      the environment. We'd get better complexity by recording how many
--      levels all types should be lifted by, and only applying the real lift
--      function when the type is finally extracted.
--
lift :: Ord n => Int -> EnvX n -> EnvX n
lift n env
        = EnvX
        { envxEnvT         = envxEnvT env
        , envxPrimFun      = envxPrimFun      env 
        , envxDataDefs     = envxDataDefs     env
        , envxMap          = Map.map (liftT n) (envxMap env)
        , envxStack        = map (liftT n) (envxStack env)
        , envxStackLength  = envxStackLength  env }