packages feed

hs-bindgen-1.0.0.0: src/HsBindgen/Internal/Macro/CExpr/Translation/Value.hs

module HsBindgen.Internal.Macro.CExpr.Translation.Value (
    translateMacroValue
  ) where

import Data.Text qualified as Text
import Data.Type.Nat qualified as Nat
import Data.Vec.Lazy qualified as Vec
import DeBruijn (EmptyCtx, Idx (..), Size (..), rzeroAdd)

import C.Type qualified as Runtime

import C.Expr.Syntax qualified as CExpr
import C.Expr.Typecheck
import C.Expr.Typecheck qualified as CExpr
import C.Expr.Typecheck.Interface.Value qualified as V
import C.Expr.Typecheck.Type qualified as CExpr

import HsBindgen.Backend.Global
import HsBindgen.Backend.Hs.Haddock.Documentation qualified as HsDoc
import HsBindgen.Backend.Hs.Name qualified as Hs
import HsBindgen.Backend.SHs.AST
import HsBindgen.Config.MangleCandidate
import HsBindgen.Errors
import HsBindgen.Imports
import HsBindgen.Internal.Macro.CExpr.Global
import HsBindgen.Internal.Macro.CExpr.Type
import HsBindgen.Language.Haskell qualified as Hs
import HsBindgen.Macro.Type qualified as Macro
import HsBindgen.NameHint

translateMacroValue ::
     Hs.Name Hs.NsVar
  -> Macro.TypecheckedValue CExpr Hs.TermName
  -> Maybe HsDoc.Comment
  -> Binding
translateMacroValue name expr comment =
    Binding
      { name       = Hs.ExportedName name
      , parameters = []
      , result     = Result (translateType (fmap snd expr.macroValueType)) Nothing
      , body       = translateBody expr
      , pragmas    = []
      , comment    = comment
      }

{-------------------------------------------------------------------------------
  Translate macro value: type
-------------------------------------------------------------------------------}

translateType :: CExpr.Quant (CExpr.Type CExpr.Ty) -> ClosedType
translateType qty@(CExpr.Quant @n _) =
    case CExpr.mkQuantTyBody qty of
      CExpr.QuantTyBody{quantTyQuant = cts, quantTyBody = ty} ->
        topLevelForall (snd <$> CExpr.tyVarNames @n) cts ty

topLevelForall ::
     Vec n Text
  -> [CExpr.Type CExpr.Ct]
  -> CExpr.Type CExpr.Ty
  -> ClosedType
topLevelForall args cts body =
    TForall
      ((\t -> NameHint (Text.unpack t)) <$> args)
      (rzeroAdd (vecSize args))
      (map (typeCt lookupIdx) cts)
      (typeTy lookupIdx body)
  where
    qtvs    = addIndices args
    lookupIdx name =
      foldr (\(t, i) acc -> if t == name then i else acc)
            (panicPure $
              "translateMacroValue: unbound type variable: "
              <> Text.unpack name)
            qtvs

typeCt :: (Text -> Idx n) -> CExpr.Type CExpr.Ct -> SType n
typeCt env = \case
    CExpr.TyConAppTy cls as ->
      tAppN (tyCon cls) (typeTy env <$> as)
    CExpr.NomEqPred a b ->
      tAppN TEq [typeTy env a, typeTy env b]

typeTy :: forall n. (Text -> Idx n) -> CExpr.Type CExpr.Ty -> SType n
typeTy env = go
  where
    go :: CExpr.Type CExpr.Ty -> SType n
    go CExpr.String =
        tBindgenGlobal ByteString_type
    go (CExpr.TyVarTy tv) =
        TBound (env (CExpr.tyVarName tv))
    go (CExpr.FunTy as r) =
        foldr (TFun . go) (go r) as
    go (CExpr.TyConAppTy tc as) =
        case simpleTyConApp tc as of
          Just ty -> tBindgenGlobal ty
          Nothing -> tAppN (tyCon tc) (go <$> as)

simpleTyConApp ::
     CExpr.TyCon n CExpr.Ty
  -> Vec n (CExpr.Type CExpr.Ty)
  -> Maybe BindgenGlobalType
simpleTyConApp (CExpr.GenerativeTyCon (CExpr.DataTyCon tc)) (arg ::: VNil) =
    case (tc, arg) of
      ( CExpr.IntLikeTyCon
        , CExpr.TyConAppTy
            (CExpr.GenerativeTyCon (CExpr.DataTyCon (CExpr.PrimIntInfoTyCon t)))
            VNil
        ) -> Just $ dslIntegral t
      ( CExpr.FloatLikeTyCon
        , CExpr.TyConAppTy
            (CExpr.GenerativeTyCon (CExpr.DataTyCon (CExpr.PrimFloatInfoTyCon t)))
            VNil
        ) -> Just $ runtimeFloating t
      _ -> Nothing
simpleTyConApp _ _ =
    Nothing

tyCon :: CExpr.TyCon args res -> SType ctx
tyCon (CExpr.GenerativeTyCon (CExpr.DataTyCon tc))  = dataTyCon   tc
tyCon (CExpr.GenerativeTyCon (CExpr.ClassTyCon tc)) = TGlobal $ cExprGlobalType $ classTyCon  tc
tyCon (CExpr.FamilyTyCon tc)                        = TGlobal $ cExprGlobalType $ familyTyCon tc

dataTyCon :: CExpr.DataTyCon n -> SType ctx
dataTyCon = \case
    CExpr.TupleTyCon (Nat.SS' (Nat.SS' n)) -> TBoxedTup $ Plus2 $ Nat.snatToNatural n
    CExpr.VoidTyCon                        -> tBindgenGlobal Void_type
    CExpr.PrimIntInfoTyCon tc              -> tBindgenGlobal $ dslIntegral tc
    CExpr.PrimFloatInfoTyCon tc            -> tBindgenGlobal $ runtimeFloating tc
    CExpr.PtrTyCon                         -> tBindgenGlobal Foreign_Ptr_type
    CExpr.CharLitTyCon                     -> tBindgenGlobal CChar_type
    CExpr.IntLikeTyCon   -> panicPure "translateMacroValue: should have been handled by simpleTyConApp: IntLikeTyCon"
    CExpr.FloatLikeTyCon -> panicPure "translateMacroValue: should have been handled by simpleTyConApp: FloatLikeTyCon"
    CExpr.MacroTypeTyCon -> panicPure "translateMacroValue: unexpected type (kind): type"

classTyCon :: CExpr.ClassTyCon args -> CExprGlobalType
classTyCon = \case
    CExpr.NotTyCon        -> Not_class
    CExpr.LogicalTyCon    -> Logical_class
    CExpr.RelEqTyCon      -> RelEq_class
    CExpr.RelOrdTyCon     -> RelOrd_class
    CExpr.PlusTyCon       -> Plus_class
    CExpr.MinusTyCon      -> Minus_class
    CExpr.AddTyCon        -> Add_class
    CExpr.SubTyCon        -> Sub_class
    CExpr.MultTyCon       -> Mult_class
    CExpr.DivTyCon        -> Div_class
    CExpr.RemTyCon        -> Rem_class
    CExpr.ComplementTyCon -> Complement_class
    CExpr.BitwiseTyCon    -> Bitwise_class
    CExpr.ShiftTyCon      -> Shift_class

familyTyCon :: CExpr.FamilyTyCon args -> CExprGlobalType
familyTyCon = \case
    CExpr.PlusResTyCon       -> Plus_resTyCon
    CExpr.MinusResTyCon      -> Minus_resTyCon
    CExpr.AddResTyCon        -> Add_resTyCon
    CExpr.SubResTyCon        -> Sub_resTyCon
    CExpr.MultResTyCon       -> Mult_resTyCon
    CExpr.DivResTyCon        -> Div_resTyCon
    CExpr.RemResTyCon        -> Rem_resTyCon
    CExpr.ComplementResTyCon -> Complement_resTyCon
    CExpr.BitsResTyCon       -> Bitwise_resTyCon
    CExpr.ShiftResTyCon      -> Shift_resTyCon

dslIntegral :: CExpr.IntegralType -> BindgenGlobalType
dslIntegral = \case
    CExpr.CIntegralType primIntTy -> runtimeIntegral primIntTy
    CExpr.HsIntType               -> Int_type

runtimeIntegral :: Runtime.IntegralType -> BindgenGlobalType
runtimeIntegral = \case
    Runtime.Bool       -> CBool_type
    Runtime.CharLike c -> runtimeCharLike c
    Runtime.IntLike i  -> runtimeIntLike i

runtimeCharLike :: Runtime.CharLikeType -> BindgenGlobalType
runtimeCharLike = \case
    Runtime.Char  -> CChar_type
    Runtime.SChar -> CSChar_type
    Runtime.UChar -> CUChar_type

runtimeIntLike :: Runtime.IntLikeType -> BindgenGlobalType
runtimeIntLike = \case
    Runtime.Short    Runtime.Signed   -> CShort_type
    Runtime.Short    Runtime.Unsigned -> CUShort_type
    Runtime.Int      Runtime.Signed   -> CInt_type
    Runtime.Int      Runtime.Unsigned -> CUInt_type
    Runtime.Long     Runtime.Signed   -> CLong_type
    Runtime.Long     Runtime.Unsigned -> CULong_type
    Runtime.LongLong Runtime.Signed   -> CLLong_type
    Runtime.LongLong Runtime.Unsigned -> CULLong_type
    Runtime.PtrDiff                   -> CPtrdiff_type

runtimeFloating :: Runtime.FloatingType -> BindgenGlobalType
runtimeFloating = \case
    Runtime.FloatType  -> CFloat_type
    Runtime.DoubleType -> CDouble_type

{-------------------------------------------------------------------------------
  Translate macro value: body
-------------------------------------------------------------------------------}

translateBody ::
     CExpr.TypecheckedMacroValueExpr Hs.TermName
  -> ClosedExpr
translateBody (CExpr.TypecheckedMacroValueExpr params body _) =
    lambdaN (Vec.reverse (identifierToHint <$> params)) (mexpr body)

identifierToHint :: CExpr.Identifier -> NameHint
identifierToHint nm =
    toHint $ mangleCandidateDefaultFallback fallback nm.getIdentifier
  where
    fallback :: Hs.Name Hs.NsVar
    fallback = Hs.UnsafeName "x"

    toHint :: Hs.Name Hs.NsVar -> NameHint
    toHint x = NameHint $ Text.unpack x.text

mexpr :: forall ctx. V.Expr ctx Hs.TermName -> SExpr ctx
mexpr = goExpr
  where
    goExpr :: V.Expr ctx Hs.TermName -> SExpr ctx
    goExpr = \case
      V.Literal lit       -> goLiteral lit
      V.LocalParam i      -> EBound i
      V.Var termName args -> eAppN (EFree termName) (map goExpr args)
      V.App fun xs        -> eAppN (mfun fun) (Vec.toList (fmap goExpr xs))

    goLiteral :: CExpr.ValueLit -> SExpr ctx
    goLiteral = \case
      CExpr.ValueInt    x -> integerLiteral  x
      CExpr.ValueFloat  x -> floatingLiteral x
      CExpr.ValueChar   x -> charLiteral     x
      CExpr.ValueString x -> stringLiteral   x

mfun :: CExpr.VaFun arity -> SExpr ctx
mfun = \case
    CExpr.MUnaryPlus  -> cExpr Plus_plus
    CExpr.MUnaryMinus -> cExpr Minus_negate
    CExpr.MLogicalNot -> cExpr Not_not
    CExpr.MBitwiseNot -> cExpr Complement_complement
    CExpr.MMult       -> cExpr Mult_mult
    CExpr.MDiv        -> cExpr Div_div
    CExpr.MRem        -> cExpr Rem_rem
    CExpr.MAdd        -> cExpr Add_add
    CExpr.MSub        -> cExpr Sub_minus
    CExpr.MShiftLeft  -> cExpr Shift_shiftL
    CExpr.MShiftRight -> cExpr Shift_shiftR
    CExpr.MRelLT      -> cExpr RelOrd_lt
    CExpr.MRelLE      -> cExpr RelOrd_le
    CExpr.MRelGT      -> cExpr RelOrd_gt
    CExpr.MRelGE      -> cExpr RelOrd_ge
    CExpr.MRelEQ      -> cExpr RelEq_eq
    CExpr.MRelNE      -> cExpr RelEq_uneq
    CExpr.MBitwiseAnd -> cExpr Bitwise_and
    CExpr.MBitwiseXor -> cExpr Bitwise_xor
    CExpr.MBitwiseOr  -> cExpr Bitwise_or
    CExpr.MLogicalAnd -> cExpr Logical_and
    CExpr.MLogicalOr  -> cExpr Logical_or
    CExpr.MTuple @n   -> EBoxedTup $ Plus2 $ Nat.reflectToNum @n Proxy
  where
    cExpr = EGlobal . cExprGlobalTerm

integerLiteral :: CExpr.IntegerLiteral -> SExpr ctx
integerLiteral lit =
    EIntegral (CExpr.integerLiteralValue lit) $
      Just $ tBindgenGlobal $
        runtimeIntegral $ Runtime.IntLike (CExpr.integerLiteralType lit)

charLiteral :: CExpr.CharLiteral -> SExpr ctx
charLiteral lit = ECChar (CExpr.charLiteralValue lit)

stringLiteral :: CExpr.StringLiteral -> SExpr ctx
stringLiteral lit = ECString (CExpr.stringLiteralValue lit)

floatingLiteral :: CExpr.FloatingLiteral -> SExpr ctx
floatingLiteral lit =
    case CExpr.floatingLiteralType lit of
      Runtime.FloatType  ->
        EFloat
          (CExpr.floatingLiteralFloatValue  lit)
          (tBindgenGlobal CFloat_type)
      Runtime.DoubleType ->
        EDouble
          (CExpr.floatingLiteralDoubleValue lit)
          (tBindgenGlobal CDouble_type)

{-------------------------------------------------------------------------------
  Auxiliary: AST construction
-------------------------------------------------------------------------------}

eAppN :: Foldable f => SExpr ctx -> f (SExpr ctx) -> SExpr ctx
eAppN = foldl' EApp

lambdaN :: Vec ctx NameHint -> SExpr ctx -> SExpr EmptyCtx
lambdaN VNil       expr = expr
lambdaN (n ::: ns) expr = lambdaN ns $ ELam n expr

tAppN :: Foldable f => SType ctx -> f (SType ctx) -> SType ctx
tAppN = foldl' TApp

{-------------------------------------------------------------------------------
  Auxiliary: de Bruijn
-------------------------------------------------------------------------------}

vecSize :: Vec n a -> Size n
vecSize VNil       = SZ
vecSize (_ ::: xs) = SS (vecSize xs)

addIndices :: Vec n a -> Vec n (a, Idx n)
addIndices VNil       = VNil
addIndices (x ::: xs) = (x, IZ) ::: fmap (second IS) (addIndices xs)