{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}

{-# HLINT ignore "Unused LANGUAGE pragma" #-}

-- |
-- Module      :   Grisette.Internal.TH.Derivation.DeriveUnifiedSimpleMergeable
-- Copyright   :   (c) Sirui Lu 2024
-- License     :   BSD-3-Clause (see the LICENSE file)
--
-- Maintainer  :   siruilu@cs.washington.edu
-- Stability   :   Experimental
-- Portability :   GHC only
module Grisette.Internal.TH.Derivation.DeriveUnifiedSimpleMergeable
  ( deriveUnifiedSimpleMergeable,
    deriveUnifiedSimpleMergeable1,
    deriveUnifiedSimpleMergeable2,
  )
where

import Grisette.Internal.Internal.Decl.Unified.Class.UnifiedSimpleMergeable
  ( UnifiedSimpleMergeable (withBaseSimpleMergeable),
    UnifiedSimpleMergeable1 (withBaseSimpleMergeable1),
    UnifiedSimpleMergeable2 (withBaseSimpleMergeable2),
  )
import Grisette.Internal.TH.Derivation.Common (DeriveConfig (evalModeConfig))
import Grisette.Internal.TH.Derivation.UnaryOpCommon
  ( UnaryOpClassConfig
      ( UnaryOpClassConfig,
        unaryOpAllowExistential,
        unaryOpConfigs,
        unaryOpContextNames,
        unaryOpExtraVars,
        unaryOpInstanceNames,
        unaryOpInstanceTypeFromConfig
      ),
    UnaryOpConfig (UnaryOpConfig),
    genUnaryOpClass,
  )
import Grisette.Internal.TH.Derivation.UnifiedOpCommon
  ( UnaryOpUnifiedConfig (UnaryOpUnifiedConfig, unifiedFun),
    defaultUnaryOpUnifiedFun,
  )
import Grisette.Internal.Unified.EvalModeTag (EvalModeTag)
import Language.Haskell.TH
  ( Dec,
    Name,
    Q,
    Type (ConT, VarT),
    appT,
    conT,
    newName,
  )

unifiedSimpleMergeableConfig :: UnaryOpClassConfig
unifiedSimpleMergeableConfig :: UnaryOpClassConfig
unifiedSimpleMergeableConfig =
  UnaryOpClassConfig
    { unaryOpConfigs :: [UnaryOpConfig]
unaryOpConfigs =
        [ UnaryOpUnifiedConfig -> [Name] -> UnaryOpConfig
forall config.
UnaryOpFunConfig config =>
config -> [Name] -> UnaryOpConfig
UnaryOpConfig
            UnaryOpUnifiedConfig
              { unifiedFun :: Type -> (Type, Type) -> Q (Maybe Exp)
unifiedFun =
                  [Name] -> Type -> (Type, Type) -> Q (Maybe Exp)
defaultUnaryOpUnifiedFun
                    [ 'withBaseSimpleMergeable,
                      'withBaseSimpleMergeable1,
                      'withBaseSimpleMergeable2
                    ]
              }
            [ 'withBaseSimpleMergeable,
              'withBaseSimpleMergeable1,
              'withBaseSimpleMergeable2
            ]
        ],
      unaryOpInstanceNames :: [Name]
unaryOpInstanceNames =
        [ ''UnifiedSimpleMergeable,
          ''UnifiedSimpleMergeable1,
          ''UnifiedSimpleMergeable2
        ],
      unaryOpExtraVars :: DeriveConfig -> Q [(Type, Type)]
unaryOpExtraVars = \DeriveConfig
config -> do
        let modeConfigs :: [(Int, EvalModeConfig)]
modeConfigs = DeriveConfig -> [(Int, EvalModeConfig)]
evalModeConfig DeriveConfig
config
        case [(Int, EvalModeConfig)]
modeConfigs of
          [] -> do
            nm <- String -> Q Name
forall (m :: * -> *). Quote m => String -> m Name
newName String
"mode"
            return [(VarT nm, ConT ''EvalModeTag)]
          [(Int, EvalModeConfig)
_] -> [(Type, Type)] -> Q [(Type, Type)]
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return []
          [(Int, EvalModeConfig)]
_ -> String -> Q [(Type, Type)]
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"UnifiedSimpleMergeable does not support multiple evaluation modes",
      unaryOpInstanceTypeFromConfig :: DeriveConfig -> [(Type, Type)] -> [(Type, Type)] -> Name -> Q Type
unaryOpInstanceTypeFromConfig =
        \DeriveConfig
config [(Type, Type)]
newModeVars [(Type, Type)]
keptNewVars Name
con -> do
          let modeConfigs :: [(Int, EvalModeConfig)]
modeConfigs = DeriveConfig -> [(Int, EvalModeConfig)]
evalModeConfig DeriveConfig
config
          modeVar <- case [(Int, EvalModeConfig)]
modeConfigs of
            [] -> (Type, Type) -> Q (Type, Type)
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return ((Type, Type) -> Q (Type, Type)) -> (Type, Type) -> Q (Type, Type)
forall a b. (a -> b) -> a -> b
$ [(Type, Type)] -> (Type, Type)
forall a. HasCallStack => [a] -> a
head [(Type, Type)]
newModeVars
            [(Int
i, EvalModeConfig
_)] -> do
              if Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= [(Type, Type)] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [(Type, Type)]
keptNewVars
                then String -> Q (Type, Type)
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"UnifiedSimpleMergeable reference to a non-existent mode variable"
                else (Type, Type) -> Q (Type, Type)
forall a. a -> Q a
forall (m :: * -> *) a. Monad m => a -> m a
return ((Type, Type) -> Q (Type, Type)) -> (Type, Type) -> Q (Type, Type)
forall a b. (a -> b) -> a -> b
$ [(Type, Type)]
keptNewVars [(Type, Type)] -> Int -> (Type, Type)
forall a. HasCallStack => [a] -> Int -> a
!! Int
i
            [(Int, EvalModeConfig)]
_ -> String -> Q (Type, Type)
forall a. String -> Q a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"UnifiedSimpleMergeable does not support multiple evaluation modes"
          appT (conT con) (return $ fst modeVar),
      unaryOpAllowExistential :: Bool
unaryOpAllowExistential = Bool
True,
      unaryOpContextNames :: Maybe [Name]
unaryOpContextNames = Maybe [Name]
forall a. Maybe a
Nothing
    }

-- | Derive 'UnifiedSimpleMergeable' instance for a data type.
deriveUnifiedSimpleMergeable :: DeriveConfig -> Name -> Q [Dec]
deriveUnifiedSimpleMergeable :: DeriveConfig -> Name -> Q [Dec]
deriveUnifiedSimpleMergeable DeriveConfig
deriveConfig =
  DeriveConfig -> UnaryOpClassConfig -> Int -> Name -> Q [Dec]
genUnaryOpClass DeriveConfig
deriveConfig UnaryOpClassConfig
unifiedSimpleMergeableConfig Int
0

-- | Derive 'UnifiedSimpleMergeable1' instance for a data type.
deriveUnifiedSimpleMergeable1 :: DeriveConfig -> Name -> Q [Dec]
deriveUnifiedSimpleMergeable1 :: DeriveConfig -> Name -> Q [Dec]
deriveUnifiedSimpleMergeable1 DeriveConfig
deriveConfig =
  DeriveConfig -> UnaryOpClassConfig -> Int -> Name -> Q [Dec]
genUnaryOpClass DeriveConfig
deriveConfig UnaryOpClassConfig
unifiedSimpleMergeableConfig Int
1

-- | Derive 'UnifiedSimpleMergeable2' instance for a data type.
deriveUnifiedSimpleMergeable2 :: DeriveConfig -> Name -> Q [Dec]
deriveUnifiedSimpleMergeable2 :: DeriveConfig -> Name -> Q [Dec]
deriveUnifiedSimpleMergeable2 DeriveConfig
deriveConfig =
  DeriveConfig -> UnaryOpClassConfig -> Int -> Name -> Q [Dec]
genUnaryOpClass DeriveConfig
deriveConfig UnaryOpClassConfig
unifiedSimpleMergeableConfig Int
2