hs-bindgen-1.0.0.0: src-internal/HsBindgen/Backend/SHs/Translation.hs
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE UnboxedTuples #-}
-- | Simplified HS translation (from high level HS)
module HsBindgen.Backend.SHs.Translation (
translateDecls,
translateType,
) where
import Data.ByteString qualified as BS
import Data.Char qualified
import Data.Map.Strict qualified as Map
import Data.Text qualified as Text
import DeBruijn.Idx
import HsBindgen.Backend.Category
import HsBindgen.Backend.Global
import HsBindgen.Backend.Hs.AST qualified as Hs
import HsBindgen.Backend.Hs.CallConv
import HsBindgen.Backend.Hs.Haddock.Documentation qualified as HsDoc
import HsBindgen.Backend.Hs.Name qualified as Hs
import HsBindgen.Backend.Level
import HsBindgen.Backend.SHs.AST
import HsBindgen.Backend.SHs.AST.Expr (FBind (FBind))
import HsBindgen.Backend.SHs.Translation.Common
import HsBindgen.Errors
import HsBindgen.Frontend.Pass.Final
import HsBindgen.Frontend.Pass.TypecheckMacros.IsPass
import HsBindgen.Imports
import HsBindgen.Instances qualified as Inst
import HsBindgen.IR.C qualified as C
import HsBindgen.IR.Hs qualified as Hs
import HsBindgen.IR.Pass
import HsBindgen.IR.Translation
import HsBindgen.Language.Haskell qualified as Hs
import HsBindgen.Macro.Interface qualified as Macro
import HsBindgen.Macro.Type qualified as Macro
import HsBindgen.NameHint
{-------------------------------------------------------------------------------
Declarations
-------------------------------------------------------------------------------}
translateDecls ::
forall l. Macro.HasTypes l
=> Macro.Lang l
-> ByCategory_ [Hs.Decl l]
-> ByCategory_ ([CWrapper], [SDecl])
translateDecls macroLang = fmap go
where
go :: [Hs.Decl l] -> ([CWrapper], [SDecl])
go decls = (wrappers, map (translateDecl macroLang) decls)
where
wrappers = getCWrappers decls
-- Find and assemble C sources required by foreign imports.
getCWrappers :: [Hs.Decl l] -> [CWrapper]
getCWrappers decls = mapMaybe getCWrapper decls
where
getCWrapper :: Hs.Decl l -> Maybe CWrapper
getCWrapper = \case
Hs.DeclForeignImport importDecl ->
case importDecl.callConv of
CallConvUserlandCapi w -> Just w
_otherCallConv -> Nothing
_otherDecl -> Nothing
translateDecl :: Macro.HasTypes l => Macro.Lang l -> Hs.Decl l -> SDecl
translateDecl macroLang = \case
Hs.DeclTypSyn x -> translateDeclTypSyn x
Hs.DeclData x -> translateDeclData x
Hs.DeclEmpty x -> translateDeclEmpty x
Hs.DeclNewtype x -> translateNewtype x
Hs.DeclDefineInstance x -> translateDefineInstanceDecl x
Hs.DeclDeriveInstance x -> translateDeriveInstance x
Hs.DeclMacroValue x -> translateMacroValue' macroLang x
Hs.DeclForeignImport x -> translateForeignImportDecl x
Hs.DeclForeignImportWrapper x -> translateForeignImportWrapper x
Hs.DeclForeignImportDynamic x -> translateForeignImportDynamic x
Hs.DeclFunction x -> translateFunctionDecl x
Hs.DeclPatSyn x -> translatePatSyn x
Hs.DeclCompletePragma x -> translateCompletePragma x
Hs.DeclVar x -> translateDeclVar x
translateDeclTypSyn :: Hs.TypSyn -> SDecl
translateDeclTypSyn d = DTypSyn $ TypeSynonym {
name = d.name
, typ = translateType d.typ
, origin = d.origin
, comment = d.comment
}
translateDefineInstanceDecl :: Hs.DefineInstance -> SDecl
translateDefineInstanceDecl defInst =
case defInst.instanceDecl of
Hs.InstanceStaticSize name i ->
DInst $ translateStaticSizeInstance name i defInst.comment
Hs.InstanceReadRaw struct i ->
DInst $ translateReadRawInstance struct i defInst.comment
Hs.InstanceWriteRaw struct i ->
DInst $ translateWriteRawInstance struct i defInst.comment
Hs.InstanceStorable struct i ->
DInst $ translateStorableInstance struct i defInst.comment
Hs.InstanceHasCField i ->
DInst $ translateHasCFieldInstance i defInst.comment
Hs.InstanceHasCBitfield i ->
DInst $ translateHasCBitfieldInstance i defInst.comment
Hs.InstanceHasField i ->
DInst $ translateHasFieldInstance i defInst.comment
Hs.InstanceHasFieldCompat i ->
DInst $ translateHasFieldCompatInstance i defInst.comment
Hs.InstanceHasFieldPtr i ->
DInst $ translateHasFieldPtrInstance i defInst.comment
Hs.InstanceHasFlam struct i ->
DInst Instance{
clss = Inst.Flam_Offset
, args = [ translateType i.typ, TCon struct.name ]
, super = []
, types = []
, comment = defInst.comment
, decs = [ ( bindgenGlobalTerm Flam_Offset_offset
, ELam (NameHint "_proxy") $
EIntegral (toInteger i.offset) Nothing)
]
}
Hs.InstanceCEnum struct i ->
DInst $
translateCEnumInstance
struct
i.fieldType
i.valueNames
i.isSequential
defInst.comment
Hs.InstanceSequentialCEnum struct i ->
DInst $
translateSequentialCEnum struct i.minName i.maxName defInst.comment
Hs.InstanceCEnumShow struct ->
DInst $ translateCEnumInstanceShow struct defInst.comment
Hs.InstanceCEnumRead struct ->
DInst $ translateCEnumInstanceRead struct defInst.comment
Hs.InstanceToFunPtr inst ->
DInst Instance{
clss = Inst.ToFunPtr
, args = [translateType inst.typ]
, super = []
, types = []
, comment = defInst.comment
, decs = [ ( bindgenGlobalTerm ToFunPtr_toFunPtr
, EFree $ Hs.InternalName inst.body
)
]
}
Hs.InstanceFromFunPtr inst ->
DInst Instance{
clss = Inst.FromFunPtr
, args = [translateType inst.typ]
, super = []
, types = []
, comment = defInst.comment
, decs = [ ( bindgenGlobalTerm FromFunPtr_fromFunPtr
, EFree $ Hs.InternalName inst.body
)
]
}
translateDeclData :: Hs.Struct -> SDecl
translateDeclData struct = DRecord Record{
typ = struct.name
, con = struct.constr
, deriv = []
, comment = struct.comment
, origin = case struct.origin of
Just origin -> origin
Nothing -> panicPure "Missing structOrigin"
, fields = [
Field{
name = f.name
, typ = translateType f.typ
, origin = f.origin
, comment = f.comment
}
| f <- struct.fields
]
}
translateDeclEmpty :: Hs.EmptyData -> SDecl
translateDeclEmpty d = DEmptyData EmptyData{
name = d.name
, origin = d.origin
, comment = d.comment
}
translateNewtype :: Hs.Newtype -> SDecl
translateNewtype n = DNewtype Newtype{
name = n.name
, con = n.constr
, origin = n.origin
, deriv = []
, comment = n.comment
, field = Field {
name = n.field.name
, typ = translateType n.field.typ
, origin = n.field.origin
, comment = n.field.comment
}
}
translateDeriveInstance :: Hs.DeriveInstance -> SDecl
translateDeriveInstance deriv = DDerivingInstance DerivingInstance {
strategy = fmap translateType deriv.strategy
, typ = TApp (TClass deriv.clss) (TCon deriv.name)
, comment = deriv.comment
}
translateMacroValue' :: Macro.HasTypes l => Macro.Lang l -> Hs.MacroValue l -> SDecl
translateMacroValue' macroLang macro = DBinding $
withCLiteralComment $
macroLang.translateValue
macro.name
(fmap macroIdToHsName macro.expr.body)
macro.comment
where
macroIdToHsName :: Id Final -> Hs.TermName
macroIdToHsName namePair =
Hs.ExportedName $ Hs.assertNs (Proxy @Hs.NsVar) namePair.hsName
-- | Augment a macro binding's Haddock comment with the C literal representation
-- when the binding body is a character or string literal.
withCLiteralComment :: Binding -> Binding
withCLiteralComment binding =
case cLiteralText binding.body of
Nothing -> binding
Just lit -> binding
& #comment .~ Just ((fromMaybe mempty binding.comment) & #literal .~ Just lit)
cLiteralText :: ClosedExpr -> Maybe Text
cLiteralText = \case
ECChar c -> Just $ Text.pack $ show (Data.Char.chr (fromIntegral c))
ECString bs -> Just $ Text.pack $ show (map (Data.Char.chr . fromIntegral) (BS.unpack bs))
_ -> Nothing
translateForeignImportDecl :: Hs.ForeignImportDecl -> SDecl
translateForeignImportDecl importDecl = DForeignImport ForeignImport{
parameters = map translateFFIParam importDecl.parameters
, result = Result (translateFFIResType importDecl.result) Nothing
-- The rest of the fields are copied over as-is
, name = importDecl.name
, origName = importDecl.origName
, callConv = importDecl.callConv
, origin = importDecl.origin
, comment = importDecl.comment
, safety = importDecl.safety
}
translateFFIParam :: Hs.FunctionParameter Hs.FFIType -> Parameter
translateFFIParam param = Parameter {
typ = translateFFIType param.typ
, comment = param.comment
}
translateForeignImportWrapper :: Hs.ForeignImportWrapper -> SDecl
translateForeignImportWrapper importWrapper = DForeignImport ForeignImport{
parameters = [
Parameter {
typ = translateFFIFunType importWrapper.funType
, comment = Nothing
}
]
, result = flip Result Nothing $
tBindgenGlobal IO_type `TApp`
(tBindgenGlobal Foreign_FunPtr_type `TApp`
translateFFIFunType importWrapper.funType)
, name = Hs.InternalName importWrapper.name
, origName = C.DeclName "wrapper" C.NameKindOrdinary
, callConv = CallConvGhcCCall ImportAsValue
, origin = importWrapper.origin
, comment = importWrapper.comment
, safety = Safe
}
translateForeignImportDynamic :: Hs.ForeignImportDynamic -> SDecl
translateForeignImportDynamic importDyn = DForeignImport ForeignImport{
parameters = [
Parameter {
typ =
tBindgenGlobal Foreign_FunPtr_type `TApp`
translateFFIFunType importDyn.funType
, comment = Nothing
}
]
, result = Result (translateFFIFunType importDyn.funType) Nothing
, name = Hs.InternalName importDyn.name
, origName = C.DeclName "dynamic" C.NameKindOrdinary
, callConv = CallConvGhcCCall ImportAsValue
, origin = importDyn.origin
, comment = importDyn.comment
, safety = Safe
}
translateFunctionDecl :: Hs.FunctionDecl -> SDecl
translateFunctionDecl functionDecl = DBinding Binding{
parameters = map translateFunctionParam functionDecl.parameters
, result = Result (translateType functionDecl.result) Nothing
-- The other fields are copied as-is
, name = functionDecl.name
, body = functionDecl.body
, pragmas = functionDecl.pragmas
, comment = functionDecl.comment
}
translateFunctionParam :: Hs.FunctionParameter Hs.Type -> Parameter
translateFunctionParam param = Parameter {
typ = translateType param.typ
, comment = param.comment
}
translatePatSyn :: Hs.PatSyn -> SDecl
translatePatSyn patSyn = DPatternSynonym PatternSynonym{
typ = translateType patSyn.typ
, rhs =
case patSyn.constr of
Just c -> PEApps c [PELit patSyn.value]
Nothing -> PELit patSyn.value
-- The other fields are copied as-is
, name = patSyn.name
, origin = patSyn.origin
, comment = patSyn.comment
}
translateCompletePragma :: Hs.CompletePragma -> SDecl
translateCompletePragma = DCompletePragma
{-------------------------------------------------------------------------------
Types
-------------------------------------------------------------------------------}
translateHsPrimType :: Hs.PrimType -> SType ctx
translateHsPrimType = \case
Hs.PrimVoid -> tBindgenGlobal Void_type
Hs.PrimUnit -> TUnit
Hs.PrimInt -> tBindgenGlobal Int_type
Hs.PrimCChar -> tBindgenGlobal CChar_type
Hs.PrimCSChar -> tBindgenGlobal CSChar_type
Hs.PrimCUChar -> tBindgenGlobal CUChar_type
Hs.PrimCShort -> tBindgenGlobal CShort_type
Hs.PrimCUShort -> tBindgenGlobal CUShort_type
Hs.PrimCInt -> tBindgenGlobal CInt_type
Hs.PrimCUInt -> tBindgenGlobal CUInt_type
Hs.PrimCLong -> tBindgenGlobal CLong_type
Hs.PrimCULong -> tBindgenGlobal CULong_type
Hs.PrimCLLong -> tBindgenGlobal CLLong_type
Hs.PrimCULLong -> tBindgenGlobal CULLong_type
Hs.PrimCBool -> tBindgenGlobal CBool_type
Hs.PrimCFloat -> tBindgenGlobal CFloat_type
Hs.PrimCDouble -> tBindgenGlobal CDouble_type
translateType :: Hs.Type -> ClosedType
translateType = \case
Hs.PrimType t -> translateHsPrimType t
Hs.TypRef r _ -> TCon r
Hs.ConstArray n t -> tBindgenGlobal ConstantArray_type `TApp` TLit n `TApp` (translateType t)
Hs.IncompleteArray t -> tBindgenGlobal IncompleteArray_type `TApp` (translateType t)
Hs.PtrArrayElem t -> tBindgenGlobal Foreign_Ptr_type `TApp` (tBindgenGlobal IsArray_Elem `TApp` translateType t)
Hs.PtrConstArrayElem t -> tBindgenGlobal PtrConst_type `TApp` (tBindgenGlobal IsArray_Elem `TApp` translateType t)
Hs.Ptr t -> TApp (tBindgenGlobal Foreign_Ptr_type) (translateType t)
Hs.FunPtr t -> TApp (tBindgenGlobal Foreign_FunPtr_type) (translateType t)
Hs.PtrConst t -> TApp (tBindgenGlobal PtrConst_type) (translateType t)
Hs.IO t -> TApp (tBindgenGlobal IO_type) (translateType t)
Hs.Fun a b -> TFun (translateType a) (translateType b)
Hs.ExtBinding r _c _hs _-> TExt r
Hs.ByteArray -> tBindgenGlobal ByteArray_type
Hs.SizedByteArray n m -> tBindgenGlobal SizedByteArray_type `TApp` TLit n `TApp` TLit m
Hs.Block t -> tBindgenGlobal Block_type `TApp` translateType t
Hs.ComplexType t -> TApp (tBindgenGlobal Complex_type) (translateType (Hs.PrimType t))
Hs.StrLit s -> TStrLit s
Hs.WithFlam x y ->
TApp (TApp (tBindgenGlobal Flam_WithFlam_type) (translateType x)) (translateType y)
Hs.EquivStorable t -> TApp (tBindgenGlobal EquivStorable_type) (translateType t)
Hs.IsStructViaReadRaw t -> TApp (tBindgenGlobal IsStructViaReadRaw_type) (translateType t)
{-------------------------------------------------------------------------------
FFI types
-------------------------------------------------------------------------------}
translateFFIFunType :: Hs.FFIFunType -> ClosedType
translateFFIFunType ty = case Hs.unconsArg ty of
Left (arg, ty') -> TFun (translateFFIType arg) (translateFFIFunType ty')
Right res -> translateFFIResType res
translateFFIResType :: Hs.FFIResType -> ClosedType
translateFFIResType = \case
Hs.FFIResUnit -> TUnit
Hs.FFIResIOUnit -> TApp (tBindgenGlobal IO_type) TUnit
Hs.FFIResIO ty -> TApp (tBindgenGlobal IO_type) (translateFFIType ty)
Hs.FFIRes ty -> translateFFIType ty
translateFFIType :: Hs.FFIType -> ClosedType
translateFFIType = \case
Hs.FFIExternal r -> TExt r
Hs.FFIPtrVoid -> TApp (tBindgenGlobal Foreign_Ptr_type) (tBindgenGlobal Void_type)
Hs.FFIFunPtrVoid -> TApp (tBindgenGlobal Foreign_FunPtr_type) (tBindgenGlobal Void_type)
Hs.FFIPrimCChar -> tBindgenGlobal CChar_type
Hs.FFIPrimCSChar -> tBindgenGlobal CSChar_type
Hs.FFIPrimCUChar -> tBindgenGlobal CUChar_type
Hs.FFIPrimCShort -> tBindgenGlobal CShort_type
Hs.FFIPrimCUShort -> tBindgenGlobal CUShort_type
Hs.FFIPrimCInt -> tBindgenGlobal CInt_type
Hs.FFIPrimCUInt -> tBindgenGlobal CUInt_type
Hs.FFIPrimCLong -> tBindgenGlobal CLong_type
Hs.FFIPrimCULong -> tBindgenGlobal CULong_type
Hs.FFIPrimCLLong -> tBindgenGlobal CLLong_type
Hs.FFIPrimCULLong -> tBindgenGlobal CULLong_type
Hs.FFIPrimCBool -> tBindgenGlobal CBool_type
Hs.FFIPrimCFloat -> tBindgenGlobal CFloat_type
Hs.FFIPrimCDouble -> tBindgenGlobal CDouble_type
{-------------------------------------------------------------------------------
@StaticSize@, @ReadRaw@, @WriteRaw@
-------------------------------------------------------------------------------}
translateStaticSizeInstance ::
Hs.Name Hs.NsTypeConstr
-> Hs.StaticSizeInstance
-> Maybe HsDoc.Comment
-> Instance
translateStaticSizeInstance name inst mbComment = Instance{
clss = Inst.StaticSize
, args = [TCon name]
, super = []
, types = []
, comment = mbComment
, decs = [
(bindgenGlobalTerm StaticSize_staticSizeOf , EUnusedLam $ eInt inst.staticSizeOf)
, (bindgenGlobalTerm StaticSize_staticAlignment , EUnusedLam $ eInt inst.staticAlignment)
]
}
translateReadRawInstance ::
Hs.Struct
-> Hs.ReadRawInstance
-> Maybe HsDoc.Comment
-> Instance
translateReadRawInstance struct inst mbComment = Instance{
clss = Inst.ReadRaw
, args = [TCon struct.name]
, super = []
, types = []
, comment = mbComment
, decs = [(bindgenGlobalTerm ReadRaw_readRaw, readRaw)]
}
where
readRaw = lambda (idiom structCon translateReadRawCField) inst.readRaw
translateWriteRawInstance ::
Hs.Struct
-> Hs.WriteRawInstance
-> Maybe HsDoc.Comment
-> Instance
translateWriteRawInstance struct inst mbComment = Instance{
clss = Inst.WriteRaw
, args = [TCon struct.name]
, super = []
, types = []
, comment = mbComment
, decs = [(bindgenGlobalTerm WriteRaw_writeRaw, writeRaw)]
}
where
writeRaw =
lambda
(lambda (translateElimStruct (doAll translateWriteRawCField)))
inst.writeRaw
translateReadRawCField :: Hs.ReadRawCField ctx -> SExpr ctx
translateReadRawCField = \case
Hs.ReadRawCField field ptr ->
appMany HasCField_readRaw [
eBindgenGlobal Proxy_constructor `ETypeApp` translateType field
, EBound ptr
]
Hs.ReadRawCBitfield field ptr ->
appMany HasCBitfield_peek [
eBindgenGlobal Proxy_constructor `ETypeApp` translateType field
, EBound ptr
]
Hs.ReadRawByteOff ptr i ->
appMany ReadRaw_readRawByteOff [EBound ptr, eInt i]
translateWriteRawCField :: Hs.WriteRawCField ctx -> SExpr ctx
translateWriteRawCField = \case
Hs.WriteRawCField field ptr x ->
appMany HasCField_writeRaw [
eBindgenGlobal Proxy_constructor `ETypeApp` translateType field
, EBound ptr
, EBound x
]
Hs.WriteRawCBitfield field ptr x ->
appMany HasCBitfield_poke [
eBindgenGlobal Proxy_constructor `ETypeApp` translateType field
, EBound ptr
, EBound x
]
Hs.WriteRawByteOff ptr i x ->
appMany WriteRaw_writeRawByteOff [EBound ptr, eInt i, EBound x]
{-------------------------------------------------------------------------------
'Storable'
-------------------------------------------------------------------------------}
translateStorableInstance ::
Hs.Struct
-> Hs.StorableInstance
-> Maybe HsDoc.Comment
-> Instance
translateStorableInstance struct inst mbComment = Instance{
clss = Inst.Storable
, args = [TCon struct.name]
, super = []
, types = []
, comment = mbComment
, decs = map (first bindgenGlobalTerm) [
(Storable_sizeOf , EUnusedLam $ eInt inst.sizeOf)
, (Storable_alignment , EUnusedLam $ eInt inst.alignment)
, (Storable_peek , peek)
, (Storable_poke , poke)
]
}
where
peek = lambda (idiom structCon translatePeekCField) inst.peek
poke = lambda (lambda (translateElimStruct (doAll translatePokeCField))) inst.poke
translatePeekCField :: Hs.PeekCField ctx -> SExpr ctx
translatePeekCField (Hs.PeekCField field ptr) =
appMany HasCField_peek [eBindgenGlobal Proxy_constructor `ETypeApp` translateType field, EBound ptr]
translatePeekCField (Hs.PeekCBitfield field ptr) =
appMany HasCBitfield_peek [eBindgenGlobal Proxy_constructor `ETypeApp` translateType field, EBound ptr]
translatePeekCField (Hs.PeekByteOff ptr i) =
appMany Storable_peekByteOff [EBound ptr, eInt i]
translatePokeCField :: Hs.PokeCField ctx -> SExpr ctx
translatePokeCField (Hs.PokeCField field ptr x) =
appMany HasCField_poke [eBindgenGlobal Proxy_constructor `ETypeApp` translateType field, EBound ptr, EBound x]
translatePokeCField (Hs.PokeCBitfield field ptr x) =
appMany HasCBitfield_poke [eBindgenGlobal Proxy_constructor `ETypeApp` translateType field, EBound ptr, EBound x]
translatePokeCField (Hs.PokeByteOff ptr i x) =
appMany Storable_pokeByteOff [EBound ptr, eInt i, EBound x]
{-------------------------------------------------------------------------------
'HasCField'
-------------------------------------------------------------------------------}
translateHasCFieldInstance ::
Hs.HasCFieldInstance
-> Maybe HsDoc.Comment
-> Instance
translateHasCFieldInstance inst mbComment = Instance {
clss = Inst.HasCField
, args = [parent, fieldLit]
, super = []
, comment = mbComment
, types = [ ( bindgenGlobalType HasCField_CFieldType
, [parent, fieldLit], fieldType)
]
, decs = [ ( bindgenGlobalTerm HasCField_offset#
, EUnusedLam $ EUnusedLam $ EIntegral o Nothing
)
]
}
where
parent = translateType inst.parentType
fieldLit = translateType $ Hs.StrLit $ Hs.nameToStr inst.fieldName
fieldType = translateType inst.cFieldType
o = fromIntegral inst.fieldOffset
{-------------------------------------------------------------------------------
'HasCBitfield'
-------------------------------------------------------------------------------}
translateHasCBitfieldInstance ::
Hs.HasCBitfieldInstance
-> Maybe HsDoc.Comment
-> Instance
translateHasCBitfieldInstance inst mbComment = Instance{
clss = Inst.HasCBitfield
, args = [parent, fieldLit]
, super = []
, comment = mbComment
, types = [ (bindgenGlobalType HasCBitfield_CBitfieldType
, [parent, fieldLit], fieldType
)
]
, decs = [ ( bindgenGlobalTerm HasCBitfield_bitfieldOffset#
, EUnusedLam $ EUnusedLam $ EIntegral o Nothing
)
, ( bindgenGlobalTerm HasCBitfield_bitfieldWidth#
, EUnusedLam $ EUnusedLam $ EIntegral w Nothing
)
]
}
where
parent = translateType inst.parentType
fieldLit = translateType $ Hs.StrLit $ Hs.nameToStr inst.fieldName
fieldType = translateType inst.cBitfieldType
o = fromIntegral inst.bitOffset
w = fromIntegral inst.bitWidth
{-------------------------------------------------------------------------------
'GHC.Records.HasField'
-------------------------------------------------------------------------------}
translateHasFieldInstance ::
Hs.HasFieldInstance
-> Maybe HsDoc.Comment
-> Instance
translateHasFieldInstance inst mbComment = Instance{
clss = Inst.HasField
, args = [fieldLit, parent, tyTypeVar]
, types = []
, comment = mbComment
, super = [ TApp (TApp TEq tyTypeVar) field ]
, decs = [ ( bindgenGlobalTerm HasField_getField
, exprGetter
)
]
}
where
parent = translateType inst.parentType
field = translateType inst.fieldType
fieldLit = translateType $ Hs.StrLit $ Hs.nameToStr inst.fieldName
-- This is not actually a free type variable.
tyTypeVar = TFree $ Hs.UnsafeName "ty"
exprGetter :: SExpr Z
exprGetter = case inst.impl of
Hs.HasFieldImplUnion -> eBindgenGlobal ByteArray_getUnionPayload
Hs.HasFieldImplUnionBits {bitOffset, bitWidth} ->
eBindgenGlobal ByteArray_getUnionPayloadBits
`EApp` EIntegral (fromIntegral bitOffset) Nothing
`EApp` EIntegral (fromIntegral bitWidth) Nothing
Hs.HasFieldImplIndirect {nameTopToAnon, nameAnonToTarget} ->
let strLitTopToAnon = translateType (Hs.StrLit (Hs.nameToStr nameTopToAnon))
strLitAnonToTarget = translateType (Hs.StrLit (Hs.nameToStr nameAnonToTarget)) in
ELam (NameHint "x") $
EApp (eBindgenGlobal HasField_getField `ETypeApp` strLitAnonToTarget)
(eBindgenGlobal HasField_getField `ETypeApp` strLitTopToAnon `EApp` EBound IZ)
{-------------------------------------------------------------------------------
'GHC.Records.Compat.HasField'
-------------------------------------------------------------------------------}
translateHasFieldCompatInstance ::
Hs.HasFieldCompatInstance
-> Maybe HsDoc.Comment
-> Instance
translateHasFieldCompatInstance inst mbComment = Instance{
clss = Inst.HasFieldCompat
, args = [fieldLit, parent, tyTypeVar]
, types = []
, comment = mbComment
, super = [ TApp (TApp TEq tyTypeVar) field ]
, decs = [ ( bindgenGlobalTerm HasFieldCompat_hasField
, ELam (NameHint "x") $ appManyExpr (EBoxedTup $ Plus2 0) [
exprSetter
, exprGetter
]
)
]
}
where
parent = translateType inst.parentType
field = translateType inst.fieldType
fieldLit = translateType $ Hs.StrLit $ Hs.nameToStr inst.fieldName
-- TODO <https://github.com/well-typed/hs-bindgen/issues/1287>
-- This is not actually a free type variable.
tyTypeVar = TFree $ Hs.UnsafeName "ty"
exprGetter :: SExpr (S Z)
exprGetter = eBindgenGlobal HasField_getField `ETypeApp` fieldLit `EApp` EBound IZ
-- For the setter we use total record construction. The advantage with
-- record fields is that they can be used in any order regardless of the
-- order in which the fields were defined the datatype. This leads to a
-- simpler implementation compared to non-record construction. However,
-- because we use record fields we need total record construction instead of
-- a partial record update because the latter can cause warnings about
-- ambiguous field names. The implementation cost for record construction
-- compared to record update is low.
exprSetter :: SExpr (S Z)
exprSetter =
case inst.impl of
Hs.HasFieldCompatImplRecord {constr, otherFields} ->
ELam (NameHint "y") $
ERecCon constr $ concat [
[ FBind (Hs.nameToStr inst.fieldName) (EBound IZ) ]
, map mkFBindIdentity otherFields
]
Hs.HasFieldCompatImplUnion ->
ELam (NameHint "y")
( eBindgenGlobal ByteArray_setUnionPayload
`EApp` (EBound IZ)
`EApp` (EBound (IS IZ))
)
Hs.HasFieldCompatImplUnionBits {bitOffset, bitWidth} ->
ELam (NameHint "y")
( eBindgenGlobal ByteArray_setUnionPayloadBits
`EApp` EIntegral (fromIntegral bitOffset) Nothing
`EApp` EIntegral (fromIntegral bitWidth) Nothing
`EApp` (EBound IZ)
`EApp` (EBound (IS IZ))
)
Hs.HasFieldCompatImplIndirect {nameTopToAnon, nameAnonToTarget} ->
let strLitTopToAnon = translateType (Hs.StrLit (Hs.nameToStr nameTopToAnon))
strLitAnonToTarget = translateType (Hs.StrLit (Hs.nameToStr nameAnonToTarget)) in
ELam (NameHint "y")
(
( eBindgenGlobal HasFieldCompat_modifyField
`ETypeApp` strLitTopToAnon
)
`EApp` (EBound (IS IZ))
`EApp` (ELam (NameHint "z")
(
eBindgenGlobal HasFieldCompat_setField
`ETypeApp` strLitAnonToTarget
`EApp` EBound IZ
`EApp` EBound (IS IZ)
)
)
)
where
-- An 'FBind' that leaves the original field unchanged
mkFBindIdentity :: Hs.Name Hs.NsVar -> FBind (S (S n))
mkFBindIdentity fieldName = FBind (Hs.nameToStr fieldName) $
eBindgenGlobal HasField_getField
`ETypeApp` (translateType (Hs.StrLit (Hs.nameToStr fieldName)))
`EApp` EBound (IS IZ)
{-------------------------------------------------------------------------------
'GHC.Records.HasField' for the pointer manipulation API
-------------------------------------------------------------------------------}
translateHasFieldPtrInstance ::
Hs.HasFieldPtrInstance
-> Maybe HsDoc.Comment
-> Instance
translateHasFieldPtrInstance inst mbComment = Instance{
clss = Inst.HasField
, args = [fieldLit, parentPtr, tyPtr]
, types = []
, comment = mbComment
, super = [ TApp (TApp TEq tyTypeVar) field ]
, decs = [ ( bindgenGlobalTerm HasField_getField
, eBindgenGlobal ptrToFieldGlobal `EApp`
(eBindgenGlobal Proxy_constructor `ETypeApp` fieldLit)
)
]
}
where
(ptrToFieldGlobal, tyPtr) =
case inst.deriveVia of
Hs.ViaHasCField -> (
HasCField_fromPtr
, tBindgenGlobal Foreign_Ptr_type `TApp` tyTypeVar
)
Hs.ViaHasCBitfield -> (
HasCBitfield_toPtr
, tBindgenGlobal HasCBitfield_BitfieldPtr_type `TApp` tyTypeVar
)
parent = translateType inst.parentType
parentPtr = tBindgenGlobal Foreign_Ptr_type `TApp` parent
field = translateType inst.fieldType
fieldLit = translateType $ Hs.StrLit $ Hs.nameToStr inst.fieldName
-- TODO <https://github.com/well-typed/hs-bindgen/issues/1287>
-- This is not actually a free type variable.
tyTypeVar = TFree $ Hs.UnsafeName "ty"
{-------------------------------------------------------------------------------
Variables
-------------------------------------------------------------------------------}
translateDeclVar :: Hs.Var -> SDecl
translateDeclVar var = DBinding Binding{
name = var.name
, parameters= []
, result = Result var.typ Nothing
, body = var.expr
, pragmas = var.pragmas
, comment = var.comment
}
{-------------------------------------------------------------------------------
Enums
-------------------------------------------------------------------------------}
translateCEnumInstance ::
Hs.Struct
-> Hs.Type
-> Map Integer (NonEmpty String)
-> Bool
-> Maybe HsDoc.Comment
-> Instance
translateCEnumInstance struct fTyp vMap isSequential mbComment = Instance {
clss = Inst.CEnum
, args = [tcon]
, super = []
, types = [(bindgenGlobalType CEnumZ_type, [tcon], translateType fTyp)]
, comment = mbComment
, decs = map (first bindgenGlobalTerm) [
(CEnum_toCEnum , ECon struct.constr)
, (CEnum_fromCEnum , fromCEnumE)
, (CEnum_declaredValues , EUnusedLam declaredValuesE)
, (CEnum_showsUndeclared , EApp (eBindgenGlobal CEnum_showsWrappedUndeclared) dconStrE)
, (CEnum_readPrecUndeclared , EApp (eBindgenGlobal CEnum_readPrecWrappedUndeclared) dconStrE)
] ++ seqDecs
}
where
tcon :: ClosedType
tcon = TCon struct.name
dconStrE :: SExpr ctx
dconStrE = EString $ Hs.nameToStr struct.constr
fname :: Hs.Name Hs.NsVar
fname = case struct.fields of
(f : _) -> f.name
[] -> panicPure "translateCEnumInstance: empty fields"
fnameStr :: String
fnameStr = Hs.nameToStr fname
fromCEnumE :: ClosedExpr
fromCEnumE = eBindgenGlobal HasField_getField `ETypeApp` translateType (Hs.StrLit fnameStr)
declaredValuesE :: SExpr ctx
declaredValuesE = EApp (eBindgenGlobal CEnum_declaredValuesFromList) $ EList [
appManyExpr (EBoxedTup $ Plus2 0) [
EIntegral v Nothing
, if null names
then EApp (eBindgenGlobal NonEmpty_singleton) (EString name)
else
EInfix
InfixNonEmpty_constructor
(EString name)
(EList (EString <$> names))
]
| (v, name :| names) <- Map.toList vMap
]
seqDecs :: [(Global LvlTerm, ClosedExpr)]
seqDecs
| isSequential = [
(bindgenGlobalTerm CEnum_isDeclared, eBindgenGlobal CEnum_seqIsDeclared)
, (bindgenGlobalTerm CEnum_mkDeclared, eBindgenGlobal CEnum_seqMkDeclared)
]
| otherwise = []
translateSequentialCEnum ::
Hs.Struct
-> Hs.Name Hs.NsConstr
-> Hs.Name Hs.NsConstr
-> Maybe HsDoc.Comment
-> Instance
translateSequentialCEnum struct nameMin nameMax mbComment = Instance {
clss = Inst.SequentialCEnum
, args = [tcon]
, super = []
, types = []
, comment = mbComment
, decs = [
(bindgenGlobalTerm SequentialCEnum_minDeclaredValue, ECon nameMin)
, (bindgenGlobalTerm SequentialCEnum_maxDeclaredValue, ECon nameMax)
]
}
where
tcon :: ClosedType
tcon = TCon struct.name
translateCEnumInstanceShow ::
Hs.Struct
-> Maybe HsDoc.Comment
-> Instance
translateCEnumInstanceShow struct mbComment = Instance {
clss = Inst.Show
, args = [tcon]
, super = []
, types = []
, comment = mbComment
, decs = [(bindgenGlobalTerm Show_showsPrec, eBindgenGlobal CEnum_showsCEnum)]
}
where
tcon :: ClosedType
tcon = TCon struct.name
translateCEnumInstanceRead ::
Hs.Struct
-> Maybe HsDoc.Comment
-> Instance
translateCEnumInstanceRead struct mbComment = Instance {
clss = Inst.Read
, args = [tcon]
, super = []
, types = []
, comment = mbComment
, decs = map (bimap bindgenGlobalTerm eBindgenGlobal) [
(Read_readPrec , CEnum_readPrecCEnum)
, (Read_readList , Read_readListDefault)
, (Read_readListPrec , Read_readListPrecDefault)
]
}
where
tcon :: ClosedType
tcon = TCon struct.name