g2-0.2.0.0: src/G2/Execution/PrimitiveEval.hs
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE FlexibleContexts #-}
module G2.Execution.PrimitiveEval (evalPrims, maybeEvalPrim, evalPrimSymbolic) where
import G2.Language.AST
import G2.Language.Expr
import G2.Language.Naming
import G2.Language.Primitives
import G2.Language.Support
import G2.Language.Syntax
import G2.Language.Typing
import Control.Exception
import Data.Char
import qualified Data.HashMap.Lazy as M
import qualified Data.List as L
import Data.Maybe
import qualified G2.Language.ExprEnv as E
evalPrims :: ASTContainer m Expr => TypeEnv -> KnownValues -> m -> m
evalPrims tenv kv = modifyContainedASTs (evalPrims' tenv kv . simplifyCasts)
evalPrims' :: TypeEnv -> KnownValues -> Expr -> Expr
evalPrims' tenv kv a@(App x y) =
case unApp a of
[p@(Prim _ _), l] -> evalPrim' tenv kv [p, evalPrims' tenv kv l]
[p@(Prim _ _), l1, l2] ->
evalPrim' tenv kv [p, evalPrims' tenv kv l1, evalPrims' tenv kv l2]
_ -> App (evalPrims' tenv kv x) (evalPrims' tenv kv y)
evalPrims' tenv kv e = modifyChildren (evalPrims' tenv kv) e
evalPrim' :: TypeEnv -> KnownValues -> [Expr] -> Expr
evalPrim' tenv kv xs = fromMaybe (mkApp xs) (maybeEvalPrim' tenv kv xs)
maybeEvalPrim :: TypeEnv -> KnownValues -> Expr -> Maybe Expr
maybeEvalPrim tenv kv = maybeEvalPrim' tenv kv . unApp
maybeEvalPrim' :: TypeEnv -> KnownValues -> [Expr] -> Maybe Expr
maybeEvalPrim' tenv kv xs
| [Prim p _, x] <- xs
, Lit x' <- x
, Just e <- evalPrim1 p x' = Just e
| [Prim p _, x] <- xs
, Lit x' <- x = evalPrim1' tenv kv p x'
| [Prim p _, x, y] <- xs
, Lit x' <- x
, Lit y' <- y = evalPrim2 kv p x' y'
| [Prim p _, Type t, dc_e] <- xs, Data dc:_ <- unApp dc_e =
evalTypeDCPrim2 tenv p t dc
| [Prim p _, Type t, Lit (LitInt l)] <- xs =
evalTypeLitPrim2 tenv p t l
| otherwise = Nothing
evalPrim1 :: Primitive -> Lit -> Maybe Expr
evalPrim1 Negate (LitInt x) = Just . Lit $ LitInt (-x)
evalPrim1 Negate (LitFloat x) = Just . Lit $ LitFloat (-x)
evalPrim1 Negate (LitDouble x) = Just . Lit $ LitDouble (-x)
evalPrim1 Abs (LitInt x) = Just . Lit $ LitInt (abs x)
evalPrim1 Abs (LitFloat x) = Just . Lit $ LitFloat (abs x)
evalPrim1 Abs (LitDouble x) = Just . Lit $ LitDouble (abs x)
evalPrim1 SqRt x = evalPrim1Floating (sqrt) x
evalPrim1 IntToFloat (LitInt x) = Just . Lit $ LitFloat (fromIntegral x)
evalPrim1 IntToDouble (LitInt x) = Just . Lit $ LitDouble (fromIntegral x)
evalPrim1 Chr (LitInt x) = Just . Lit $ LitChar (chr $ fromInteger x)
evalPrim1 OrdChar (LitChar x) = Just . Lit $ LitInt (toInteger $ ord x)
evalPrim1 WGenCat (LitInt x) = Just . Lit $ LitInt (toInteger . fromEnum . generalCategory . toEnum $ fromInteger x)
evalPrim1 _ _ = Nothing
evalPrim1' :: TypeEnv -> KnownValues -> Primitive -> Lit -> Maybe Expr
evalPrim1' tenv kv IntToString (LitInt x) =
let
char_dc = mkDCChar kv tenv
in
Just . mkG2List kv tenv TyLitChar . map (App char_dc . Lit . LitChar) $ show x
evalPrim1' _ _ _ _ = Nothing
evalPrim2 :: KnownValues -> Primitive -> Lit -> Lit -> Maybe Expr
evalPrim2 kv Ge x y = evalPrim2NumCharBool (>=) kv x y
evalPrim2 kv Gt x y = evalPrim2NumCharBool (>) kv x y
evalPrim2 kv Eq x y = evalPrim2NumCharBool (==) kv x y
evalPrim2 kv Lt x y = evalPrim2NumCharBool (<) kv x y
evalPrim2 kv Le x y = evalPrim2NumCharBool (<=) kv x y
evalPrim2 _ Plus x y = evalPrim2Num (+) x y
evalPrim2 _ Minus x y = evalPrim2Num (-) x y
evalPrim2 _ Mult x y = evalPrim2Num (*) x y
evalPrim2 _ Div x y = if isZero y then error "Have Div _ 0" else evalPrim2Fractional (/) x y
evalPrim2 _ Quot x y = if isZero y then error "Have Quot _ 0" else evalPrim2Integral quot x y
evalPrim2 _ Mod x y = evalPrim2Integral mod x y
evalPrim2 _ _ _ _ = Nothing
evalTypeDCPrim2 :: TypeEnv -> Primitive -> Type -> DataCon -> Maybe Expr
evalTypeDCPrim2 tenv DataToTag t dc =
case unTyApp t of
TyCon n _:_ | Just adt <- M.lookup n tenv ->
let
dcs = dataCon adt
in
fmap (Lit . LitInt . fst) . L.find ((==) dc . snd) $ zip [1..] dcs
_ -> error "evalTypeDCPrim2: Unsupported Primitive Op"
evalTypeDCPrim2 _ _ _ _ = Nothing
evalTypeLitPrim2 :: TypeEnv -> Primitive -> Type -> Integer -> Maybe Expr
evalTypeLitPrim2 tenv TagToEnum t i =
case unTyApp t of
TyCon n _:_ | Just adt <- M.lookup n tenv ->
let
dcs = dataCon adt
in
maybe (Just $ Prim Error TyBottom) (Just . Data)
$ ith dcs (fromInteger i)
_ -> error "evalTypeLitPrim2: Unsupported Primitive Op"
evalTypeLitPrim2 _ _ _ _ = Nothing
ith :: [a] -> Integer -> Maybe a
ith (x:_) 0 = Just x
ith (_:xs) n = assert (n > 0) ith xs (n - 1)
ith _ _ = Nothing
isZero :: Lit -> Bool
isZero (LitInt 0) = True
isZero (LitFloat 0) = True
isZero (LitDouble 0) = True
isZero _ = False
evalPrim2NumCharBool :: (forall a . Ord a => a -> a -> Bool) -> KnownValues -> Lit -> Lit -> Maybe Expr
evalPrim2NumCharBool f kv (LitInt x) (LitInt y) = Just . mkBool kv $ f x y
evalPrim2NumCharBool f kv (LitFloat x) (LitFloat y) = Just . mkBool kv $ f x y
evalPrim2NumCharBool f kv (LitDouble x) (LitDouble y) = Just . mkBool kv $ f x y
evalPrim2NumCharBool f kv (LitChar x) (LitChar y) = Just . mkBool kv $ f x y
evalPrim2NumCharBool _ _ _ _ = Nothing
evalPrim2Num :: (forall a . Num a => a -> a -> a) -> Lit -> Lit -> Maybe Expr
evalPrim2Num f (LitInt x) (LitInt y) = Just . Lit . LitInt $ f x y
evalPrim2Num f (LitFloat x) (LitFloat y) = Just . Lit . LitFloat $ f x y
evalPrim2Num f (LitDouble x) (LitDouble y) = Just . Lit . LitDouble $ f x y
evalPrim2Num _ _ _ = Nothing
evalPrim2Fractional :: (forall a . Fractional a => a -> a -> a) -> Lit -> Lit -> Maybe Expr
evalPrim2Fractional f (LitFloat x) (LitFloat y) = Just . Lit . LitFloat $ f x y
evalPrim2Fractional f (LitDouble x) (LitDouble y) = Just . Lit . LitDouble $ f x y
evalPrim2Fractional _ _ _ = Nothing
evalPrim2Integral :: (forall a . Integral a => a -> a -> a) -> Lit -> Lit -> Maybe Expr
evalPrim2Integral f (LitInt x) (LitInt y) = Just . Lit . LitInt $ f x y
evalPrim2Integral _ _ _ = Nothing
evalPrim1Floating :: (forall a . Floating a => a -> a) -> Lit -> Maybe Expr
evalPrim1Floating f (LitFloat x) = Just . Lit . LitFloat . toRational $ f (fromRational x :: Double)
evalPrim1Floating f (LitDouble x) = Just . Lit . LitDouble . toRational $ f (fromRational x :: Double)
evalPrim1Floating _ _ = Nothing
-- | Evaluate certain primitives applied to symbolic expressions, when possible
evalPrimSymbolic :: ExprEnv -> TypeEnv -> NameGen -> KnownValues -> Expr -> Maybe (Expr, ExprEnv, [PathCond], NameGen)
evalPrimSymbolic eenv tenv ng kv e
| [Prim DataToTag _, type_t, cse] <- unApp e
, Type t <- dig eenv type_t
, Case v@(Var (Id n _)) _ _ alts <- cse
, TyCon tn _:_ <- unTyApp t
, Just adt <- M.lookup tn tenv =
let
alt_p = map (\(Alt (LitAlt l) e) ->
let
Data dc = head $ unApp e
in
(l, dc)) alts
dcs = dataCon adt
num_dcs = zip (map (Lit . LitInt) [0..]) dcs
(ret, ng') = freshId TyLitInt ng
new_pc = map (`ExtCond` True)
$ mapMaybe (\(alt_l, alt_dc) ->
fmap (\(nn, n_dc) -> eq v (Lit alt_l) `eq` eq (Var ret) nn)
$ L.find ((==) alt_dc . snd) num_dcs) alt_p
in
Just (Var ret, E.insertSymbolic ret eenv, new_pc, ng')
-- G2 uses actual Bools in primitive comparisons, whereas GHC uses 0# and 1#.
-- We thus need special handling so that, in G2, we don't try to convert to Bool via a TagToEnum
-- (which has type Int# -> a). Instead, we simply return the Bool directly.
| tBool <- tyBool kv
, [Prim TagToEnum _, _, pe] <- unApp e
, typeOf (dig eenv pe) == tBool = Just (pe, eenv, [], ng)
| [Prim TagToEnum _, type_t, pe] <- unApp e
, Type t <- dig eenv type_t =
case unTyApp t of
TyCon n _:_ | Just adt <- M.lookup n tenv ->
let
(b, ng') = freshId TyLitInt ng
dcs = dataCon adt
alts = zipWith (\l dc -> Alt (LitAlt (LitInt l)) (Data dc)) [0..] dcs
alt_d = Alt Default (Prim Error t)
in
Just (Case pe b t (alt_d:alts), E.insertSymbolic b eenv, [], ng')
_ -> error "evalTypeLitPrim2: Unsupported Primitive Op"
| otherwise = Nothing
where
eq e1 = App (App (mkEqPrimType (typeOf e1) kv) e1)
dig :: ExprEnv -> Expr -> Expr
dig eenv (Var (Id n _)) | Just (E.Conc e) <- E.lookupConcOrSym n eenv = dig eenv e
dig _ e = e