packages feed

fficxx-0.7.0.0: src/FFICXX/Generate/Code/HsFFI.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}

module FFICXX.Generate.Code.HsFFI where

import Data.Maybe (fromMaybe, mapMaybe)
import FFICXX.Generate.Code.Primitive
  ( CFunSig (..),
    accessorCFunSig,
    genericFuncArgs,
    genericFuncRet,
    hsFFIFuncTyp,
  )
import FFICXX.Generate.Dependency
  ( class_allparents,
  )
import FFICXX.Generate.Name
  ( aliasedFuncName,
    ffiClassName,
    hscAccessorName,
    hscFuncName,
    subModuleName,
  )
import FFICXX.Generate.Type.Class
  ( Accessor (Getter, Setter),
    Arg (..),
    Class (..),
    Function (..),
    Selfness (NoSelf, Self),
    TLOrdinary (..),
    Variable (unVariable),
    isAbstractClass,
    isNewFunc,
    isStaticFunc,
    virtualFuncs,
  )
import FFICXX.Generate.Type.Module
  ( ClassImportHeader (..),
    ClassModule (..),
    TopLevelImportHeader (..),
  )
import FFICXX.Generate.Util (toLowers)
import FFICXX.Generate.Util.HaskellSrcExts (mkForImpCcall, mkImport)
import FFICXX.Runtime.CodeGen.Cxx (HeaderName (..))
import Language.Haskell.Exts.Syntax (Decl (..), ImportDecl (..))
import System.FilePath ((<.>))

genHsFFI :: ClassImportHeader -> [Decl ()]
genHsFFI header =
  let c = cihClass header
      -- TODO: This C header information should not be necessary according to up-to-date
      --       version of Haskell FFI.
      h = cihSelfHeader header
      -- NOTE: We need to generate FFI both for member functions at the current class level
      --       and parent level. For example, consider a class A with method foo, which a
      --       subclass of B with method bar. Then, A::foo (c_a_foo) and A::bar (c_a_bar)
      --       are made into a FFI function.
      allfns =
        concatMap
          (virtualFuncs . class_funcs)
          (class_allparents c)
          <> (class_funcs c)
   in mapMaybe (hsFFIClassFunc h c) allfns
        <> concatMap
          (\v -> [hsFFIAccessor c v Getter, hsFFIAccessor c v Setter])
          (class_vars c)

hsFFIClassFunc :: HeaderName -> Class -> Function -> Maybe (Decl ())
hsFFIClassFunc headerfilename c f =
  if isAbstractClass c
    then Nothing
    else
      let hfile = unHdrName headerfilename
          -- TODO: Make this a separate function
          cname = ffiClassName c <> "_" <> aliasedFuncName c f
          csig = CFunSig (genericFuncArgs f) (genericFuncRet f)
          typ =
            if (isNewFunc f || isStaticFunc f)
              then hsFFIFuncTyp (Just (NoSelf, c)) csig
              else hsFFIFuncTyp (Just (Self, c)) csig
       in Just (mkForImpCcall (hfile <> " " <> cname) (hscFuncName c f) typ)

hsFFIAccessor :: Class -> Variable -> Accessor -> Decl ()
hsFFIAccessor c v a =
  let -- TODO: make this a separate function
      cname = ffiClassName c <> "_" <> arg_name (unVariable v) <> "_" <> (case a of Getter -> "get"; Setter -> "set")
      typ = hsFFIFuncTyp (Just (Self, c)) (accessorCFunSig (arg_type (unVariable v)) a)
   in mkForImpCcall cname (hscAccessorName c v a) typ

-- import for FFI
genImportInFFI :: ClassModule -> [ImportDecl ()]
genImportInFFI = fmap (mkImport . subModuleName) . cmImportedSubmodulesForFFI

----------------------------
-- for top level function --
----------------------------

genTopLevelFFI :: TopLevelImportHeader -> TLOrdinary -> Decl ()
genTopLevelFFI header tfn = mkForImpCcall (hfilename <> " TopLevel_" <> fname) cfname typ
  where
    (fname, args, ret) =
      case tfn of
        TopLevelFunction {..} -> (fromMaybe toplevelfunc_name toplevelfunc_alias, toplevelfunc_args, toplevelfunc_ret)
        TopLevelVariable {..} -> (fromMaybe toplevelvar_name toplevelvar_alias, [], toplevelvar_ret)
    hfilename = tihHeaderFileName header <.> "h"
    -- TODO: This must be exposed as a top-level function
    cfname = "c_" <> toLowers fname
    typ = hsFFIFuncTyp Nothing (CFunSig args ret)