packages feed

c-expr-runtime-0.1.0.0: core/C/Operator/GenInstances.hs

{-# LANGUAGE TemplateHaskellQuotes #-}

module C.Operator.GenInstances
  ( cExprInstances ) where

import Prelude hiding (Fractional (..), Integral (..), Num (..))
import Prelude qualified

import Control.Monad (guard)
import Data.Bits qualified as Bits
import Foreign.C.Types
import Language.Haskell.TH qualified as TH

import C.Type qualified as C

import C.Operator.Classes qualified as C
import C.Operator.Internal qualified as C
import C.Operator.TH

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

-- | All instances for arithmetic classes on standard types, for the given
-- 'C.Platform'.
cExprInstances :: C.Platform -> TH.Q [ TH.Dec ]
cExprInstances platform = do
  concat <$> sequence [

    ----------------------------------------------------------------------------
    -- Not, Logical

    do impl <- [| \ i -> if C.notNull i then 0 else 1 |]
       withInstanceProofs
         [ genUnaryInstances ''C.Not ( Left $ TH.ConT ''CInt ) ( C.unaryLogicalType platform )
           [ ClassMethod 'C.not "singNot" 1 impl ]
         ]
    ,

    do impl1 <- [| \ i j -> if C.notNull i Prelude.&& C.notNull j then 1 else 0 |]
       impl2 <- [| \ i j -> if C.notNull i Prelude.|| C.notNull j then 1 else 0 |]
       withInstanceProofs
         [ genBinaryInstances ''C.Logical ( Left $ TH.ConT ''CInt ) ( C.binaryLogicalType platform )
           [ ClassMethod '(C.&&) "singAnd" 2 impl1
           , ClassMethod '(C.||) "singOr"  2 impl2
           ]
         ]
    ,

    ----------------------------------------------------------------------------
    -- RelEq, RelOrd

    do impl1 <- [| \ a b -> if a Prelude.== b then 1 else 0 |]
       impl2 <- [| \ a b -> if a Prelude./= b then 1 else 0 |]
       withInstanceProofs
         [ genBinaryInstances ''C.RelEq ( Left $ TH.ConT ''CInt ) ( C.binaryEqType platform )
           [ ClassMethod '(C.==) "singEq"  2 impl1
           , ClassMethod '(C.!=) "singNEq" 2 impl2
           ]
         ]
    ,

    do impl1 <- [| \ a b -> if a Prelude.>  b then 1 else 0 |]
       impl2 <- [| \ a b -> if a Prelude.>= b then 1 else 0 |]
       impl3 <- [| \ a b -> if a Prelude.<  b then 1 else 0 |]
       impl4 <- [| \ a b -> if a Prelude.<= b then 1 else 0 |]
       withInstanceProofs
         [ genBinaryInstances ''C.RelOrd ( Left $ TH.ConT ''CInt ) ( C.binaryRelType platform )
           [ ClassMethod '(C.>)  "singGT" 2 impl1
           , ClassMethod '(C.>=) "singGTE" 2 impl2
           , ClassMethod '(C.<)  "singLT" 2 impl3
           , ClassMethod '(C.<=) "singLTE" 2 impl4
           ]
         ]
    ,

    ----------------------------------------------------------------------------
    -- Plus, Minus

    withInstanceProofs
      [ genUnaryInstances ''C.Plus ( withAssoc "PlusRes" "PlusResImpl" SameArgs )
        ( C.unaryPlusType platform )
        [ ClassMethod 'C.plus "singPlus" 1 ( TH.VarE 'Prelude.id ) ]
      ]
    ,

    genUnaryTyFam platform ( TH.mkName "PlusResImpl" ) C.unaryPlusType
    ,

    withInstanceProofs
      [ genUnaryInstances ''C.Minus ( withAssoc "MinusRes" "MinusResImpl" SameArgs )
        ( C.unaryMinusType platform )
        [ ClassMethod 'C.negate "singNegate" 1 ( TH.VarE 'Prelude.negate ) ]
      ]
    ,

    genUnaryTyFam platform ( TH.mkName "MinusResImpl" ) C.unaryMinusType
    ,

    ----------------------------------------------------------------------------
    -- Add, Sub, Mult, Div, Rem

    withInstanceProofs
      [ genBinaryInstances ''C.Add ( withAssoc "AddRes" "AddResImpl" SameArgs )
        ( C.binaryAddType platform )
        [ ClassMethod '(C.+) "singAdd" 2 ( TH.VarE '(Prelude.+) ) ]
      ]
    ,

    genBinaryTyFam platform ( TH.mkName "AddResImpl" ) C.binaryAddType
    ,

    withInstanceProofs
      [ genBinaryInstances ''C.Sub ( withAssoc "SubRes" "SubResImpl" SameArgs )
        ( C.binarySubType platform )
        [ ClassMethod '(C.-) "singSub" 2 ( TH.VarE '(Prelude.-) ) ]
      ]
    ,

    genBinaryTyFam platform ( TH.mkName "SubResImpl" ) C.binarySubType
    ,

    withInstanceProofs
      [ genBinaryInstances ''C.Mult ( withAssoc "MultRes" "MultResImpl" SameArgs )
        ( C.binaryMultiplicativeType platform )
        [ ClassMethod '(C.*) "singMult" 2 ( TH.VarE '(Prelude.*) ) ]
      ]
    ,

    genBinaryTyFam platform ( TH.mkName "MultResImpl" ) C.binaryMultiplicativeType
    ,

    -- NB: this is the key usage of 'withInstanceProofs' with a non-singleton list
    withInstanceProofs
        -- division for integral types
      [ genBinaryInstances ''C.Div ( withAssoc "DivRes" "MultResImpl" SameArgs ) -- NB: re-use 'MultResImpl'
          ( \ a b ->
            do op@( resTy, _ ) <- C.integralBinaryType platform a b
               guard ( case resTy of C.Arithmetic ( C.FloatLike {} ) -> False; _ -> True )
               return op
          )
          [ ClassMethod '(C./) "singDiv" 2 ( TH.VarE 'Prelude.div ) ]
        -- division for floating-point types
      , genBinaryInstances ''C.Div ( withAssoc "DivRes" "MultResImpl" SameArgs ) -- NB: re-use 'MultResImpl'
          ( \ a b ->
            do op@( resTy, _ ) <- C.binaryMultiplicativeType platform a b
               guard ( case resTy of C.Arithmetic ( C.FloatLike {} ) -> True; _ -> False )
               return op
          )
          [ ClassMethod '(C./) "singDiv" 2 ( TH.VarE '(Prelude./) ) ]
      ]
    ,

    withInstanceProofs
      [ genBinaryInstances ''C.Rem ( withAssoc "RemRes" "BinResImpl" SameArgs ) -- NB: use 'BinResImpl'
        ( C.integralBinaryType platform )
        [ ClassMethod '(C.%) "singRem" 2 ( TH.VarE 'Prelude.rem ) ]
      ]
    ,

    genBinaryTyFam platform ( TH.mkName "BinResImpl" ) C.integralBinaryType
    ,

    ----------------------------------------------------------------------------
    -- Complement, Bitwise, Shift

    withInstanceProofs
      [ genUnaryInstances ''C.Complement ( withAssoc "ComplementRes" "ComplementResImpl" SameArgs )
        ( C.integralUnaryType platform )
        [ ClassMethod '(C..~) "singComplement" 1 ( TH.VarE 'Bits.complement ) ]
      ]
    ,

    genUnaryTyFam platform ( TH.mkName "ComplementResImpl" ) C.integralUnaryType
    ,

    withInstanceProofs
      [ genBinaryInstances ''C.Bitwise ( withAssoc "BitsRes" "BinResImpl" SameArgs ) -- NB: use 'BinResImpl'
        ( C.integralBinaryType platform )
          [ ClassMethod '(C..&.) "singBitAnd" 2 ( TH.VarE '(Bits..&.) )
          , ClassMethod '(C..|.) "singBitOr" 2 ( TH.VarE '(Bits..|.) )
          , ClassMethod '(C..^.) "singBitXor" 2 ( TH.VarE 'Bits.xor )
          ]
      ]
    ,

    do impl1 <- [| \ a i -> Bits.shiftL a ( Prelude.fromIntegral i ) |]
       impl2 <- [| \ a i -> Bits.shiftR a ( Prelude.fromIntegral i ) |]
       withInstanceProofs
         [ genBinaryInstances ''C.Shift ( withAssoc "ShiftRes" "ShiftResImpl" FirstArgOnly )
              -- NB: use 'FirstArgOnly', because the result type only depends on the
              -- first argument.
             ( C.shiftType platform )
             [ ClassMethod '(C.<<) "singShiftL" 2 impl1
             , ClassMethod '(C.>>) "singShiftR" 2 impl2
             ]
         ]
    ,

    genUnaryTyFam platform ( TH.mkName "ShiftResImpl" ) $
      -- The associated type family for Shift is unary, as the result type
      -- only depends on the shiftee type, not the type of the shift amount,
      -- which undergoes an independent arithmetic promotion.
      \ plat ty -> C.shiftType plat ty ( C.Arithmetic $ C.Integral $ C.IntLike $ C.Int C.Signed )


    ]

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

-- | Utility function to construct a 'C.Operator.TH.AssocTyFam' argument to pass
-- to 'genUnaryInstances' or 'genBinaryInstances'.
withAssoc :: String -> String -> AssocTyFamArgs -> Either TH.Type AssocTyFam
withAssoc famName implName args =
  Right $
    AssocTyFam
      { assocTyFamName     = TH.mkName famName
      , assocTyFamImplName = TH.mkName implName
      , assocTyFamArgs     = args
      }