hermit-0.1.4.0: src/Language/HERMIT/Context.hs
{-# LANGUAGE InstanceSigs #-}
module Language.HERMIT.Context
( -- * The HERMIT Context
HermitC
, initHermitC
-- ** Adding to the Context
, (@@)
, addAltBindings
, addBinding
, addCaseBinding
, addLambdaBinding
-- ** Reading from the Context
, hermitBindings
, hermitDepth
, hermitPath
, hermitModGuts
, lookupHermitBinding
, boundVars
, boundIn
, findBoundVars
-- ** Bindings
, HermitBinding(..)
, hermitBindingDepth
) where
import Prelude hiding (lookup)
import GhcPlugins hiding (empty)
import Data.Map hiding (map, foldr, filter)
import qualified Language.Haskell.TH as TH
import Language.HERMIT.GHC
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. We store both the scrutinised expression,
-- and the case alternative 'AltCon' (which can be converted to Constructor or Literal) and identifiers.
-- | 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 HermitC = HermitC
{ hermitBindings :: Map Var 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 HermitC where
contextPath :: HermitC -> AbsolutePath
contextPath = hermitPath
-- | Create the initial HERMIT 'HermitC' by providing a 'ModGuts'.
initHermitC :: ModGuts -> HermitC
initHermitC modGuts = HermitC empty 0 rootAbsPath modGuts
-- | Update the context by extending the stored 'AbsolutePath' to a child.
(@@) :: HermitC -> Int -> HermitC
(@@) c v = c { hermitPath = extendAbsPath v (hermitPath c) }
------------------------------------------------------------------------
-- | Add all bindings in a binding group to a context.
addBinding :: CoreBind -> HermitC -> HermitC
addBinding corebind c = let nextDepth = succ (hermitDepth c)
hbds = hermitBindings c
newBds = case corebind of
NonRec v e -> insert v (BIND nextDepth False e) hbds
Rec bds -> hbds `union` fromList [ (b, BIND nextDepth True e) | (b,e) <- bds ]
-- Notice how all recursive binding in a binding group are at the same depth.
in c { hermitBindings = newBds
, hermitDepth = nextDepth
}
-- | Add the bindings for a specific case alternative.
addCaseBinding :: (Id,CoreExpr,CoreAlt) -> HermitC -> HermitC
addCaseBinding (v,e,(con,vs,_)) c = let nextDepth = succ (hermitDepth c)
in c { hermitBindings = insert v (CASE nextDepth e (con,vs)) (hermitBindings c)
, hermitDepth = nextDepth
}
-- | Add a lambda bound variable to a context.
-- All that is known is the variable, which may shadow something.
-- If so, we don't worry about that here, it is instead checked during inlining.
addLambdaBinding :: Var -> HermitC -> HermitC
addLambdaBinding v c = let nextDepth = succ (hermitDepth c)
in c { hermitBindings = insert v (LAM nextDepth) (hermitBindings c)
, hermitDepth = nextDepth
}
-- | Add the identifiers 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] -> HermitC -> HermitC
addAltBindings vs c = let nextDepth = succ (hermitDepth c)
in c { hermitBindings = foldr (\ v bds -> insert v (LAM nextDepth) bds) (hermitBindings c) vs
, hermitDepth = nextDepth
}
------------------------------------------------------------------------
-- | Lookup the binding for a variable in a context.
lookupHermitBinding :: Var -> HermitC -> Maybe HermitBinding
lookupHermitBinding n env = lookup n (hermitBindings env)
-- | List all the variables bound in a context.
boundVars :: HermitC -> [Var]
boundVars = keys . hermitBindings
-- | Determine if a variable is bound in a context.
boundIn :: Var -> HermitC -> Bool
boundIn i c = i `elem` boundVars c
------------------------------------------------------------------------
-- | List all variables bound in the context that match the given name.
findBoundVars :: TH.Name -> HermitC -> [Var]
findBoundVars nm = filter (cmpTHName2Var nm) . boundVars
------------------------------------------------------------------------