{-# 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