hermit-0.1.1.0: src/Language/HERMIT/Context.hs
module Language.HERMIT.Context
(
-- * HERMIT Bindings
HermitBinding(..)
, hermitBindingDepth
-- * The HERMIT Context
, Context
, initContext
, (@@)
, addAltBindings
, addBinding
, addCaseBinding
, addLambdaBinding
, hermitBindings
, hermitDepth
, hermitPath
, hermitModGuts
, lookupHermitBinding
, listBindings
, boundIn
) where
import Prelude hiding (lookup)
import GhcPlugins hiding (empty)
import Data.Map hiding (map, foldr)
import Language.KURE
------------------------------------------------------------------------
-- | HERMIT\'s representation of variable bindings.
data HermitBinding
= BIND Int Bool CoreExpr -- ^ Binding depth, whether it is recursive, and the bound value
-- (which cannot be inlined without checking for scoping issues).
| LAM Int -- ^ For a lambda binding you only know the depth.
| CASE Int CoreExpr (AltCon,[Id]) -- ^ For case wildcard binders. First expr points to scrutinee,
-- second to AltCon (which can be converted to Constructor or Literal).
-- | Get the depth of a binding.
hermitBindingDepth :: HermitBinding -> Int
hermitBindingDepth (LAM d) = d
hermitBindingDepth (BIND d _ _) = d
hermitBindingDepth (CASE d _ _) = d
------------------------------------------------------------------------
-- | The HERMIT context, containing all bindings in scope and the current location in the AST.
-- The bindings here are lazy by choice, so that we can avoid the cost
-- of building the context if we never use it.
data Context = Context
{ hermitBindings :: Map Id HermitBinding -- ^ All (important) bindings in scope.
, hermitDepth :: Int -- ^ The depth of the bindings.
, hermitPath :: AbsolutePath -- ^ The 'AbsolutePath' to the current node from the root.
, hermitModGuts :: ModGuts -- ^ The 'ModGuts' of the current module.
}
------------------------------------------------------------------------
-- | The HERMIT context stores an 'AbsolutePath' to the current node in the tree.
instance PathContext Context where
contextPath = hermitPath
-- | Create the initial HERMIT 'Context' by providing a 'ModGuts'.
initContext :: ModGuts -> Context
initContext modGuts = Context empty 0 rootAbsPath modGuts
-- | Update the context by extending the stored 'AbsolutePath' to a child.
(@@) :: Context -> Int -> Context
(@@) env n = env { hermitPath = extendAbsPath n (hermitPath env) }
-- | Add all bindings in a binding group to the 'Context'.
addBinding :: CoreBind -> Context -> Context
addBinding (NonRec n e) env
= env { hermitBindings = insert n (BIND next_depth False e) (hermitBindings env)
, hermitDepth = next_depth
}
where
next_depth = succ (hermitDepth env)
addBinding (Rec bds) env
= env { hermitBindings = bds_env `union` hermitBindings env
, hermitDepth = next_depth
}
where
next_depth = succ (hermitDepth env)
-- notice how all recursive binding in a binding group are at the same depth.
bds_env = fromList
[ (b,BIND next_depth True e)
| (b,e) <- bds
]
-- | Add the bindings for a specific case alternative.
addCaseBinding :: (Id,CoreExpr,CoreAlt) -> Context -> Context
addCaseBinding (n,e,(ac,is,_)) env
= env { hermitBindings = insert n (CASE next_depth e (ac,is)) (hermitBindings env)
, hermitDepth = next_depth
}
where
next_depth = succ (hermitDepth env)
-- | Add a binding that you know nothing about, except that it may shadow something.
-- If so, do not worry about it here, just remember the binding and the depth.
-- When we want to inline a value from the environment,
-- we then check to see what is free in the inlinee, and see
-- if any of the frees will stop the validity of the inlining.
addLambdaBinding :: Id -> Context -> Context
addLambdaBinding n env
= env { hermitBindings = insert n (LAM next_depth) (hermitBindings env)
, hermitDepth = next_depth
}
where
next_depth = succ (hermitDepth env)
-- | Add the Ids bound by a DataCon in a case. Like lambda bindings,
-- in that we know nothing about them, but all bound at the same depth,
-- so we cannot just fold addLambdaBinding over the list.
addAltBindings :: [Id] -> Context -> Context
addAltBindings ns env
= env { hermitBindings = foldr (\n bds -> insert n (LAM next_depth) bds) (hermitBindings env) ns
, hermitDepth = next_depth
}
where next_depth = succ (hermitDepth env)
-- | Lookup the binding for an identifier in a 'Context'.
lookupHermitBinding :: Id -> Context -> Maybe HermitBinding
lookupHermitBinding n env = lookup n (hermitBindings env)
-- | List all the identifiers bound in the 'Context'.
listBindings :: Context -> [Id]
listBindings = keys . hermitBindings
-- | Determine if an identifier is bound in the 'Context'.
boundIn :: Id -> Context -> Bool
boundIn i c = i `elem` listBindings c
------------------------------------------------------------------------