packages feed

gigaparsec-0.3.1.0: src/Text/Gigaparsec/Internal/TH/VersionAgnostic.hs

{-# LANGUAGE Trustworthy #-}

{-# OPTIONS_HADDOCK hide #-}

{-# LANGUAGE TemplateHaskell, CPP, PatternSynonyms, LambdaCase #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveTraversable, PatternSynonyms #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleContexts #-}
{-# OPTIONS_GHC -Wno-missing-kind-signatures #-}
{-|
Mostly some utils for dealing with TH in a version-agnostic way.
-}
module Text.Gigaparsec.Internal.TH.VersionAgnostic (
  -- * `TH.Type` Smart Constructors
  mkVarT,
  mkConT,
  mkPromotedT,
  mkSigT,
  mkAppT,
  mkForallT,
  -- *** Version ≥ 2.11
  mkInfixT,
  mkUInfixT,
  mkParensT,
  -- *** Version ≥ 2.15
  mkAppKindT,
  mkImplicitParamT,
  -- *** Version ≥ 2.16
  mkForallVisT,
  -- *** Version ≥ 2.19
  mkPromotedInfixT,
  mkPromotedUInfixT,
  -- * TyVarBndr
  -- ** Constructors and Recursors
  TyVarBndr,
  mkPlainTV,
  mkKindedTV,
  recTyVarBndr,
  -- ** Base Functor
  TyVarBndrF,
  pattern PlainTVF,
  pattern KindedTVF,
  recTyVarBndrF,
  projectBnd,
  embedBnd,
  -- * `TH.Type` Base Functor
  TypeF,
  -- ** View Patterns
  pattern ForallTF,
  pattern ForallVisTF, 
  pattern AppTF, 
  pattern AppKindTF, 
  pattern SigTF, 
  pattern InfixTF, 
  pattern UInfixTF, 
  pattern PromotedInfixTF, 
  pattern PromotedUInfixTF, 
  pattern ImplicitParamTF,
  pattern AtomicF, 
  pattern ParensTF,
  pattern VarTF,
  -- ** Recursion and Corecursion
  projectType,
  embedType,
  cataType,
  zygoType,
  -- * Template Haskell select re-exports
  module TH,
  module TH.Lib,
  TH.pprint,
#if !(MIN_VERSION_template_haskell(2,17,0))
  Quote(..),
  DocLoc(..),
  pattern MulArrowT,
  getDoc,
  putDoc,
#endif
  ) where

#if (MIN_VERSION_template_haskell(2,17,0))
import Language.Haskell.TH.Syntax hiding (TyVarBndr(..), Specificity)
import Language.Haskell.TH qualified as TH hiding (TyVarBndr(..), Specificity)
import Language.Haskell.TH.Syntax qualified as TH hiding (TyVarBndr(..), Specificity)
import Language.Haskell.TH.Syntax qualified as THAll
import Language.Haskell.TH.Lib as TH.Lib
#else
import Data.IORef (atomicModifyIORef')
import Language.Haskell.TH.Syntax hiding (TyVarBndr(..), Specificity, newName)
import Language.Haskell.TH qualified as TH hiding (TyVarBndr(..), Specificity)
import Language.Haskell.TH.Syntax qualified as TH hiding (TyVarBndr(..), Specificity)
import Language.Haskell.TH.Syntax qualified as THAll
import Language.Haskell.TH.Lib as TH.Lib
#endif


import Control.Applicative (liftA2)

import GHC.Generics (Generic)
import Data.Kind (Constraint)
import Data.Bitraversable (bisequence)


---------------------------------------------------------------------------------------------------
-- TyVarBndr Base Functor

-- use this defn so that TypeF doesn't need to have two recursion params
type TyVarBndrF :: * -> * -> *
type TyVarBndrF flag k = Either (Name, flag) (Name, flag, k)


{-# COMPLETE PlainTVF, KindedTVF #-}


pattern PlainTVF :: Name -> flag -> TyVarBndrF flag k
pattern KindedTVF :: Name -> flag -> k -> TyVarBndrF flag k
pattern PlainTVF n f = Left (n, f)
pattern KindedTVF n f knd = Right (n, f, knd)


recTyVarBndrF 
  :: (Name -> flag -> a) -- The `PlainTV` case
  -> (Name -> flag -> k -> a) -- The `KindedTV` case
  -> TyVarBndrF flag k
  -> a
recTyVarBndrF f _ (PlainTVF nm ~flag) = f nm flag
recTyVarBndrF _ g (KindedTVF nm ~flag k) = g nm flag k

{-| Unrolls one step of recursion on `TyVarBndr`.

Projects a `TyVarBndr` onto its base functor.
-}
projectBnd :: TyVarBndr flag -> TyVarBndrF flag Type
projectBnd = recTyVarBndr PlainTVF KindedTVF

{-| Rolls up one step of recursion into a `TyVarBndr`.

Embeds a `TyVarBndrF` onto the standard representation `TyVarBndr`.
-}
embedBnd :: TyVarBndrF flag Type -> TyVarBndr flag
embedBnd = recTyVarBndrF mkPlainTV mkKindedTV

---------------------------------------------------------------------------------------------------
-- Type Base Functor

{-|
Base functor for `Type`.

Use a hand-rolled base functor, as we then only need to handle the TH version inconsistencies
in the `embed` and `project` functions.
-}
type TypeF :: * -> *
data TypeF k = 
    ForallTF_ [TyVarBndrF Specificity k] [k] k
  | ForallVisTF_ [TyVarBndrF () k] k
  | AppTF_ k k
  | AppKindTF_ k k
  | SigTF_ k k
  | InfixTF_ k Name k
  | UInfixTF_ k Name k
  | PromotedInfixTF_ k Name k
  | PromotedUInfixTF_ k Name k
  | ImplicitParamTF_ String k
  | AtomicF_ Type
  | ParensTF_ k
  deriving stock (Functor, Foldable, Traversable, Generic)

{-# COMPLETE ForallTF,ForallVisTF, AppTF, AppKindTF, SigTF, InfixTF, UInfixTF, 
  PromotedInfixTF, PromotedUInfixTF, ImplicitParamTF, AtomicF, ParensTF #-}

pattern ForallTF :: [TyVarBndrF Specificity k] -> [k] -> k -> TypeF k
pattern ForallTF bnds ctx t <- ForallTF_ bnds ctx t
pattern ForallVisTF :: [TyVarBndrF () k] -> k -> TypeF k
pattern ForallVisTF bnds t <- ForallVisTF_ bnds t 
pattern AppTF :: k -> k -> TypeF k
pattern AppTF a b <- AppTF_ a b
pattern AppKindTF :: k -> k -> TypeF k
pattern AppKindTF a k <- AppKindTF_ a k
pattern SigTF :: k -> k -> TypeF k
pattern SigTF a k <- SigTF_ a k
pattern InfixTF :: k -> Name -> k -> TypeF k
pattern InfixTF  a n b      <- InfixTF_ a n b
pattern UInfixTF :: k -> Name -> k -> TypeF k
pattern UInfixTF a n b      <- UInfixTF_ a n b
pattern PromotedInfixTF :: k -> Name -> k -> TypeF k
pattern PromotedInfixTF a n b <- PromotedInfixTF_ a n b 
pattern PromotedUInfixTF :: k -> Name -> k -> TypeF k
pattern PromotedUInfixTF a n b <- PromotedUInfixTF_ a n b 
pattern ImplicitParamTF :: String -> k -> TypeF k
pattern ImplicitParamTF x a <- ImplicitParamTF_ x a
pattern AtomicF :: Type -> TypeF k
pattern AtomicF  a <- AtomicF_ a
pattern ParensTF :: k -> TypeF k
pattern ParensTF a <- ParensTF_ a

pattern VarTF :: Name -> TypeF k
pattern VarTF nm = AtomicF_ (VarT nm)

-------------------------------------------------------------------------------
-- Recursion Schemes Stuff
{-
To avoid adding a dependency on `recursion-schemes`, we re-implement some of
the core features from this library.
The originals can be found at:
https://hackage.haskell.org/package/recursion-schemes
-}

type Base :: * -> * -> *
type family Base t :: * -> *

type Recursive :: * -> Constraint
class Functor (Base t) => Recursive t where
  project :: t -> Base t t
  cata :: (Base t a -> a) -> t -> a
  cata f = c 
    where 
    c = f . fmap c . project

type Corecursive :: * -> Constraint
class Functor (Base t) => Corecursive t where
  embed :: Base t t -> t

zygo :: Recursive t => (Base t b -> b) -> (Base t (b, a) -> a) -> t -> a
zygo f g = snd . cata (bisequence (f . fmap fst, g))

-------------------------------------------------------------------------------
-- Base Functor Recursive/Corecursive instances

-- | Newtype for `TH.Type`, so we can make an instance of Recursive and Corecursive.
type THType :: *
newtype THType = THType {getTHType :: TH.Type}

type instance Base THType = TypeF

projectType :: Type -> TypeF Type
projectType = fmap getTHType . project . THType

embedType :: TypeF Type -> Type
embedType = getTHType . embed . fmap THType

cataType :: (TypeF a -> a) -> Type -> a
cataType alg = cata alg . THType

zygoType :: (TypeF b -> b) -> (TypeF (b, a) -> a) -> Type -> a
zygoType α β = zygo α β . THType


instance Recursive THType where
  project :: THType -> Base THType THType
  project = fmap THType . go . getTHType
    where
    go :: Type -> Base THType Type
    go t = case t of
      ForallT bnds ctx a -> 
        ForallTF_ (map projectBnd bnds) ctx a
      AppT a b -> AppTF_ a b
      SigT a k -> SigTF_ a k
#if MIN_VERSION_template_haskell(2,11,0)
      InfixT a n b -> InfixTF_ a n b
      UInfixT a n b -> UInfixTF_ a n b
      ParensT k -> ParensTF_ k
#endif
#if MIN_VERSION_template_haskell(2,15,0)
      AppKindT a k -> AppKindTF_ a k
      ImplicitParamT x a -> ImplicitParamTF_ x a
#endif
#if MIN_VERSION_template_haskell(2,16,0)
      ForallVisT bnds a ->
        ForallVisTF_ (map projectBnd bnds) a
#endif
#if MIN_VERSION_template_haskell(2,19,0)
      PromotedInfixT a n b -> PromotedInfixTF_ a n b
      PromotedUInfixT a n b -> PromotedUInfixTF_ a n b
#endif
      a -> AtomicF_ a
    
instance Corecursive THType where
  embed :: Base THType THType -> THType
  embed = THType . go . fmap getTHType 
    where
    go :: TypeF Type -> Type
    go t = case t of
      ForallTF bnds ctx a -> 
        mkForallT (map embedBnd bnds) ctx a
      ForallVisTF bnds a ->
        mkForallVisT (map embedBnd bnds) a
      AppTF a b -> mkAppT a b
      AppKindTF a k -> mkAppKindT a k
      SigTF a k -> mkSigT a k
      InfixTF a n b -> mkInfixT a n b
      UInfixTF a n b -> mkUInfixT a n b
      PromotedInfixTF a n b -> mkPromotedInfixT a n b
      PromotedUInfixTF a n b -> mkPromotedUInfixT a n b
      ImplicitParamTF x a -> mkImplicitParamT x a
      ParensTF k -> mkParensT k
      AtomicF a -> a


---------------------------------------------------------------------------------------------------
-- Smart Types and Constructors for version agnosticism

{-| A type variable binder.

__Do not__ pattern match on this, instead use `recTyVarBndr`, which safely handles pattern matching in different
versions of template haskell.

/Note:/ In TemplateHaskell < 2.17, the flag parameter is ignored.
-}
#if MIN_VERSION_template_haskell(2,17,0)
type TyVarBndr :: * -> *
type TyVarBndr flag = THAll.TyVarBndr flag
#else
type TyVarBndr flag = THAll.TyVarBndr 
#endif

{-| The specificity of a binding; whether it is inferred or given by a user.

__Note:__ In TemplateHaskell < 2.17, this is unit.
-}
type Specificity :: *
#if MIN_VERSION_template_haskell(2,17,0)
type Specificity = THAll.Specificity
#else
type Specificity = ()
#endif

{-| Version-safe way to pattern match on `TyVarBndr`.

First case is for `TH.PlainTV`, the second for `TH.KindedTV`.
-}
recTyVarBndr 
  :: (Name -> flag -> a) -- The `PlainTV` case
  -> (Name -> flag -> Kind -> a) -- The `KindedTV` case
  -> TyVarBndr flag 
  -> a
#if MIN_VERSION_template_haskell(2,17,0)
recTyVarBndr f _ (THAll.PlainTV nm flag) = f nm flag
recTyVarBndr _ g (THAll.KindedTV nm flag k) = g nm flag k
#else
  -- TODO: this is quite naughty
recTyVarBndr f _ (THAll.PlainTV nm) = f nm undefined
recTyVarBndr _ g (THAll.KindedTV nm k) = g nm undefined k
#endif

{-| Version-safe `TH.PlainTV` constructor for `TyVarBndr`.

In Template Haskell < 2.17, @flag@ can be assumed to be @()@.
-}
mkPlainTV :: Name -> flag -> TyVarBndr flag

{-| Version-safe `TH.KindedTV` constructor for `TyVarBndr`.

In Template Haskell < 2.17, @flag@ can be assumed to be @()@.
-}
mkKindedTV :: Name -> flag -> Type -> TyVarBndr flag
#if MIN_VERSION_template_haskell(2,17,0)
mkPlainTV  = THAll.PlainTV
mkKindedTV = THAll.KindedTV
#else
mkPlainTV  n ~_ = THAll.PlainTV n
mkKindedTV n ~_ = THAll.KindedTV n
#endif

mkVarT :: Name -> Type
mkVarT = VarT

mkConT :: Name -> Type
mkConT = ConT

mkPromotedT :: Name -> Type
mkPromotedT = PromotedT

-- | Smart constructor for `ForallT`. Version-safe.
mkForallT :: [TyVarBndr Specificity] -> Cxt -> Type -> Type
mkForallT = ForallT
-- | Smart constructor for `SigT`. Version-safe.
mkAppT :: Type -> Type -> Type
mkAppT = AppT
-- | Smart constructor for `SigT`. Version-safe.
mkSigT :: Type -> Kind -> Type
mkSigT = SigT

{-| Smart constructor for `InfixT`.

Equal to `undefined` for template haskell versions < 2.11, so ensure any code that
uses this will need only be run in versions ≥ 2.11 .
-}
mkInfixT :: Type -> Name -> Type -> Type
{-| Smart constructor for `UInfixT`.

Equal to `undefined` for template haskell versions < 2.11, so ensure any code that
uses this will need only be run in versions ≥ 2.11 .
-}
mkUInfixT :: Type -> Name -> Type -> Type

{-| Smart constructor for `ParensT`.

Equal to `undefined` for template haskell versions < 2.11, so ensure any code that
uses this will need only be run in versions ≥ 2.11 .
-}
mkParensT :: Type -> Type
#if MIN_VERSION_template_haskell(2,11,0)
mkParensT = ParensT
mkUInfixT = UInfixT
mkInfixT = InfixT
#else
mkUInfixT = undefined
mkParensT = undefined
mkInfixT = undefined
#endif

{-| Smart constructor for `AppKindT`.

Equal to `undefined` for template haskell versions < 2.15, so ensure any code that
uses this will need only be run in versions ≥ 2.15 .
-}
mkAppKindT :: Type -> Type -> Type

{-| Smart constructor for `ImplicitParamT`.

Equal to `undefined` for template haskell versions < 2.15, so ensure any code that
uses this will need only be run in versions ≥ 2.15 .
-}
mkImplicitParamT :: String -> Type -> Type
#if MIN_VERSION_template_haskell(2,15,0)
mkAppKindT = AppKindT
mkImplicitParamT = ImplicitParamT
#else
mkAppKindT = undefined
mkImplicitParamT = undefined
#endif

{-| Smart constructor for `ForallVisT`.

Equal to `undefined` for template haskell versions < 2.16, so ensure any code that
uses this will need only be run in versions ≥ 2.16 .
-}
mkForallVisT :: [TyVarBndr ()] -> Type -> Type
#if MIN_VERSION_template_haskell(2,16,0)
mkForallVisT bnds tp = ForallVisT bnds tp
#else
mkForallVisT bnds tp = undefined
#endif

{-| Smart constructor for `PromotedInfixT`.

Equal to `undefined` for template haskell versions < 2.19, so ensure any code that
uses this will need only be run in versions ≥ 2.19 .
-}
mkPromotedInfixT :: Type -> Name -> Type -> Type

{-| Smart constructor for `PromotedUInfixT`.

Equal to `undefined` for template haskell versions < 2.19, so ensure any code that
uses this will need only be run in versions ≥ 2.19 .
-}
mkPromotedUInfixT :: Type -> Name -> Type -> Type
#if MIN_VERSION_template_haskell(2,19,0)
mkPromotedInfixT = PromotedInfixT
mkPromotedUInfixT = PromotedUInfixT
#else
mkPromotedInfixT = undefined
mkPromotedUInfixT = undefined
#endif


#if !(MIN_VERSION_template_haskell(2,17,0))

{-
All of this is ported from:
https://hackage.haskell.org/package/template-haskell-2.22.0.0/docs/src/Language.Haskell.TH.Syntax.html

-}

class Monad m => Quote m where
  newName :: String -> m Name

instance Quote IO where
  newName s = do { n <- atomicModifyIORef' counter (\x -> (x + 1, x))
                 ; pure (mkNameU s n) }
instance Quote Q where
  newName = qNewName

instance (Semigroup a) => Semigroup (Q a) where
  (<>) = liftA2 (<>)
instance (Monoid a) => Monoid (Q a) where
  mempty = pure mempty


#endif


#if !(MIN_VERSION_template_haskell(2,17,0))

data DocLoc = DeclDoc Name

getDoc :: DocLoc -> Q (Maybe String)
getDoc _ = pure Nothing

putDoc :: DocLoc -> String -> Q ()
putDoc _ _ = pure ()

-- Is this awful?
pattern MulArrowT = ArrowT


#endif