fficxx-0.7.0.0: src/FFICXX/Generate/Code/HsFrontEnd.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module FFICXX.Generate.Code.HsFrontEnd where
import Control.Monad.Reader (Reader)
import Data.Either (lefts, rights)
import qualified Data.List as L
import FFICXX.Generate.Code.Primitive
( CFunSig (..),
HsFunSig (..),
accessorSignature,
classConstraints,
convertCpp2HS,
extractArgRetTypes,
functionSignature,
hsFuncXformer,
)
import FFICXX.Generate.Dependency
( argumentDependency,
extractClassDepForTLOrdinary,
extractClassDepForTLTemplate,
returnDependency,
)
import FFICXX.Generate.Dependency.Graph
( getCyclicDepSubmodules,
locateInDepCycles,
)
import FFICXX.Generate.Name
( accessorName,
aliasedFuncName,
getClassModuleBase,
getTClassModuleBase,
hsClassName,
hsFrontNameForTopLevel,
hsFuncName,
hscAccessorName,
hscFuncName,
subModuleName,
typeclassName,
)
import FFICXX.Generate.Type.Annotate (AnnotateMap)
import FFICXX.Generate.Type.Class
( Accessor (..),
Class (..),
TLOrdinary (..),
TLTemplate,
TopLevel (TLOrdinary),
Types (..),
constructorFuncs,
isAbstractClass,
isNewFunc,
isVirtualFunc,
nonVirtualNotNewFuncs,
staticFuncs,
virtualFuncs,
)
import FFICXX.Generate.Type.Module
( ClassModule (..),
DepCycles,
TemplateClassModule (..),
)
import FFICXX.Generate.Util (toLowers)
import FFICXX.Generate.Util.HaskellSrcExts
( classA,
clsDecl,
con,
conDecl,
cxEmpty,
cxTuple,
eabs,
ethingall,
evar,
ihcon,
insDecl,
insType,
irule,
mkBind1,
mkClass,
mkData,
mkDeriving,
mkFun,
mkFunSig,
mkImport,
mkImportSrc,
mkInstance,
mkNewtype,
mkPVar,
mkPVarSig,
mkTBind,
mkTVar,
mkVar,
nonamespace,
pbind,
qualConDecl,
tyForall,
tyPtr,
tyapp,
tycon,
tyfun,
unkindedVar,
unqual,
)
import Language.Haskell.Exts.Build (app, letE, name, pApp)
import Language.Haskell.Exts.Syntax
( Context (CxTuple),
Decl (..),
ExportSpec (..),
ImportDecl (..),
)
import System.FilePath ((<.>))
genHsFrontDecl :: Bool -> Class -> Reader AnnotateMap (Decl ())
genHsFrontDecl isHsBoot c = do
-- TODO: revive annotation
-- for the time being, let's ignore annotation.
-- amap <- ask
-- let cann = maybe "" id $ M.lookup (PkgClass,class_name c) amap
let cdecl = mkClass (classConstraints c) (typeclassName c) [mkTBind "a"] body
-- for hs-boot, we only have instance head.
cdecl' = mkClass (CxTuple () []) (typeclassName c) [mkTBind "a"] []
sigdecl f = mkFunSig (hsFuncName c f) (functionSignature c f)
body = map (clsDecl . sigdecl) . virtualFuncs . class_funcs $ c
if isHsBoot
then return cdecl'
else return cdecl
-------------------
genHsFrontInst :: Class -> Class -> [Decl ()]
genHsFrontInst parent child
| (not . isAbstractClass) child =
let idecl = mkInstance cxEmpty (typeclassName parent) [convertCpp2HS (Just child) SelfType] body
defn f = mkBind1 (hsFuncName child f) [] rhs Nothing
where
rhs = app (mkVar (hsFuncXformer f)) (mkVar (hscFuncName child f))
body = map (insDecl . defn) . virtualFuncs . class_funcs $ parent
in [idecl]
| otherwise = []
---------------------
genHsFrontInstNew ::
-- | only concrete class
Class ->
Reader AnnotateMap [Decl ()]
genHsFrontInstNew c = do
-- amap <- ask
let fs = filter isNewFunc (class_funcs c)
return . flip concatMap fs $ \f ->
let -- for the time being, let's ignore annotation.
-- cann = maybe "" id $ M.lookup (PkgMethod, constructorName c) amap
-- newfuncann = mkComment 0 cann
rhs = app (mkVar (hsFuncXformer f)) (mkVar (hscFuncName c f))
in mkFun (aliasedFuncName c f) (functionSignature c f) [] rhs Nothing
genHsFrontInstNonVirtual :: Class -> [Decl ()]
genHsFrontInstNonVirtual c =
flip concatMap nonvirtualFuncs $ \f ->
let rhs = app (mkVar (hsFuncXformer f)) (mkVar (hscFuncName c f))
in mkFun (aliasedFuncName c f) (functionSignature c f) [] rhs Nothing
where
nonvirtualFuncs = nonVirtualNotNewFuncs (class_funcs c)
-----
genHsFrontInstStatic :: Class -> [Decl ()]
genHsFrontInstStatic c =
flip concatMap (staticFuncs (class_funcs c)) $ \f ->
let rhs = app (mkVar (hsFuncXformer f)) (mkVar (hscFuncName c f))
in mkFun (aliasedFuncName c f) (functionSignature c f) [] rhs Nothing
-----
genHsFrontInstVariables :: Class -> [Decl ()]
genHsFrontInstVariables c =
flip concatMap (class_vars c) $ \v ->
let rhs accessor =
app
(mkVar (case accessor of Getter -> "xform0"; _ -> "xform1"))
(mkVar (hscAccessorName c v accessor))
in mkFun (accessorName c v Getter) (accessorSignature c v Getter) [] (rhs Getter) Nothing
<> mkFun (accessorName c v Setter) (accessorSignature c v Setter) [] (rhs Setter) Nothing
--------------------------
hsClassRawType :: Class -> [Decl ()]
hsClassRawType c =
[ mkData rawname [] [] Nothing,
mkNewtype highname [] [qualConDecl Nothing Nothing (conDecl highname [tyapp tyPtr rawtype])] mderiv,
mkInstance
cxEmpty
"FPtr"
[hightype]
[ insType (tyapp (tycon "Raw") hightype) rawtype,
insDecl (mkBind1 "get_fptr" [pApp (name highname) [mkPVar "ptr"]] (mkVar "ptr") Nothing),
insDecl (mkBind1 "cast_fptr_to_obj" [] (con highname) Nothing)
]
]
where
(highname, rawname) = hsClassName c
hightype = tycon highname
rawtype = tycon rawname
mderiv = Just (mkDeriving [i_eq, i_ord, i_show])
where
i_eq = irule Nothing Nothing (ihcon (unqual "Eq"))
i_ord = irule Nothing Nothing (ihcon (unqual "Ord"))
i_show = irule Nothing Nothing (ihcon (unqual "Show"))
------------
-- upcast --
------------
genHsFrontUpcastClass :: Class -> [Decl ()]
genHsFrontUpcastClass c = mkFun ("upcast" <> highname) typ [mkPVar "h"] rhs Nothing
where
(highname, rawname) = hsClassName c
hightype = tycon highname
rawtype = tycon rawname
iname = typeclassName c
a_bind = unkindedVar (name "a")
a_tvar = mkTVar "a"
typ =
tyForall
(Just [a_bind])
(Just (cxTuple [classA (unqual "FPtr") [a_tvar], classA (unqual iname) [a_tvar]]))
(tyfun a_tvar hightype)
rhs =
letE
[ pbind (mkPVar "fh") (app (mkVar "get_fptr") (mkVar "h")) Nothing,
pbind
(mkPVarSig "fh2" (tyapp tyPtr rawtype))
(app (mkVar "castPtr") (mkVar "fh"))
Nothing
]
(mkVar "cast_fptr_to_obj" `app` mkVar "fh2")
--------------
-- downcast --
--------------
genHsFrontDowncastClass :: Class -> [Decl ()]
genHsFrontDowncastClass c = mkFun ("downcast" <> highname) typ [mkPVar "h"] rhs Nothing
where
(highname, _rawname) = hsClassName c
hightype = tycon highname
iname = typeclassName c
a_bind = unkindedVar (name "a")
a_tvar = mkTVar "a"
typ =
tyForall
(Just [a_bind])
(Just (cxTuple [classA (unqual "FPtr") [a_tvar], classA (unqual iname) [a_tvar]]))
(tyfun hightype a_tvar)
rhs =
letE
[ pbind (mkPVar "fh") (app (mkVar "get_fptr") (mkVar "h")) Nothing,
pbind (mkPVar "fh2") (app (mkVar "castPtr") (mkVar "fh")) Nothing
]
(mkVar "cast_fptr_to_obj" `app` mkVar "fh2")
------------------------
-- Top Level Function --
------------------------
genTopLevelDef :: TLOrdinary -> [Decl ()]
genTopLevelDef f@TopLevelFunction {..} =
let fname = hsFrontNameForTopLevel (TLOrdinary f)
HsFunSig typs assts =
extractArgRetTypes
Nothing
False
(CFunSig toplevelfunc_args toplevelfunc_ret)
sig = tyForall Nothing (Just (cxTuple assts)) (foldr1 tyfun typs)
xformerstr = let len = length toplevelfunc_args in if len > 0 then "xform" <> show (len - 1) else "xformnull"
cfname = "c_" <> toLowers fname
rhs = app (mkVar xformerstr) (mkVar cfname)
in mkFun fname sig [] rhs Nothing
genTopLevelDef v@TopLevelVariable {..} =
let fname = hsFrontNameForTopLevel (TLOrdinary v)
cfname = "c_" <> toLowers fname
rtyp = convertCpp2HS Nothing toplevelvar_ret
sig = tyapp (tycon "IO") rtyp
rhs = app (mkVar "xformnull") (mkVar cfname)
in mkFun fname sig [] rhs Nothing
------------
-- Export --
------------
genExport :: Class -> [ExportSpec ()]
genExport c =
let espec n =
if null . (filter isVirtualFunc) $ (class_funcs c)
then eabs nonamespace (unqual n)
else ethingall (unqual n)
in if isAbstractClass c
then [espec (typeclassName c)]
else
[ ethingall (unqual ((fst . hsClassName) c)),
espec (typeclassName c),
evar (unqual ("upcast" <> (fst . hsClassName) c)),
evar (unqual ("downcast" <> (fst . hsClassName) c))
]
<> genExportConstructorAndNonvirtual c
<> genExportStatic c
-- | constructor and non-virtual function
genExportConstructorAndNonvirtual :: Class -> [ExportSpec ()]
genExportConstructorAndNonvirtual c = map (evar . unqual) fns
where
fs = class_funcs c
fns =
map
(aliasedFuncName c)
( constructorFuncs fs
<> nonVirtualNotNewFuncs fs
)
-- | staic function export list
genExportStatic :: Class -> [ExportSpec ()]
genExportStatic c = map (evar . unqual) fns
where
fs = class_funcs c
fns = map (aliasedFuncName c) (staticFuncs fs)
------------
-- Import --
------------
genExtraImport :: ClassModule -> [ImportDecl ()]
genExtraImport cm = map mkImport (cmExtraImport cm)
genImportInModule :: Class -> [ImportDecl ()]
genImportInModule x = map (\y -> mkImport (getClassModuleBase x <.> y)) ["RawType", "Interface", "Implementation"]
mkImportWithDepCycles :: DepCycles -> String -> String -> ImportDecl ()
mkImportWithDepCycles depCycles self imported =
let mloc = locateInDepCycles (self, imported) depCycles
in case mloc of
Just (idxSelf, idxImported)
| idxImported > idxSelf ->
mkImportSrc imported
_ -> mkImport imported
genImportInInterface :: Bool -> DepCycles -> ClassModule -> [ImportDecl ()]
genImportInInterface isHsBoot depCycles m =
let modSelf = cmModule m <.> "Interface"
imported = cmImportedSubmodulesForInterface m
(rdepsU, rdepsD) = getCyclicDepSubmodules modSelf depCycles
in if isHsBoot
then -- for hs-boot file, we ignore all module imports in the cycle.
-- TODO: This is likely to be broken in more general cases.
-- Keep improving this as hs-boot allows.
let imported' = fmap subModuleName imported L.\\ (rdepsU <> rdepsD)
in fmap mkImport imported'
else fmap (mkImportWithDepCycles depCycles modSelf . subModuleName) imported
-- |
genImportInCast :: ClassModule -> [ImportDecl ()]
genImportInCast m =
fmap (mkImport . subModuleName) $ cmImportedSubmodulesForCast m
genImportInImplementation :: ClassModule -> [ImportDecl ()]
genImportInImplementation m =
fmap (mkImport . subModuleName) $ cmImportedSubmodulesForImplementation m
-- | generate import list for a given top-level ordinary function
-- currently this may generate duplicate import list.
-- TODO: eliminate duplicated imports.
-- TODO2: should be refactored out.
genImportForTLOrdinary :: TLOrdinary -> [ImportDecl ()]
genImportForTLOrdinary f =
let dep4func = extractClassDepForTLOrdinary f
ecs = returnDependency dep4func ++ argumentDependency dep4func
cmods = L.nub $ map getClassModuleBase $ rights ecs
tmods = L.nub $ map getTClassModuleBase $ lefts ecs
in concatMap (\x -> map (\y -> mkImport (x <.> y)) ["RawType", "Cast", "Interface"]) cmods
<> concatMap (\x -> map (\y -> mkImport (x <.> y)) ["Template"]) tmods
-- | generate import list for a given top-level template function
-- currently this may generate duplicate import list.
-- TODO: eliminate duplicated imports.
-- TODO2: should be refactored out.
genImportForTLTemplate :: TLTemplate -> [ImportDecl ()]
genImportForTLTemplate f =
let dep4func = extractClassDepForTLTemplate f
ecs = returnDependency dep4func ++ argumentDependency dep4func
cmods = L.nub $ map getClassModuleBase $ rights ecs
tmods = L.nub $ map getTClassModuleBase $ lefts ecs
in concatMap (\x -> map (\y -> mkImport (x <.> y)) ["RawType", "Cast", "Interface"]) cmods
<> concatMap (\x -> map (\y -> mkImport (x <.> y)) ["Template"]) tmods
-- | generate import list for top level module
genImportInTopLevel ::
String ->
([ClassModule], [TemplateClassModule]) ->
[ImportDecl ()]
genImportInTopLevel modname (mods, _tmods) =
map (mkImport . cmModule) mods
++ map mkImport [modname <.> "Template", modname <.> "TH", modname <.> "Ordinary"]