g2-0.2.0.0: src/G2/Language/ArbValueGen.hs
{-# LANGUAGE OverloadedStrings #-}
module G2.Language.ArbValueGen ( ArbValueGen
, ArbValueFunc
, arbValueInit
, arbValue
, arbValueInfinite
, constArbValue ) where
import G2.Language.Expr
import G2.Language.Support
import G2.Language.Syntax
import G2.Language.Typing
import Data.List
import qualified Data.HashMap.Lazy as HM
import Data.Ord
import Data.Tuple
-- | A default `ArbValueGen`.
arbValueInit :: ArbValueGen
arbValueInit = ArbValueGen { intGen = 0
, floatGen = 0
, doubleGen = 0
, charGen = charGenInit -- See [CharGenInit]
, boolGen = True
}
type ArbValueFunc = Type -> TypeEnv -> ArbValueGen -> (Expr, ArbValueGen)
-- [CharGenInit]
-- Do NOT make this a cycle. It would simplify arbValue, but causes an infinite loop
-- when we have to output a State (in the QuasiQuoter, for example)
charGenInit :: [Char]
charGenInit = ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9']
-- | Allows the generation of arbitrary values of the given type.
-- Cuts off recursive ADTs with a Prim Undefined
-- Returns a new ArbValueGen that (in the case of the primitives)
-- will give a different value the next time arbValue is called with
-- the same Type.
arbValue :: Type -> TypeEnv -> ArbValueGen -> (Expr, ArbValueGen)
arbValue t tenv = arbValue' getFiniteADT HM.empty t tenv
-- | Allows the generation of arbitrary values of the given type.
-- Cuts off recursive ADTs with a Prim Undefined
-- Returns a new ArbValueGen that is identical to the passed ArbValueGen
constArbValue :: Type -> TypeEnv -> ArbValueGen -> (Expr, ArbValueGen)
constArbValue = constArbValue' getFiniteADT HM.empty
-- | Allows the generation of arbitrary values of the given type.
-- Does not always cut off recursive ADTs.
-- Returns a new ArbValueGen that (in the case of the primitives)
-- will give a different value the next time arbValue is called with
-- the same Type.
arbValueInfinite :: Type -> TypeEnv -> ArbValueGen -> (Expr, ArbValueGen)
arbValueInfinite t = arbValueInfinite' cutOffVal HM.empty t
arbValueInfinite' :: Int -> HM.HashMap Name Type -> Type -> TypeEnv -> ArbValueGen -> (Expr, ArbValueGen)
arbValueInfinite' cutoff = arbValue' (getADT cutoff)
arbValue' :: GetADT
-> HM.HashMap Name Type -- ^ Maps TyVar's to Types
-> Type
-> TypeEnv
-> ArbValueGen
-> (Expr, ArbValueGen)
arbValue' getADTF m (TyFun t t') tenv av =
let
(e, av') = arbValue' getADTF m t' tenv av
in
(Lam TermL (Id (Name "_" Nothing 0 Nothing) t) e, av')
arbValue' getADTF m t tenv av
| TyCon n _ <- tyAppCenter t
, ts <- tyAppArgs t =
maybe (Prim Undefined TyBottom, av)
(\adt -> getADTF m tenv av adt ts)
(HM.lookup n tenv)
arbValue' getADTF m (TyApp t1 t2) tenv av =
let
(e1, av') = arbValue' getADTF m t1 tenv av
(e2, av'') = arbValue' getADTF m t2 tenv av'
in
(App e1 e2, av'')
arbValue' _ _ TyLitInt _ av =
let
i = intGen av
in
(Lit (LitInt $ i), av { intGen = i + 1 })
arbValue' _ _ TyLitFloat _ av =
let
f = floatGen av
in
(Lit (LitFloat $ f), av { floatGen = f + 1 })
arbValue' _ _ TyLitDouble _ av =
let
d = doubleGen av
in
(Lit (LitDouble $ d), av { doubleGen = d + 1 })
arbValue' _ _ TyLitChar _ av =
let
c:cs = case charGen av of
xs@(_:_) -> xs
_ -> charGenInit
in
(Lit (LitChar c), av { charGen = cs})
arbValue' getADTF m (TyVar (Id n _)) tenv av
| Just t <- HM.lookup n m = arbValue' getADTF m t tenv av
arbValue' _ _ t _ av = (Prim Undefined t, av)
constArbValue' :: GetADT -> HM.HashMap Name Type -> Type -> TypeEnv -> ArbValueGen -> (Expr, ArbValueGen)
constArbValue' getADTF m (TyFun t t') tenv av =
let
(e, _) = constArbValue' getADTF m t' tenv av
in
(Lam TermL (Id (Name "_" Nothing 0 Nothing) t) e, av)
constArbValue' getADTF m t tenv av
| TyCon n _ <- tyAppCenter t
, ts <- tyAppArgs t =
maybe (Prim Undefined TyBottom, av)
(\adt -> getADTF m tenv av adt ts)
(HM.lookup n tenv)
constArbValue' getADTF m (TyApp t1 t2) tenv av =
let
(e1, _) = constArbValue' getADTF m t1 tenv av
(e2, _) = constArbValue' getADTF m t2 tenv av
in
(App e1 e2, av)
constArbValue' _ _ TyLitInt _ av =
let
i = intGen av
in
(Lit (LitInt $ i), av)
constArbValue' _ _ TyLitFloat _ av =
let
f = floatGen av
in
(Lit (LitFloat $ f), av)
constArbValue' _ _ TyLitDouble _ av =
let
d = doubleGen av
in
(Lit (LitDouble $ d), av)
constArbValue' _ _ TyLitChar _ av =
let
c:_ = case charGen av of
xs@(_:_) -> xs
_ -> charGenInit
in
(Lit (LitChar c), av)
constArbValue' getADTF m (TyVar (Id n _)) tenv av
| Just t <- HM.lookup n m = constArbValue' getADTF m t tenv av
constArbValue' _ _ t _ av = (Prim Undefined t, av)
type GetADT = HM.HashMap Name Type -> TypeEnv -> ArbValueGen -> AlgDataTy -> [Type] -> (Expr, ArbValueGen)
-- | Generates an arbitrary value of the given ADT,
-- but will return something containing @(Prim Undefined)@ instead of an infinite Expr.
getFiniteADT :: HM.HashMap Name Type -> TypeEnv -> ArbValueGen -> AlgDataTy -> [Type] -> (Expr, ArbValueGen)
getFiniteADT m tenv av adt ts =
let
(e, av') = getADT cutOffVal m tenv av adt ts
in
(cutOff [] e, av')
-- | How long to go before cutting off finite ADTs?
cutOffVal :: Int
cutOffVal = 3
cutOff :: [Name] -> Expr -> Expr
cutOff ns a@(App _ _)
| Data (DataCon n _) <- appCenter a =
case length (filter (== n) ns) > cutOffVal of
True -> Prim Undefined TyBottom
False -> mapArgs (cutOff (n:ns)) a
cutOff _ e = e
-- | Generates an arbitrary value of the given AlgDataTy
-- If there is no such finite value, this may return an infinite Expr.
--
-- Has a bit of a hack: will update the ArbValueGen, but only for a limited number of loops.
-- This is to ensure that an ArbValueGen is eventually returned.
-- To see why this is needed, suppose we are returning an infinitely large Expr.
-- This Expr will be returned lazily. But the return of the ArbValueGen is not lazy-
-- so we must just cut off and return at some point.
getADT :: Int -> HM.HashMap Name Type -> TypeEnv -> ArbValueGen -> AlgDataTy -> [Type] -> (Expr, ArbValueGen)
getADT cutoff m tenv av adt ts
| dcs <- dataCon adt
, _:_ <- dcs =
let
ids = bound_ids adt
-- Finds the DataCon for adt with the least arguments
min_dc = minimumBy (comparing (length . anonArgumentTypes)) dcs
m' = foldr (uncurry HM.insert) m $ zip (map idName ids) ts
(av', es) = mapAccumL (\av_ t -> swap $ arbValueInfinite' (cutoff - 1) m' (applyTypeHashMap m' t) tenv av_) av $ anonArgumentTypes min_dc
final_av = if cutoff >= 0 then av' else av
in
(mkApp $ Data min_dc:map Type ts ++ es, final_av)
| otherwise = (Prim Undefined TyBottom, av)