packages feed

sdl3-bindgen-sys-0.0.0.1: runtime-cexpr/C/Type/Internal/Universe.hs

{-# LANGUAGE ScopedTypeVariables #-}

-- | **Internal** module listing out all n-tuples of types
-- for the purposes of generating code with Template Haskell
-- and for testing.
module C.Type.Internal.Universe (
  OpaqueTy (..),
  enumerateTypeTuples,
  allArithmeticTypes,
  allIntegralTypes,
  allIntLikeTypes,
) where

import Data.Functor ((<&>))
import Data.Type.Nat qualified as Fin
import Data.Vec.Lazy (Vec (..))
import Data.Vec.Lazy qualified as Vec (snoc)
import GHC.Generics (Generic)

import C.Type

--------------------------------------------------------------------------------

-- | Helper type to use 'Fin.induction' on.
newtype F n = F {unF :: [(Vec n (Type OpaqueTy), Maybe Int)]}

-- | An opaque named type with a unique identifier.
newtype OpaqueTy = OpaqueTy Int
  deriving stock (Eq, Generic, Ord, Show)

-- | Enumerate all tuples of types.
--
-- For example, for @n = 2@, this will list out pairs of types.
-- This list will include all pairwise combination of primitive types,
-- and also two pairs of pointer types:
--
--  - @( Ptr ty_1, Ptr ty_2 )@ – pointers to different types,
--  - @( Ptr ty_1, Ptr ty_1 )@ – pointers to the same type.
--
-- This is used for generating type family and class instances.
enumerateTypeTuples :: forall n. (Fin.SNatI n) => [Vec n (Type OpaqueTy)]
enumerateTypeTuples =
  fmap fst $
    unF $
      Fin.induction
        (F [(VNil, Nothing)])
        ( \((F prev) :: F m) -> F $ do
            (tys, mbLastUsedTyVarNumber) <- prev
            let m = case mbLastUsedTyVarNumber of
                  Nothing -> 1
                  Just i -> i + 1
            (ty, mbNextUsedTyVar) <- allTypes m
            let mbUsedTv = maxMaybe mbLastUsedTyVarNumber mbNextUsedTyVar
            return (Vec.snoc tys ty, mbUsedTv)
        )

maxMaybe :: (Ord i) => Maybe i -> Maybe i -> Maybe i
maxMaybe (Just i) (Just j) = Just $ max i j
maxMaybe j@(Just{}) Nothing = j
maxMaybe Nothing r = r

allTypes :: Int -> [(Type OpaqueTy, Maybe Int)]
allTypes n =
  fmap ((,Nothing) . Arithmetic) allArithmeticTypes
    ++ (Void, Nothing)
    : [(Ptr (OpaqueTy i), Just i) | i <- [1 .. n]]

-- For unary functions, just have a single pointer type "Ptr a".
-- For binary functions, the first argument has "Ptr a1",
-- while the second argument has both "Ptr a1" and "Ptr a2".
-- etc

allArithmeticTypes :: [ArithmeticType]
allArithmeticTypes =
  fmap FloatLike [FloatType, DoubleType]
    ++ fmap Integral allIntegralTypes

allIntegralTypes :: [IntegralType]
allIntegralTypes = Bool : fmap CharLike [Char, SChar, UChar] ++ fmap IntLike allIntLikeTypes

allIntLikeTypes :: [IntLikeType]
allIntLikeTypes =
  concatMap ([Signed, Unsigned] <&>) [Short, Int, Long, LongLong]
    ++ [PtrDiff]