g2-0.1.0.0: src/G2/Lib/Printers.hs
{-# LANGUAGE OverloadedStrings #-}
module G2.Lib.Printers ( mkCleanExprHaskell
, mkUnsugaredExprHaskell
, ppExprEnv
, ppRelExprEnv
, ppCurrExpr
, ppPathConds
, ppPathCond
, pprExecStateStr
, pprExecEEnvStr) where
import G2.Execution.Memory
import G2.Language.Expr
import qualified G2.Language.ExprEnv as E
import G2.Language.KnownValues
import G2.Language.Naming
import qualified G2.Language.PathConds as PC
import G2.Language.TypeClasses
import G2.Language.Typing
import G2.Language.Stack
import G2.Language.Syntax
import G2.Language.Support
import Data.Char
import Data.List
import qualified Data.HashMap.Lazy as HM
import qualified Data.Map as M
import qualified Data.Text as T
mkIdHaskell :: Id -> String
mkIdHaskell (Id n _) = mkNameHaskell n
mkNameHaskell :: Name -> String
mkNameHaskell = T.unpack . nameOcc
mkUnsugaredExprHaskell :: State t -> Expr -> String
mkUnsugaredExprHaskell (State {known_values = kv, type_classes = tc}) =
mkExprHaskell False kv . modifyFix (mkCleanExprHaskell' kv tc)
mkCleanExprHaskell :: State t -> Expr -> String
mkCleanExprHaskell (State {known_values = kv, type_classes = tc}) =
mkExprHaskell True kv . modifyFix (mkCleanExprHaskell' kv tc)
mkCleanExprHaskell' :: KnownValues -> TypeClasses -> Expr -> Expr
mkCleanExprHaskell' kv tc e
| (App (Data (DataCon n _)) e') <- e
, n == dcInt kv || n == dcFloat kv || n == dcDouble kv || n == dcInteger kv || n == dcChar kv = e'
| (App e' e'') <- e
, t <- typeOf e'
, isTypeClass tc t = e''
| (App e' e'') <- e
, t <- typeOf e''
, isTypeClass tc t = e'
| App e' (Type _) <- e = e'
| otherwise = e
mkExprHaskell :: Bool -> KnownValues -> Expr -> String
mkExprHaskell sugar kv ex = mkExprHaskell' ex 0
where
mkExprHaskell' :: Expr -> Int -> String
mkExprHaskell' (Var ids) _ = mkIdHaskell ids
mkExprHaskell' (Lit c) _ = mkLitHaskell c
mkExprHaskell' (Prim p _) _ = mkPrimHaskell p
mkExprHaskell' (Lam _ ids e) i = "\\" ++ mkIdHaskell ids ++ " -> " ++ mkExprHaskell' e i
mkExprHaskell' a@(App ea@(App e1 e2) e3) i
| Data (DataCon n _) <- appCenter a
, isTuple n
, sugar = printTuple kv a
| Data (DataCon n1 _) <- e1
, nameOcc n1 == ":"
, sugar =
if isLitChar e2 then printString a else printList kv a
| isInfixable e1 =
let
e2P = if isApp e2 then "(" ++ mkExprHaskell' e2 i ++ ")" else mkExprHaskell' e2 i
e3P = if isApp e3 then "(" ++ mkExprHaskell' e3 i ++ ")" else mkExprHaskell' e3 i
in
e2P ++ " " ++ mkExprHaskell' e1 i ++ " " ++ e3P
| App _ _ <- e3 = mkExprHaskell' ea i ++ " (" ++ mkExprHaskell' e3 i ++ ")"
| otherwise = mkExprHaskell' ea i ++ " " ++ mkExprHaskell' e3 i
mkExprHaskell' (App e1 ea@(App _ _)) i = mkExprHaskell' e1 i ++ " (" ++ mkExprHaskell' ea i ++ ")"
mkExprHaskell' (App e1 e2) i = mkExprHaskell' e1 i ++ " " ++ mkExprHaskell' e2 i
mkExprHaskell' (Data d) _ = mkDataConHaskell d
mkExprHaskell' (Case e _ ae) i = "\n" ++ off (i + 1) ++ "case " ++ (mkExprHaskell' e i) ++ " of\n"
++ intercalate "\n" (map (mkAltHaskell (i + 2)) ae)
mkExprHaskell' (Type _) _ = ""
mkExprHaskell' (Cast e (_ :~ t)) i = "((coerce " ++ mkExprHaskell' e i ++ ") :: " ++ mkTypeHaskell t ++ ")"
mkExprHaskell' e _ = "e = " ++ show e ++ " NOT SUPPORTED"
mkAltHaskell :: Int -> Alt -> String
mkAltHaskell i (Alt am e) =
off i ++ mkAltMatchHaskell am ++ " -> " ++ mkExprHaskell' e i
mkAltMatchHaskell :: AltMatch -> String
mkAltMatchHaskell (DataAlt dc ids) = mkDataConHaskell dc ++ " " ++ intercalate " " (map mkIdHaskell ids)
mkAltMatchHaskell (LitAlt l) = mkLitHaskell l
mkAltMatchHaskell Default = "_"
mkDataConHaskell :: DataCon -> String
-- Special casing for Data.Map in the modified base
mkDataConHaskell (DataCon (Name "Assocs" _ _ _) _) = "fromList"
mkDataConHaskell (DataCon n _) = mkNameHaskell n
off :: Int -> String
off i = duplicate " " i
printList :: KnownValues -> Expr -> String
printList kv a = "[" ++ intercalate ", " (printList' kv a) ++ "]"
printList' :: KnownValues -> Expr -> [String]
printList' kv (App (App _ e) e') = mkExprHaskell True kv e:printList' kv e'
printList' _ _ = []
printString :: Expr -> String
printString a =
let
str = printString' a
in
if all isPrint str then "\"" ++ str ++ "\""
else "[" ++ intercalate ", " (map stringToEnum str) ++ "]"
where
stringToEnum c
| isPrint c = '\'':c:'\'':[]
| otherwise = "toEnum " ++ show (ord c)
printString' :: Expr -> String
printString' (App (App _ (Lit (LitChar c))) e') = c:printString' e'
printString' _ = []
isTuple :: Name -> Bool
isTuple (Name n _ _ _) = T.head n == '(' && T.last n == ')'
&& T.all (\c -> c == '(' || c == ')' || c == ',') n
printTuple :: KnownValues -> Expr -> String
printTuple kv a = "(" ++ intercalate ", " (reverse $ printTuple' kv a) ++ ")"
printTuple' :: KnownValues -> Expr -> [String]
printTuple' kv (App e e') = mkExprHaskell True kv e':printTuple' kv e
printTuple' _ _ = []
isInfixable :: Expr -> Bool
isInfixable (Data (DataCon n _)) = not $ T.any isAlphaNum $ nameOcc n
isInfixable _ = False
isApp :: Expr -> Bool
isApp (App _ _) = True
isApp _ = False
isLitChar :: Expr -> Bool
isLitChar (Lit (LitChar _)) = True
isLitChar _ = False
mkLitHaskell :: Lit -> String
mkLitHaskell (LitInt i) = if i < 0 then "(" ++ show i ++ ")" else show i
mkLitHaskell (LitInteger i) = if i < 0 then "(" ++ show i ++ ")" else show i
mkLitHaskell (LitFloat r) = "(" ++ show ((fromRational r) :: Float) ++ ")"
mkLitHaskell (LitDouble r) = "(" ++ show ((fromRational r) :: Double) ++ ")"
mkLitHaskell (LitChar c) = ['\'', c, '\'']
mkLitHaskell (LitString s) = s
mkPrimHaskell :: Primitive -> String
mkPrimHaskell Ge = ">="
mkPrimHaskell Gt = ">"
mkPrimHaskell Eq = "=="
mkPrimHaskell Neq = "/="
mkPrimHaskell Lt = "<"
mkPrimHaskell Le = "<="
mkPrimHaskell And = "&&"
mkPrimHaskell Or = "||"
mkPrimHaskell Not = "not"
mkPrimHaskell Plus = "+"
mkPrimHaskell Minus = "-"
mkPrimHaskell Mult = "*"
mkPrimHaskell Div = "/"
mkPrimHaskell DivInt = "/"
mkPrimHaskell Quot = "quot"
mkPrimHaskell Mod = "mod"
mkPrimHaskell Negate = "-"
mkPrimHaskell SqRt = "sqrt"
mkPrimHaskell IntToFloat = "fromIntegral"
mkPrimHaskell IntToDouble = "fromIntegral"
mkPrimHaskell FromInteger = "fromInteger"
mkPrimHaskell ToInteger = "toInteger"
mkPrimHaskell ToInt = "toInt"
mkPrimHaskell Error = "error"
mkPrimHaskell Undefined = "undefined"
mkPrimHaskell Implies = "undefined"
mkPrimHaskell Iff = "undefined"
mkPrimHaskell BindFunc = "undefined"
mkTypeHaskell :: Type -> String
mkTypeHaskell (TyVar i) = mkIdHaskell i
mkTypeHaskell (TyFun t1 t2) = mkTypeHaskell t1 ++ " -> " ++ mkTypeHaskell t2
mkTypeHaskell (TyCon n _) = mkNameHaskell n
mkTypeHaskell (TyApp t1 t2) = "(" ++ mkTypeHaskell t1 ++ " " ++ mkTypeHaskell t2 ++ ")"
mkTypeHaskell _ = "Unsupported type in printer."
duplicate :: String -> Int -> String
duplicate _ 0 = ""
duplicate s n = s ++ duplicate s (n - 1)
ppExprEnv :: State t -> String
ppExprEnv s@(State {expr_env = eenv}) =
let
eenvs = M.toList $ E.map' (mkUnsugaredExprHaskell s) eenv
in
intercalate "\n" $ map (\(n, es) -> mkNameHaskell n ++ " = " ++ es) eenvs
-- | ppRelExprEnv
-- Prints all variable definitions from the expression environment,
-- that are required to understand the curr expr and path constraints
ppRelExprEnv :: State t -> Bindings -> String
ppRelExprEnv s b =
let
(s', _) = markAndSweep s b
in
ppExprEnv s'
ppCurrExpr :: State t -> String
ppCurrExpr s@(State {curr_expr = CurrExpr _ e}) = mkUnsugaredExprHaskell s e
ppPathConds :: State t -> String
ppPathConds s@(State {path_conds = pc}) = intercalate "\n" $ PC.map (ppPathCond s) pc
ppPathCond :: State t -> PathCond -> String
ppPathCond s (AltCond l e b) =
mkLitHaskell l ++ (if b then " == " else " /= ") ++ mkUnsugaredExprHaskell s e
ppPathCond s (ExtCond e b) =
let
es = mkUnsugaredExprHaskell s e
in
if b then es else "not (" ++ es ++ ")"
ppPathCond s (ConsCond dc e b) =
let
dcs = mkDataConHaskell dc
es = mkUnsugaredExprHaskell s e
in
if b then es ++ " is " ++ dcs else es ++ " is not " ++ dcs
ppPathCond _ (PCExists i) = "Exists " ++ mkIdHaskell i
injNewLine :: [String] -> String
injNewLine strs = intercalate "\n" strs
injTuple :: [String] -> String
injTuple strs = "(" ++ (intercalate "," strs) ++ ")"
-- | More raw version of state dumps.
pprExecStateStr :: State t -> Bindings -> String
pprExecStateStr ex_state b = injNewLine acc_strs
where
eenv_str = pprExecEEnvStr (expr_env ex_state)
tenv_str = pprTEnvStr (type_env ex_state)
estk_str = pprExecStackStr (exec_stack ex_state)
code_str = pprExecCodeStr (curr_expr ex_state)
names_str = pprExecNamesStr (name_gen b)
input_str = pprInputIdsStr (symbolic_ids ex_state)
paths_str = pprPathsStr (PC.toList $ path_conds ex_state)
non_red_paths_str = injNewLine (map show $ non_red_path_conds ex_state)
tc_str = pprTCStr (type_classes ex_state)
walkers_str = show (deepseq_walkers b)
cleaned_str = pprCleanedNamesStr (cleaned_names b)
model_str = pprModelStr (model ex_state)
rules_str = intercalate "\n" $ map show (zip ([0..] :: [Integer]) $ rules ex_state)
acc_strs = [ ">>>>> [State] >>>>>>>>>>>>>>>>>>>>>"
, "----- [Code] ----------------------"
, code_str
, "----- [Stack] ---------------------"
, estk_str
, "----- [Env] -----------------------"
, eenv_str
, "----- [TEnv] -----------------------"
, tenv_str
, "----- [Names] ---------------------"
, names_str
, "----- [Input Ids] -----------------"
, input_str
, "----- [Walkers] -------------------"
, walkers_str
, "----- [Paths] ---------------------"
, paths_str
, "----- [Non Red Paths] ---------------------"
, non_red_paths_str
, "----- [True Assert] ---------------------"
, "True Assert = " ++ show (true_assert ex_state)
, "----- [Assert Ids] ---------------------"
, show (assert_ids ex_state)
, "----- [TypeClasses] ---------------------"
, tc_str
, "----- [Cleaned] -------------------"
, cleaned_str
, "----- [Model] -------------------"
, model_str
, "----- [Rules] -------------------"
, rules_str
, "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
]
pprExecEEnvStr :: E.ExprEnv -> String
pprExecEEnvStr eenv = injNewLine kv_strs
where
kv_strs = map (show) $ E.toList eenv
pprTEnvStr :: TypeEnv -> String
pprTEnvStr tenv = injNewLine kv_strs
where
kv_strs = map show $ M.toList tenv
pprModelStr :: Model -> String
pprModelStr m = injNewLine kv_strs
where
kv_strs = map show $ M.toList m
pprExecStackStr :: Stack Frame -> String
pprExecStackStr stk = injNewLine frame_strs
where
frame_strs = map pprExecFrameStr $ toList stk
pprExecFrameStr :: Frame -> String
pprExecFrameStr frame = show frame
pprExecCodeStr :: CurrExpr -> String
pprExecCodeStr code = show code
pprExecNamesStr :: NameGen -> String
pprExecNamesStr _ = ""
pprPathsStr :: [PathCond] -> String
pprPathsStr paths = injNewLine cond_strs
where
cond_strs = map pprPathCondStr paths
pprTCStr :: TypeClasses -> String
pprTCStr tc = injNewLine cond_strs
where
cond_strs = map show $ M.toList $ toMap tc
pprInputIdsStr :: InputIds -> String
pprInputIdsStr i = injNewLine id_strs
where
id_strs = map show i
pprPathCondStr :: PathCond -> String
pprPathCondStr (AltCond am expr b) = injTuple acc_strs
where
am_str = show am
expr_str = show expr
b_str = show b
acc_strs = [am_str, expr_str, b_str]
pprPathCondStr (ExtCond am b) = injTuple acc_strs
where
am_str = show am
b_str = show b
acc_strs = [am_str, b_str]
pprPathCondStr (ConsCond d expr b) = injTuple acc_strs
where
d_str = show d
expr_str = show expr
b_str = show b
acc_strs = [d_str, expr_str, b_str]
pprPathCondStr (PCExists p) = show p
pprCleanedNamesStr :: CleanedNames -> String
pprCleanedNamesStr = injNewLine . map show . HM.toList