capnp-0.18.0.0: cmd/capnpc-haskell/Trans/Stage1ToFlat.hs
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
-- Translate from the 'Stage1' IR to the 'Flat' IR.
--
-- As the name of the latter suggests, this involves flattening the namepace.
module Trans.Stage1ToFlat (cgrToCgr) where
import qualified Data.Map as M
import Data.Word
import qualified IR.Common as C
import qualified IR.Flat as Flat
import qualified IR.Name as Name
import qualified IR.Stage1 as Stage1
type NodeMap = M.Map Word64 Flat.Node
cgrToCgr :: Stage1.CodeGenReq -> Flat.CodeGenReq
cgrToCgr Stage1.CodeGenReq {allFiles, reqFiles = inFiles} =
Flat.CodeGenReq
{ reqFiles = outFiles,
allNodes
}
where
outFiles = map (reqFileToFile fileMap) inFiles
fileMap =
M.fromList
[ (fileId, fileToNodes nodeMap file)
| file@Stage1.File {fileId} <- allFiles
]
allNodes = concatMap snd (M.toList fileMap)
nodeMap = M.fromList [(nodeId, node) | node@Flat.Node {nodeId} <- allNodes]
fileToNodes :: NodeMap -> Stage1.File -> [Flat.Node]
fileToNodes nodeMap Stage1.File {fileNodes, fileId} =
concatMap
( \(unQ, node) ->
nestedToNodes nodeMap fileId (Name.unQToLocal unQ) node []
)
fileNodes
reqFileToFile :: M.Map Word64 [Flat.Node] -> Stage1.ReqFile -> Flat.File
reqFileToFile fileMap Stage1.ReqFile {fileName, file = Stage1.File {fileId}} =
Flat.File
{ nodes = fileMap M.! fileId,
fileName,
fileId
}
paramsToParams :: NodeMap -> Word64 -> [Name.UnQ] -> [C.TypeParamRef Flat.Node]
paramsToParams nodeMap nodeId names =
[ C.TypeParamRef
{ paramName = name,
paramIndex = i,
paramScope = nodeMap M.! nodeId
}
| (i, name) <- zip [0 ..] names
]
applyBrandNode :: C.MapBrand Flat.Node -> Flat.Node -> C.ListBrand Flat.Node
applyBrandNode m Flat.Node {typeParams} = applyBrandParams typeParams m
applyBrandParams :: [C.TypeParamRef Flat.Node] -> C.MapBrand Flat.Node -> C.ListBrand Flat.Node
applyBrandParams params m = C.ListBrand $ map (`applyBrandParam` m) params
applyBrandParam ::
C.TypeParamRef Flat.Node ->
C.MapBrand Flat.Node ->
C.PtrType (C.ListBrand Flat.Node) Flat.Node
applyBrandParam param@C.TypeParamRef {paramIndex, paramScope = Flat.Node {nodeId}} (C.MapBrand m) =
case M.lookup nodeId m of
Nothing -> C.PtrParam param
Just (C.Bind bindings) ->
let binding = bindings !! paramIndex
in case binding of
C.Unbound -> C.PtrParam param
C.BoundType ty -> applyBrandPtrType ty
type ApplyBrandFn f =
f (C.MapBrand Flat.Node) Flat.Node ->
f (C.ListBrand Flat.Node) Flat.Node
applyBrandCompositeType :: ApplyBrandFn C.CompositeType
applyBrandCompositeType (C.StructType n b) = C.StructType n (applyBrandNode b n)
applyBrandInterfaceType :: ApplyBrandFn C.InterfaceType
applyBrandInterfaceType (C.InterfaceType n b) = C.InterfaceType n (applyBrandNode b n)
applyBrandValue :: ApplyBrandFn C.Value
applyBrandValue = \case
C.VoidValue -> C.VoidValue
C.WordValue v t -> C.WordValue v t
C.PtrValue t p -> C.PtrValue (applyBrandPtrType t) p
applyBrandPtrType :: ApplyBrandFn C.PtrType
applyBrandPtrType = \case
C.ListOf t -> C.ListOf $ applyBrandType t
C.PrimPtr p -> C.PrimPtr p
C.PtrInterface t -> C.PtrInterface (applyBrandInterfaceType t)
C.PtrComposite t -> C.PtrComposite (applyBrandCompositeType t)
C.PtrParam p -> C.PtrParam p
applyBrandType :: ApplyBrandFn C.Type
applyBrandType = \case
C.CompositeType t -> C.CompositeType $ applyBrandCompositeType t
C.VoidType -> C.VoidType
C.WordType t -> C.WordType t
C.PtrType t -> C.PtrType $ applyBrandPtrType t
applyBrandFieldLocType :: ApplyBrandFn C.FieldLocType
applyBrandFieldLocType = \case
C.DataField l t -> C.DataField l t
C.PtrField i t -> C.PtrField i $ applyBrandPtrType t
C.HereField t -> C.HereField $ applyBrandCompositeType t
C.VoidField -> C.VoidField
-- | Generate @'Flat.Node'@s from a 'Stage1.Node' and its local name.
nestedToNodes :: NodeMap -> Word64 -> Name.LocalQ -> Stage1.Node -> [C.TypeParamRef Flat.Node] -> [Flat.Node]
nestedToNodes
nodeMap
thisMod
localName
Stage1.Node
{ nodeCommon = Stage1.NodeCommon {nodeId, nodeNested, nodeParams},
nodeUnion
}
typeParams =
mine ++ kids
where
myParams = typeParams ++ paramsToParams nodeMap nodeId nodeParams
kidsNS = Name.localQToNS localName
kids =
concatMap
( \(unQ, node) ->
nestedToNodes nodeMap thisMod (Name.mkLocal kidsNS unQ) node myParams
)
nodeNested
name =
Name.CapnpQ
{ local = localName,
fileId = thisMod
}
mine = case nodeUnion of
Stage1.NodeEnum enumerants ->
[ Flat.Node
{ name,
nodeId,
typeParams = myParams,
union_ = Flat.Enum enumerants
}
]
Stage1.NodeStruct struct ->
structToNodes nodeMap thisMod nodeId name kidsNS struct myParams
Stage1.NodeInterface iface ->
interfaceToNodes nodeMap thisMod nodeId name kidsNS iface myParams
Stage1.NodeConstant value ->
[ Flat.Node
{ name =
Name.CapnpQ
{ local = Name.unQToLocal $ Name.valueName localName,
fileId = thisMod
},
nodeId,
union_ =
Flat.Constant
{ value =
applyBrandValue $
C.bothMap
(\Stage1.Node {nodeCommon = Stage1.NodeCommon {nodeId}} -> nodeMap M.! nodeId)
value
},
typeParams = myParams
}
]
Stage1.NodeOther ->
[ Flat.Node
{ name,
nodeId,
union_ = Flat.Other,
typeParams = myParams
}
]
interfaceToNodes :: NodeMap -> Word64 -> Word64 -> Name.CapnpQ -> Name.NS -> Stage1.Interface -> [C.TypeParamRef Flat.Node] -> [Flat.Node]
interfaceToNodes nodeMap thisMod nodeId name kidsNS Stage1.Interface {methods, supers} typeParams =
let translateSuper =
applyBrandInterfaceType
. C.bothMap (\Stage1.Node {nodeCommon = Stage1.NodeCommon {nodeId}} -> nodeMap M.! nodeId)
prType =
applyBrandCompositeType
. C.bothMap
( \Stage1.Node {nodeCommon = Stage1.NodeCommon {nodeId}} ->
nodeMap M.! nodeId
)
in Flat.Node
{ name,
nodeId,
union_ =
Flat.Interface
{ methods =
[ Flat.Method
{ name,
paramType = prType paramType,
resultType = prType resultType
}
| Stage1.Method {name, paramType, resultType} <- methods
],
supers = map translateSuper supers
},
typeParams
}
: concatMap (\method -> methodToNodes nodeMap thisMod kidsNS method typeParams) methods
structToNodes :: NodeMap -> Word64 -> Word64 -> Name.CapnpQ -> Name.NS -> Stage1.Struct -> [C.TypeParamRef Flat.Node] -> [Flat.Node]
structToNodes
nodeMap
thisMod
nodeId
name
kidsNS
Stage1.Struct
{ fields,
isGroup,
dataWordCount,
pointerCount,
tagOffset
}
typeParams =
let mkField fieldUnQ locType =
Flat.Field
{ fieldName = Name.mkSub name fieldUnQ,
fieldLocType =
applyBrandFieldLocType $
C.bothMap
(\Stage1.Node {nodeCommon = Stage1.NodeCommon {nodeId}} -> nodeMap M.! nodeId)
locType
}
variants =
[ Flat.Variant
{ field = mkField fieldUnQ locType,
tagValue
}
| Stage1.Field {name = fieldUnQ, locType, tag = Just tagValue} <- fields
]
commonFields =
[ mkField fieldUnQ locType
| Stage1.Field {name = fieldUnQ, locType, tag = Nothing} <- fields
]
fieldNodes =
concatMap (fieldToNodes nodeMap thisMod kidsNS typeParams) fields
commonNode =
Flat.Node
{ name,
nodeId,
union_ =
Flat.Struct
{ fields = commonFields,
union =
if null variants
then Nothing
else
Just
Flat.Union
{ variants,
tagOffset
},
isGroup,
dataWordCount,
pointerCount
},
typeParams
}
in commonNode : fieldNodes
fieldToNodes :: NodeMap -> Word64 -> Name.NS -> [C.TypeParamRef Flat.Node] -> Stage1.Field -> [Flat.Node]
fieldToNodes nodeMap thisMod ns typeParams Stage1.Field {name, locType} = case locType of
C.HereField
( C.StructType
struct@Stage1.Node
{ nodeUnion = Stage1.NodeStruct Stage1.Struct {isGroup = True}
}
_ -- Type parameters will be the same as the enclosing scope.
) -> nestedToNodes nodeMap thisMod (Name.mkLocal ns name) struct typeParams
_ ->
[]
methodToNodes :: NodeMap -> Word64 -> Name.NS -> Stage1.Method -> [C.TypeParamRef Flat.Node] -> [Flat.Node]
methodToNodes nodeMap thisMod ns Stage1.Method {name, paramType, resultType} typeParams =
-- If the parameter and result types are anonymous, we need to generate
-- structs for them.
let maybeAnon ty suffix =
case ty of
C.StructType node@Stage1.Node {nodeCommon = Stage1.NodeCommon {nodeParent = Nothing}} _ ->
let localName = Name.mkLocal ns name
kidsNS = Name.localQToNS localName
in nestedToNodes nodeMap thisMod (Name.mkLocal kidsNS suffix) node typeParams
_ ->
[]
in maybeAnon paramType "params" ++ maybeAnon resultType "results"