capnp-0.3.0.0: exe/capnpc-haskell/Backends/Raw.hs
-- Generate low-level accessors from type types in IR.
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module Backends.Raw
( fmtModule
) where
import Data.Function ((&))
import Data.List (sortOn)
import Data.Monoid ((<>))
import Data.Ord (Down(..))
import Data.String (IsString(..))
import GHC.Exts (IsList(fromList))
import Text.PrettyPrint.Leijen.Text (hcat, vcat)
import Text.Printf (printf)
import qualified Data.ByteString.Lazy as LBS
import qualified Data.Map.Strict as M
import qualified Data.Text as T
import qualified Text.PrettyPrint.Leijen.Text as PP
import Fmt
import IR
import Util
import Backends.Common (dataFieldSize, fmtPrimWord)
import Data.Capnp
(cerialize, createPure, defaultLimit, msgToLBS, newMessage, setRoot)
import qualified Data.Capnp.Untyped.Pure as Untyped
-- | Sort varaints by their tag, in decending order (with no tag at all being last).
sortVariants = sortOn (Down . variantTag)
fmtModule :: Module -> [(FilePath, PP.Doc)]
fmtModule thisMod@Module{modName=Namespace modNameParts,..} =
[ ( T.unpack $ mintercalate "/" humanParts <> ".hs"
, mainContent
)
, ( printf "Capnp/ById/X%x.hs" modId
, vcat
[ "{-# OPTIONS_GHC -Wno-unused-imports #-}"
, "{-# OPTIONS_HADDOCK hide #-}"
, "{- |"
, hcat [ "Module: ", machineMod ]
, hcat [ "Description: machine-addressable alias for '", humanMod, "'." ]
, "-}"
, hcat [ "module ", machineMod, " (module ", humanMod, ") where" ]
, hcat [ "import ", humanMod ]
]
)
] where
machineMod = fromString (printf "Capnp.ById.X%x" modId)
humanMod = fmtModRef $ FullyQualified $ Namespace humanParts
humanParts = "Capnp":modNameParts
modFileText = PP.textStrict modFile
mainContent = vcat
[ "{-# OPTIONS_GHC -Wno-unused-imports #-}"
, "{-# LANGUAGE FlexibleContexts #-}"
, "{-# LANGUAGE FlexibleInstances #-}"
, "{-# LANGUAGE MultiParamTypeClasses #-}"
, "{-# LANGUAGE TypeFamilies #-}"
, "{-# LANGUAGE DeriveGeneric #-}"
, "{- |"
, "Module: " <> humanMod
, "Description: Low-level generated module for " <> modFileText
, ""
, "This module is the generated code for " <> modFileText <> ", for the"
, "low-level api."
, "-}"
, "module " <> humanMod <> " where"
, ""
, "-- Code generated by capnpc-haskell. DO NOT EDIT."
, "-- Generated from schema file: " <> modFileText
, ""
, "import Data.Int"
, "import Data.Word"
, ""
, "import GHC.Generics (Generic)"
, ""
, "import Data.Capnp.Bits (Word1)"
, ""
, "import qualified Data.Bits"
, "import qualified Data.Maybe"
, "import qualified Data.ByteString"
-- The trailing ' is to avoid possible name collisions:
, "import qualified Data.Capnp.Classes as C'"
, "import qualified Data.Capnp.Basics as B'"
, "import qualified Data.Capnp.GenHelpers as H'"
, "import qualified Data.Capnp.TraversalLimit as TL'"
, "import qualified Data.Capnp.Untyped as U'"
, "import qualified Data.Capnp.Message as M'"
, ""
, vcat $ map fmtImport modImports
, ""
, vcat $ map (fmtDecl thisMod) (M.toList modDecls)
]
fmtModRef :: ModuleRef -> PP.Doc
fmtModRef (ByCapnpId id) = fromString $ printf "Capnp.ById.X%x" id
fmtModRef (FullyQualified (Namespace ns)) = mintercalate "." (map PP.textStrict ns)
fmtImport :: Import -> PP.Doc
fmtImport (Import ref) = "import qualified " <> fmtModRef ref
-- | format the IsPtr instance for a list of the struct type with
-- the given name.
fmtStructListIsPtr :: PP.Doc -> PP.Doc
fmtStructListIsPtr nameText =
instance_ [] ("C'.IsPtr msg (B'.List msg (" <> nameText <> " msg))")
[ hcat [ "fromPtr msg ptr = List_", nameText, " <$> C'.fromPtr msg ptr" ]
, hcat [ "toPtr (List_", nameText, " l) = C'.toPtr l" ]
]
-- | Generate declarations common to all types which are represented
-- by 'Untyped.Struct'.
--
-- parameters:
--
-- * thisMod - the module that we are generating.
-- * name - the name of the type.
-- * info - the StructInfo; this is a group, some instances will be skipped.
fmtNewtypeStruct :: Module -> Name -> IR.StructInfo -> PP.Doc
fmtNewtypeStruct thisMod name info =
let typeCon = fmtName thisMod name
dataCon = typeCon <> "_newtype_"
in vcat
[ hcat [ "newtype ", typeCon, " msg = ", dataCon, " (U'.Struct msg)" ]
, instance_ [] ("C'.FromStruct msg (" <> typeCon <> " msg)")
[ hcat [ "fromStruct = pure . ", dataCon ]
]
, instance_ [] ("C'.ToStruct msg (" <> typeCon <> " msg)")
[ hcat [ "toStruct (", dataCon, " struct) = struct" ]
]
, instance_ [] ("U'.HasMessage (" <> typeCon <> " msg)")
[ hcat [ "type InMessage (", typeCon, " msg) = msg" ]
, hcat [ "message (", dataCon, " struct) = U'.message struct" ]
]
, instance_ [] ("U'.MessageDefault (" <> typeCon <> " msg)")
[ hcat [ "messageDefault = ", dataCon, " . U'.messageDefault" ]
]
, case info of
IR.IsGroup ->
""
IR.IsStandalone{dataSz, ptrSz} -> vcat
[ fmtStructListElem typeCon
, instance_ [] ("C'.IsPtr msg (" <> typeCon <> " msg)")
[ hcat [ "fromPtr msg ptr = ", dataCon, " <$> C'.fromPtr msg ptr" ]
, hcat [ "toPtr (", dataCon, " struct) = C'.toPtr struct" ]
]
, instance_ [] ("B'.MutListElem s (" <> typeCon <> " (M'.MutMsg s))")
[ hcat [ "setIndex (", dataCon, " elt) i (List_", typeCon, " l) = U'.setIndex elt i l" ]
, hcat
[ "newList msg len = List_", typeCon, " <$> U'.allocCompositeList msg "
, fromString (show dataSz), " "
, fromString (show ptrSz), " len"
]
]
, instance_ [] ("C'.Allocate s (" <> typeCon <> " (M'.MutMsg s))")
[ hcat
[ "new msg = ", dataCon , " <$> U'.allocStruct msg "
, fromString (show dataSz), " "
, fromString (show ptrSz)
]
]
, fmtStructListIsPtr typeCon
]
]
-- | Generate an instance of ListElem for a struct type. The parameter is the name of
-- the type constructor.
fmtStructListElem :: PP.Doc -> PP.Doc
fmtStructListElem nameText =
instance_ [] ("B'.ListElem msg (" <> nameText <> " msg)")
[ hcat [ "newtype List msg (", nameText, " msg) = List_", nameText, " (U'.ListOf msg (U'.Struct msg))" ]
, hcat [ "length (List_", nameText, " l) = U'.length l" ]
, hcat [ "index i (List_", nameText, " l) = U'.index i l >>= ", fmtRestrictedFromStruct nameText ]
]
-- | Output an expression equivalent to fromStruct, but restricted to the type
-- with the given type constructor (which must have kind * -> *).
fmtRestrictedFromStruct :: PP.Doc -> PP.Doc
fmtRestrictedFromStruct nameText = hcat
[ "(let {"
, "go :: U'.ReadCtx m msg => U'.Struct msg -> m (", nameText, " msg); "
, "go = C'.fromStruct"
, "} in go)"
]
-- | Generate a call to 'H'.getWordField' based on a 'DataLoc'.
-- The first argument is an expression for the struct.
fmtGetWordField :: PP.Doc -> DataLoc -> PP.Doc
fmtGetWordField struct DataLoc{..} = mintercalate " "
[ " H'.getWordField"
, struct
, fromString (show dataIdx)
, fromString (show dataOff)
, fromString (show dataDef)
]
-- | @'fmtSetWordField' struct value loc@ is like 'fmtGetWordField', except that
-- it generates a call to 'H'.setWordField'. The extra value parameter corresponds
-- to the extra parameter in 'H'.setWordField'.
fmtSetWordField :: PP.Doc -> PP.Doc -> DataLoc -> PP.Doc
fmtSetWordField struct value DataLoc{..} = mintercalate " "
[ "H'.setWordField"
, struct
, value
, fromString (show dataIdx)
, fromString (show dataOff)
, fromString (show dataDef)
]
-- | Format the various accessors @(set_*, get_*, has_*, new_*)@ for a field.
-- .
-- Parameters (in order):
-- .
-- * @thisMod@: the module we're generating.
-- * @typeName@: the name of the type to which the field belongs.
-- * @variantName@: the name of the variant of @typeName@.
-- * @field@: the field to format.
fmtFieldAccessor :: Module -> Name -> Name -> Field -> PP.Doc
fmtFieldAccessor thisMod typeName variantName Field{..} = vcat
[ fmtGetter
, fmtHas
, fmtSetter
, fmtNew thisMod accessorName typeCon fieldLocType
]
where
accessorName prefix = fmtName thisMod $ prefixName prefix (subName variantName fieldName)
getName = accessorName "get_"
hasName = accessorName "has_"
setName = accessorName "set_"
newName = accessorName "new_"
typeCon = fmtName thisMod typeName
dataCon = typeCon <> "_newtype_"
fmtGetter =
let getType fieldType = typeCon <> " msg -> m " <> fmtType thisMod "msg" fieldType
typeAnnotation fieldType =
hcat [ getName, " :: U'.ReadCtx m msg => ", getType fieldType ]
getDef def = hcat [ getName, " (", dataCon, " struct) =", def ]
in case fieldLocType of
DataField loc ty -> vcat
[ typeAnnotation (WordType ty)
, getDef $ fmtGetWordField "struct" loc
]
PtrField idx ty -> vcat
[ typeAnnotation (PtrType ty)
, getDef $ PP.line <> indent (vcat
[ hcat [ "U'.getPtr ", fromString (show idx), " struct" ]
, hcat [ ">>= C'.fromPtr (U'.message struct)" ]
])
]
HereField ty -> vcat
[ typeAnnotation (CompositeType ty)
, getDef " C'.fromStruct struct"
]
VoidField -> vcat
[ typeAnnotation VoidType
, getDef " Data.Capnp.TraversalLimit.invoice 1 >> pure ()"
]
fmtHas =
case fieldLocType of
PtrField idx _ -> vcat
[ hcat [ hasName, " :: U'.ReadCtx m msg => ", typeCon, " msg -> m Bool" ]
, hcat
[ hasName, "(", dataCon, " struct) = "
, "Data.Maybe.isJust <$> U'.getPtr "
, fromString (show idx)
, " struct"
]
]
_ ->
""
fmtSetter =
let setType fieldType = typeCon <> " (M'.MutMsg s) -> " <> fmtType thisMod "(M'.MutMsg s)" fieldType <> " -> m ()"
typeAnnotation fieldType = setName <> " :: U'.RWCtx m s => " <> setType fieldType
in
case fieldLocType of
DataField loc@DataLoc{..} ty -> vcat
[ typeAnnotation (WordType ty)
, hcat
[ setName, " (", dataCon, " struct) value = "
, fmtSetWordField
"struct"
("(fromIntegral (C'.toWord value) :: Word" <> fromString (show $ dataFieldSize ty) <> ")")
loc
]
]
VoidField -> vcat
[ typeAnnotation VoidType
, setName <> " _ = pure ()"
]
PtrField idx ty -> vcat
[ typeAnnotation (PtrType ty)
, hcat
[ setName, " (", dataCon, " struct) value = "
, "U'.setPtr (C'.toPtr value) ", fromString (show idx), " struct"
]
]
HereField _ ->
-- We don't generate setters for these fields; instead, the
-- user should call the getter and then modify the child in-place.
""
-- | format a @new_*@ function for a field.
-- .
-- Parameters (in order):
-- .
-- * @thisMod@: The module we're generating.
-- * @accessorName@: function getting the accessor for a specific prefix;
-- takes an argument that is @"set_"@, @"get_"@, etc.
-- * @typeCon@: The name of the type constructor for the type owning the
-- field.
-- * @fieldLocType@: the field location and type.
fmtNew thisMod accessorName typeCon fieldLocType =
case fieldLocType of
PtrField _ fieldType ->
let newType = hcat
[ typeCon
, " (M'.MutMsg s) -> m ("
, fmtType thisMod "(M'.MutMsg s)" (PtrType fieldType)
, ")"
]
in case fieldType of
ListOf _ ->
fmtNewListLike newType "C'.newList"
PrimPtr PrimText ->
fmtNewListLike newType "B'.newText"
PrimPtr PrimData ->
fmtNewListLike newType "B'.newData"
PrimPtr (PrimAnyPtr _) ->
""
PtrComposite _ -> vcat
[ hcat [ newName, " :: U'.RWCtx m s => ", newType ]
, hcat [ newName, " struct = do" ]
, indent $ vcat
[ hcat [ "result <- C'.new (U'.message struct)" ]
, hcat [ setName, " struct result" ]
, "pure result"
]
]
_ ->
""
where
newName = accessorName "new_"
setName = accessorName "set_"
fmtNewListLike newType allocFn = vcat
[ hcat [ newName, " :: U'.RWCtx m s => Int -> ", newType ]
, hcat [ newName, " len struct = do" ]
, indent $ vcat
[ hcat [ "result <- ", allocFn, " (U'.message struct) len" ]
, hcat [ setName, " struct result" ]
, "pure result"
]
]
-- Generate setters for union variants, plus new_* functions where the argument
-- is a pointer type.
fmtUnionSetter :: Module -> Name -> DataLoc -> Maybe Variant -> PP.Doc
fmtUnionSetter thisMod parentType tagLoc variant =
let (variantName, variantParams) = case variant of
Just Variant{..} ->
(variantName, Just variantParams)
Nothing ->
( subName parentType "unknown'"
, Nothing
)
accessorName prefix = prefix <> fmtName thisMod variantName
setName = "set_" <> fmtName thisMod variantName
parentTypeCon = fmtName thisMod parentType
parentDataCon = parentTypeCon <> "_newtype_"
fmtSetTag = fmtSetWordField
"struct"
(case variant of
Just Variant{variantTag} ->
hcat [ "(", fromString (show variantTag), " :: Word16)" ]
Nothing ->
"(tagValue :: Word16)")
tagLoc
in case variantParams of
Nothing -> vcat
[ hcat [ setName, " :: U'.RWCtx m s => ", parentTypeCon, " (M'.MutMsg s) -> Word16 -> m ()" ]
, hcat
[ setName, "(", parentDataCon, " struct) tagValue = "
, fmtSetTag
]
]
Just (Record _) ->
-- Variant is a group; we return a reference to the group so the user can
-- modify it.
let childTypeCon = fmtName thisMod (subName variantName "group'")
childDataCon = childTypeCon <> "_newtype_"
in vcat
[ hcat
[ setName, " :: U'.RWCtx m s => ", parentTypeCon, " (M'.MutMsg s) -> "
, "m (", childTypeCon, " (M'.MutMsg s))"
]
, hcat [ setName, " (", parentDataCon, " struct) = do" ]
, indent $ vcat
[ fmtSetTag
, hcat [ "pure $ ", childDataCon, " struct" ]
]
]
Just (Unnamed _ (DataField loc typ)) -> vcat
[ hcat
[ setName, " :: U'.RWCtx m s => ", parentTypeCon, " (M'.MutMsg s) -> "
, fmtType thisMod "(M'.MutMsg s)" (WordType typ), " -> m ()"
]
, hcat [ setName, " (", parentDataCon, " struct) value = do" ]
, indent $ vcat
[ fmtSetTag
, let size = dataFieldSize typ
in fmtSetWordField "struct"
("(fromIntegral (C'.toWord value) :: Word" <> fromString (show size) <> ")")
loc
]
]
Just (Unnamed _ fieldLocType@(PtrField index typ)) -> vcat
[ hcat
[ setName, " :: U'.RWCtx m s => ", parentTypeCon, " (M'.MutMsg s) -> "
, fmtType thisMod "(M'.MutMsg s)" (PtrType typ), " -> m ()"
]
, hcat [ setName, "(", parentDataCon, " struct) value = do" ]
, indent $ vcat
[ fmtSetTag
, hcat [ "U'.setPtr (C'.toPtr value) ", fromString (show index), " struct" ]
]
-- Also generate a new_* function.
, fmtNew thisMod accessorName parentTypeCon fieldLocType
]
Just (Unnamed _ VoidField) -> vcat
[ hcat [ setName, " :: U'.RWCtx m s => ", parentTypeCon, " (M'.MutMsg s) -> m ()" ]
, hcat [ setName, " (", parentDataCon, " struct) = ", fmtSetTag ]
]
Just (Unnamed _ (HereField typ)) -> vcat
[ hcat
[ setName, " :: U'.RWCtx m s => ", parentTypeCon, " (M'.MutMsg s) -> "
, "m (", fmtType thisMod " (M'.MutMsg s)" (CompositeType typ), ")"
]
, hcat [ setName, "(", parentDataCon, " struct) value = do" ]
, indent $ vcat
[ fmtSetTag
, "fromStruct struct"
]
]
fmtDecl :: Module -> (Name, Decl) -> PP.Doc
fmtDecl thisMod (name, DeclDef d) = fmtDataDef thisMod name d
fmtDecl thisMod (name, DeclConst c) = fmtConst thisMod name c
-- | Format a constant declaration.
fmtConst :: Module -> Name -> Const -> PP.Doc
fmtConst thisMod name value =
let nameText = fmtName thisMod (valueName name)
in case value of
WordConst{wordType,wordValue} -> vcat
[ hcat
[ nameText, " :: "
, case wordType of
PrimWord ty -> fmtPrimWord ty
EnumType tyName -> fmtName thisMod tyName
]
, hcat [ nameText, " = C'.fromWord ", fromString (show wordValue) ]
]
VoidConst -> vcat
[ hcat [ nameText, " :: ()" ]
, hcat [ nameText, " = ()" ]
]
PtrConst{ptrType,ptrValue} ->
vcat
[ hcat [ nameText, " :: ", fmtType thisMod "M'.ConstMsg" (PtrType ptrType) ]
, hcat
[ nameText, " = H'.getPtrConst $ Data.ByteString.pack "
, makePtrByteList ptrValue
]
]
where
makePtrByteList ptr =
let assertRight (Left e) = error (show e)
assertRight (Right v) = v
msg = assertRight $ createPure defaultLimit $ do
msg <- newMessage
rootPtr <- cerialize msg $ Untyped.Struct
(fromList [])
(fromList [ptr])
setRoot rootPtr
pure msg
in
msgToLBS msg &
LBS.unpack &
show &
T.pack &
PP.textStrict
fmtDataDef :: Module -> Name -> DataDef -> PP.Doc
fmtDataDef thisMod dataName (DefStruct StructDef{fields, info}) = vcat
[ fmtNewtypeStruct thisMod dataName info
, vcat $ map (fmtFieldAccessor thisMod dataName dataName) fields
]
fmtDataDef thisMod dataName DefUnion{dataVariants,dataTagLoc,parentStruct=StructDef{info}} =
let unionName = subName dataName ""
unionNameText = fmtName thisMod unionName
unknownName = subName dataName "unknown'"
in vcat
[ fmtNewtypeStruct thisMod dataName info
, data_
(unionNameText <> " msg")
(map fmtDataVariant dataVariants ++
[fmtName thisMod unknownName <> " Word16"]
)
[]
, fmtFieldAccessor thisMod dataName dataName Field
{ fieldName = ""
, fieldLocType = HereField $ StructType unionName []
}
, vcat $ map (fmtUnionSetter thisMod dataName dataTagLoc . Just) dataVariants
, fmtUnionSetter thisMod dataName dataTagLoc Nothing
-- Generate auxiliary newtype definitions for group fields:
, vcat $ map fmtVariantAuxNewtype dataVariants
, instance_ [] ("C'.FromStruct msg (" <> unionNameText <> " msg)")
[ vcat
[ "fromStruct struct = do"
, indent $ vcat
[ hcat [ "tag <- ", fmtGetWordField "struct" dataTagLoc ]
, "case tag of"
, indent $ vcat
[ vcat $ map fmtVariantCase $ sortVariants dataVariants
, hcat [ "_ -> pure $ ", fmtName thisMod unknownName, " tag" ]
]
]
]
]
]
where
fmtDataVariant Variant{..} = fmtName thisMod variantName <>
case variantParams of
Record _ -> " (" <> fmtName thisMod (subName variantName "group' msg)")
Unnamed VoidType _ -> ""
Unnamed ty _ -> " " <> fmtType thisMod "msg" ty
fmtVariantCase Variant{..} =
let nameText = fmtName thisMod variantName
in hcat
[ fromString (show variantTag), " -> "
, case variantParams of
Record _ -> nameText <> " <$> C'.fromStruct struct"
Unnamed _ (HereField _) -> nameText <> " <$> C'.fromStruct struct"
Unnamed _ VoidField ->
"pure " <> nameText
Unnamed _ (DataField loc _) ->
nameText <> " <$> " <> fmtGetWordField "struct" loc
Unnamed _ (PtrField idx _) -> hcat
[ nameText," <$> "
, " (U'.getPtr ", fromString (show idx), " struct"
, " >>= C'.fromPtr (U'.message struct))"
]
]
fmtVariantAuxNewtype Variant{variantName, variantParams=Record fields} =
let typeName = subName variantName "group'"
in vcat
[ fmtNewtypeStruct thisMod typeName IR.IsGroup
, vcat $ map (fmtFieldAccessor thisMod typeName variantName) fields
]
fmtVariantAuxNewtype _ = ""
fmtDataDef thisMod dataName (DefEnum enumerants) =
let typeName = fmtName thisMod dataName
unknownName = subName dataName "unknown'"
in vcat
[ data_ typeName
(map (fmtName thisMod) enumerants ++
[fmtName thisMod unknownName <> " Word16"]
)
["Show", "Read", "Eq", "Generic"]
-- Generate an Enum instance. This is a trivial wrapper around the
-- IsWord instance, below.
, instance_ [] ("Enum " <> typeName)
[ "toEnum = C'.fromWord . fromIntegral"
, "fromEnum = fromIntegral . C'.toWord"
]
-- Generate an IsWord instance.
, instance_ [] ("C'.IsWord " <> typeName)
[ "fromWord n = go (fromIntegral n :: Word16) where"
, indent $ vcat $
zipWith fmtFromWordCase enumerants [0..]
++
[ hcat
[ "go tag = "
, fmtName thisMod unknownName
, " (fromIntegral tag)"
]
]
, vcat $
zipWith fmtToWordCase enumerants [0..]
++
[ hcat [ "toWord (", fmtName thisMod unknownName, " tag) = fromIntegral tag" ] ]
]
, instance_ [] ("B'.ListElem msg " <> typeName)
[ hcat [ "newtype List msg ", typeName, " = List_", typeName, " (U'.ListOf msg Word16)" ]
, hcat [ "length (List_", typeName, " l) = U'.length l" ]
, hcat [ "index i (List_", typeName, " l) = (C'.fromWord . fromIntegral) <$> U'.index i l" ]
]
, instance_ [] ("B'.MutListElem s " <> typeName)
[ hcat [ "setIndex elt i (List_", typeName, " l) = U'.setIndex (fromIntegral $ C'.toWord elt) i l" ]
, hcat [ "newList msg size = List_", typeName, " <$> U'.allocList16 msg size" ]
]
, instance_ [] ("C'.IsPtr msg (B'.List msg " <> typeName <> ")")
[ hcat [ "fromPtr msg ptr = List_", typeName, " <$> C'.fromPtr msg ptr" ]
, hcat [ "toPtr (List_", typeName, " l) = C'.toPtr l" ]
]
]
where
-- | Format an equation in an enum's IsWord.fromWord implementation.
fmtFromWordCase name ordinal =
hcat [ "go ", fromString (show ordinal), " = ", fmtName thisMod name ]
-- | Format an equation in an enum's IsWord.toWord implementation.
fmtToWordCase name ordinal =
hcat [ "toWord ", fmtName thisMod name, " = ", fromString (show ordinal) ]
-- | @'fmtType ident msg ty@ formats the type @ty@ from module @ident@,
-- using @msg@ as the message parameter, if any.
fmtType :: Module -> PP.Doc -> Type -> PP.Doc
fmtType thisMod msg = \case
WordType (EnumType name) ->
fmtName thisMod name
WordType (PrimWord ty) ->
fmtPrimWord ty
VoidType ->
"()"
PtrType (ListOf eltType) ->
"(B'.List " <> msg <> " " <> fmtType thisMod msg eltType <> ")"
PtrType (PrimPtr PrimText) ->
"(B'.Text " <> msg <> ")"
PtrType (PrimPtr PrimData) ->
"(B'.Data " <> msg <> ")"
PtrType (PrimPtr (PrimAnyPtr anyPtr)) ->
"(Maybe " <> fmtAnyPtr msg anyPtr <> ")"
PtrType (PtrComposite ty) ->
fmtType thisMod msg (CompositeType ty)
CompositeType (StructType name params) -> hcat
[ "("
, fmtName thisMod name
, " "
, mintercalate " " $ msg : map (fmtType thisMod msg) params
, ")"
]
fmtAnyPtr :: PP.Doc -> AnyPtr -> PP.Doc
fmtAnyPtr msg Struct = "(U'.Struct " <> msg <> ")"
fmtAnyPtr msg List = "(U'.List " <> msg <> ")"
fmtAnyPtr _ Cap = "Word32"
fmtAnyPtr msg Ptr = "(U'.Ptr " <> msg <> ")"
fmtName :: Module -> Name -> PP.Doc
fmtName Module{modId=thisMod} Name{nameModule, nameLocalNS=Namespace parts, nameUnqualified=localName} =
modPrefix <> mintercalate "'" (map PP.textStrict $ parts <> [localName])
where
modPrefix = case nameModule of
ByCapnpId id | id == thisMod -> ""
FullyQualified (Namespace []) -> ""
_ -> fmtModRef nameModule <> "."