{-# language
DeriveGeneric
, DeriveDataTypeable
, FlexibleContexts
, GADTs
, OverloadedStrings
, DefaultSignatures
, ScopedTypeVariables
, FlexibleInstances
, LambdaCase
, TemplateHaskell
#-}
{-# OPTIONS_GHC -Wall #-}
{-# OPTIONS_GHC -Wno-type-defaults #-}
module Data.Generics.Encode.Internal (gflattenHM, gflattenGT,
VP(..),
vpInt, vpDouble, vpFloat, vpString, vpText, vpBool, vpScientific, vpChar, vpOneHot,
getIntM, getInt8M, getInt16M, getInt32M, getInt64M, getWordM, getWord8M, getWord16M, getWord32M, getWord64M, getBoolM, getFloatM, getDoubleM, getScientificM, getCharM, getStringM, getTextM, getOneHotM, TypeError(..),
TC(..), tcTyN, tcTyCon, mkTyN, mkTyCon,
Heidi) where
import qualified GHC.Generics as G
import Data.Int (Int8, Int16, Int32, Int64)
import Data.Word (Word, Word8, Word16, Word32, Word64)
import Data.Typeable (Typeable)
import Control.Monad.Catch(Exception(..), MonadThrow(..))
import Generics.SOP (All, DatatypeName, datatypeName, DatatypeInfo, FieldInfo(..), FieldName, ConstructorInfo(..), constructorInfo, All, All2, hcliftA2, hcmap, Proxy(..), SOP(..), NP(..), I(..), K(..), mapIK, hcollapse)
import Generics.SOP.GGP (GCode, GDatatypeInfo, GFrom, gdatatypeInfo, gfrom)
import qualified Data.GenericTrie as GT
import Data.Hashable (Hashable(..))
import Lens.Micro.TH (makeLenses)
import Data.Scientific (Scientific)
import Data.Text (Text, unpack)
import qualified Data.HashMap.Strict as HM
import Data.Generics.Encode.OneHot (OneHot, mkOH)
import Prelude hiding (getChar)
data VP =
VPInt { _vpInt :: Int }
| VPInt8 Int8
| VPInt16 Int16
| VPInt32 Int32
| VPInt64 Int64
| VPWord Word
| VPWord8 Word8
| VPWord16 Word16
| VPWord32 Word32
| VPWord64 Word64
| VPBool { _vpBool :: Bool }
| VPFloat { _vpFloat :: Float }
| VPDouble { _vpDouble :: Double }
| VPScientific { _vpScientific :: Scientific }
| VPChar { _vpChar :: Char }
| VPString { _vpString :: String }
| VPText { _vpText :: Text }
| VPOH { _vpOneHot :: OneHot Int }
deriving (Eq, Ord, G.Generic)
instance Hashable VP
makeLenses ''VP
instance Show VP where
show = \case
VPInt x -> show x
VPInt8 x -> show x
VPInt16 x -> show x
VPInt32 x -> show x
VPInt64 x -> show x
VPWord x -> show x
VPWord8 x -> show x
VPWord16 x -> show x
VPWord32 x -> show x
VPWord64 x -> show x
VPBool b -> show b
VPFloat f -> show f
VPDouble d -> show d
VPScientific s -> show s
VPChar d -> pure d
VPString s -> s
VPText t -> unpack t
VPOH oh -> show oh
gflattenHM :: Heidi a => a -> HM.HashMap [TC] VP
gflattenHM = flattenHM . toVal
gflattenGT :: Heidi a => a -> GT.Trie [TC] VP
gflattenGT = flattenGT . toVal
data TC = TC String String deriving (Eq, Show, Ord, G.Generic)
instance Hashable TC
instance GT.TrieKey TC
tcTyN :: TC -> String
tcTyN (TC n _) = n
tcTyCon :: TC -> String
tcTyCon (TC _ c) = c
mkTyCon :: String -> TC
mkTyCon x = TC "" x
mkTyN :: String -> TC
mkTyN x = TC x ""
flattenHM :: Val -> HM.HashMap [TC] VP
flattenHM = flatten HM.empty HM.insert
flattenGT :: Val -> GT.Trie [TC] VP
flattenGT = flatten GT.empty GT.insert
flatten :: t -> ([TC] -> VP -> t -> t) -> Val -> t
flatten z insf = go ([], z) where
insRev ks = insf (reverse ks)
go (ks, hmacc) = \case
VRec ty hm -> HM.foldlWithKey' (\hm' k t -> go (TC ty k : ks, hm') t) hmacc hm
VEnum ty cn oh -> insRev (TC ty cn : ks) (VPOH oh) hmacc
VPrim vp -> insRev ks vp hmacc
data Val =
VRec String (HM.HashMap String Val)
| VEnum String String (OneHot Int)
| VPrim VP
deriving (Eq, Show)
class Heidi a where
toVal :: a -> Val
default toVal ::
(G.Generic a, All2 Heidi (GCode a), GFrom a, GDatatypeInfo a) => a -> Val
toVal x = sopHeidi (gdatatypeInfo (Proxy :: Proxy a)) (gfrom x)
sopHeidi :: All2 Heidi xss => DatatypeInfo xss -> SOP I xss -> Val
sopHeidi di sop@(SOP xss) = hcollapse $ hcliftA2
(Proxy :: Proxy (All Heidi))
(\ci xs -> K (mkVal ci xs tyName oneHot))
(constructorInfo di)
xss
where
tyName = datatypeName di
oneHot = mkOH di sop
mkVal :: All Heidi xs =>
ConstructorInfo xs -> NP I xs -> DatatypeName -> OneHot Int -> Val
mkVal cinfo xs tyn oh = case cinfo of
Infix cn _ _ -> VRec cn $ mkAnonProd xs
Constructor cn
| null cns -> VEnum tyn cn oh
| otherwise -> VRec cn $ mkAnonProd xs
Record _ fi -> VRec tyn $ mkProd fi xs
where
cns :: [Val]
cns = npHeidis xs
mkProd :: All Heidi xs => NP FieldInfo xs -> NP I xs -> HM.HashMap String Val
mkProd fi xs = HM.fromList $ hcollapse $ hcliftA2 (Proxy :: Proxy Heidi) mk fi xs where
mk :: Heidi v => FieldInfo v -> I v -> K (FieldName, Val) v
mk (FieldInfo n) (I x) = K (n, toVal x)
mkAnonProd :: All Heidi xs => NP I xs -> HM.HashMap String Val
mkAnonProd xs = HM.fromList $ zip labels cns where
cns = npHeidis xs
npHeidis :: All Heidi xs => NP I xs -> [Val]
npHeidis xs = hcollapse $ hcmap (Proxy :: Proxy Heidi) (mapIK toVal) xs
labels :: [String]
labels = map (('_' :) . show) [0 ..]
instance Heidi Bool where toVal = VPrim . VPBool
instance Heidi Int where toVal = VPrim . VPInt
instance Heidi Int8 where toVal = VPrim . VPInt8
instance Heidi Int16 where toVal = VPrim . VPInt16
instance Heidi Int32 where toVal = VPrim . VPInt32
instance Heidi Int64 where toVal = VPrim . VPInt64
instance Heidi Word8 where toVal = VPrim . VPWord8
instance Heidi Word16 where toVal = VPrim . VPWord16
instance Heidi Word32 where toVal = VPrim . VPWord32
instance Heidi Word64 where toVal = VPrim . VPWord64
instance Heidi Float where toVal = VPrim . VPFloat
instance Heidi Double where toVal = VPrim . VPDouble
instance Heidi Scientific where toVal = VPrim . VPScientific
instance Heidi Char where toVal = VPrim . VPChar
instance Heidi String where toVal = VPrim . VPString
instance Heidi Text where toVal = VPrim . VPText
instance Heidi a => Heidi (Maybe a) where
toVal = \case
Nothing -> VRec "Maybe" HM.empty
Just x -> VRec "Maybe" $ HM.singleton "Just" $ toVal x
instance (Heidi a, Heidi b) => Heidi (Either a b) where
toVal = \case
Left l -> VRec "Either" $ HM.singleton "Left" $ toVal l
Right r -> VRec "Either" $ HM.singleton "Right" $ toVal r
instance (Heidi a, Heidi b) => Heidi (a, b) where
toVal (x, y) = VRec "(,)" $ HM.fromList $ zip labels [toVal x, toVal y]
instance (Heidi a, Heidi b, Heidi c) => Heidi (a, b, c) where
toVal (x, y, z) = VRec "(,,)" $ HM.fromList $ zip labels [toVal x, toVal y, toVal z]
getInt :: VP -> Maybe Int
getInt = \case {VPInt i -> Just i; _ -> Nothing}
getInt8 :: VP -> Maybe Int8
getInt8 = \case {VPInt8 i -> Just i; _ -> Nothing}
getInt16 :: VP -> Maybe Int16
getInt16 = \case {VPInt16 i -> Just i; _ -> Nothing}
getInt32 :: VP -> Maybe Int32
getInt32 = \case {VPInt32 i -> Just i; _ -> Nothing}
getInt64 :: VP -> Maybe Int64
getInt64 = \case {VPInt64 i -> Just i; _ -> Nothing}
getWord :: VP -> Maybe Word
getWord = \case {VPWord i -> Just i; _ -> Nothing}
getWord8 :: VP -> Maybe Word8
getWord8 = \case {VPWord8 i -> Just i; _ -> Nothing}
getWord16 :: VP -> Maybe Word16
getWord16 = \case {VPWord16 i -> Just i; _ -> Nothing}
getWord32 :: VP -> Maybe Word32
getWord32 = \case {VPWord32 i -> Just i; _ -> Nothing}
getWord64 :: VP -> Maybe Word64
getWord64 = \case {VPWord64 i -> Just i; _ -> Nothing}
getBool :: VP -> Maybe Bool
getBool = \case {VPBool i -> Just i; _ -> Nothing}
getFloat :: VP -> Maybe Float
getFloat = \case {VPFloat i -> Just i; _ -> Nothing}
getDouble :: VP -> Maybe Double
getDouble = \case {VPDouble i -> Just i; _ -> Nothing}
getScientific :: VP -> Maybe Scientific
getScientific = \case {VPScientific i -> Just i; _ -> Nothing}
getChar :: VP -> Maybe Char
getChar = \case {VPChar i -> Just i; _ -> Nothing}
getString :: VP -> Maybe String
getString = \case {VPString i -> Just i; _ -> Nothing}
getText :: VP -> Maybe Text
getText = \case {VPText i -> Just i; _ -> Nothing}
getOneHot :: VP -> Maybe (OneHot Int)
getOneHot = \case {VPOH i -> Just i; _ -> Nothing}
decodeM :: (MonadThrow m, Exception e) =>
e -> (a -> m b) -> Maybe a -> m b
decodeM e = maybe (throwM e)
getIntM :: MonadThrow m => VP -> m Int
getIntM x = decodeM IntCastE pure (getInt x)
getInt8M :: MonadThrow m => VP -> m Int8
getInt8M x = decodeM Int8CastE pure (getInt8 x)
getInt16M :: MonadThrow m => VP -> m Int16
getInt16M x = decodeM Int16CastE pure (getInt16 x)
getInt32M :: MonadThrow m => VP -> m Int32
getInt32M x = decodeM Int32CastE pure (getInt32 x)
getInt64M :: MonadThrow m => VP -> m Int64
getInt64M x = decodeM Int64CastE pure (getInt64 x)
getWordM :: MonadThrow m => VP -> m Word
getWordM x = decodeM WordCastE pure (getWord x)
getWord8M :: MonadThrow m => VP -> m Word8
getWord8M x = decodeM Word8CastE pure (getWord8 x)
getWord16M :: MonadThrow m => VP -> m Word16
getWord16M x = decodeM Word16CastE pure (getWord16 x)
getWord32M :: MonadThrow m => VP -> m Word32
getWord32M x = decodeM Word32CastE pure (getWord32 x)
getWord64M :: MonadThrow m => VP -> m Word64
getWord64M x = decodeM Word64CastE pure (getWord64 x)
getBoolM :: MonadThrow m => VP -> m Bool
getBoolM x = decodeM BoolCastE pure (getBool x)
getFloatM :: MonadThrow m => VP -> m Float
getFloatM x = decodeM FloatCastE pure (getFloat x)
getDoubleM :: MonadThrow m => VP -> m Double
getDoubleM x = decodeM DoubleCastE pure (getDouble x)
getScientificM :: MonadThrow m => VP -> m Scientific
getScientificM x = decodeM ScientificCastE pure (getScientific x)
getCharM :: MonadThrow m => VP -> m Char
getCharM x = decodeM CharCastE pure (getChar x)
getStringM :: MonadThrow m => VP -> m String
getStringM x = decodeM StringCastE pure (getString x)
getTextM :: MonadThrow m => VP -> m Text
getTextM x = decodeM TextCastE pure (getText x)
getOneHotM :: MonadThrow m => VP -> m (OneHot Int)
getOneHotM x = decodeM OneHotCastE pure (getOneHot x)
data TypeError =
FloatCastE
| DoubleCastE
| ScientificCastE
| IntCastE
| Int8CastE
| Int16CastE
| Int32CastE
| Int64CastE
| WordCastE
| Word8CastE
| Word16CastE
| Word32CastE
| Word64CastE
| BoolCastE
| CharCastE
| StringCastE
| TextCastE
| OneHotCastE
deriving (Show, Eq, Typeable)
instance Exception TypeError