g2-0.1.0.0: src/G2/Language/ExprEnv.hs
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-}
module G2.Language.ExprEnv
( ExprEnv
, ConcOrSym (..)
, empty
, singleton
, fromList
, null
, size
, member
, lookup
, lookupConcOrSym
, deepLookup
, isSymbolic
, occLookup
, lookupNameMod
, insert
, insertSymbolic
, insertExprs
, redirect
, union
, (!)
, map
, map'
, mapWithKey
, mapWithKey'
, mapKeys
, mapM
, mapWithKeyM
, filter
, filterWithKey
, filterToSymbolic
, getIdFromName
, funcsOfType
, keys
, symbolicKeys
, elems
, higherOrderExprs
, toList
, toExprList
, fromExprList
) where
import G2.Language.AST
import G2.Language.Naming
import G2.Language.Syntax
import G2.Language.Typing
import Prelude hiding( filter
, lookup
, map
, mapM
, null)
import qualified Prelude as Pre
import Data.Coerce
import Data.Data (Data, Typeable)
import qualified Data.List as L
import qualified Data.Map as M
import Data.Maybe
import qualified Data.Text as T
data ConcOrSym = Conc Expr
| Sym Id
-- From a user perspective, `ExprEnv`s are mappings from `Name` to
-- `Expr`s. however, there are two complications:
-- 1) Redirection pointers can map two names to the same expr
-- 2) Certain names are symbolic. This means they represent a symbolic variable
-- Nonsymbolic names map to an ExprObj, symbolic names to a SymObj.
data EnvObj = ExprObj Expr
| RedirObj Name
| SymbObj Id
deriving (Show, Eq, Read, Typeable, Data)
newtype ExprEnv = ExprEnv (M.Map Name EnvObj)
deriving (Show, Eq, Read, Typeable, Data)
unwrapExprEnv :: ExprEnv -> M.Map Name EnvObj
unwrapExprEnv = coerce
-- | Constructs an empty `ExprEnv`
empty :: ExprEnv
empty = ExprEnv M.empty
-- | Constructs an `ExprEnv` with a single `Expr`.
singleton :: Name -> Expr -> ExprEnv
singleton n e = ExprEnv $ M.singleton n (ExprObj e)
-- | Constructs an `ExprEnv` from a list of `Name` and `Expr` pairs.
fromList :: [(Name, Expr)] -> ExprEnv
fromList = ExprEnv . M.fromList . Pre.map (\(n, e) -> (n, ExprObj e))
-- Is the `ExprEnv` empty?
null :: ExprEnv -> Bool
null = M.null . unwrapExprEnv
-- | Returns the number of `Expr` in the `ExprEnv`.
size :: ExprEnv -> Int
size = M.size . unwrapExprEnv
-- | Is the given `Name` bound in the `ExprEnv`?
member :: Name -> ExprEnv -> Bool
member n = M.member n . unwrapExprEnv
-- | Lookup the `Expr` with the given `Name`.
-- Returns `Nothing` if the `Name` is not in the `ExprEnv`.
lookup :: Name -> ExprEnv -> Maybe Expr
lookup n (ExprEnv smap) =
case M.lookup n smap of
Just (ExprObj expr) -> Just expr
Just (RedirObj redir) -> lookup redir (ExprEnv smap)
Just (SymbObj i) -> Just $ Var i
Nothing -> Nothing
lookupConcOrSym :: Name -> ExprEnv -> Maybe ConcOrSym
lookupConcOrSym n (ExprEnv smap) =
case M.lookup n smap of
Just (ExprObj expr) -> Just $ Conc expr
Just (RedirObj redir) -> lookupConcOrSym redir (ExprEnv smap)
Just (SymbObj i) -> Just $ Sym i
Nothing -> Nothing
-- | Lookup the `Expr` with the given `Name`.
-- If the name is bound to a @Var@, recursively searches that @Vars@ name.
-- Returns `Nothing` if the `Name` is not in the `ExprEnv`.
deepLookup :: Name -> ExprEnv -> Maybe Expr
deepLookup n eenv =
case lookup n eenv of
Just (Var (Id n' _)) -> lookup n' eenv
r -> r
-- | Checks if the given `Name` belongs to a symbolic variable.
isSymbolic :: Name -> ExprEnv -> Bool
isSymbolic n (ExprEnv eenv') =
case M.lookup n eenv' of
Just (SymbObj _) -> True
_ -> False
-- TODO -- This seems kinda too much like a special case to be here...
occLookup :: T.Text -> Maybe T.Text -> ExprEnv -> Maybe Expr
occLookup n m (ExprEnv eenv) =
let ex = L.find (\(Name n' m' _ _, _) -> n == n' && (m == m' || m' == Just "PrimDefs")) -- TODO: The PrimDefs exception should not be here!
. M.toList . M.map (\(ExprObj e) -> e) . M.filter (isExprObj) $ eenv
in
fmap (\(n', e) -> Var $ Id n' (typeOf e)) ex
lookupNameMod :: T.Text -> Maybe T.Text -> ExprEnv -> Maybe (Name, Expr)
lookupNameMod ns ms =
listToMaybe . L.filter (\(Name n m _ _, _) -> ns == n && ms == m) . toExprList
-- | Looks up a `Name` in the `ExprEnv`. Crashes if the `Name` is not found.
(!) :: ExprEnv -> Name -> Expr
(!) env@(ExprEnv env') n =
case M.lookup n env' of
Just (RedirObj n') -> env ! n'
Just (ExprObj e) -> e
Just (SymbObj i) -> Var i
Nothing -> error $ "ExprEnv.!: Given key is not an element of the expr env" ++ show n
-- | Inserts a new `Expr` into the `ExorEnv`, at the given `Name`.
-- If the `Name` already exists in the `ExprEnv`, the `Expr` is replaced.
insert :: Name -> Expr -> ExprEnv -> ExprEnv
insert n e = ExprEnv . M.insert n (ExprObj e) . unwrapExprEnv
insertSymbolic :: Name -> Id -> ExprEnv -> ExprEnv
insertSymbolic n i = ExprEnv. M.insert n (SymbObj i) . unwrapExprEnv
insertExprs :: [(Name, Expr)] -> ExprEnv -> ExprEnv
insertExprs kvs scope = foldr (uncurry insert) scope kvs
-- | Maps the two `Name`@s@ so that they point to the same value
redirect :: Name -> Name -> ExprEnv -> ExprEnv
redirect n n' = ExprEnv . M.insert n (RedirObj n') . unwrapExprEnv
union :: ExprEnv -> ExprEnv -> ExprEnv
union (ExprEnv eenv) (ExprEnv eenv') = ExprEnv $ eenv `M.union` eenv'
-- | Map a function over all `Expr` in the `ExprEnv`.
-- Will not replace symbolic variables with non-symbolic values,
-- but will rename symbolic values.
map :: (Expr -> Expr) -> ExprEnv -> ExprEnv
map f = mapWithKey (\_ -> f)
-- | Maps a function with an arbitrary return type over all `Expr` in the `ExprEnv`, to get a `Data.Map`.
map' :: (Expr -> a) -> ExprEnv -> M.Map Name a
map' f = mapWithKey' (\_ -> f)
-- | Map a function over all `Expr` in the `ExprEnv`, with access to the `Name`.
-- Will not replace symbolic variables with non-symbolic values,
-- but will rename symbolic values.
mapWithKey :: (Name -> Expr -> Expr) -> ExprEnv -> ExprEnv
mapWithKey f (ExprEnv env) = ExprEnv $ M.mapWithKey f' env
where
f' :: Name -> EnvObj -> EnvObj
f' n (ExprObj e) = ExprObj $ f n e
f' n s@(SymbObj i) =
case f n (Var i) of
Var i' -> SymbObj i'
_ -> s
f' _ n = n
mapWithKey' :: (Name -> Expr -> a) -> ExprEnv -> M.Map Name a
mapWithKey' f = M.mapWithKey f . toExprMap
mapKeys :: (Name -> Name) -> ExprEnv -> ExprEnv
mapKeys f = coerce . M.mapKeys f . unwrapExprEnv
mapM :: Monad m => (Expr -> m Expr) -> ExprEnv -> m ExprEnv
mapM f eenv = return . ExprEnv =<< Pre.mapM f' (unwrapExprEnv eenv)
where
f' (ExprObj e) = return . ExprObj =<< f e
f' s@(SymbObj i) = do
e' <- f (Var i)
case e' of
Var i' -> return $ SymbObj i'
_ -> return s
f' n = return n
mapWithKeyM :: Monad m => (Name -> Expr -> m Expr) -> ExprEnv -> m ExprEnv
mapWithKeyM f eenv = return . ExprEnv . M.fromList =<< Pre.mapM (uncurry f') (toList eenv)
where
f' n (ExprObj e) = return . (n,) . ExprObj =<< f n e
f' n s@(SymbObj i) = do
e' <- f n (Var i)
case e' of
Var i' -> return $ (n, SymbObj i')
_ -> return (n, s)
f' n e = return (n, e)
filter :: (Expr -> Bool) -> ExprEnv -> ExprEnv
filter p = filterWithKey (\_ -> p)
filterWithKey :: (Name -> Expr -> Bool) -> ExprEnv -> ExprEnv
filterWithKey p env@(ExprEnv env') = ExprEnv $ M.filterWithKey p' env'
where
p' :: Name -> EnvObj -> Bool
p' n (RedirObj n') = p n (env ! n')
p' n (ExprObj e) = p n e
p' n (SymbObj i) = p n (Var i)
-- | Returns a new `ExprEnv`, which contains only the symbolic values.
filterToSymbolic :: ExprEnv -> ExprEnv
filterToSymbolic eenv = filterWithKey (\n _ -> isSymbolic n eenv) eenv
-- | Returns the names of all expressions with the given type in the expression environment
funcsOfType :: Type -> ExprEnv -> [Name]
funcsOfType t = keys . filter (\e -> t == typeOf e)
keys :: ExprEnv -> [Name]
keys = M.keys . unwrapExprEnv
symbolicKeys :: ExprEnv -> [Name]
symbolicKeys eenv = M.keys . unwrapExprEnv . filterWithKey (\n _ -> isSymbolic n eenv) $ eenv
-- | Returns all `Expr`@s@ in the `ExprEnv`
elems :: ExprEnv -> [Expr]
elems = exprObjs . M.elems . unwrapExprEnv
-- | Returns a list of all argument function types
higherOrderExprs :: ExprEnv -> [Type]
higherOrderExprs = concatMap (higherOrderFuncs) . elems
toList :: ExprEnv -> [(Name, EnvObj)]
toList = M.toList . unwrapExprEnv
-- | Creates a list of Name to Expr coorespondences
-- Loses information about names that are mapped to the same value
toExprList :: ExprEnv -> [(Name, Expr)]
toExprList env@(ExprEnv env') =
M.toList
. M.mapWithKey (\k _ -> env ! k) $ env'
fromExprList :: [(Name, Expr)] -> ExprEnv
fromExprList = ExprEnv . M.fromList . L.map (\(n, e) -> (n, ExprObj e))
toExprMap :: ExprEnv -> M.Map Name Expr
toExprMap env = M.mapWithKey (\k _ -> env ! k) $ unwrapExprEnv env
getIdFromName :: ExprEnv -> Name -> Maybe Id
getIdFromName eenv name =
case (lookup name eenv) of
Just (Var i) -> Just i
_ -> Nothing
-- Symbolic objects will be returned by calls to eval functions, however
-- calling AST modify functions on the expressions in an ExprEnv will have
-- no effect on contained symbolic objects, unless the returned type is also a Var.
-- This is to mantain the invariant that a symbolic object is just a Var
instance ASTContainer ExprEnv Expr where
containedASTs = elems
modifyContainedASTs f = map f
instance ASTContainer ExprEnv Type where
containedASTs = containedASTs . elems
modifyContainedASTs f = map (modifyContainedASTs f)
instance ASTContainer EnvObj Expr where
containedASTs (ExprObj e) = [e]
containedASTs (RedirObj _) = []
containedASTs (SymbObj i) = [Var i]
modifyContainedASTs f (ExprObj e) = ExprObj (f e)
modifyContainedASTs f s@(SymbObj i) =
case f (Var i) of
(Var i') -> SymbObj i'
_ -> s
modifyContainedASTs _ r = r
instance ASTContainer EnvObj Type where
containedASTs (ExprObj e) = containedASTs e
containedASTs (RedirObj _) = []
containedASTs (SymbObj i) = containedASTs i
modifyContainedASTs f (ExprObj e) = ExprObj (modifyContainedASTs f e)
modifyContainedASTs f (SymbObj i) = SymbObj (modifyContainedASTs f i)
modifyContainedASTs _ r = r
instance Named ExprEnv where
names (ExprEnv eenv) = names (M.keys eenv) ++ names eenv
rename old new =
ExprEnv
. M.mapKeys (\k -> if k == old then new else k)
. rename old new
. unwrapExprEnv
renames hm =
ExprEnv
. M.mapKeys (renames hm)
. renames hm
. unwrapExprEnv
instance Named EnvObj where
names (ExprObj e) = names e
names (RedirObj r) = [r]
names (SymbObj s) = names s
rename old new (ExprObj e) = ExprObj $ rename old new e
rename old new (RedirObj r) = RedirObj $ rename old new r
rename old new (SymbObj s) = SymbObj $ rename old new s
renames hm (ExprObj e) = ExprObj $ renames hm e
renames hm (RedirObj r) = RedirObj $ renames hm r
renames hm (SymbObj s) = SymbObj $ renames hm s
-- Helpers for EnvObjs
isExprObj :: EnvObj -> Bool
isExprObj (ExprObj _) = True
isExprObj _ = False
exprObjs :: [EnvObj] -> [Expr]
exprObjs [] = []
exprObjs (ExprObj e:xs) = e:exprObjs xs
exprObjs (SymbObj i:xs) = Var i:exprObjs xs
exprObjs (_:xs) = exprObjs xs