Files
Ian Denhardt 7101c568a1 Fix #72
Several instances are defined in .Pure modules, so we need to pull these
in even if the only thing we directly reference is the type, which is
defined in the raw module.
2020-10-07 19:16:14 -04:00

756 lines
30 KiB
Haskell

{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
module Trans.PureToHaskell (fileToModules) where
import Data.Word
import Control.Monad (guard)
import Text.Printf (printf)
import qualified Data.Text as T
import IR.Haskell
import Trans.ToHaskellCommon
import qualified IR.Common as C
import qualified IR.Name as Name
import qualified IR.Pure as P
-- | Modules imported by all generated modules.
commonImports :: [Import]
commonImports =
[ ImportAs { importAs = "V", parts = reExp ["Data", "Vector"] }
, ImportAs { importAs = "T", parts = reExp ["Data", "Text"] }
, ImportAs { importAs = "BS", parts = reExp ["Data", "ByteString"] }
, ImportAs { importAs = "Default", parts = reExp ["Data", "Default"] }
, ImportAs { importAs = "Generics", parts = ["GHC", "Generics"] }
, ImportAs { importAs = "MonadIO", parts = ["Control", "Monad", "IO", "Class"] }
, ImportAs { importAs = "UntypedPure", parts = ["Capnp", "Untyped", "Pure"] }
, ImportAs { importAs = "Untyped", parts = ["Capnp", "Untyped"] }
, ImportAs { importAs = "Message", parts = ["Capnp", "Message"] }
, ImportAs { importAs = "Classes", parts = ["Capnp", "Classes"] }
, ImportAs { importAs = "BasicsPure", parts = ["Capnp", "Basics", "Pure" ] }
, ImportAs { importAs = "GenHelpersPure", parts = ["Capnp", "GenHelpers", "Pure"] }
]
-- | Modules imported by generated modules that use rpc.
rpcImports :: [Import]
rpcImports =
[ ImportAs { importAs = "Rpc", parts = ["Capnp", "Rpc", "Untyped"] }
, ImportAs { importAs = "Server", parts = ["Capnp", "Rpc", "Server"] }
, ImportAs { importAs = "RpcHelpers", parts = ["Capnp", "GenHelpers", "Rpc"] }
, ImportAs { importAs = "STM", parts = reExp ["Control", "Concurrent", "STM"] }
, ImportAs { importAs = "STM", parts = reExp [ "Control", "Monad", "STM", "Class"] }
, ImportAs { importAs = "Supervisors", parts = reExp ["Supervisors"] }
]
reExp :: [Name.UnQ] -> [Name.UnQ]
reExp parts = ["Capnp", "GenHelpers", "ReExports"] ++ parts
-- | Whether the serialized and unserialized forms of this type
-- are the same. If not, there is a marshalling step, if so, the
-- accessors work with the decerialized form directly.
--
-- For example, this is True for Enums, basic integers, etc. but
-- False for structs, interfaces, etc.
cerialEq :: C.Type r -> Bool
cerialEq C.VoidType = True
cerialEq (C.WordType _) = True
cerialEq (C.CompositeType _) = False
cerialEq (C.PtrType _) = False
fileToModules :: P.File -> [Module]
fileToModules file =
[ fileToMainModule file
, fileToModuleAlias file
]
fileToModuleAlias :: P.File -> Module
fileToModuleAlias P.File{fileName, fileId} =
let reExport = ["Capnp", "Gen"] ++ makeModName fileName ++ ["Pure"]
in Module
{ modName = idToModule fileId ++ ["Pure"]
, modLangPragmas = []
, modExports = Just [ExportMod reExport]
, modImports = [ ImportAll { parts = reExport } ]
, modDecls = []
}
fileToMainModule :: P.File -> Module
fileToMainModule P.File{fileName, fileId, decls, reExportEnums, usesRpc} =
fixImports $ Module
{ modName = ["Capnp", "Gen"] ++ makeModName fileName ++ ["Pure"]
, modLangPragmas =
[ "DeriveGeneric"
, "DuplicateRecordFields"
, "FlexibleContexts"
, "FlexibleInstances"
, "RecordWildCards"
, "MultiParamTypeClasses"
, "TypeFamilies"
]
, modExports = Just $
[ExportGCtors (gName (rawModule fileId) name) | name <- reExportEnums]
++ concatMap (declToExport fileId) decls
, modImports =
commonImports ++ (guard usesRpc >> rpcImports)
, modDecls =
concatMap (declToDecls fileId) decls
++ concatMap (enumInstances fileId) reExportEnums
}
-- | Convert a declaration into the list of related exports we need.
-- The first argument is the id for this module.
declToExport :: Word64 -> P.Decl -> [Export]
declToExport fileId = \case
P.DataDecl P.Data{typeName} ->
[ ExportLCtors typeName ]
P.ConstDecl P.Constant { name, value=C.WordValue _ _ } ->
[ ExportGName $ gName (rawModule fileId) name ]
P.ConstDecl P.Constant { name, value=C.VoidValue } ->
[ ExportGName $ gName (rawModule fileId) name ]
P.ConstDecl P.Constant { name, value=C.PtrValue _ _ } ->
[ ExportLName name ]
P.IFaceDecl P.IFace{ name=Name.CapnpQ{local} } ->
[ ExportLCtors local
, ExportLCtors (Name.mkSub local "server_")
, ExportLName (Name.unQToLocal $ Name.UnQ $ "export_" <> Name.renderLocalQ local)
]
-- | enumInstances' generates some type class instances an enum data type.
enumInstances :: Word64 -> Name.LocalQ -> [Decl]
enumInstances thisMod name =
let rawName = gName (rawModule thisMod) name in
instance_ [] ["Classes"] "Decerialize" [TGName rawName]
[ iType "Cerial" [tuName "msg", TGName rawName] (TGName rawName)
, iValue "decerialize" [] (eStd_ "pure")
]
: instance_ [] ["Classes"] "Cerialize" [TGName rawName]
[ iValue "cerialize" [PVar "_"] (eStd_ "pure")
]
: [ instance_ [] ["Classes"] "Cerialize" [t]
[ iValue "cerialize" [] (egName ["Classes"] "cerializeBasicVec")
]
| t <- take 6 $ drop 1 $ iterate
(\t -> TApp (tgName ["V"] "Vector") [t])
(TGName rawName)
]
-- | Convert an 'IR.Pure.Decl' into a series of Haskell declarations.
declToDecls :: Word64 -> P.Decl -> [Decl]
declToDecls thisMod (P.DataDecl data_) = dataToDecls thisMod data_
declToDecls thisMod (P.ConstDecl constant) = constToDecls thisMod constant
declToDecls thisMod (P.IFaceDecl iface) = ifaceToDecls thisMod iface
dataToDecls :: Word64 -> P.Data -> [Decl]
dataToDecls thisMod data_@P.Data{firstClass} = concat $
[ dataToDataDecl thisMod data_
, dataToSimpleInstances thisMod data_
, dataToDecerialize thisMod data_
, dataToMarshal thisMod data_
]
++
[ firstClassInstances thisMod data_ | firstClass ]
-- | Get the name of a data constructor for a product type. Arguments are the
-- name of the type constructor and whether or not the type is "first class".
productVariantName :: Name.LocalQ -> Bool -> Name.LocalQ
productVariantName typeName firstClass
| firstClass = typeName
| otherwise = Name.mkSub typeName ""
-- ^ This is a group. If it's part of a union, then the union
-- will have a data constructor that is the same as typeName,
-- so we appent a ' to avoid a colision.
dataToDataDecl :: Word64 -> P.Data -> [Decl]
dataToDataDecl thisMod P.Data
{ typeName
, cerialName
, firstClass
, def
} =
let unknownCtor = Name.mkSub cerialName "unknown'" in
[ DcData Data
{ dataName = Name.localToUnQ typeName
, typeArgs = []
, derives =
["Std_.Show", "Std_.Eq", "Generics.Generic"]
, dataNewtype = False
, dataVariants =
case def of
P.Product fields ->
[ DataVariant
{ dvCtorName = Name.localToUnQ $ productVariantName typeName firstClass
, dvArgs = ARec (map (fieldToField thisMod) fields)
}
]
P.Sum variants ->
[ DataVariant
{ dvCtorName = Name.localToUnQ name
, dvArgs = case arg of
Nothing -> APos []
Just ty -> APos [typeToType thisMod ty]
}
| P.Variant{name, arg} <- variants
]
++
[ DataVariant
{ dvCtorName = Name.localToUnQ unknownCtor
, dvArgs = APos [TPrim $ C.PrimInt $ C.IntType C.Unsigned C.Sz16]
}
]
}
]
dataToSimpleInstances :: Word64 -> P.Data -> [Decl]
dataToSimpleInstances _thisMod P.Data{ typeName } =
[ instance_ [] ["Default"] "Default" [TLName typeName]
[ iValue "def" [] (egName ["GenHelpersPure"] "defaultStruct")
]
, instance_ [] ["Classes"] "FromStruct" [tgName ["Message"] "ConstMsg", TLName typeName]
[ iValue "fromStruct" [PVar "struct"] $ EBind
(EApp (egName ["Classes"] "fromStruct") [euName "struct"])
(egName ["Classes"] "decerialize")
]
]
dataToDecerialize :: Word64 -> P.Data -> [Decl]
dataToDecerialize thisMod P.Data
{ typeName
, cerialName
, firstClass
, def
} =
[ instance_ [] ["Classes"] "Decerialize" [TLName typeName]
[ iType "Cerial" [tuName "msg", TLName typeName] $
TApp (tgName (rawModule thisMod) cerialName) [tuName "msg"]
, iValue "decerialize" [PVar "raw"] $
case def of
P.Sum variants ->
variantsToDecerialize thisMod cerialName variants
P.Product fields ->
fieldsToDecerialize thisMod typeName firstClass fields
]
]
variantsToDecerialize :: Word64 -> Name.LocalQ -> [P.Variant] -> Exp
variantsToDecerialize thisMod cerialName variants =
let unknownCtor = Name.mkSub cerialName "unknown'"
fieldGetter parentName name = egName
(rawModule thisMod)
(Name.unQToLocal $ Name.getterName $ Name.mkSub parentName name)
in
EDo
[DoBind "raw" $ EApp (fieldGetter cerialName "") [euName "raw"]
]
(ECase (ELName "raw") $
[ case arg of
Nothing ->
( pgName (rawModule thisMod) name []
, EApp (eStd_ "pure") [ELName name]
)
Just type_ | cerialEq type_ ->
( pgName (rawModule thisMod) name [PVar "raw"]
, EApp (eStd_ "pure") [EApp (ELName name) [euName "raw"]]
)
Just _ ->
( pgName (rawModule thisMod) name [PVar "raw"]
, EFApp
(ELName name)
[EApp (egName ["Classes"] "decerialize") [euName "raw"]]
)
| P.Variant{name, arg} <- variants
]
++
[ ( pgName (rawModule thisMod) unknownCtor [PVar "tag"]
, EApp
(eStd_ "pure")
[EApp (ELName unknownCtor) [euName "tag"]]
)
]
)
fieldsToDecerialize :: Word64 -> Name.LocalQ -> Bool -> [P.Field] -> Exp
fieldsToDecerialize thisMod typeName firstClass fields =
let fieldGetter parentName name = egName
(rawModule thisMod)
(Name.unQToLocal $ Name.getterName $ Name.mkSub parentName name)
variantName = productVariantName typeName firstClass
in
case fields of
[] ->
EApp (eStd_ "pure") [ELName variantName]
_ ->
EFApp
(ELName variantName)
[
let getter = EApp (fieldGetter typeName name) [euName "raw"] in
if name == "union'" then
-- unions decerialize from the same type as their parents. Don't
-- do anything but pass it off.
EApp (egName ["Classes"] "decerialize") [euName "raw"]
else if cerialEq type_ then
getter
else
EBind getter (egName ["Classes"] "decerialize")
| P.Field{name, type_} <- fields
]
dataToMarshal :: Word64 -> P.Data -> [Decl]
dataToMarshal thisMod P.Data
{ typeName
, cerialName
, firstClass
, def
} =
[ instance_ [] ["Classes"] "Marshal" [TLName typeName]
[ iValue "marshalInto" [PVar "raw_", PVar "value_"] $
case def of
P.Sum variants ->
variantsToMarshal thisMod cerialName variants
P.Product fields ->
fieldsToMarshal thisMod typeName firstClass fields
]
]
variantsToMarshal :: Word64 -> Name.LocalQ -> [P.Variant] -> Exp
variantsToMarshal thisMod cerialName variants =
let unknownCtor = Name.mkSub cerialName "unknown'" in
ECase (euName "value_") $
[ let setter = Name.unQToLocal $ Name.setterName variantName
setExp = EApp (egName (rawModule thisMod) setter) [euName "raw_"]
in case arg of
Nothing ->
( PLCtor variantName []
, setExp
)
Just type_ ->
( PLCtor variantName [PVar "arg_"]
, marshalField MarshalField
{ thisMod
, into = "raw_"
, localQField = variantName
, from = "arg_"
, type_
, inUnion = True
}
)
| P.Variant{name=variantName, arg} <- variants
]
++
let setter = Name.unQToLocal $ Name.setterName unknownCtor in
[ ( PLCtor unknownCtor [PVar "tag"]
, EApp (egName (rawModule thisMod) setter) [euName "raw_", euName "tag"]
)
]
fieldsToMarshal :: Word64 -> Name.LocalQ -> Bool -> [P.Field] -> Exp
fieldsToMarshal thisMod typeName firstClass fields =
let variantName = productVariantName typeName firstClass in
ECase (euName "value_")
[ ( case fields of
-- If there are no fields, use of RecordWildCards will
-- cause an error:
[] -> PLCtor variantName []
_ -> PLRecordWildCard variantName
, EDo
[ DoE $ marshalField MarshalField
{ thisMod
, into = "raw_"
, localQField = Name.mkSub typeName fieldName
, from = fieldName
, type_
, inUnion = False
}
| P.Field{name=fieldName, type_} <- fields
]
ePureUnit
)
]
firstClassInstances :: Word64 -> P.Data -> [Decl]
firstClassInstances _thisMod P.Data{ typeName } =
[ instance_ [] ["Classes"] "Cerialize" [TLName typeName] []
, instance_ [] ["Classes"] "Cerialize" [TApp (tgName ["V"] "Vector") [TLName typeName]]
[ iValue "cerialize" [] (egName ["GenHelpersPure"] "cerializeCompositeVec")
]
] ++
-- Generate instances of Cerialize (Vector (Vector ... t)) up to some reasonable
-- nesting level. I(zenhack) can't figure out how to get a general case
-- Cerialize (Vector a) => Cerialize (Vector (Vector a)) to type check, so this
-- will have to do for now.
[ instance_ [] ["Classes"] "Cerialize" [t]
[ iValue "cerialize" [] (egName ["GenHelpersPure"] "cerializeBasicVec")
]
| t <- take 6 $ drop 2 $ iterate
(\t -> TApp (tgName ["V"] "Vector") [t])
(TLName typeName)
]
ifaceToDecls :: Word64 -> P.Interface -> [Decl]
ifaceToDecls thisMod iface =
[ ifaceClientDecl thisMod iface
, ifaceClassDecl thisMod iface
, ifaceExportFn thisMod iface
]
++
ifaceInstances thisMod iface
-- | Declare the newtype wrapper for clients of this interface.
ifaceClientDecl :: Word64 -> P.Interface -> Decl
ifaceClientDecl _thisMod P.IFace{ name=Name.CapnpQ{local=name} } =
DcData Data
{ dataName = Name.localToUnQ name
, dataNewtype = True
, dataVariants =
[ DataVariant
{ dvCtorName = Name.localToUnQ name
, dvArgs = APos [tgName ["Message"] "Client"]
}
]
, typeArgs = []
, derives =
[ "Std_.Show"
, "Std_.Eq"
, "Generics.Generic"
]
}
-- | define the *'server_ class for the interface.
ifaceClassDecl :: Word64 -> P.Interface -> Decl
ifaceClassDecl thisMod P.IFace { name=Name.CapnpQ{local=name}, methods, supers } =
DcClass
{ ctx =
let superConstraints =
-- Add class constraints for superclasses:
[ let superClass = pureTName thisMod fileId (Name.mkSub local "server_")
in TApp superClass [tuName "m", tuName "cap"]
| P.IFace{name=Name.CapnpQ{local, fileId}} <- supers
]
in
TApp (tgName ["MonadIO"] "MonadIO") [tuName "m"]
: case superConstraints of
-- If this interface doesn't have any superclasses, then we need to
-- specify a constraint for the 'Server' class. Otherwise, it's
-- implied by the supers.
[] -> [ TApp (tgName ["Server"] "Server") [tuName "m", tuName "cap"] ]
_ -> superConstraints
, name = Name.mkSub name "server_"
, params = ["m", "cap"]
, decls =
let mkName = mkMethodName name in
CdMinimal [ mkName name | P.Method{name} <- methods ]
: concat
[ [ CdValueDecl
(mkName methodName)
(TFn
[ tuName "cap"
, TApp
(tgName ["Server"] "MethodHandler")
[ tuName "m"
, typeToType thisMod $ C.CompositeType $ C.StructType paramType
, typeToType thisMod $ C.CompositeType $ C.StructType resultType
]
]
)
, CdValueDef DfValue
{ name = mkName methodName
, params = [PVar "_"]
, value = egName ["Server"] "methodUnimplemented"
}
]
| P.Method
{ name=methodName
, paramType
, resultType
}
<- methods
]
}
-- | Define the export_Foo function for the interface.
ifaceExportFn :: Word64 -> P.Interface -> Decl
ifaceExportFn thisMod iface@P.IFace { name=Name.CapnpQ{ local }, ancestors } =
DcValue
{ typ = TCtx
[ TApp (TLName (Name.mkSub local "server_")) [tStd_ "IO", tuName "a"]
, TApp (tgName ["STM"] "MonadSTM") [tuName "m"]
]
(TFn
[ tgName ["Supervisors"] "Supervisor"
, tuName "a"
, TApp (tuName "m") [TLName local]
]
)
, def = DfValue
{ name = Name.UnQ $ "export_" <> Name.renderLocalQ local
, params = [PVar "sup_", PVar "server_"]
, value = EApp (egName ["STM"] "liftSTM") $ pure $ EFApp (ELName local)
[ EApp (egName ["Rpc"] "export")
[ euName "sup_"
, ERecord (egName ["Server"] "ServerOps")
[ ( "handleCast"
, EApp (egName ["Server"] "unwrap") [ELName "server_"]
)
, ( "handleStop"
, EApp (egName ["Server"] "shutdown") [ELName "server_"]
)
, ( "handleCall"
, ELambda [PVar "interfaceId_", PVar "methodId_"] $
ECase (euName "interfaceId_") $
[ ( PInt (fromIntegral interfaceId)
, ECase (euName "methodId_") $
[ ( PInt i
, EApp
(egName ["Server"] "toUntypedHandler")
[ let methodName = mkMethodName ifaceName mname in
EApp
(if thisMod == ifaceMod
then euName methodName
else egName (pureModule ifaceMod) (Name.unQToLocal methodName)
)
[euName "server_"]
]
)
| (i, P.Method{name=mname}) <- zip [0..] methods
]
++
[ ( PVar "_"
, egName ["Server"] "methodUnimplemented"
)
]
)
| P.IFace
{ name = Name.CapnpQ{local=ifaceName, fileId=ifaceMod}
, interfaceId
, methods
}
<- iface:ancestors
]
++
[ ( PVar "_"
, egName ["Server"] "methodUnimplemented"
)
]
)
]
]
]
}
}
-- | Declare instances for clients for this interface.
ifaceInstances :: Word64 -> P.Interface -> [Decl]
ifaceInstances thisMod iface@P.IFace{ name=Name.CapnpQ{local=name} } =
[ instance_ [] ["Rpc"] "IsClient" [TLName name]
[ iValue "fromClient" [] (ELName name)
, iValue "toClient" [PLCtor name [PVar "client"]] (euName "client")
]
, instance_ [] ["Classes"] "FromPtr" [tuName "msg", TLName name]
[ iValue "fromPtr" [] (egName ["RpcHelpers"] "isClientFromPtr")
]
, instance_ [] ["Classes"] "ToPtr" [tuName "s", TLName name]
[ iValue "toPtr" [] (egName ["RpcHelpers"] "isClientToPtr")
]
, instance_ [] ["Classes"] "Decerialize" [TLName name]
[ iType "Cerial" [tuName "msg", TLName name] $
TApp (tgName (rawModule thisMod) name) [tuName "msg"]
, iValue "decerialize"
[ pgName (rawModule thisMod) (Name.mkSub name "newtype_") [PVar "maybeCap"]
]
(ECase (euName "maybeCap")
[ (PGCtor (std_ "Nothing") []
, EApp
(eStd_ "pure")
[ EApp (ELName name) [egName ["Message"] "nullClient"]
]
)
, (PGCtor (std_ "Just") [PVar "cap"]
, EFApp
(ELName name)
[ EApp (egName ["Untyped"] "getClient") [euName "cap"]]
)
]
)
]
, instance_ [] ["Classes"] "Cerialize" [TLName name]
[ iValue "cerialize" [PVar "msg", PLCtor name [PVar "client"]] $
EFApp
(egName (rawModule thisMod) (Name.mkSub name "newtype_"))
[ EFApp
(eStd_ "Just")
[EApp (egName ["Untyped"] "appendCap") [euName "msg", euName "client"]]
]
]
]
++
ifaceServerInstances thisMod iface
-- | Generate a reference to a type defined in a pure module given:
--
-- * The id of the module we're in (thisMod)
-- * The id of the module in which the type is defined (targetMod)
-- * The local name of the type.
pureTName :: Word64 -> Word64 -> Name.LocalQ -> Type
pureTName thisMod targetMod local
| thisMod == targetMod = TLName local
| otherwise = tgName (pureModule targetMod) local
-- | Instance declarations for this interface's client for its *'server_ class
-- and those of its ancestors.
ifaceServerInstances :: Word64 -> P.Interface -> [Decl]
ifaceServerInstances thisMod iface@P.IFace{ name=Name.CapnpQ{local=name}, ancestors } =
DcInstance
{ ctx = []
, typ = TApp (tgName ["Server"] "Server") [tStd_ "IO", TLName name]
, defs = []
}
: map go (iface:ancestors)
where
go P.IFace { name=Name.CapnpQ{local, fileId}, interfaceId, methods } =
let className = Name.mkSub local "server_"
classType = pureTName thisMod fileId className
in
-- Can't use 'instance_' here, because we don't know ahead of time what
-- module the class is in.
DcInstance
{ ctx = []
, typ =
TApp classType [tStd_ "IO", TLName name]
, defs =
[ let methodName = mkMethodName local mname in
iValue methodName [PLCtor name [PVar "client"]] $
EApp
(egName ["Rpc"] "clientMethodHandler")
[ EInt $ fromIntegral interfaceId
, EInt i
, euName "client"
]
| (i, P.Method{name=mname}) <- zip [0..] methods
]
}
-- | Generate declarations for a constant.
constToDecls :: Word64 -> P.Constant -> [Decl]
constToDecls thisMod P.Constant { name, value } = case value of
C.PtrValue ty _ ->
-- Generated code parses the corresponding constant from the raw
-- module.
[ DcValue
{ typ = typeToType thisMod (C.PtrType ty)
, def = DfValue
{ name = Name.localToUnQ name
, params = []
, value = EApp
(egName ["GenHelpersPure"] "toPurePtrConst")
[egName (rawModule thisMod) name]
}
}
]
-- For these two we just re-export the constant from the raw module, so no
-- need to do anything here:
C.WordValue _ _ -> []
C.VoidValue -> []
mkMethodName :: Name.LocalQ -> Name.UnQ -> Name.UnQ
mkMethodName typeName methodName = Name.valueName $ Name.mkSub typeName methodName
-- | Arguments to 'marshalField'
data MarshalField = MarshalField
{ thisMod :: !Word64
-- ^ The id for the module we're generating
, into :: Name.UnQ
-- ^ An variable holding the destination for the marshalled data.
, localQField :: Name.LocalQ
-- ^ The name of the field to marshal, qualified within the current module.
, from :: Name.UnQ
-- ^ A variable holding the value to marshal
, type_ :: C.Type Name.CapnpQ
, inUnion :: !Bool
-- ^ whether the parent of this field is a union
}
marshalField :: MarshalField -> Exp
marshalField MarshalField{thisMod, into, localQField, from, type_, inUnion} =
let setter = egName (rawModule thisMod) $ Name.unQToLocal (Name.setterName localQField)
getter = egName (rawModule thisMod) $ Name.unQToLocal (Name.getterName localQField)
in case type_ of
C.PtrType _ ->
EBind
(EApp
(egName ["Classes"] "cerialize")
[ EApp (egName ["Untyped"] "message") [euName "raw_"]
, euName from
]
)
(EApp setter [euName into])
C.VoidType ->
ePureUnit
C.WordType _ ->
EApp setter [euName into, euName from]
C.CompositeType _ -> EDo
(if Name.getUnQ localQField == "union'" then
-- Anonymous union; has the same Cerial as us. Just delegate.
[]
else if inUnion then
[ DoBind into $ EApp setter [euName into] ]
else
[ DoBind into $ EApp getter [euName into] ]
)
(EApp (egName ["Classes"] "marshalInto") [euName into, euName from])
fieldToField :: Word64 -> P.Field -> (Name.UnQ, Type)
fieldToField thisMod P.Field{name, type_} = (name, typeToType thisMod type_)
typeToType :: Word64 -> C.Type Name.CapnpQ -> Type
typeToType _thisMod C.VoidType =
TUnit
typeToType _thisMod (C.WordType (C.PrimWord ty)) =
TPrim ty
typeToType thisMod (C.WordType (C.EnumType Name.CapnpQ{local, fileId})) =
-- Enums are just re-exported from the raw module, so we have a choice as to
-- how to refer to them. Using their 'Pure' module makes sure we pull in any
-- type class instances in those modules, but we have to make an exception
-- if the enum is defined in the file we're generating code for, since otherwise
-- we'd introduce a cyclic dependency. In that case we use the raw name; the
-- instnaces are defined in this same module so we don't need to worry about those.
if thisMod == fileId then
tgName (rawModule fileId) local
else
tgName (pureModule fileId) local
typeToType thisMod (C.CompositeType (C.StructType n)) =
nameToType thisMod n
typeToType thisMod (C.PtrType (C.PtrComposite (C.StructType n))) =
nameToType thisMod n
typeToType thisMod (C.PtrType (C.ListOf ty)) =
TApp (tgName ["V"] "Vector") [typeToType thisMod ty]
typeToType _thisMod (C.PtrType (C.PrimPtr C.PrimText)) =
tgName ["T"] "Text"
typeToType _thisMod (C.PtrType (C.PrimPtr C.PrimData)) =
tgName ["BS"] "ByteString"
typeToType _thisMod (C.PtrType (C.PrimPtr (C.PrimAnyPtr _))) =
-- TODO: distinguish different pointer types.
TApp (tStd_ "Maybe") [tgName ["UntypedPure"] "Ptr"]
typeToType thisMod (C.PtrType (C.PtrInterface n)) =
nameToType thisMod n
nameToType :: Word64 -> Name.CapnpQ -> Type
nameToType thisMod Name.CapnpQ{local, fileId}
| thisMod == fileId = TLName local
| otherwise = tgName (pureModule fileId) local
rawModule :: Word64 -> [T.Text]
rawModule modId =
["Capnp", "Gen", "ById", T.pack $ printf "X%x" modId]
pureModule :: Word64 -> [T.Text]
pureModule modId =
rawModule modId ++ ["Pure"]