packages feed

fficxx-0.7.0.0: src/FFICXX/Generate/Type/Class.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RecordWildCards #-}

module FFICXX.Generate.Type.Class where

import Data.List (intercalate)
import qualified Data.Map as M
import Data.Maybe (mapMaybe)
import FFICXX.Generate.Type.Cabal (Cabal)

-- | C types
data CTypes
  = CTBool
  | CTChar
  | CTClock
  | CTDouble
  | CTFile
  | CTFloat
  | CTFpos
  | CTInt
  | CTIntMax
  | CTIntPtr
  | CTJmpBuf
  | CTLLong
  | CTLong
  | CTPtrdiff
  | CTSChar
  | CTSUSeconds
  | CTShort
  | CTSigAtomic
  | CTSize
  | CTTime
  | CTUChar
  | CTUInt
  | CTUIntMax
  | CTUIntPtr
  | CTULLong
  | CTULong
  | CTUSeconds
  | CTUShort
  | CTWchar
  | CTInt8
  | CTInt16
  | CTInt32
  | CTInt64
  | CTUInt8
  | CTUInt16
  | CTUInt32
  | CTUInt64
  | CTVoidStar
  | CTString
  | CEnum CTypes String
  | CPointer CTypes
  | CRef CTypes
  deriving (Show)

-- | C++ types
data CPPTypes
  = CPTClass Class
  | CPTClassRef Class
  | CPTClassCopy Class
  | CPTClassMove Class
  deriving (Show)

-- | const flag
data IsConst = Const | NoConst
  deriving (Show)

-- | Argument type which can be used as an template argument like float
--   in vector<float>.
--   For now, this distinguishes Class and non-Class.
data TemplateArgType
  = TArg_Class Class
  | TArg_TypeParam String
  | TArg_Other String
  deriving (Show)

data TemplateAppInfo = TemplateAppInfo
  { tapp_tclass :: TemplateClass,
    tapp_tparams :: [TemplateArgType],
    tapp_CppTypeForParam :: String -- TODO: remove this
  }
  deriving (Show)

-- | Supported C++ types.
data Types
  = Void
  | SelfType
  | CT CTypes IsConst
  | CPT CPPTypes IsConst
  | -- | like vector<float>*
    TemplateApp TemplateAppInfo
  | -- | like vector<float>&
    TemplateAppRef TemplateAppInfo
  | -- | like unique_ptr<float> (using std::move)
    TemplateAppMove TemplateAppInfo
  | -- | template self? TODO: clarify this.
    TemplateType TemplateClass
  | TemplateParam String
  | -- | this is A* with template<A>
    TemplateParamPointer String
  deriving (Show)

-------------

-- | Function argument, type and variable name.
data Arg = Arg
  { arg_type :: Types,
    arg_name :: String
  }
  deriving (Show)

-- | Regular member functions in a ordinary class
data Function
  = Constructor
      { func_args :: [Arg],
        func_alias :: Maybe String
      }
  | Virtual
      { func_ret :: Types,
        func_name :: String,
        func_args :: [Arg],
        func_alias :: Maybe String
      }
  | NonVirtual
      { func_ret :: Types,
        func_name :: String,
        func_args :: [Arg],
        func_alias :: Maybe String
      }
  | Static
      { func_ret :: Types,
        func_name :: String,
        func_args :: [Arg],
        func_alias :: Maybe String
      }
  | Destructor
      { func_alias :: Maybe String
      }
  deriving (Show)

-- | Member variable. Isomorphic to Arg
newtype Variable = Variable {unVariable :: Arg}
  deriving (Show)

-- | Member functions of a template class.
data TemplateMemberFunction = TemplateMemberFunction
  { tmf_params :: [String],
    tmf_ret :: Types,
    tmf_name :: String,
    tmf_args :: [Arg],
    tmf_alias :: Maybe String
  }
  deriving (Show)

-- | Function defined at top level like ordinary C functions,
--   i.e. no owning class.
data TopLevel
  = TLOrdinary TLOrdinary
  | TLTemplate TLTemplate
  deriving (Show)

filterTLOrdinary :: [TopLevel] -> [TLOrdinary]
filterTLOrdinary = mapMaybe (\case TLOrdinary f -> Just f; _ -> Nothing)

filterTLTemplate :: [TopLevel] -> [TLTemplate]
filterTLTemplate = mapMaybe (\case TLTemplate f -> Just f; _ -> Nothing)

data TLOrdinary
  = TopLevelFunction
      { toplevelfunc_ret :: Types,
        toplevelfunc_name :: String,
        toplevelfunc_args :: [Arg],
        toplevelfunc_alias :: Maybe String
      }
  | TopLevelVariable
      { toplevelvar_ret :: Types,
        toplevelvar_name :: String,
        toplevelvar_alias :: Maybe String
      }
  deriving (Show)

data TLTemplate = TopLevelTemplateFunction
  { topleveltfunc_params :: [String],
    topleveltfunc_ret :: Types,
    topleveltfunc_name :: String,
    topleveltfunc_oname :: String,
    topleveltfunc_args :: [Arg]
  }
  deriving (Show)

isNewFunc :: Function -> Bool
isNewFunc (Constructor _ _) = True
isNewFunc _ = False

isDeleteFunc :: Function -> Bool
isDeleteFunc (Destructor _) = True
isDeleteFunc _ = False

isVirtualFunc :: Function -> Bool
isVirtualFunc (Destructor _) = True
isVirtualFunc (Virtual _ _ _ _) = True
isVirtualFunc _ = False

isNonVirtualFunc :: Function -> Bool
isNonVirtualFunc (NonVirtual _ _ _ _) = True
isNonVirtualFunc _ = False

isStaticFunc :: Function -> Bool
isStaticFunc (Static _ _ _ _) = True
isStaticFunc _ = False

virtualFuncs :: [Function] -> [Function]
virtualFuncs = filter isVirtualFunc

constructorFuncs :: [Function] -> [Function]
constructorFuncs = filter isNewFunc

nonVirtualNotNewFuncs :: [Function] -> [Function]
nonVirtualNotNewFuncs =
  filter (\x -> (not . isVirtualFunc) x && (not . isNewFunc) x && (not . isDeleteFunc) x && (not . isStaticFunc) x)

staticFuncs :: [Function] -> [Function]
staticFuncs = filter isStaticFunc

--------

newtype ProtectedMethod = Protected {unProtected :: [String]}
  deriving (Semigroup, Monoid)

data ClassAlias = ClassAlias
  { caHaskellName :: String,
    caFFIName :: String
  }

-- TODO: partial record must be avoided.
data Class
  = Class
      { class_cabal :: Cabal,
        class_name :: String,
        class_parents :: [Class],
        class_protected :: ProtectedMethod,
        class_alias :: Maybe ClassAlias,
        class_funcs :: [Function],
        class_vars :: [Variable],
        class_tmpl_funcs :: [TemplateMemberFunction],
        class_has_proxy :: Bool
      }
  | AbstractClass
      { class_cabal :: Cabal,
        class_name :: String,
        class_parents :: [Class],
        class_protected :: ProtectedMethod,
        class_alias :: Maybe ClassAlias,
        class_funcs :: [Function],
        class_vars :: [Variable],
        class_tmpl_funcs :: [TemplateMemberFunction]
      }

-- TODO: we had better not override standard definitions
instance Show Class where
  show x = show (class_name x)

-- TODO: we had better not override standard definitions
instance Eq Class where
  (==) x y = class_name x == class_name y

-- TODO: we had better not override standard definitions
instance Ord Class where
  compare x y = compare (class_name x) (class_name y)

data OpExp
  = -- | unary * (deRef) operator
    OpStar
  | -- | unary prefix ++ operator
    --    | OpAdd Arg Arg
    --    | OpMul Arg Arg
    OpFPPlus

data TemplateFunction
  = TFun
      { tfun_ret :: Types,
        tfun_name :: String,
        tfun_oname :: String,
        tfun_args :: [Arg]
      }
  | TFunNew
      { tfun_new_args :: [Arg],
        tfun_new_alias :: Maybe String
      }
  | TFunDelete
  | TFunOp
      { tfun_ret :: Types,
        -- | haskell alias for the operator
        tfun_name :: String,
        tfun_opexp :: OpExp
      }

argsFromOpExp :: OpExp -> [Arg]
argsFromOpExp OpStar = []
argsFromOpExp OpFPPlus = []

-- argsFromOpExp (OpAdd x y) = [x,y]
-- argsFromOpExp (OpMul x y) = [x,y]

opSymbol :: OpExp -> String
opSymbol OpStar = "*"
opSymbol OpFPPlus = "++"

-- opSymbol (OpAdd _ _) = "+"
-- opSymbol (OpMul _ _) = "*"

-- TODO: Generalize this further.

-- | Positional string interpolation form.
--   For example, "std::map<K,V>::iterator" is FormNested "std::map" "iterator"].
data Form
  = FormSimple String
  | FormNested String String

data TemplateClass = TmplCls
  { tclass_cabal :: Cabal,
    tclass_name :: String,
    tclass_cxxform :: Form,
    tclass_params :: [String],
    tclass_funcs :: [TemplateFunction],
    tclass_vars :: [Variable]
  }

-- TODO: we had better not override standard definitions
instance Show TemplateClass where
  show x = show (tclass_name x <> " " <> intercalate " " (tclass_params x))

-- TODO: we had better not override standard definitions
instance Eq TemplateClass where
  (==) x y = tclass_name x == tclass_name y

-- TODO: we had better not override standard definitions
instance Ord TemplateClass where
  compare x y = compare (tclass_name x) (tclass_name y)

data ClassGlobal = ClassGlobal
  { cgDaughterSelfMap :: DaughterMap,
    cgDaughterMap :: DaughterMap
  }

data Selfness = Self | NoSelf

-- | Check abstract class
isAbstractClass :: Class -> Bool
isAbstractClass Class {} = False
isAbstractClass AbstractClass {} = True

-- | Check having Proxy
hasProxy :: Class -> Bool
hasProxy c@Class {} = class_has_proxy c
hasProxy AbstractClass {} = False

type DaughterMap = M.Map String [Class]

data Accessor = Getter | Setter
  deriving (Show, Eq)