hoppy-generator 0.2.1 → 0.3.0
raw patch · 11 files changed
+4976/−3400 lines, 11 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Foreign.Hoppy.Generator.Language.Haskell: importHsModuleForExtName :: ExtName -> Generator ()
- Foreign.Hoppy.Generator.Language.Haskell: toHsClassDeleteFnName :: Class -> String
- Foreign.Hoppy.Generator.Language.Haskell: toHsClassDeleteFnPtrName :: Class -> String
- Foreign.Hoppy.Generator.Spec: ClassConversionManual :: a -> ClassConversionMode a
- Foreign.Hoppy.Generator.Spec: ClassConversionNone :: ClassConversionMode a
- Foreign.Hoppy.Generator.Spec: ClassConversionToGc :: ClassConversionMode a
- Foreign.Hoppy.Generator.Spec: ClassConversionToHeap :: ClassConversionMode a
- Foreign.Hoppy.Generator.Spec: callbackToTFn :: Callback -> Type
- Foreign.Hoppy.Generator.Spec: class HasClassyExtName a where getClassyExtName cls x = toExtName $ concat [fromExtName $ classExtName cls, "_", fromExtName $ getClassyExtNameSuffix x]
- Foreign.Hoppy.Generator.Spec: classAddCtors :: [Ctor] -> Class -> Class
- Foreign.Hoppy.Generator.Spec: classAddMethods :: [Method] -> Class -> Class
- Foreign.Hoppy.Generator.Spec: data ClassConversionMode a
- Foreign.Hoppy.Generator.Spec: exportExtName :: Export -> ExtName
- Foreign.Hoppy.Generator.Spec: getClassyExtName :: HasClassyExtName a => Class -> a -> ExtName
- Foreign.Hoppy.Generator.Spec: getClassyExtNameSuffix :: HasClassyExtName a => a -> ExtName
- Foreign.Hoppy.Generator.Spec: instance Foreign.Hoppy.Generator.Spec.HasAddendum Foreign.Hoppy.Generator.Spec.Bitspace
- Foreign.Hoppy.Generator.Spec: instance Foreign.Hoppy.Generator.Spec.HasAddendum Foreign.Hoppy.Generator.Spec.Callback
- Foreign.Hoppy.Generator.Spec: instance Foreign.Hoppy.Generator.Spec.HasAddendum Foreign.Hoppy.Generator.Spec.Class
- Foreign.Hoppy.Generator.Spec: instance Foreign.Hoppy.Generator.Spec.HasAddendum Foreign.Hoppy.Generator.Spec.CppEnum
- Foreign.Hoppy.Generator.Spec: instance Foreign.Hoppy.Generator.Spec.HasAddendum Foreign.Hoppy.Generator.Spec.Function
- Foreign.Hoppy.Generator.Spec: instance Foreign.Hoppy.Generator.Spec.HasAddendum Foreign.Hoppy.Generator.Spec.Variable
- Foreign.Hoppy.Generator.Spec: instance Foreign.Hoppy.Generator.Spec.HasClassyExtName Foreign.Hoppy.Generator.Spec.Ctor
- Foreign.Hoppy.Generator.Spec: instance Foreign.Hoppy.Generator.Spec.HasClassyExtName Foreign.Hoppy.Generator.Spec.Method
- Foreign.Hoppy.Generator.Spec: instance Foreign.Hoppy.Generator.Spec.HasReqs Foreign.Hoppy.Generator.Spec.Bitspace
- Foreign.Hoppy.Generator.Spec: instance Foreign.Hoppy.Generator.Spec.HasReqs Foreign.Hoppy.Generator.Spec.Callback
- Foreign.Hoppy.Generator.Spec: instance Foreign.Hoppy.Generator.Spec.HasReqs Foreign.Hoppy.Generator.Spec.Class
- Foreign.Hoppy.Generator.Spec: instance Foreign.Hoppy.Generator.Spec.HasReqs Foreign.Hoppy.Generator.Spec.CppEnum
- Foreign.Hoppy.Generator.Spec: instance Foreign.Hoppy.Generator.Spec.HasReqs Foreign.Hoppy.Generator.Spec.Function
- Foreign.Hoppy.Generator.Spec: instance Foreign.Hoppy.Generator.Spec.HasReqs Foreign.Hoppy.Generator.Spec.Module
- Foreign.Hoppy.Generator.Spec: instance Foreign.Hoppy.Generator.Spec.HasReqs Foreign.Hoppy.Generator.Spec.Variable
- Foreign.Hoppy.Generator.Spec: instance Foreign.Hoppy.Generator.Spec.IsFnName t (Foreign.Hoppy.Generator.Spec.FnName t)
- Foreign.Hoppy.Generator.Spec: instance Foreign.Hoppy.Generator.Spec.IsFnName t Foreign.Hoppy.Generator.Spec.Operator
- Foreign.Hoppy.Generator.Spec: instance Foreign.Hoppy.Generator.Spec.IsFnName t t
- Foreign.Hoppy.Generator.Spec: instance GHC.Base.Monoid Foreign.Hoppy.Generator.Spec.Addendum
- Foreign.Hoppy.Generator.Spec: instance GHC.Base.Monoid Foreign.Hoppy.Generator.Spec.HsImportSet
- Foreign.Hoppy.Generator.Spec: instance GHC.Base.Monoid Foreign.Hoppy.Generator.Spec.Reqs
- Foreign.Hoppy.Generator.Spec: instance GHC.Classes.Eq Foreign.Hoppy.Generator.Spec.Bitspace
- Foreign.Hoppy.Generator.Spec: instance GHC.Classes.Eq Foreign.Hoppy.Generator.Spec.Callback
- Foreign.Hoppy.Generator.Spec: instance GHC.Classes.Eq Foreign.Hoppy.Generator.Spec.Class
- Foreign.Hoppy.Generator.Spec: instance GHC.Classes.Eq Foreign.Hoppy.Generator.Spec.Constness
- Foreign.Hoppy.Generator.Spec: instance GHC.Classes.Eq Foreign.Hoppy.Generator.Spec.CppEnum
- Foreign.Hoppy.Generator.Spec: instance GHC.Classes.Eq Foreign.Hoppy.Generator.Spec.ExtName
- Foreign.Hoppy.Generator.Spec: instance GHC.Classes.Eq Foreign.Hoppy.Generator.Spec.HsImportKey
- Foreign.Hoppy.Generator.Spec: instance GHC.Classes.Eq Foreign.Hoppy.Generator.Spec.IdPart
- Foreign.Hoppy.Generator.Spec: instance GHC.Classes.Eq Foreign.Hoppy.Generator.Spec.Identifier
- Foreign.Hoppy.Generator.Spec: instance GHC.Classes.Eq Foreign.Hoppy.Generator.Spec.Include
- Foreign.Hoppy.Generator.Spec: instance GHC.Classes.Eq Foreign.Hoppy.Generator.Spec.MethodApplicability
- Foreign.Hoppy.Generator.Spec: instance GHC.Classes.Eq Foreign.Hoppy.Generator.Spec.MethodImpl
- Foreign.Hoppy.Generator.Spec: instance GHC.Classes.Eq Foreign.Hoppy.Generator.Spec.Module
- Foreign.Hoppy.Generator.Spec: instance GHC.Classes.Eq Foreign.Hoppy.Generator.Spec.Operator
- Foreign.Hoppy.Generator.Spec: instance GHC.Classes.Eq Foreign.Hoppy.Generator.Spec.Purity
- Foreign.Hoppy.Generator.Spec: instance GHC.Classes.Eq Foreign.Hoppy.Generator.Spec.Staticness
- Foreign.Hoppy.Generator.Spec: instance GHC.Classes.Eq Foreign.Hoppy.Generator.Spec.Type
- Foreign.Hoppy.Generator.Spec: instance GHC.Classes.Eq Foreign.Hoppy.Generator.Spec.Variable
- Foreign.Hoppy.Generator.Spec: instance GHC.Classes.Eq name => GHC.Classes.Eq (Foreign.Hoppy.Generator.Spec.FnName name)
- Foreign.Hoppy.Generator.Spec: instance GHC.Classes.Ord Foreign.Hoppy.Generator.Spec.ExtName
- Foreign.Hoppy.Generator.Spec: instance GHC.Classes.Ord Foreign.Hoppy.Generator.Spec.HsImportKey
- Foreign.Hoppy.Generator.Spec: instance GHC.Classes.Ord Foreign.Hoppy.Generator.Spec.Include
- Foreign.Hoppy.Generator.Spec: instance GHC.Classes.Ord Foreign.Hoppy.Generator.Spec.Module
- Foreign.Hoppy.Generator.Spec: instance GHC.Classes.Ord Foreign.Hoppy.Generator.Spec.Operator
- Foreign.Hoppy.Generator.Spec: instance GHC.Classes.Ord name => GHC.Classes.Ord (Foreign.Hoppy.Generator.Spec.FnName name)
- Foreign.Hoppy.Generator.Spec: instance GHC.Enum.Bounded Foreign.Hoppy.Generator.Spec.Constness
- Foreign.Hoppy.Generator.Spec: instance GHC.Enum.Bounded Foreign.Hoppy.Generator.Spec.MethodApplicability
- Foreign.Hoppy.Generator.Spec: instance GHC.Enum.Bounded Foreign.Hoppy.Generator.Spec.Operator
- Foreign.Hoppy.Generator.Spec: instance GHC.Enum.Bounded Foreign.Hoppy.Generator.Spec.Staticness
- Foreign.Hoppy.Generator.Spec: instance GHC.Enum.Enum Foreign.Hoppy.Generator.Spec.Constness
- Foreign.Hoppy.Generator.Spec: instance GHC.Enum.Enum Foreign.Hoppy.Generator.Spec.MethodApplicability
- Foreign.Hoppy.Generator.Spec: instance GHC.Enum.Enum Foreign.Hoppy.Generator.Spec.Operator
- Foreign.Hoppy.Generator.Spec: instance GHC.Enum.Enum Foreign.Hoppy.Generator.Spec.Staticness
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.Bitspace
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.Callback
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.Class
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.Constness
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.CppEnum
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.Ctor
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.Export
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.ExtName
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.Function
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.HsImportKey
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.HsImportSet
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.HsImportSpecs
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.HsImportVal
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.IdPart
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.Identifier
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.Include
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.Interface
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.Method
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.MethodApplicability
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.MethodImpl
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.Module
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.Operator
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.Purity
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.Reqs
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.Staticness
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.Type
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show Foreign.Hoppy.Generator.Spec.Variable
- Foreign.Hoppy.Generator.Spec: instance GHC.Show.Show name => GHC.Show.Show (Foreign.Hoppy.Generator.Spec.FnName name)
- Foreign.Hoppy.Generator.Spec: mkProps :: [[Method]] -> [Method]
- Foreign.Hoppy.Generator.Spec: stringOrIdentifier :: Identifier -> Maybe String -> String
+ Foreign.Hoppy.Generator.Language.Cpp: exceptionIdArgName :: String
+ Foreign.Hoppy.Generator.Language.Cpp: exceptionPtrArgName :: String
+ Foreign.Hoppy.Generator.Language.Cpp: exceptionRethrowFnName :: String
+ Foreign.Hoppy.Generator.Language.Cpp: exceptionVarName :: String
+ Foreign.Hoppy.Generator.Language.Haskell: [outputExtensions] :: Output -> Set String
+ Foreign.Hoppy.Generator.Language.Haskell: addExtension :: String -> Generator ()
+ Foreign.Hoppy.Generator.Language.Haskell: askInterface :: Generator Interface
+ Foreign.Hoppy.Generator.Language.Haskell: askModule :: Generator Module
+ Foreign.Hoppy.Generator.Language.Haskell: askModuleName :: Generator String
+ Foreign.Hoppy.Generator.Language.Haskell: callbackToTFn :: HsTypeSide -> Callback -> Generator Type
+ Foreign.Hoppy.Generator.Language.Haskell: getModuleForExtName :: ExtName -> Generator Module
+ Foreign.Hoppy.Generator.Language.Haskell: indentSpaces :: Int -> Generator a -> Generator a
+ Foreign.Hoppy.Generator.Language.Haskell: toHsBitspaceClassName' :: Bitspace -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsBitspaceFromValueName' :: Bitspace -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsBitspaceToNumName' :: Bitspace -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsBitspaceTypeName' :: Bitspace -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsBitspaceValueName' :: Bitspace -> [String] -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsCallbackCtorName' :: Callback -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsCallbackNewFunPtrFnName :: Callback -> Generator String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsCallbackNewFunPtrFnName' :: Callback -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsCastMethodName' :: Constness -> Class -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsCastPrimitiveName' :: Class -> Class -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsClassDeleteFnName' :: Class -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsClassDeleteFnPtrName' :: Class -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsClassEntityName :: IsFnName String name => Class -> name -> Generator String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsClassEntityName' :: IsFnName String name => Class -> name -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsConstCastFnName' :: Constness -> Class -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsCtorName :: Class -> Ctor -> Generator String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsCtorName' :: Class -> Ctor -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsDataCtorName' :: Managed -> Constness -> Class -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsDataTypeName' :: Constness -> Class -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsDownCastClassName' :: Constness -> Class -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsDownCastMethodName' :: Constness -> Class -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsEnumCtorName' :: CppEnum -> [String] -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsEnumTypeName' :: CppEnum -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsFnName' :: ExtName -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsPtrClassName' :: Constness -> Class -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsValueClassName' :: Class -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsWithValuePtrName' :: Class -> String
+ Foreign.Hoppy.Generator.Spec: CECtor :: Ctor -> ClassEntity
+ Foreign.Hoppy.Generator.Spec: CEMethod :: Method -> ClassEntity
+ Foreign.Hoppy.Generator.Spec: CEProp :: Prop -> ClassEntity
+ Foreign.Hoppy.Generator.Spec: CEVar :: ClassVariable -> ClassEntity
+ Foreign.Hoppy.Generator.Spec: CatchAll :: ExceptionHandler
+ Foreign.Hoppy.Generator.Spec: CatchClass :: Class -> ExceptionHandler
+ Foreign.Hoppy.Generator.Spec: ExceptionHandlers :: [ExceptionHandler] -> ExceptionHandlers
+ Foreign.Hoppy.Generator.Spec: ExceptionId :: Int -> ExceptionId
+ Foreign.Hoppy.Generator.Spec: InterfaceOptions :: ExceptionHandlers -> InterfaceOptions
+ Foreign.Hoppy.Generator.Spec: [exceptionHandlersList] :: ExceptionHandlers -> [ExceptionHandler]
+ Foreign.Hoppy.Generator.Spec: [getExceptionId] :: ExceptionId -> Int
+ Foreign.Hoppy.Generator.Spec: [interfaceOptionsExceptionHandlers] :: InterfaceOptions -> ExceptionHandlers
+ Foreign.Hoppy.Generator.Spec: bitspaceSetValuePrefix :: String -> Bitspace -> Bitspace
+ Foreign.Hoppy.Generator.Spec: bitspaceValuePrefix :: Bitspace -> String
+ Foreign.Hoppy.Generator.Spec: callbackSetThrows :: Bool -> Callback -> Callback
+ Foreign.Hoppy.Generator.Spec: callbackThrows :: Callback -> Maybe Bool
+ Foreign.Hoppy.Generator.Spec: class HandlesExceptions a
+ Foreign.Hoppy.Generator.Spec: class HasExtNames a where getNestedExtNames _ = []
+ Foreign.Hoppy.Generator.Spec: class IsClassEntity a
+ Foreign.Hoppy.Generator.Spec: classAddEntities :: [ClassEntity] -> Class -> Class
+ Foreign.Hoppy.Generator.Spec: classEntities :: Class -> [ClassEntity]
+ Foreign.Hoppy.Generator.Spec: classEntityExtName :: IsClassEntity a => Class -> a -> ExtName
+ Foreign.Hoppy.Generator.Spec: classEntityExtNameSuffix :: IsClassEntity a => a -> ExtName
+ Foreign.Hoppy.Generator.Spec: classEntityForeignName :: IsClassEntity a => Class -> a -> ExtName
+ Foreign.Hoppy.Generator.Spec: classEntityForeignName' :: Class -> ExtName -> ExtName
+ Foreign.Hoppy.Generator.Spec: classEntityPrefix :: Class -> String
+ Foreign.Hoppy.Generator.Spec: classFindCopyCtor :: Class -> Maybe Ctor
+ Foreign.Hoppy.Generator.Spec: classHaskellConversionNone :: ClassHaskellConversion
+ Foreign.Hoppy.Generator.Spec: classIsException :: Class -> Bool
+ Foreign.Hoppy.Generator.Spec: classMakeException :: Class -> Class
+ Foreign.Hoppy.Generator.Spec: classSetEntityPrefix :: String -> Class -> Class
+ Foreign.Hoppy.Generator.Spec: classVarCName :: ClassVariable -> String
+ Foreign.Hoppy.Generator.Spec: classVarExtName :: ClassVariable -> ExtName
+ Foreign.Hoppy.Generator.Spec: classVarGettable :: ClassVariable -> Bool
+ Foreign.Hoppy.Generator.Spec: classVarGetterExtName :: Class -> ClassVariable -> ExtName
+ Foreign.Hoppy.Generator.Spec: classVarGetterForeignName :: Class -> ClassVariable -> ExtName
+ Foreign.Hoppy.Generator.Spec: classVarSetterExtName :: Class -> ClassVariable -> ExtName
+ Foreign.Hoppy.Generator.Spec: classVarSetterForeignName :: Class -> ClassVariable -> ExtName
+ Foreign.Hoppy.Generator.Spec: classVarStatic :: ClassVariable -> Staticness
+ Foreign.Hoppy.Generator.Spec: classVarType :: ClassVariable -> Type
+ Foreign.Hoppy.Generator.Spec: classVariables :: Class -> [ClassVariable]
+ Foreign.Hoppy.Generator.Spec: ctorExceptionHandlers :: Ctor -> ExceptionHandlers
+ Foreign.Hoppy.Generator.Spec: data ClassEntity
+ Foreign.Hoppy.Generator.Spec: data ClassVariable
+ Foreign.Hoppy.Generator.Spec: data ExceptionHandler
+ Foreign.Hoppy.Generator.Spec: data ExceptionHandlers
+ Foreign.Hoppy.Generator.Spec: data InterfaceOptions
+ Foreign.Hoppy.Generator.Spec: data Prop
+ Foreign.Hoppy.Generator.Spec: defaultInterfaceOptions :: InterfaceOptions
+ Foreign.Hoppy.Generator.Spec: enumSetValuePrefix :: String -> CppEnum -> CppEnum
+ Foreign.Hoppy.Generator.Spec: enumValuePrefix :: CppEnum -> String
+ Foreign.Hoppy.Generator.Spec: exceptionCatchAllId :: ExceptionId
+ Foreign.Hoppy.Generator.Spec: fnExceptionHandlers :: Function -> ExceptionHandlers
+ Foreign.Hoppy.Generator.Spec: getAddendum :: HasAddendum a => a -> Addendum
+ Foreign.Hoppy.Generator.Spec: getAllExtNames :: HasExtNames a => a -> [ExtName]
+ Foreign.Hoppy.Generator.Spec: getExceptionHandlers :: HandlesExceptions a => a -> ExceptionHandlers
+ Foreign.Hoppy.Generator.Spec: getNestedExtNames :: HasExtNames a => a -> [ExtName]
+ Foreign.Hoppy.Generator.Spec: getPrimaryExtName :: HasExtNames a => a -> ExtName
+ Foreign.Hoppy.Generator.Spec: handleExceptions :: HandlesExceptions a => [ExceptionHandler] -> a -> a
+ Foreign.Hoppy.Generator.Spec: hsImportForException :: HsImportSet
+ Foreign.Hoppy.Generator.Spec: hsImportForMap :: HsImportSet
+ Foreign.Hoppy.Generator.Spec: hsImportForTypeable :: HsImportSet
+ Foreign.Hoppy.Generator.Spec: interface' :: String -> [Module] -> InterfaceOptions -> Either ErrorMsg Interface
+ Foreign.Hoppy.Generator.Spec: interfaceAllExceptionClasses :: Interface -> [Class]
+ Foreign.Hoppy.Generator.Spec: interfaceCallbacksThrow :: Interface -> Bool
+ Foreign.Hoppy.Generator.Spec: interfaceExceptionClassId :: Interface -> Class -> Maybe ExceptionId
+ Foreign.Hoppy.Generator.Spec: interfaceExceptionHandlers :: Interface -> ExceptionHandlers
+ Foreign.Hoppy.Generator.Spec: interfaceExceptionSupportModule :: Interface -> Maybe Module
+ Foreign.Hoppy.Generator.Spec: interfaceHaskellModuleImportNames :: Interface -> Map Module String
+ Foreign.Hoppy.Generator.Spec: interfaceSetCallbacksThrow :: Bool -> Interface -> Interface
+ Foreign.Hoppy.Generator.Spec: interfaceSetExceptionSupportModule :: Module -> Interface -> Interface
+ Foreign.Hoppy.Generator.Spec: isValidExtName :: String -> Bool
+ Foreign.Hoppy.Generator.Spec: makeClassVariable :: String -> Maybe ExtName -> Type -> Staticness -> Bool -> ClassEntity
+ Foreign.Hoppy.Generator.Spec: makeClassVariable_ :: String -> Maybe ExtName -> Type -> Staticness -> Bool -> ClassVariable
+ Foreign.Hoppy.Generator.Spec: makeCtor_ :: ExtName -> [Type] -> Ctor
+ Foreign.Hoppy.Generator.Spec: makeFnMethod_ :: IsFnName Identifier name => name -> String -> MethodApplicability -> Purity -> [Type] -> Type -> Method
+ Foreign.Hoppy.Generator.Spec: makeMethod_ :: IsFnName String name => name -> ExtName -> MethodApplicability -> Purity -> [Type] -> Type -> Method
+ Foreign.Hoppy.Generator.Spec: methodExceptionHandlers :: Method -> ExceptionHandlers
+ Foreign.Hoppy.Generator.Spec: mkBoolHasProp_ :: String -> Prop
+ Foreign.Hoppy.Generator.Spec: mkBoolIsProp_ :: String -> Prop
+ Foreign.Hoppy.Generator.Spec: mkClassVariable :: String -> Type -> ClassEntity
+ Foreign.Hoppy.Generator.Spec: mkClassVariable_ :: String -> Type -> ClassVariable
+ Foreign.Hoppy.Generator.Spec: mkConstMethod'_ :: IsFnName String name => name -> String -> [Type] -> Type -> Method
+ Foreign.Hoppy.Generator.Spec: mkConstMethod_ :: IsFnName String name => name -> [Type] -> Type -> Method
+ Foreign.Hoppy.Generator.Spec: mkCtor_ :: String -> [Type] -> Ctor
+ Foreign.Hoppy.Generator.Spec: mkMethod'_ :: IsFnName String name => name -> String -> [Type] -> Type -> Method
+ Foreign.Hoppy.Generator.Spec: mkMethod_ :: IsFnName String name => name -> [Type] -> Type -> Method
+ Foreign.Hoppy.Generator.Spec: mkProp_ :: String -> Type -> Prop
+ Foreign.Hoppy.Generator.Spec: mkStaticClassVariable :: String -> Type -> ClassEntity
+ Foreign.Hoppy.Generator.Spec: mkStaticClassVariable_ :: String -> Type -> ClassVariable
+ Foreign.Hoppy.Generator.Spec: mkStaticMethod'_ :: IsFnName String name => name -> String -> [Type] -> Type -> Method
+ Foreign.Hoppy.Generator.Spec: mkStaticMethod_ :: IsFnName String name => name -> [Type] -> Type -> Method
+ Foreign.Hoppy.Generator.Spec: mkStaticProp_ :: String -> Type -> Prop
+ Foreign.Hoppy.Generator.Spec: modifyAddendum :: HasAddendum a => (Addendum -> Addendum) -> a -> a
+ Foreign.Hoppy.Generator.Spec: moduleAddendum :: Module -> Addendum
+ Foreign.Hoppy.Generator.Spec: moduleCallbacksThrow :: Module -> Maybe Bool
+ Foreign.Hoppy.Generator.Spec: moduleExceptionHandlers :: Module -> ExceptionHandlers
+ Foreign.Hoppy.Generator.Spec: moduleSetCallbacksThrow :: MonadState Module m => Maybe Bool -> m ()
+ Foreign.Hoppy.Generator.Spec: newtype ExceptionId
+ Foreign.Hoppy.Generator.Spec: setAddendum :: HasAddendum a => Addendum -> a -> a
- Foreign.Hoppy.Generator.Language.Haskell: Output :: [HsExport] -> HsImportSet -> [String] -> Output
+ Foreign.Hoppy.Generator.Language.Haskell: Output :: [HsExport] -> HsImportSet -> [String] -> Set String -> Output
- Foreign.Hoppy.Generator.Language.Haskell: evalGenerator :: Interface -> String -> Generator a -> Either ErrorMsg a
+ Foreign.Hoppy.Generator.Language.Haskell: evalGenerator :: Interface -> Module -> Generator a -> Either ErrorMsg a
- Foreign.Hoppy.Generator.Language.Haskell: execGenerator :: Interface -> String -> Generator a -> Either ErrorMsg Partial
+ Foreign.Hoppy.Generator.Language.Haskell: execGenerator :: Interface -> Module -> Generator a -> Either ErrorMsg Partial
- Foreign.Hoppy.Generator.Language.Haskell: getClassHaskellConversion :: Class -> Maybe ClassHaskellConversion
+ Foreign.Hoppy.Generator.Language.Haskell: getClassHaskellConversion :: Class -> ClassHaskellConversion
- Foreign.Hoppy.Generator.Language.Haskell: runGenerator :: Interface -> String -> Generator a -> Either ErrorMsg (Partial, a)
+ Foreign.Hoppy.Generator.Language.Haskell: runGenerator :: Interface -> Module -> Generator a -> Either ErrorMsg (Partial, a)
- Foreign.Hoppy.Generator.Language.Haskell: toHsBitspaceClassName :: Bitspace -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsBitspaceClassName :: Bitspace -> Generator String
- Foreign.Hoppy.Generator.Language.Haskell: toHsBitspaceFromValueName :: Bitspace -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsBitspaceFromValueName :: Bitspace -> Generator String
- Foreign.Hoppy.Generator.Language.Haskell: toHsBitspaceToNumName :: Bitspace -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsBitspaceToNumName :: Bitspace -> Generator String
- Foreign.Hoppy.Generator.Language.Haskell: toHsBitspaceTypeName :: Bitspace -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsBitspaceTypeName :: Bitspace -> Generator String
- Foreign.Hoppy.Generator.Language.Haskell: toHsBitspaceValueName :: Bitspace -> [String] -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsBitspaceValueName :: Bitspace -> [String] -> Generator String
- Foreign.Hoppy.Generator.Language.Haskell: toHsCallbackCtorName :: Callback -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsCallbackCtorName :: Callback -> Generator String
- Foreign.Hoppy.Generator.Language.Haskell: toHsCastMethodName :: Constness -> Class -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsCastMethodName :: Constness -> Class -> Generator String
- Foreign.Hoppy.Generator.Language.Haskell: toHsCastPrimitiveName :: Class -> Class -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsCastPrimitiveName :: Class -> Class -> Class -> Generator String
- Foreign.Hoppy.Generator.Language.Haskell: toHsConstCastFnName :: Constness -> Class -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsConstCastFnName :: Constness -> Class -> Generator String
- Foreign.Hoppy.Generator.Language.Haskell: toHsDataCtorName :: Managed -> Constness -> Class -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsDataCtorName :: Managed -> Constness -> Class -> Generator String
- Foreign.Hoppy.Generator.Language.Haskell: toHsDataTypeName :: Constness -> Class -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsDataTypeName :: Constness -> Class -> Generator String
- Foreign.Hoppy.Generator.Language.Haskell: toHsDownCastClassName :: Constness -> Class -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsDownCastClassName :: Constness -> Class -> Generator String
- Foreign.Hoppy.Generator.Language.Haskell: toHsDownCastMethodName :: Constness -> Class -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsDownCastMethodName :: Constness -> Class -> Generator String
- Foreign.Hoppy.Generator.Language.Haskell: toHsEnumCtorName :: CppEnum -> [String] -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsEnumCtorName :: CppEnum -> [String] -> Generator String
- Foreign.Hoppy.Generator.Language.Haskell: toHsEnumTypeName :: CppEnum -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsEnumTypeName :: CppEnum -> Generator String
- Foreign.Hoppy.Generator.Language.Haskell: toHsFnName :: ExtName -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsFnName :: ExtName -> Generator String
- Foreign.Hoppy.Generator.Language.Haskell: toHsMethodName :: Class -> Method -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsMethodName :: Class -> Method -> Generator String
- Foreign.Hoppy.Generator.Language.Haskell: toHsMethodName' :: IsFnName String name => Class -> name -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsMethodName' :: Class -> Method -> String
- Foreign.Hoppy.Generator.Language.Haskell: toHsPtrClassName :: Constness -> Class -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsPtrClassName :: Constness -> Class -> Generator String
- Foreign.Hoppy.Generator.Language.Haskell: toHsValueClassName :: Class -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsValueClassName :: Class -> Generator String
- Foreign.Hoppy.Generator.Language.Haskell: toHsWithValuePtrName :: Class -> String
+ Foreign.Hoppy.Generator.Language.Haskell: toHsWithValuePtrName :: Class -> Generator String
- Foreign.Hoppy.Generator.Spec: ClassConversion :: ClassConversionMode ClassHaskellConversion -> ClassConversion
+ Foreign.Hoppy.Generator.Spec: ClassConversion :: ClassHaskellConversion -> ClassConversion
- Foreign.Hoppy.Generator.Spec: ClassHaskellConversion :: Generator HsType -> Generator () -> Generator () -> ClassHaskellConversion
+ Foreign.Hoppy.Generator.Spec: ClassHaskellConversion :: Maybe (Generator HsType) -> Maybe (Generator ()) -> Maybe (Generator ()) -> ClassHaskellConversion
- Foreign.Hoppy.Generator.Spec: [classHaskellConversionFromCppFn] :: ClassHaskellConversion -> Generator ()
+ Foreign.Hoppy.Generator.Spec: [classHaskellConversionFromCppFn] :: ClassHaskellConversion -> Maybe (Generator ())
- Foreign.Hoppy.Generator.Spec: [classHaskellConversionToCppFn] :: ClassHaskellConversion -> Generator ()
+ Foreign.Hoppy.Generator.Spec: [classHaskellConversionToCppFn] :: ClassHaskellConversion -> Maybe (Generator ())
- Foreign.Hoppy.Generator.Spec: [classHaskellConversionType] :: ClassHaskellConversion -> Generator HsType
+ Foreign.Hoppy.Generator.Spec: [classHaskellConversionType] :: ClassHaskellConversion -> Maybe (Generator HsType)
- Foreign.Hoppy.Generator.Spec: [classHaskellConversion] :: ClassConversion -> ClassConversionMode ClassHaskellConversion
+ Foreign.Hoppy.Generator.Spec: [classHaskellConversion] :: ClassConversion -> ClassHaskellConversion
- Foreign.Hoppy.Generator.Spec: makeClass :: Identifier -> Maybe ExtName -> [Class] -> [Ctor] -> [Method] -> Class
+ Foreign.Hoppy.Generator.Spec: makeClass :: Identifier -> Maybe ExtName -> [Class] -> [ClassEntity] -> Class
- Foreign.Hoppy.Generator.Spec: makeCtor :: ExtName -> [Type] -> Ctor
+ Foreign.Hoppy.Generator.Spec: makeCtor :: ExtName -> [Type] -> ClassEntity
- Foreign.Hoppy.Generator.Spec: makeFnMethod :: IsFnName Identifier name => name -> String -> MethodApplicability -> Purity -> [Type] -> Type -> Method
+ Foreign.Hoppy.Generator.Spec: makeFnMethod :: IsFnName Identifier name => name -> String -> MethodApplicability -> Purity -> [Type] -> Type -> ClassEntity
- Foreign.Hoppy.Generator.Spec: makeMethod :: IsFnName String name => name -> ExtName -> MethodApplicability -> Purity -> [Type] -> Type -> Method
+ Foreign.Hoppy.Generator.Spec: makeMethod :: IsFnName String name => name -> ExtName -> MethodApplicability -> Purity -> [Type] -> Type -> ClassEntity
- Foreign.Hoppy.Generator.Spec: mkBoolHasProp :: String -> [Method]
+ Foreign.Hoppy.Generator.Spec: mkBoolHasProp :: String -> ClassEntity
- Foreign.Hoppy.Generator.Spec: mkBoolIsProp :: String -> [Method]
+ Foreign.Hoppy.Generator.Spec: mkBoolIsProp :: String -> ClassEntity
- Foreign.Hoppy.Generator.Spec: mkConstMethod :: IsFnName String name => name -> [Type] -> Type -> Method
+ Foreign.Hoppy.Generator.Spec: mkConstMethod :: IsFnName String name => name -> [Type] -> Type -> ClassEntity
- Foreign.Hoppy.Generator.Spec: mkConstMethod' :: IsFnName String name => name -> String -> [Type] -> Type -> Method
+ Foreign.Hoppy.Generator.Spec: mkConstMethod' :: IsFnName String name => name -> String -> [Type] -> Type -> ClassEntity
- Foreign.Hoppy.Generator.Spec: mkCtor :: String -> [Type] -> Ctor
+ Foreign.Hoppy.Generator.Spec: mkCtor :: String -> [Type] -> ClassEntity
- Foreign.Hoppy.Generator.Spec: mkMethod :: IsFnName String name => name -> [Type] -> Type -> Method
+ Foreign.Hoppy.Generator.Spec: mkMethod :: IsFnName String name => name -> [Type] -> Type -> ClassEntity
- Foreign.Hoppy.Generator.Spec: mkMethod' :: IsFnName String name => name -> String -> [Type] -> Type -> Method
+ Foreign.Hoppy.Generator.Spec: mkMethod' :: IsFnName String name => name -> String -> [Type] -> Type -> ClassEntity
- Foreign.Hoppy.Generator.Spec: mkProp :: String -> Type -> [Method]
+ Foreign.Hoppy.Generator.Spec: mkProp :: String -> Type -> ClassEntity
- Foreign.Hoppy.Generator.Spec: mkStaticMethod :: IsFnName String name => name -> [Type] -> Type -> Method
+ Foreign.Hoppy.Generator.Spec: mkStaticMethod :: IsFnName String name => name -> [Type] -> Type -> ClassEntity
- Foreign.Hoppy.Generator.Spec: mkStaticMethod' :: IsFnName String name => name -> String -> [Type] -> Type -> Method
+ Foreign.Hoppy.Generator.Spec: mkStaticMethod' :: IsFnName String name => name -> String -> [Type] -> Type -> ClassEntity
- Foreign.Hoppy.Generator.Spec: mkStaticProp :: String -> Type -> [Method]
+ Foreign.Hoppy.Generator.Spec: mkStaticProp :: String -> Type -> ClassEntity
Files
- hoppy-generator.cabal +6/−1
- src/Foreign/Hoppy/Generator/Common.hs +5/−0
- src/Foreign/Hoppy/Generator/Language/Cpp.hs +26/−2
- src/Foreign/Hoppy/Generator/Language/Cpp/Internal.hs +277/−45
- src/Foreign/Hoppy/Generator/Language/Haskell.hs +402/−127
- src/Foreign/Hoppy/Generator/Language/Haskell/Internal.hs +1656/−1333
- src/Foreign/Hoppy/Generator/Spec.hs +30/−1875
- src/Foreign/Hoppy/Generator/Spec/Base.hs +2484/−0
- src/Foreign/Hoppy/Generator/Spec/ClassFeature.hs +12/−16
- src/Foreign/Hoppy/Generator/Spec/Conversion.hs +77/−0
- src/Foreign/Hoppy/Generator/Types.hs +1/−1
hoppy-generator.cabal view
@@ -1,5 +1,5 @@ name: hoppy-generator-version: 0.2.1+version: 0.3.0 synopsis: C++ FFI generator - Code generator homepage: http://khumba.net/projects/hoppy license: AGPL-3@@ -29,11 +29,16 @@ , Foreign.Hoppy.Generator.Common.Consume , Foreign.Hoppy.Generator.Language.Cpp.Internal , Foreign.Hoppy.Generator.Language.Haskell.Internal+ , Foreign.Hoppy.Generator.Spec.Base+ , Foreign.Hoppy.Generator.Spec.Conversion default-extensions: FlexibleContexts , FlexibleInstances , FunctionalDependencies+ , LambdaCase , MultiParamTypeClasses+ other-extensions:+ GeneralizedNewtypeDeriving build-depends: base >=4.7 && <5 , containers >=0.5 && <0.6
src/Foreign/Hoppy/Generator/Common.hs view
@@ -22,6 +22,7 @@ fromMaybeM, fromEitherM, maybeFail,+ for, listSubst, zipWithM, writeFileIfDifferent,@@ -51,6 +52,10 @@ -- | @maybeFail s x = maybe (fail s) x@ maybeFail :: Monad m => String -> Maybe a -> m a maybeFail = fromMaybeM . fail++-- | @for = flip map@+for :: [a] -> (a -> b) -> [b]+for = flip map -- | @listSubst a b xs@ replaces all @x@ in @xs@ such that @x == a@ with @b@. listSubst :: Eq a => a -> a -> [a] -> [a]
src/Foreign/Hoppy/Generator/Language/Cpp.hs view
@@ -27,6 +27,10 @@ callbackFnName, toArgName, toArgNameAlt,+ exceptionIdArgName,+ exceptionPtrArgName,+ exceptionVarName,+ exceptionRethrowFnName, Chunk (..), runChunkWriter, evalChunkWriter,@@ -45,6 +49,7 @@ import Control.Monad.Writer (MonadWriter, Writer, WriterT, runWriter, runWriterT, tell) import Data.Foldable (forM_) import Data.List (intercalate, intersperse)+import Foreign.Hoppy.Generator.Common import Foreign.Hoppy.Generator.Spec import Foreign.Hoppy.Generator.Types @@ -108,6 +113,25 @@ toArgNameAlt :: Int -> String toArgNameAlt n = "arg" ++ show n ++ "_" +-- | The C++ variable name to use for the exception ID argument in a gateway+-- function.+exceptionIdArgName :: String+exceptionIdArgName = "excId"++-- | The C++ variable name to use for the exception pointer argument in a+-- gateway function.+exceptionPtrArgName :: String+exceptionPtrArgName = "excPtr"++-- | The C++ variable name to use in a @catch@ statement in a gateway function.+exceptionVarName :: String+exceptionVarName = "exc_"++-- | The name of the C++ function that receives an exception from a foreign+-- language and throws it in C++.+exceptionRethrowFnName :: String+exceptionRethrowFnName = "genthrow"+ -- TODO Fixme, this is most likely backwards, it should be a finite set of -- non-identifier chars. Also (maybe) share some logic with the toExtName -- requirements?@@ -155,7 +179,7 @@ combineChunks :: [Chunk] -> String combineChunks chunks = let strs = map chunkContents chunks- in concat $ flip map (zip ("":strs) strs) $ \(prev, cur) ->+ in concat $ for (zip ("":strs) strs) $ \(prev, cur) -> let needsSpace = not (null prev) && not (null cur) && (let a = last prev@@ -246,7 +270,7 @@ outer say "(" sequence_ $ intersperse (say ", ") $- flip map (zip paramTypes $ maybe (repeat Nothing) (map Just) maybeParamNames) $+ for (zip paramTypes $ maybe (repeat Nothing) (map Just) maybeParamNames) $ \(ptype, pname) -> sayType' ptype Nothing topPrecedence $ forM_ pname say say ")"
src/Foreign/Hoppy/Generator/Language/Cpp/Internal.hs view
@@ -32,9 +32,12 @@ import Control.Monad.Writer (WriterT, execWriterT, runWriterT, tell) import Control.Monad.Trans (lift) import Data.Foldable (forM_)+#if !MIN_VERSION_base(4,8,0)+import Data.Functor ((<$))+#endif import Data.List (intersperse) import qualified Data.Map as M-import Data.Maybe (fromMaybe, isJust)+import Data.Maybe (catMaybes, fromMaybe, isJust) #if !MIN_VERSION_base(4,8,0) import Data.Monoid (mappend, mconcat, mempty) #endif@@ -136,12 +139,20 @@ addReqsM $ moduleReqs m mapM_ (sayExport False) $ M.elems $ moduleExports m + iface <- askInterface+ when (interfaceExceptionSupportModule iface == Just m) $+ sayExceptionSupport False+ sayModuleSource :: Generator () sayModuleSource = do m <- askModule addInclude $ includeLocal $ moduleHppPath m mapM_ (sayExport True) $ M.elems $ moduleExports m + iface <- askInterface+ when (interfaceExceptionSupportModule iface == Just m) $+ sayExceptionSupport True+ sayExport :: Bool -> Export -> Generator () sayExport sayBody export = case export of ExportVariable v -> when sayBody $ sayExportVariable v@@ -161,6 +172,7 @@ Nothing (fnParams fn) (fnReturn fn)+ (fnExceptionHandlers fn) sayBody ExportClass cls -> when sayBody $ do@@ -171,11 +183,12 @@ -- Export each of the class's constructors. forM_ (classCtors cls) $ \ctor ->- sayExportFn (getClassyExtName cls ctor)+ sayExportFn (classEntityExtName cls ctor) (CallFn $ say "new" >> sayIdentifier (classIdentifier cls)) Nothing (ctorParams ctor) clsPtr+ (ctorExceptionHandlers ctor) sayBody -- Export a delete function for the class.@@ -185,6 +198,9 @@ (fnT [ptrT $ constT $ objT cls] voidT) $ Just $ say "delete self;\n" + -- Export each of the class's variables.+ forM_ (classVariables cls) $ sayExportClassVariable cls+ -- Export each of the class's methods. forM_ (classMethods cls) $ \method -> do let nonMemberCall =@@ -193,7 +209,7 @@ RealMethod {} -> False FnMethod {} -> True let static = methodStatic method == Static- sayExportFn (getClassyExtName cls method)+ sayExportFn (classEntityExtName cls method) (case methodImpl method of RealMethod name -> case name of FnName cName -> CallFn $ do@@ -208,6 +224,7 @@ (if nonMemberCall then Nothing else justClsPtr) (methodParams method) (methodReturn method)+ (methodExceptionHandlers method) sayBody -- Export upcast functions for the class to its direct superclasses.@@ -243,43 +260,82 @@ forM_ (classSuperclasses ancestorCls) $ genDowncastFns cls sayExportVariable :: Variable -> Generator ()-sayExportVariable v = do- let (isConst, deconstType) = case varType v of+sayExportVariable v =+ sayExportVariable' (varType v)+ Nothing+ True+ (varGetterExtName v)+ (varSetterExtName v)+ (sayIdentifier $ varIdentifier v)++sayExportClassVariable :: Class -> ClassVariable -> Generator ()+sayExportClassVariable cls v =+ sayExportVariable' (classVarType v)+ (case classVarStatic v of+ Nonstatic -> Just (ptrT $ constT $ objT cls, ptrT $ objT cls)+ Static -> Nothing)+ (classVarGettable v)+ (classVarGetterExtName cls v)+ (classVarSetterExtName cls v)+ (case classVarStatic v of+ Nonstatic -> say $ classVarCName v+ Static -> do sayIdentifier $ classIdentifier cls+ says ["::", classVarCName v])++sayExportVariable' :: Type+ -> Maybe (Type, Type)+ -> Bool+ -> ExtName+ -> ExtName+ -> Generator ()+ -> Generator ()+sayExportVariable' t maybeThisTypes gettable getterName setterName sayVarName = do+ let (isConst, deconstType) = case t of Internal_TConst t -> (True, t) t -> (False, t) -- Say a getter function.- sayExportFn (varGetterExtName v)- (VarRead $ varIdentifier v)- Nothing- []- deconstType- True+ when gettable $+ sayExportFn getterName+ (VarRead sayVarName)+ (fmap fst maybeThisTypes)+ []+ deconstType+ mempty+ True -- Say a setter function. unless isConst $- sayExportFn (varSetterExtName v)- (VarWrite $ varIdentifier v)- Nothing+ sayExportFn setterName+ (VarWrite sayVarName)+ (fmap snd maybeThisTypes) [deconstType] voidT+ mempty True data CallType = CallOp Operator | CallFn (Generator ())- | VarRead Identifier- | VarWrite Identifier+ | VarRead (Generator ())+ | VarWrite (Generator ()) sayExportFn :: ExtName -> CallType -> Maybe Type -> [Type] -> Type+ -> ExceptionHandlers -> Bool -> Generator ()-sayExportFn extName callType maybeThisType paramTypes retType sayBody = do- let paramCount = length paramTypes+sayExportFn extName callType maybeThisType paramTypes retType exceptionHandlers sayBody = do+ handlerList <- exceptionHandlersList <$> getEffectiveExceptionHandlers exceptionHandlers+ let catches = not $ null handlerList+ addExceptionParamNames =+ if catches then (++ [exceptionIdArgName, exceptionPtrArgName]) else id+ addExceptionParamTypes = if catches then (++ [ptrT intT, ptrT $ ptrT voidT]) else id++ paramCount = length paramTypes paramCTypeMaybes = map typeToCType paramTypes paramCTypes = zipWith fromMaybe paramTypes paramCTypeMaybes retCTypeMaybe = typeToCType retType@@ -289,14 +345,20 @@ sayFunction (externalNameToCpp extName) (maybe id (const ("self":)) maybeThisType $+ addExceptionParamNames $ zipWith3 (\t ctm -> case t of Internal_TCallback {} -> toArgNameAlt _ -> if isJust ctm then toArgNameAlt else toArgName) paramTypes paramCTypeMaybes [1..paramCount])- (fnT (maybe id (:) maybeThisType paramCTypes) retCType) $+ (fnT (addExceptionParamTypes $ maybe id (:) maybeThisType paramCTypes)+ retCType) $ if not sayBody then Nothing else Just $ do+ when catches $ do+ say "try {\n"+ says ["*", exceptionIdArgName, " = 0;\n"]+ -- Convert arguments that aren't passed in directly. mapM_ (sayArgRead DoDecode) $ zip3 [1..] paramTypes paramCTypeMaybes @@ -326,8 +388,13 @@ say "(" sayArgNames paramCount say ")"- VarRead identifier -> sayIdentifier identifier- VarWrite identifier -> sayIdentifier identifier >> says [" = ", toArgName 1]+ VarRead sayVarName -> do+ when (isJust maybeThisType) $ say "self->"+ sayVarName+ VarWrite sayVarName -> do+ when (isJust maybeThisType) $ say "self->"+ sayVarName+ says [" = ", toArgName 1] -- Writes the call, transforming the return value if necessary. -- These translations should be kept in sync with typeToCType.@@ -358,6 +425,39 @@ sayCallAndReturn retType retCTypeMaybe + when catches $ do+ iface <- askInterface++ forM_ handlerList $ \handler -> do+ say "} catch ("+ case handler of+ CatchClass cls -> sayVar exceptionVarName Nothing $ refT $ constT $ objT cls+ CatchAll -> say "..."+ say ") {\n"++ exceptionId <- case handler of+ CatchClass cls -> case interfaceExceptionClassId iface cls of+ Just exceptionId -> return exceptionId+ Nothing -> abort $ concat+ ["sayExportFn: Trying to catch non-exception class ", show cls,+ " while generating binding for ", show extName, "."]+ CatchAll -> return exceptionCatchAllId+ says ["*", exceptionIdArgName, " = ", show $ getExceptionId exceptionId, ";\n"]++ case handler of+ CatchAll -> says ["*", exceptionPtrArgName, " = 0;\n"]+ CatchClass cls -> do+ -- Object pointers don't convert automatically to void*.+ says ["*", exceptionPtrArgName, " = reinterpret_cast<void*>(new "]+ sayType Nothing $ objT cls+ says ["(", exceptionVarName, "));\n"]++ -- For all of the types our gateway functions actually return, "return+ -- 0" is a valid statement.+ when (retType /= Internal_TVoid) $ say "return 0;\n"++ say "}\n"+ where sayReturnNew cls sayCall = say "return new" >> sayIdentifier (classIdentifier cls) >> say "(" >> sayCall >> say ");\n"@@ -396,6 +496,22 @@ show cb, "."] says [callbackClassName cb, " ", toArgName n, "(", toArgNameAlt n, ");\n"] + t@(Internal_TPtr (Internal_TFn paramTypes retType)) -> do+ -- Assert that all types referred to in a function pointer type are all+ -- representable as C types.+ let check label t' = (label ++ " " ++ show t') <$ typeToCType t'+ mismatches = catMaybes $+ check "return type" retType :+ map (\paramType -> check "parameter" paramType)+ paramTypes+ unless (null mismatches) $+ abort $ concat $+ "sayArgRead: Some types within a function pointer type use non-C types, " :+ "but only C types may be used. The unsupported types are: " :+ intersperse "; " mismatches ++ [". The whole function type is ", show t, "."]++ convertDefault+ Internal_TRef t -> convertObj t Internal_TObj _ -> convertObj $ constT cppType@@ -418,17 +534,19 @@ _ -> t' sayArgRead dir (n, cppType, typeToCType newCppType) - -- Primitive types don't need to be encoded/decoded. But if maybeCType is a- -- Just, then we're expected to do some encoding/decoding, so something is- -- wrong.- --- -- TODO Do we need to handle TConst?- _ -> forM_ maybeCType $ \cType ->- abort $ concat- ["sayArgRead: Don't know how to ", show dir, " between C-type ", show cType,- " and C++-type ", show cppType, "."]+ _ -> convertDefault - where convertObj cppType' = case dir of+ where -- Primitive types don't need to be encoded/decoded. But if maybeCType is a+ -- Just, then we're expected to do some encoding/decoding, so something is+ -- wrong.+ --+ -- TODO Do we need to handle TConst?+ convertDefault = forM_ maybeCType $ \cType ->+ abort $ concat+ ["sayArgRead: Don't know how to ", show dir, " between C-type ", show cType,+ " and C++-type ", show cppType, "."]++ convertObj cppType' = case dir of DoDecode -> do sayVar (toArgName n) Nothing $ refT cppType' says [" = *", toArgNameAlt n, ";\n"]@@ -442,6 +560,8 @@ sayExportCallback :: Bool -> Callback -> Generator () sayExportCallback sayBody cb = do+ throws <- getEffectiveCallbackThrows cb+ let className = callbackClassName cb implClassName = callbackImplClassName cb fnName = callbackFnName cb@@ -458,7 +578,9 @@ addReqsM . mconcat =<< mapM typeReqs (retType:paramTypes) - let fnCType = fnT paramCTypes retCType+ let fnCType = fnT ((if throws then ([ptrT intT, ptrT $ ptrT voidT] ++) else id)+ paramCTypes)+ retCType fnPtrCType = ptrT fnCType if not sayBody@@ -520,31 +642,74 @@ fnType $ Just $ do -- Convert arguments that aren't passed in directly. mapM_ (sayArgRead DoEncode) $ zip3 [1..] paramTypes paramCTypeMaybes++ when throws $ do+ says ["int ", exceptionIdArgName, " = 0;\n"]+ says ["void *", exceptionPtrArgName, " = 0;\n"]++ -- Add an include for the exception support module to be able to call the+ -- C++ rethrow function.+ iface <- askInterface+ currentModule <- askModule+ case interfaceExceptionSupportModule iface of+ Just exceptionSupportModule ->+ when (exceptionSupportModule /= currentModule) $+ -- TODO Should this be includeStd?+ addReqsM $ reqInclude $ includeLocal $ moduleHppPath exceptionSupportModule+ Nothing -> abort $ "sayExportCallback: " ++ show iface +++ " uses exceptions, so it needs an exception support " +++ "module. Please use interfaceSetExceptionSupportModule."+ -- Invoke the function pointer into foreign code.- let sayCall = say "f_(" >> sayArgNames paramCount >> say ")"+ let sayCall = do+ say "f_("+ when throws $ do+ says ["&", exceptionIdArgName, ", &", exceptionPtrArgName]+ when (paramCount /= 0) $ say ", "+ sayArgNames paramCount+ say ")"++ sayExceptionCheck = when throws $ do+ says ["if (", exceptionIdArgName, " != 0) { ",+ exceptionRethrowFnName, "(", exceptionIdArgName, ", ",+ exceptionPtrArgName, "); }\n"]+ case (retType, retCTypeMaybe) of- (Internal_TVoid, Nothing) -> sayCall >> say ";\n"- (_, Nothing) -> say "return " >> sayCall >> say ";\n"+ (Internal_TVoid, Nothing) -> do+ sayCall >> say ";\n"+ sayExceptionCheck+ (_, Nothing) -> do+ sayVar "result" Nothing retType >> say " = " >> sayCall >> say ";\n"+ sayExceptionCheck+ say "return result;\n" (Internal_TBitspace b, Just _) -> do addReqsM $ bitspaceReqs b let convFn = bitspaceToCppValueFn b- say "return "+ sayVar "result" Nothing retType+ say " = " forM_ convFn $ \f -> says [f, "("] sayCall when (isJust convFn) $ say ")" say ";\n";+ sayExceptionCheck+ say "return result;\n" (Internal_TObj cls1, Just retCType@(Internal_TPtr (Internal_TConst (Internal_TObj cls2)))) | cls1 == cls2 -> do- sayVar "resultPtr" Nothing retCType >> say " = " >> sayCall >> say ";\n"- sayVar "result" Nothing retType >> say " = *resultPtr;\n"- say "delete resultPtr;\n"- say "return result;\n"+ sayVar "resultPtr" Nothing retCType >> say " = " >> sayCall >> say ";\n"+ sayVar "result" Nothing retType >> say " = *resultPtr;\n"+ say "delete resultPtr;\n"+ sayExceptionCheck+ say "return result;\n" (Internal_TRef (Internal_TConst (Internal_TObj cls1)),- Just (Internal_TPtr (Internal_TConst (Internal_TObj cls2)))) | cls1 == cls2 ->- say "return *(" >> sayCall >> say ");\n"+ Just (Internal_TPtr (Internal_TConst (Internal_TObj cls2)))) | cls1 == cls2 -> do+ sayVar "result" Nothing retType >> say " = *" >> sayCall >> say ";\n"+ sayExceptionCheck+ say "return result;\n" (Internal_TRef (Internal_TObj cls1),- Just (Internal_TPtr (Internal_TObj cls2))) | cls1 == cls2 ->- say "return *(" >> sayCall >> say ");\n"+ Just (Internal_TPtr (Internal_TObj cls2))) | cls1 == cls2 -> do+ sayVar "result" Nothing retType >> say " = *" >> sayCall >> say ";\n"+ sayExceptionCheck+ say "return result;\n" ts -> abort $ concat ["sayExportCallback: Unexpected return types ", show ts, "."] @@ -568,6 +733,39 @@ sayFunction fnName ["f", "release", "releaseRelease"] newCallbackFnType $ Just $ says ["return new ", implClassName, "(f, release, releaseRelease);\n"] +-- | Outputs interface-wide code needed to support exceptions. Currently, this+-- comprises the function for rethrowing in C++ an exception transferred from+-- a foreign language.+sayExceptionSupport :: Bool -> Generator ()+sayExceptionSupport sayBody =+ sayFunction exceptionRethrowFnName+ ["excId", "voidPtr"]+ (fnT [intT, ptrT voidT] voidT) $+ if not sayBody+ then Nothing+ else Just $ do+ iface <- askInterface+ let excClasses = interfaceAllExceptionClasses iface++ says ["switch (excId) {\n"]++ forM_ excClasses $ \cls -> do+ excId <- fmap getExceptionId $+ fromMaybeM (abort $ "sayExceptionSupport: Internal error, " ++ show cls +++ "should have an exception ID, but doesn't.") $+ interfaceExceptionClassId iface cls+ says ["case ", show excId, ": {\n"]+ sayVar "excPtr" Nothing (ptrT $ objT cls) >> say " = reinterpret_cast<" >>+ sayType Nothing (ptrT $ objT cls) >> says [">(voidPtr);\n"]+ sayVar "exc" Nothing (objT cls) >> say " = *excPtr;\n"+ say "delete excPtr;\n"+ say "throw exc;\n"+ say "}\n"++ say "}\n"+ says ["throw \"Internal Hoppy error, ", exceptionRethrowFnName,+ " got an unknown exception ID.\";\n"]+ -- | Returns a 'Type' iff there is a C type distinct from the given C++ type -- that should be used for conversion. typeToCType :: Type -> Maybe Type@@ -622,10 +820,11 @@ -- TODO Is the right 'ReqsType' being used recursively here? mconcat <$> mapM typeReqs (retType:paramTypes) Internal_TCallback cb -> do+ -- TODO Should this be includeStd? cbClassReqs <- reqInclude . includeLocal . moduleHppPath <$> findExportModule (callbackExtName cb) -- TODO Is the right 'ReqsType' being used recursively here?- fnTypeReqs <- typeReqs $ callbackToTFn cb+ fnTypeReqs <- typeReqs =<< callbackToTFn cb return $ cbClassReqs `mappend` fnTypeReqs Internal_TObj cls -> return $ classReqs cls Internal_TObjToHeap cls -> return $ classReqs cls@@ -643,3 +842,36 @@ fromMaybeM (abort $ concat ["findExportModule: Can't find module exporting ", fromExtName extName, "."]) =<< fmap (M.lookup extName . interfaceNamesToModules) askInterface++getEffectiveExceptionHandlers :: ExceptionHandlers -> Generator ExceptionHandlers+getEffectiveExceptionHandlers handlers = do+ ifaceHandlers <- interfaceExceptionHandlers <$> askInterface+ moduleHandlers <- getExceptionHandlers <$> askModule+ -- Exception handlers declared lower in the hierarchy take precedence over+ -- those in the hierarchy; ExceptionHandlers is a left-biased monoid.+ return $ mconcat [handlers, moduleHandlers, ifaceHandlers]++getEffectiveCallbackThrows :: Callback -> Generator Bool+getEffectiveCallbackThrows cb = case callbackThrows cb of+ Just b -> return b+ Nothing -> moduleCallbacksThrow <$> askModule >>= \case+ Just b -> return b+ Nothing -> interfaceCallbacksThrow <$> askInterface++-- | Constructs the function type for a callback. A callback that throws has+-- additional parameters.+--+-- Keep this in sync with the Haskell generator's version.+callbackToTFn :: Callback -> Generator Type+callbackToTFn cb = do+ throws <- mayThrow+ return $ Internal_TFn ((if throws then addExcParams else id) $ callbackParams cb)+ (callbackReturn cb)++ where mayThrow = case callbackThrows cb of+ Just t -> return t+ Nothing -> moduleCallbacksThrow <$> askModule >>= \mt -> case mt of+ Just t -> return t+ Nothing -> interfaceCallbacksThrow <$> askInterface++ addExcParams = (++ [ptrT intT, ptrT $ ptrT voidT])
src/Foreign/Hoppy/Generator/Language/Haskell.hs view
@@ -31,6 +31,10 @@ evalGenerator, execGenerator, renderPartial,+ askInterface,+ askModule,+ askModuleName,+ getModuleForExtName, withErrorContext, inFunction, -- * Exports@@ -40,40 +44,68 @@ addExports, -- * Imports addImports,- importHsModuleForExtName,+ -- * Language extensions+ addExtension, -- * Code generation sayLn, saysLn, ln, indent,+ indentSpaces, sayLet, toHsEnumTypeName,+ toHsEnumTypeName', toHsEnumCtorName,+ toHsEnumCtorName', toHsBitspaceTypeName,+ toHsBitspaceTypeName', toHsBitspaceValueName,+ toHsBitspaceValueName', toHsBitspaceToNumName,+ toHsBitspaceToNumName', toHsBitspaceClassName,+ toHsBitspaceClassName', toHsBitspaceFromValueName,+ toHsBitspaceFromValueName', toHsValueClassName,+ toHsValueClassName', toHsWithValuePtrName,+ toHsWithValuePtrName', toHsPtrClassName,+ toHsPtrClassName', toHsCastMethodName,+ toHsCastMethodName', toHsDownCastClassName,+ toHsDownCastClassName', toHsDownCastMethodName,+ toHsDownCastMethodName', toHsCastPrimitiveName,+ toHsCastPrimitiveName', toHsConstCastFnName,+ toHsConstCastFnName', toHsDataTypeName,+ toHsDataTypeName', toHsDataCtorName,- toHsClassDeleteFnName,- toHsClassDeleteFnPtrName,+ toHsDataCtorName',+ toHsClassDeleteFnName',+ toHsClassDeleteFnPtrName',+ toHsCtorName,+ toHsCtorName', toHsMethodName, toHsMethodName',+ toHsClassEntityName,+ toHsClassEntityName', toHsCallbackCtorName,+ toHsCallbackCtorName',+ toHsCallbackNewFunPtrFnName,+ toHsCallbackNewFunPtrFnName', toHsFnName,+ toHsFnName', toArgName, HsTypeSide (..), cppTypeToHsTypeAndUse, getClassHaskellConversion,+ callbackToTFn, prettyPrint, ) where @@ -81,7 +113,6 @@ import Control.Applicative ((<$>)) #endif import Control.Arrow (first)-import Control.Monad (when) #if MIN_VERSION_mtl(2,2,1) import Control.Monad.Except (Except, catchError, runExcept, throwError) #else@@ -99,9 +130,10 @@ #if !MIN_VERSION_base(4,8,0) import Data.Monoid (Monoid, mappend, mconcat, mempty) #endif+import qualified Data.Set as S import Data.Tuple (swap)-import Foreign.Hoppy.Generator.Common (capitalize, lowerFirst)-import Foreign.Hoppy.Generator.Spec+import Foreign.Hoppy.Generator.Common+import Foreign.Hoppy.Generator.Spec.Base import Foreign.Hoppy.Generator.Types import qualified Language.Haskell.Pretty as P import Language.Haskell.Syntax (@@ -250,19 +282,35 @@ -- | Context information for generating Haskell code. data Env = Env { envInterface :: Interface+ , envModule :: Module , envModuleName :: String } +-- | Returns the currently generating interface. askInterface :: Generator Interface askInterface = envInterface <$> ask +-- | Returns the currently generating module.+askModule :: Generator Module+askModule = envModule <$> ask++-- | Returns the currently generating module's Haskell module name. askModuleName :: Generator String askModuleName = envModuleName <$> ask +-- | Looks up the 'Module' containing a given external name, throwing an error+-- if it can't be found.+getModuleForExtName :: ExtName -> Generator Module+getModuleForExtName extName = inFunction "getModuleForExtName" $ do+ iface <- askInterface+ case M.lookup extName $ interfaceNamesToModules iface of+ Just mod -> return mod+ Nothing -> throwError $ "Can't find module for " ++ show extName+ -- | A partially-rendered 'Module'. Contains all of the module's bindings, but -- may be subject to further processing. data Partial = Partial- { partialModuleHsName :: String+ { partialModuleHsName :: String -- ^ This is just the module name. , partialOutput :: Output } @@ -286,40 +334,43 @@ -- ^ Lines of Haskell code (possibly empty). These lines may not contain -- the newline character in them. There is an implicit newline between each -- string, as given by @intercalate \"\\n\" . outputBody@.+ , outputExtensions :: S.Set String+ -- ^ Language extensions to enable via the @{-# LANGUAGE #-}@ pragma for the+ -- whole module. } instance Monoid Output where- mempty = Output mempty mempty mempty+ mempty = Output mempty mempty mempty mempty - (Output e i b) `mappend` (Output e' i' b') =- Output (e `mappend` e') (i `mappend` i') (b `mappend` b')+ (Output e i b x) `mappend` (Output e' i' b' x') =+ Output (e `mappend` e') (i `mappend` i') (b `mappend` b') (x `mappend` x') mconcat os = Output (mconcat $ map outputExports os) (mconcat $ map outputImports os) (mconcat $ map outputBody os)+ (mconcat $ map outputExtensions os) -- | Runs a generator action for the given interface and module name string. -- Returns an error message if an error occurred, otherwise the action's output -- together with its value.-runGenerator :: Interface -> String -> Generator a -> Either ErrorMsg (Partial, a)-runGenerator iface modName generator =- fmap (first (Partial modName) . swap) $+runGenerator :: Interface -> Module -> Generator a -> Either ErrorMsg (Partial, a)+runGenerator iface mod generator =+ let modName = getModuleName iface mod+ in fmap (first (Partial modName) . swap) $ #if MIN_VERSION_mtl(2,2,1)- runExcept $+ runExcept $ #endif- flip catchError (\msg -> throwError $ msg ++ ".") $- runWriterT $ runReaderT generator $ Env iface modName+ flip catchError (\msg -> throwError $ msg ++ ".") $+ runWriterT $ runReaderT generator $ Env iface mod modName -- | Runs a generator action and returns the its value.-evalGenerator :: Interface -> String -> Generator a -> Either ErrorMsg a-evalGenerator iface modName =- fmap snd . runGenerator iface modName+evalGenerator :: Interface -> Module -> Generator a -> Either ErrorMsg a+evalGenerator iface mod = fmap snd . runGenerator iface mod -- | Runs a generator action and returns its output.-execGenerator :: Interface -> String -> Generator a -> Either ErrorMsg Partial-execGenerator iface modName =- fmap fst . runGenerator iface modName+execGenerator :: Interface -> Module -> Generator a -> Either ErrorMsg Partial+execGenerator iface mod = fmap fst . runGenerator iface mod -- | Converts a 'Partial' into a complete Haskell module. renderPartial :: Partial -> String@@ -332,6 +383,11 @@ [ [ "---------- GENERATED FILE, EDITS WILL BE LOST ----------" , "" ]+ , case S.elems $ outputExtensions output of+ [] -> []+ extensions -> [ concat $ "{-# LANGUAGE " : intersperse ", " extensions ++ [" #-}"]+ , ""+ ] , case outputExports output of [] -> [concat ["module ", modName, " where"]] exports ->@@ -376,24 +432,10 @@ addImports :: HsImportSet -> Generator () addImports imports = tell mempty { outputImports = imports } --- | Imports all of the objects for the given external name into the current--- module. This is a no-op of the external name is defined in the current--- module.-importHsModuleForExtName :: ExtName -> Generator ()-importHsModuleForExtName extName = inFunction "importHsModuleForExtName" $ do- iface <- askInterface- case M.lookup extName $ interfaceNamesToModules iface of- Just ownerModule -> do- let ownerModuleName = getModuleName iface ownerModule- currentModuleName <- askModuleName- when (currentModuleName /= ownerModuleName) $- -- Yes, this currently imports the whole dang module to keep things- -- simple.- addImports $ hsWholeModuleImport ownerModuleName- Nothing ->- throwError $ concat- ["Couldn't find module for ", show extName,- " (maybe you forgot to include it in an exports list?)"]+-- | Adds a Haskell language extension to the current module.+addExtension :: String -> Generator ()+addExtension extensionName =+ tell $ mempty { outputExtensions = S.singleton extensionName } -- | Outputs a line of Haskell code. A newline will be added on the end of the -- input. Newline characters must not be given to this function.@@ -418,6 +460,11 @@ indent :: Generator a -> Generator a indent = censor $ \o -> o { outputBody = map (\x -> ' ':' ':x) $ outputBody o } +-- | Runs the given action, indenting all code output by the action N spaces.+indentSpaces :: Int -> Generator a -> Generator a+indentSpaces n = censor $ \o -> o { outputBody = map (\x -> indentation ++ x) $ outputBody o }+ where indentation = replicate n ' '+ -- | Takes a list of binding actions and a body action, and outputs a @let@ -- expression. By passing in 'Nothing' for the body, it will be omitted, so -- @let@ statements in @do@ blocks can be created as well. Output is of the@@ -442,11 +489,62 @@ sayLn "in" indent body +getExtNameModule :: ExtName -> Generator Module+getExtNameModule extName = inFunction "getExtNameModule" $ do+ iface <- askInterface+ fromMaybeM (throwError $ "Couldn't find module for " ++ show extName +++ " (is it included in a module's export list?)") $+ M.lookup extName $+ interfaceNamesToModules iface++-- | Returns a module's unique short name that should be used for a qualified+-- import of the module.+getModuleImportName :: Module -> Generator String+getModuleImportName mod = do+ iface <- askInterface+ fromMaybeM (throwError $ "Couldn't find a Haskell import name for " ++ show mod +++ " (is it included in the interface's module list?)") $+ M.lookup mod $+ interfaceHaskellModuleImportNames iface++-- | Adds a qualified import of the given external name's module into the current+-- module, and returns the qualified name of the import. If the external name+-- is defined in the current module, then this is a no-op and 'Nothing' is+-- returned.+importHsModuleForExtName :: ExtName -> Generator (Maybe String)+importHsModuleForExtName extName = do+ currentModule <- askModule+ owningModule <- getExtNameModule extName+ if currentModule == owningModule+ then return Nothing+ else do iface <- askInterface+ let fullName = getModuleName iface owningModule+ qualifiedName <- getModuleImportName owningModule+ addImports $ hsQualifiedImport fullName qualifiedName+ return $ Just qualifiedName++-- | Used like @addExtNameModule extName hsEntity@. @hsEntity@ is a name in+-- Haskell code that is generated from the definition of @extName@, and thus+-- lives in @extName@'s module. This function adds imports and returns a+-- qualified name as necessary to refer to the given entity.+addExtNameModule :: ExtName -> String -> Generator String+addExtNameModule extName hsEntity = do+ maybeImportName <- importHsModuleForExtName extName+ return $ case maybeImportName of+ Nothing -> hsEntity -- Same module.+ Just importName -> concat [importName, ".", hsEntity] -- Different module.+ -- | Internal helper function for constructing Haskell names from external -- names. Returns a name that is a suitable Haskell type name for the external -- name, and if given 'Const', then with @\"Const\"@ appended.-toHsTypeName :: Constness -> ExtName -> String+toHsTypeName :: Constness -> ExtName -> Generator String toHsTypeName cst extName =+ inFunction "toHsTypeName" $+ addExtNameModule extName $ toHsTypeName' cst extName++-- | Pure version of 'toHsTypeName' that doesn't create a qualified name.+toHsTypeName' :: Constness -> ExtName -> String+toHsTypeName' cst extName = (case cst of Const -> (++ "Const") Nonconst -> id) $@@ -455,67 +553,141 @@ [] -> [] -- | Returns the Haskell name for an enum.-toHsEnumTypeName :: CppEnum -> String-toHsEnumTypeName = toHsTypeName Nonconst . enumExtName+toHsEnumTypeName :: CppEnum -> Generator String+toHsEnumTypeName enum =+ inFunction "toHsEnumTypeName" $+ addExtNameModule (enumExtName enum) $ toHsEnumTypeName' enum +-- | Pure version of 'toHsEnumTypeName' that doesn't create a qualified name.+toHsEnumTypeName' :: CppEnum -> String+toHsEnumTypeName' = toHsTypeName' Nonconst . enumExtName+ -- | Constructs the data constructor name for a value in an enum. Like C++ and -- unlike say Java, Haskell enum values aren't in a separate enum-specific -- namespace, so we prepend the enum name to the value name to get the data -- constructor name. The value name is a list of words; see 'enumValueNames'.-toHsEnumCtorName :: CppEnum -> [String] -> String+toHsEnumCtorName :: CppEnum -> [String] -> Generator String toHsEnumCtorName enum words =- concat $ toHsEnumTypeName enum : "_" : map capitalize words+ inFunction "toHsEnumCtorName" $+ addExtNameModule (enumExtName enum) $ toHsEnumCtorName' enum words +-- | Pure version of 'toHsEnumCtorName' that doesn't create a qualified name.+toHsEnumCtorName' :: CppEnum -> [String] -> String+toHsEnumCtorName' enum words =+ concat $ enumValuePrefix enum : map capitalize words+ -- | Returns the Haskell name for a bitspace. See 'toHsEnumTypeName'.-toHsBitspaceTypeName :: Bitspace -> String-toHsBitspaceTypeName = toHsTypeName Nonconst . bitspaceExtName+toHsBitspaceTypeName :: Bitspace -> Generator String+toHsBitspaceTypeName bitspace =+ inFunction "toHsBitspaceTypeName" $+ addExtNameModule (bitspaceExtName bitspace) $ toHsBitspaceTypeName' bitspace +-- | Pure version of 'toHsBitspaceTypeName' that doesn't create a qualified name.+toHsBitspaceTypeName' :: Bitspace -> String+toHsBitspaceTypeName' = toHsTypeName' Nonconst . bitspaceExtName+ -- | Constructs the data constructor name for a value in a bitspace. See -- 'toHsEnumCtorName'.-toHsBitspaceValueName :: Bitspace -> [String] -> String+toHsBitspaceValueName :: Bitspace -> [String] -> Generator String toHsBitspaceValueName bitspace words =- lowerFirst $ concat $ toHsBitspaceTypeName bitspace : "_" : map capitalize words+ inFunction "toHsBitspaceValueName" $+ addExtNameModule (bitspaceExtName bitspace) $+ toHsBitspaceValueName' bitspace words +-- | Pure version of 'toHsBitspaceValueName' that doesn't create a qualified name.+toHsBitspaceValueName' :: Bitspace -> [String] -> String+toHsBitspaceValueName' bitspace words =+ lowerFirst $ concat $ bitspaceValuePrefix bitspace : map capitalize words+ -- | Returns the name of the function that will convert a bitspace value into a -- raw numeric value.-toHsBitspaceToNumName :: Bitspace -> String-toHsBitspaceToNumName = ("from" ++) . toHsBitspaceTypeName+toHsBitspaceToNumName :: Bitspace -> Generator String+toHsBitspaceToNumName bitspace =+ inFunction "toHsBitspaceToNumName" $+ addExtNameModule (bitspaceExtName bitspace) $ toHsBitspaceToNumName' bitspace +-- | Pure version of 'toHsBitspaceToNumName' that doesn't create a qualified name.+toHsBitspaceToNumName' :: Bitspace -> String+toHsBitspaceToNumName' = ("from" ++) . toHsBitspaceTypeName'+ -- | The name of the Haskell typeclass that contains a method for converting to -- a bitspace value.-toHsBitspaceClassName :: Bitspace -> String-toHsBitspaceClassName bitspace = 'I':'s':toHsBitspaceTypeName bitspace+toHsBitspaceClassName :: Bitspace -> Generator String+toHsBitspaceClassName bitspace =+ inFunction "toHsBitspaceClassName" $+ addExtNameModule (bitspaceExtName bitspace) $ toHsBitspaceClassName' bitspace +-- | Pure version of 'toHsBitspaceClassName' that doesn't create a qualified name.+toHsBitspaceClassName' :: Bitspace -> String+toHsBitspaceClassName' bitspace = 'I':'s':toHsBitspaceTypeName' bitspace+ -- | The name of the method in the 'toHsBitspaceClassName' typeclass that -- constructs bitspace values.-toHsBitspaceFromValueName :: Bitspace -> String-toHsBitspaceFromValueName = ("to" ++) . toHsBitspaceTypeName+toHsBitspaceFromValueName :: Bitspace -> Generator String+toHsBitspaceFromValueName bitspace =+ inFunction "toHsBitspaceFromValueName" $+ addExtNameModule (bitspaceExtName bitspace) $ toHsBitspaceFromValueName' bitspace +-- | Pure version of 'toHsBitspaceFromValueName' that doesn't create a qualified name.+toHsBitspaceFromValueName' :: Bitspace -> String+toHsBitspaceFromValueName' = ("to" ++) . toHsBitspaceTypeName'+ -- | The name for the typeclass of types that can be represented as values of -- the given C++ class.-toHsValueClassName :: Class -> String-toHsValueClassName cls = toHsDataTypeName Nonconst cls ++ "Value"+toHsValueClassName :: Class -> Generator String+toHsValueClassName cls =+ inFunction "toHsValueClassName" $+ addExtNameModule (classExtName cls) $ toHsValueClassName' cls +-- | Pure version of 'toHsValueClassName' that doesn't create a qualified name.+toHsValueClassName' :: Class -> String+toHsValueClassName' cls = toHsDataTypeName' Nonconst cls ++ "Value"+ -- | The name of the method within the 'toHsValueClassName' typeclass for -- accessing an object of the type as a pointer.-toHsWithValuePtrName :: Class -> String-toHsWithValuePtrName cls = concat ["with", toHsDataTypeName Nonconst cls, "Ptr"]+toHsWithValuePtrName :: Class -> Generator String+toHsWithValuePtrName cls =+ inFunction "toHsWithValuePtrName" $+ addExtNameModule (classExtName cls) $ toHsWithValuePtrName' cls +-- | Pure version of 'toHsWithValuePtrName' that doesn't create a qualified name.+toHsWithValuePtrName' :: Class -> String+toHsWithValuePtrName' cls = concat ["with", toHsDataTypeName' Nonconst cls, "Ptr"]+ -- | The name for the typeclass of types that are (possibly const) pointers to -- objects of the given C++ class, or subclasses.-toHsPtrClassName :: Constness -> Class -> String-toHsPtrClassName cst cls = toHsDataTypeName cst cls ++ "Ptr"+toHsPtrClassName :: Constness -> Class -> Generator String+toHsPtrClassName cst cls =+ inFunction "toHsPtrClassName" $+ addExtNameModule (classExtName cls) $ toHsPtrClassName' cst cls +-- | Pure version of 'toHsPtrClassName' that doesn't create a qualified name.+toHsPtrClassName' :: Constness -> Class -> String+toHsPtrClassName' cst cls = toHsDataTypeName' cst cls ++ "Ptr"+ -- | The name of the function that upcasts pointers to the specific class type -- and constness.-toHsCastMethodName :: Constness -> Class -> String-toHsCastMethodName cst cls = "to" ++ toHsDataTypeName cst cls+toHsCastMethodName :: Constness -> Class -> Generator String+toHsCastMethodName cst cls =+ inFunction "toHsCastMethodName" $+ addExtNameModule (classExtName cls) $ toHsCastMethodName' cst cls +-- | Pure version of 'toHsCastMethodName' that doesn't create a qualified name.+toHsCastMethodName' :: Constness -> Class -> String+toHsCastMethodName' cst cls = "to" ++ toHsDataTypeName' cst cls+ -- | The name of the typeclass that provides a method to downcast to a specific -- class type. See 'toHsDownCastMethodName'.-toHsDownCastClassName :: Constness -> Class -> String+toHsDownCastClassName :: Constness -> Class -> Generator String toHsDownCastClassName cst cls =- concat [toHsDataTypeName Nonconst cls,+ inFunction "toHsDownCastClassName" $+ addExtNameModule (classExtName cls) $ toHsDownCastClassName' cst cls++-- | Pure version of 'toHsDownCastClassName' that doesn't create a qualified+-- name.+toHsDownCastClassName' :: Constness -> Class -> String+toHsDownCastClassName' cst cls =+ concat [toHsDataTypeName' Nonconst cls, "Super", case cst of Const -> "Const"@@ -523,77 +695,167 @@ -- | The name of the function that downcasts pointers to the specific class type -- and constness.-toHsDownCastMethodName :: Constness -> Class -> String-toHsDownCastMethodName cst cls = "downTo" ++ toHsDataTypeName cst cls+toHsDownCastMethodName :: Constness -> Class -> Generator String+toHsDownCastMethodName cst cls =+ inFunction "toHsDownCastMethodName" $+ addExtNameModule (classExtName cls) $ toHsDownCastMethodName' cst cls +-- | Pure version of 'toHsDownCastMethodName' that doesn't create a qualified+-- name.+toHsDownCastMethodName' :: Constness -> Class -> String+toHsDownCastMethodName' cst cls = "downTo" ++ toHsDataTypeName' cst cls+ -- | The import name for the foreign function that casts between two specific -- pointer types. Used for upcasting and downcasting.-toHsCastPrimitiveName :: Class -> Class -> String-toHsCastPrimitiveName from to =- concat ["cast", toHsDataTypeName Nonconst from, "To", toHsDataTypeName Nonconst to]+--+-- We need to know which module the cast function resides in, and while we could+-- look this up, the caller always knows, so we just have them pass it in.+toHsCastPrimitiveName :: Class -> Class -> Class -> Generator String+toHsCastPrimitiveName descendentClass from to =+ inFunction "toHsCastPrimitiveName" $+ addExtNameModule (classExtName descendentClass) $ toHsCastPrimitiveName' from to +-- | Pure version of 'toHsCastPrimitiveName' that doesn't create a qualified+-- name.+toHsCastPrimitiveName' :: Class -> Class -> String+toHsCastPrimitiveName' from to =+ concat ["cast", toHsDataTypeName' Nonconst from, "To", toHsDataTypeName' Nonconst to]+ -- | The name of one of the functions that add/remove const to/from a class's -- pointer type. Given 'Const', it will return the function that adds const, -- and given 'Nonconst', it will return the function that removes const.-toHsConstCastFnName :: Constness -> Class -> String+toHsConstCastFnName :: Constness -> Class -> Generator String toHsConstCastFnName cst cls =- concat ["cast", toHsDataTypeName Nonconst cls,+ inFunction "toHsConstCastFnName" $+ addExtNameModule (classExtName cls) $ toHsConstCastFnName' cst cls++-- | Pure version of 'toHsConstCastFnName' that doesn't create a qualified name.+toHsConstCastFnName' :: Constness -> Class -> String+toHsConstCastFnName' cst cls =+ concat ["cast", toHsDataTypeName' Nonconst cls, case cst of Const -> "ToConst" Nonconst -> "ToNonconst"] -- | The name of the data type that represents a pointer to an object of the -- given class and constness.-toHsDataTypeName :: Constness -> Class -> String-toHsDataTypeName cst cls = toHsTypeName cst $ classExtName cls+toHsDataTypeName :: Constness -> Class -> Generator String+toHsDataTypeName cst cls =+ inFunction "toHsDataTypeName" $+ addExtNameModule (classExtName cls) $ toHsDataTypeName' cst cls +-- | Pure version of 'toHsDataTypeName' that doesn't create a qualified name.+toHsDataTypeName' :: Constness -> Class -> String+toHsDataTypeName' cst cls = toHsTypeName' cst $ classExtName cls+ -- | The name of a data constructor for one of the object pointer types.-toHsDataCtorName :: Managed -> Constness -> Class -> String-toHsDataCtorName m cst cls = case m of+toHsDataCtorName :: Managed -> Constness -> Class -> Generator String+toHsDataCtorName m cst cls =+ inFunction "toHsDataCtorName" $+ addExtNameModule (classExtName cls) $ toHsDataCtorName' m cst cls++-- | Pure version of 'toHsDataCtorName' that doesn't create a qualified name.+toHsDataCtorName' :: Managed -> Constness -> Class -> String+toHsDataCtorName' m cst cls = case m of Unmanaged -> base Managed -> base ++ "Gc"- where base = toHsDataTypeName cst cls+ where base = toHsDataTypeName' cst cls -- | The name of the foreign function import wrapping @delete@ for the given -- class type. This is in internal to the binding; normal users should use -- 'Foreign.Hoppy.Runtime.delete'.-toHsClassDeleteFnName :: Class -> String-toHsClassDeleteFnName cls = 'd':'e':'l':'e':'t':'e':'\'':toHsDataTypeName Nonconst cls+--+-- This is internal to a generated Haskell module, so it does not have a public+-- (qualified) form.+toHsClassDeleteFnName' :: Class -> String+toHsClassDeleteFnName' cls = 'd':'e':'l':'e':'t':'e':'\'':toHsDataTypeName' Nonconst cls -- | The name of the foreign import that imports the same function as -- 'toHsClassDeleteFnName', but as a 'Foreign.Ptr.FunPtr' rather than an actual -- function.-toHsClassDeleteFnPtrName :: Class -> String-toHsClassDeleteFnPtrName cls =- 'd':'e':'l':'e':'t':'e':'P':'t':'r':'\'':toHsDataTypeName Nonconst cls+--+-- This is internal to a generated Haskell module, so it does not have a public+-- (qualified) form.+toHsClassDeleteFnPtrName' :: Class -> String+toHsClassDeleteFnPtrName' cls =+ 'd':'e':'l':'e':'t':'e':'P':'t':'r':'\'':toHsDataTypeName' Nonconst cls +-- | Returns the name of the Haskell function that invokes the given+-- constructor.+toHsCtorName :: Class -> Ctor -> Generator String+toHsCtorName cls ctor =+ inFunction "toHsCtorName" $+ toHsClassEntityName cls $ fromExtName $ ctorExtName ctor++-- | Pure version of 'toHsCtorName' that doesn't create a qualified name.+toHsCtorName' :: Class -> Ctor -> String+toHsCtorName' cls ctor =+ toHsClassEntityName' cls $ fromExtName $ ctorExtName ctor+ -- | Returns the name of the Haskell function that invokes the given method.------ See also 'getClassyExtName'.-toHsMethodName :: Class -> Method -> String-toHsMethodName cls method = toHsMethodName' cls $ fromExtName $ methodExtName method+toHsMethodName :: Class -> Method -> Generator String+toHsMethodName cls method =+ inFunction "toHsMethodName" $+ toHsClassEntityName cls $ fromExtName $ methodExtName method --- | Returns the name of the Haskell function that invokes a method with a--- specific name in a class.-toHsMethodName' :: IsFnName String name => Class -> name -> String-toHsMethodName' cls methodName =- lowerFirst $- concat [fromExtName $ classExtName cls, "_",- case toFnName methodName of- FnName name -> name- FnOp op -> fromExtName $ operatorPreferredExtName op]+-- | Pure version of 'toHsMethodName' that doesn't create a qualified name.+toHsMethodName' :: Class -> Method -> String+toHsMethodName' cls method =+ toHsClassEntityName' cls $ fromExtName $ methodExtName method +-- | Returns the name of the Haskell function for an entity in a class.+toHsClassEntityName :: IsFnName String name => Class -> name -> Generator String+toHsClassEntityName cls name =+ addExtNameModule (classExtName cls) $ toHsClassEntityName' cls name++-- | Pure version of 'toHsClassEntityName' that doesn't create a qualified name.+toHsClassEntityName' :: IsFnName String name => Class -> name -> String+toHsClassEntityName' cls name =+ lowerFirst $ fromExtName $+ classEntityForeignName' cls $+ case toFnName name of+ FnName name -> toExtName name+ FnOp op -> operatorPreferredExtName op+ -- | The name of the function that takes a Haskell function and wraps it in a -- callback object. This is internal to the binding; normal users can pass -- Haskell functions to be used as callbacks inplicitly.-toHsCallbackCtorName :: Callback -> String-toHsCallbackCtorName = toHsFnName . callbackExtName+toHsCallbackCtorName :: Callback -> Generator String+toHsCallbackCtorName callback =+ inFunction "toHsCallbackCtorName" $+ addExtNameModule (callbackExtName callback) $ toHsCallbackCtorName' callback +-- | Pure version of 'toHsCallbackCtorName' that doesn't create a qualified+-- name.+toHsCallbackCtorName' :: Callback -> String+toHsCallbackCtorName' callback =+ toHsFnName' $ toExtName $ fromExtName (callbackExtName callback) ++ "_new"++-- | The name of the function that takes a Haskell function with Haskell-side+-- types and wraps it in a 'Foreign.Ptr.FunPtr' that does appropriate+-- conversions to and from C-side types.+toHsCallbackNewFunPtrFnName :: Callback -> Generator String+toHsCallbackNewFunPtrFnName callback =+ inFunction "toHsCallbackNewFunPtrFnName" $+ addExtNameModule (callbackExtName callback) $ toHsCallbackNewFunPtrFnName' callback++-- | Pure version of 'toHsCallbackNewFunPtrFnName' that doesn't create a qualified+-- name.+toHsCallbackNewFunPtrFnName' :: Callback -> String+toHsCallbackNewFunPtrFnName' callback =+ toHsFnName' $ toExtName $ fromExtName (callbackExtName callback) ++ "_newFunPtr"+ -- | Converts an external name into a name suitable for a Haskell function or -- variable.-toHsFnName :: ExtName -> String-toHsFnName = lowerFirst . fromExtName+toHsFnName :: ExtName -> Generator String+toHsFnName extName =+ inFunction "toHsFnName" $+ addExtNameModule extName $ toHsFnName' extName +-- | Pure version of 'toHsFnName' that doesn't create a qualified name.+toHsFnName' :: ExtName -> String+toHsFnName' = lowerFirst . fromExtName+ -- | Returns a distinct argument variable name for each nonnegative number. toArgName :: Int -> String toArgName = ("arg'" ++) . show@@ -652,15 +914,16 @@ addImports hsImportForSystemPosixTypes $> HsTyCon (UnQual $ HsIdent "HoppySPT.CSsize") Internal_TEnum e -> HsTyCon . UnQual . HsIdent <$> case side of HsCSide -> addImports hsImportForForeignC $> "HoppyFC.CInt"- HsHsSide -> importHsModuleForExtName (enumExtName e) $> toHsEnumTypeName e+ HsHsSide -> toHsEnumTypeName e Internal_TBitspace b -> case side of HsCSide -> cppTypeToHsTypeAndUse side $ bitspaceType b- HsHsSide -> importHsModuleForExtName (bitspaceExtName b) $>- HsTyCon (UnQual $ HsIdent $ toHsBitspaceTypeName b)+ HsHsSide -> do+ typeName <- toHsBitspaceTypeName b+ return $ HsTyCon $ UnQual $ HsIdent typeName Internal_TPtr (Internal_TObj cls) -> do -- Same as TPtr (TConst (TObj cls)), but nonconst.- importHsModuleForExtName (classExtName cls)- let dataType = HsTyCon $ UnQual $ HsIdent $ toHsTypeName Nonconst $ classExtName cls+ typeName <- toHsTypeName Nonconst $ classExtName cls+ let dataType = HsTyCon $ UnQual $ HsIdent typeName case side of HsCSide -> do addImports hsImportForForeign@@ -668,23 +931,16 @@ HsHsSide -> return dataType Internal_TPtr (Internal_TConst (Internal_TObj cls)) -> do -- Same as TPtr (TObj cls), but const.- importHsModuleForExtName (classExtName cls)- let dataType = HsTyCon $ UnQual $ HsIdent $ toHsTypeName Const $ classExtName cls+ typeName <- toHsTypeName Const $ classExtName cls+ let dataType = HsTyCon $ UnQual $ HsIdent typeName case side of HsCSide -> do addImports hsImportForForeign return $ HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyF.Ptr") dataType HsHsSide -> return dataType- Internal_TPtr (Internal_TFn paramTypes retType) -> do- paramHsTypes <- mapM (cppTypeToHsTypeAndUse side) paramTypes- retHsType <- cppTypeToHsTypeAndUse side retType- sideFn <- case side of- HsCSide -> do addImports hsImportForForeign- return $ HsTyApp $ HsTyCon $ UnQual $ HsIdent "HoppyF.FunPtr"- HsHsSide -> return id- addImports hsImportForPrelude- return $ sideFn $- foldr HsTyFun (HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyP.IO") retHsType) paramHsTypes+ Internal_TPtr fn@(Internal_TFn {}) -> do+ addImports hsImportForForeign+ HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyF.FunPtr") <$> cppTypeToHsTypeAndUse HsCSide fn Internal_TPtr t' -> do addImports hsImportForForeign -- Pointers to types not covered above point to raw C++ values, so we need@@ -698,7 +954,7 @@ return $ foldr HsTyFun (HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyP.IO") retHsType) paramHsTypes Internal_TCallback cb -> do- hsType <- cppTypeToHsTypeAndUse side $ callbackToTFn cb+ hsType <- cppTypeToHsTypeAndUse side =<< callbackToTFn side cb case side of HsHsSide -> return hsType HsCSide -> do@@ -706,11 +962,11 @@ return $ HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyFHR.CCallback") hsType Internal_TObj cls -> case side of HsCSide -> cppTypeToHsTypeAndUse side $ ptrT $ constT t- HsHsSide -> case getClassHaskellConversion cls of+ HsHsSide -> case classHaskellConversionType $ getClassHaskellConversion cls of+ Just typeGen -> typeGen Nothing -> throwError $ concat ["Expected a Haskell type for ", show cls, " but there isn't one"]- Just c -> classHaskellConversionType c Internal_TObjToHeap cls -> cppTypeToHsTypeAndUse side $ ptrT $ objT cls Internal_TToGc t' -> case t' of Internal_TRef _ -> cppTypeToHsTypeAndUse side t' -- References behave the same as pointers.@@ -719,17 +975,36 @@ _ -> throwError $ tToGcInvalidFormErrorMessage Nothing t' Internal_TConst t' -> cppTypeToHsTypeAndUse side t' --- | Returns the 'ClassHaskellConversion' of a class, if it has one.-getClassHaskellConversion :: Class -> Maybe ClassHaskellConversion-getClassHaskellConversion cls = case classHaskellConversion $ classConversion cls of- ClassConversionNone -> Nothing- ClassConversionManual c -> Just c- ClassConversionToHeap -> Nothing- ClassConversionToGc -> Nothing+-- | Returns the 'ClassHaskellConversion' of a class.+getClassHaskellConversion :: Class -> ClassHaskellConversion+getClassHaskellConversion = classHaskellConversion . classConversion +-- | Constructs the function type for a callback. For Haskell, the type depends+-- on the side; the C++ side has additional parameters.+--+-- Keep this in sync with the C++ generator's version.+callbackToTFn :: HsTypeSide -> Callback -> Generator Type+callbackToTFn side cb = do+ needsExcParams <- case side of+ HsCSide -> mayThrow+ HsHsSide -> return False+ return $ Internal_TFn ((if needsExcParams then addExcParams else id) $ callbackParams cb)+ (callbackReturn cb)++ where mayThrow = case callbackThrows cb of+ Just t -> return t+ Nothing -> moduleCallbacksThrow <$> askModule >>= \mt -> case mt of+ Just t -> return t+ Nothing -> interfaceCallbacksThrow <$> askInterface++ addExcParams = (++ [ptrT intT, ptrT $ ptrT voidT])+ -- | Prints a value like 'P.prettyPrint', but removes newlines so that they -- don't cause problems with this module's textual generation. Should be mainly -- used for printing types; stripping newlines from definitions for example -- could go badly. prettyPrint :: P.Pretty a => a -> String-prettyPrint = filter (/= '\n') . P.prettyPrint+prettyPrint = collapseSpaces . filter (/= '\n') . P.prettyPrint+ where collapseSpaces (' ':xs) = ' ' : collapseSpaces (dropWhile (== ' ') xs)+ collapseSpaces (x:xs) = x : collapseSpaces xs+ collapseSpaces [] = []
src/Foreign/Hoppy/Generator/Language/Haskell/Internal.hs view
@@ -27,1336 +27,1659 @@ #if !MIN_VERSION_base(4,8,0) import Control.Applicative ((<$>), (<*>), pure) #endif-import Control.Arrow ((&&&), second)-import Control.Monad (forM, unless, when)-#if MIN_VERSION_mtl(2,2,1)-import Control.Monad.Except (throwError)-#else-import Control.Monad.Error (throwError)-#endif-import Control.Monad.Trans (lift)-import Control.Monad.Writer (execWriterT, tell)-import Data.Foldable (forM_)-import Data.Graph (SCC (AcyclicSCC, CyclicSCC), stronglyConnComp)-import Data.List (intersperse)-import qualified Data.Map as M-import Data.Maybe (isJust, mapMaybe)-#if !MIN_VERSION_base(4,8,0)-import Data.Monoid (mconcat)-#endif-import qualified Data.Set as S-import Foreign.Hoppy.Generator.Common-import Foreign.Hoppy.Generator.Spec-import Foreign.Hoppy.Generator.Types-import Foreign.Hoppy.Generator.Language.Cpp (- classCastFnCppName,- classDeleteFnCppName,- externalNameToCpp,- )-import Foreign.Hoppy.Generator.Language.Haskell-import Language.Haskell.Syntax (- HsAsst,- HsContext,- HsName (HsIdent),- HsQName (Special, UnQual),- HsQualType (HsQualType),- HsSpecialCon (HsUnitCon),- HsType (HsTyApp, HsTyCon, HsTyFun, HsTyVar),- )-import System.FilePath ((<.>), pathSeparator)---- | The in-memory result of generating Haskell code for an interface.-data Generation = Generation- { generatedFiles :: M.Map FilePath String- -- ^ A map from paths of generated files to the contents of those files.- -- The file paths are relative paths below the Haskell generation root.- }---- | Runs the C++ code generator against an interface.-generate :: Interface -> Either ErrorMsg Generation-generate iface = do- -- Build the partial generation of each module.- modPartials <- forM (M.elems $ interfaceModules iface) $ \m ->- (,) m <$> execGenerator iface (getModuleName iface m) (generateSource m)-- -- Compute the strongly connected components. If there is a nontrivial SCC,- -- then there is a module import cycle that we'll have to break with hs-boot- -- files.- let partialsByHsName :: M.Map HsModuleName Partial- partialsByHsName = M.fromList $ map ((partialModuleHsName &&& id) . snd) modPartials-- sccInput :: [((Module, Partial), Partial, [Partial])]- sccInput = flip map modPartials $ \x@(_, p) ->- (x, p,- mapMaybe (flip M.lookup partialsByHsName . hsImportModule) $- M.keys $ getHsImportSet $ outputImports $ partialOutput p)-- sccs :: [SCC (Module, Partial)]- sccs = stronglyConnComp sccInput-- fileContents <- execWriterT $ forM_ sccs $ \scc -> case scc of- AcyclicSCC (_, p) -> tell [finishPartial p "hs"]- CyclicSCC mps -> do- let cycleModNames = S.fromList $ map (partialModuleHsName . snd) mps- forM_ mps $ \(m, p) -> do- -- Create a boot partial.- pBoot <- lift $ execGenerator iface (partialModuleHsName p) (generateBootSource m)-- -- Change the source and boot partials so that all imports of modules in- -- this cycle are {-# SOURCE #-} imports.- let p' = setSourceImports cycleModNames p- pBoot' = setSourceImports cycleModNames pBoot-- -- Emit the completed partials.- tell [finishPartial p' "hs", finishPartial pBoot' "hs-boot"]-- return $ Generation $ M.fromList fileContents-- where finishPartial :: Partial -> String -> (FilePath, String)- finishPartial p fileExt =- (listSubst '.' pathSeparator (partialModuleHsName p) <.> fileExt,- prependExtensions $ renderPartial p)-- setSourceImports :: S.Set HsModuleName -> Partial -> Partial- setSourceImports modulesToSourceImport p =- let output = partialOutput p- imports = outputImports output- imports' = makeHsImportSet $- M.mapWithKey (setSourceImportIfIn modulesToSourceImport) $- getHsImportSet imports- output' = output { outputImports = imports' }- in p { partialOutput = output' }-- setSourceImportIfIn :: S.Set HsModuleName -> HsImportKey -> HsImportSpecs -> HsImportSpecs- setSourceImportIfIn modulesToSourceImport key specs =- if hsImportModule key `S.member` modulesToSourceImport- then specs { hsImportSource = True }- else specs--prependExtensions :: String -> String-prependExtensions = (prependExtensionsPrefix ++)--prependExtensionsPrefix :: String-prependExtensionsPrefix =- -- MultiParamTypeClasses is necessary for instances of Decodable and- -- Encodable. FlexibleContexts is needed for the type signature of the- -- function that wraps the actual callback function in callback creation- -- functions.- --- -- FlexibleInstances and TypeSynonymInstances are enabled to allow conversions- -- to and from String, which is really [Char].- --- -- UndecidableInstances is needed for instances of the form "SomeClassConstPtr- -- a => SomeClassValue a", and overlapping instances are used for the overlap- -- between these instances and instances of SomeClassValue for the class's- -- native Haskell type, when it's convertible. CPP is used for warning-free- -- compatibility using overlapping instances with both GHC 7.8 and 7.10.- --- -- GeneralizedNewtypeDeriving is to enable automatic deriving of- -- Data.Bits.Bits instances for bitspace newtypes.- concat- [ "{-# LANGUAGE CPP, FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving"- , ", MultiParamTypeClasses, TypeSynonymInstances, UndecidableInstances #-}\n"- , "#if !MIN_VERSION_base(4,8,0)\n"- , "{-# LANGUAGE OverlappingInstances #-}\n"- , "#endif\n\n"- ]--generateSource :: Module -> Generator ()-generateSource m = do- forM_ (moduleExports m) $ sayExport SayExportForeignImports- forM_ (moduleExports m) $ sayExport SayExportDecls--generateBootSource :: Module -> Generator ()-generateBootSource m =- forM_ (moduleExports m) $ sayExport SayExportBoot--data SayExportMode = SayExportForeignImports | SayExportDecls | SayExportBoot- deriving (Eq, Show)--sayExport :: SayExportMode -> Export -> Generator ()-sayExport mode export = do- case export of- ExportVariable v -> sayExportVar mode v- ExportEnum enum -> sayExportEnum mode enum- ExportBitspace bitspace -> sayExportBitspace mode bitspace- ExportFn fn ->- (sayExportFn mode <$> fnExtName <*> pure Nothing <*> fnPurity <*> fnParams <*> fnReturn) fn- ExportClass cls -> sayExportClass mode cls- ExportCallback cb -> sayExportCallback mode cb-- when (mode == SayExportDecls) $- addendumHaskell $ exportAddendum export--sayExportVar :: SayExportMode -> Variable -> Generator ()-sayExportVar mode v = do- withErrorContext ("generating variable " ++ show (varExtName v)) $ do- let (isConst, deconstType) = case varType v of- Internal_TConst t -> (True, t)- t -> (False, t)- sayExportFn mode (varGetterExtName v) Nothing Nonpure [] deconstType- unless isConst $- sayExportFn mode (varSetterExtName v) Nothing Nonpure [deconstType] voidT--sayExportEnum :: SayExportMode -> CppEnum -> Generator ()-sayExportEnum mode enum =- withErrorContext ("generating enum " ++ show (enumExtName enum)) $- case mode of- -- Nothing to import from the C++ side of an enum.- SayExportForeignImports -> return ()-- SayExportDecls -> do- let hsTypeName = toHsEnumTypeName enum- values :: [(Int, String)]- values = map (second $ toHsEnumCtorName enum) $ enumValueNames enum- addImports $ mconcat [hsImports "Prelude" ["($)", "(++)"], hsImportForPrelude]-- -- Print out the data declaration.- ln- addExport' hsTypeName- saysLn ["data ", hsTypeName, " ="]- indent $ do- forM_ (zip (False:repeat True) values) $ \(cont, (_, hsCtorName)) ->- saysLn [if cont then "| " else "", hsCtorName]- sayLn "deriving (HoppyP.Bounded, HoppyP.Eq, HoppyP.Ord, HoppyP.Show)"-- -- Print out the Enum instance.- ln- saysLn ["instance HoppyP.Enum ", hsTypeName, " where"]- indent $ do- forM_ values $ \(num, hsCtorName) ->- saysLn ["fromEnum ", hsCtorName, " = ", show num]- ln- forM_ values $ \(num, hsCtorName) ->- saysLn ["toEnum (", show num, ") = ", hsCtorName]- saysLn ["toEnum n' = HoppyP.error $ ",- show (concat ["Unknown ", hsTypeName, " numeric value: "]),- " ++ HoppyP.show n'"]-- SayExportBoot -> do- let hsTypeName = toHsEnumTypeName enum- addImports hsImportForPrelude- addExport hsTypeName- ln- saysLn ["data ", hsTypeName]- saysLn ["instance HoppyP.Bounded ", hsTypeName]- saysLn ["instance HoppyP.Enum ", hsTypeName]- saysLn ["instance HoppyP.Eq ", hsTypeName]- saysLn ["instance HoppyP.Ord ", hsTypeName]- saysLn ["instance HoppyP.Show ", hsTypeName]--sayExportBitspace :: SayExportMode -> Bitspace -> Generator ()-sayExportBitspace mode bitspace =- withErrorContext ("generating bitspace " ++ show (bitspaceExtName bitspace)) $- let hsTypeName = toHsBitspaceTypeName bitspace- fromFnName = toHsBitspaceToNumName bitspace- className = toHsBitspaceClassName bitspace- toFnName = toHsBitspaceFromValueName bitspace- hsType = HsTyCon $ UnQual $ HsIdent hsTypeName- in case mode of- -- Nothing to import from the C++ side of a bitspace.- SayExportForeignImports -> return ()-- SayExportDecls -> do- let values :: [(Int, String)]- values = map (second $ toHsBitspaceValueName bitspace) $ bitspaceValueNames bitspace-- hsCNumType <- cppTypeToHsTypeAndUse HsCSide $ bitspaceType bitspace- hsHsNumType <- cppTypeToHsTypeAndUse HsHsSide $ bitspaceType bitspace-- -- Print out the data declaration and conversion functions.- addImports $ mconcat [hsImportForBits, hsImportForPrelude, hsImportForRuntime]- addExport' hsTypeName- addExport' className- ln- saysLn ["newtype ", hsTypeName, " = ", hsTypeName, " { ",- fromFnName, " :: ", prettyPrint hsCNumType, " }"]- indent $ sayLn "deriving (HoppyDB.Bits, HoppyP.Bounded, HoppyP.Eq, HoppyP.Ord, HoppyP.Show)"- ln- saysLn ["class ", className, " a where"]- indent $ do- let tyVar = HsTyVar $ HsIdent "a"- saysLn [toFnName, " :: ", prettyPrint $ HsTyFun tyVar hsType]- ln- saysLn ["instance ", className, " (", prettyPrint hsCNumType, ") where"]- indent $ saysLn [toFnName, " = ", hsTypeName]- saysLn ["instance ", className, " (", prettyPrint hsHsNumType, ") where"]- indent $ saysLn [toFnName, " = ", hsTypeName, " . HoppyFHR.coerceIntegral"]- saysLn ["instance ", className, " ", hsTypeName, " where"]- indent $ saysLn [toFnName, " = HoppyP.id"]-- -- If the bitspace has an associated enum, then print out a conversion- -- instance for it as well.- forM_ (bitspaceEnum bitspace) $ \enum -> do- let enumTypeName = toHsEnumTypeName enum- importHsModuleForExtName $ enumExtName enum- addImports $ mconcat [hsImport1 "Prelude" "(.)", hsImportForPrelude, hsImportForRuntime]- ln- saysLn ["instance ", className, " ", enumTypeName, " where"]- indent $- saysLn [toFnName, " = ", hsTypeName, " . HoppyFHR.coerceIntegral . HoppyP.fromEnum"]-- -- Print out the constants.- ln- forM_ values $ \(num, valueName) -> do- addExport valueName- saysLn [valueName, " = ", hsTypeName, " ", show num]-- SayExportBoot -> do- hsCNumType <- cppTypeToHsTypeAndUse HsCSide $ bitspaceType bitspace- hsHsNumType <- cppTypeToHsTypeAndUse HsHsSide $ bitspaceType bitspace-- addImports $ mconcat [hsImportForBits, hsImportForPrelude]- addExport' hsTypeName- addExport' className- ln- saysLn ["newtype ", hsTypeName, " = ", hsTypeName, " { ",- fromFnName, " :: ", prettyPrint hsCNumType, " }"]- ln- saysLn ["instance HoppyDB.Bits ", hsTypeName]- saysLn ["instance HoppyP.Bounded ", hsTypeName]- saysLn ["instance HoppyP.Eq ", hsTypeName]- saysLn ["instance HoppyP.Ord ", hsTypeName]- saysLn ["instance HoppyP.Show ", hsTypeName]- ln- saysLn ["class ", className, " a where"]- indent $ do- let tyVar = HsTyVar $ HsIdent "a"- saysLn [toFnName, " :: ", prettyPrint $ HsTyFun tyVar hsType]- ln- saysLn ["instance ", className, " (", prettyPrint hsCNumType, ")"]- saysLn ["instance ", className, " (", prettyPrint hsHsNumType, ")"]- saysLn ["instance ", className, " ", hsTypeName]- forM_ (bitspaceEnum bitspace) $ \enum -> do- let enumTypeName = toHsEnumTypeName enum- importHsModuleForExtName $ enumExtName enum- saysLn ["instance ", className, " ", enumTypeName]--sayExportFn :: SayExportMode- -> ExtName- -> Maybe (Constness, Class)- -> Purity- -> [Type]- -> Type- -> Generator ()-sayExportFn mode name methodInfo purity paramTypes retType =- let hsFnName = toHsFnName name- hsFnImportedName = hsFnName ++ "'"- in case mode of- SayExportForeignImports ->- withErrorContext ("generating imports for function " ++ show name) $ do- -- Print a "foreign import" statement.- hsCType <- fnToHsTypeAndUse HsCSide methodInfo purity paramTypes retType- saysLn ["foreign import ccall \"", externalNameToCpp name, "\" ", hsFnImportedName, " :: ",- prettyPrint hsCType]-- SayExportDecls -> withErrorContext ("generating function " ++ show name) $ do- -- Print the type signature.- ln- addExport hsFnName- hsHsType <- fnToHsTypeAndUse HsHsSide methodInfo purity paramTypes retType- saysLn [hsFnName, " :: ", prettyPrint hsHsType]-- case purity of- Nonpure -> return ()- Pure -> saysLn ["{-# NOINLINE ", hsFnName, " #-}"]-- -- Print the function body.- let argNames = map toArgName [1..length paramTypes]- argNamesWithThis = (if isJust methodInfo then ("this":) else id) argNames- convertedArgNames = map (++ "'") argNames- -- Operators on this line must bind more weakly than operators used below,- -- namely ($) and (>>=). (So finish the line with ($).)- lineEnd <- case purity of- Nonpure -> return [" ="]- Pure -> do addImports $ mconcat [hsImport1 "Prelude" "($)", hsImportForUnsafeIO]- return [" = HoppySIU.unsafePerformIO $"]- saysLn $ hsFnName : map (' ':) argNamesWithThis ++ lineEnd- indent $ do- forM_ (zip3 paramTypes argNames convertedArgNames) $ \(t, argName, argName') ->- sayArgProcessing ToCpp t argName argName'-- sayCallAndProcessReturn ToCpp retType $- hsFnImportedName :- (case methodInfo of- Just (cst, cls) -> " (" ++ toHsCastMethodName cst cls ++ " this)"- Nothing -> "") :- map (' ':) convertedArgNames-- SayExportBoot ->- -- Functions (methods included) cannot be referenced from other exports,- -- so we don't need to emit anything.- return ()---- | Prints \"foreign import\" statements and an internal callback construction--- function for a given 'Callback' specification. For example, for a callback--- of 'HsHsSide' type @Int -> String -> IO Int@, we will generate the following--- bindings:------ > foreign import ccall "wrapper" name'newFunPtr--- > :: (CInt -> Ptr CChar -> IO CInt)--- > -> IO (FunPtr (CInt -> Ptr CChar -> IO CInt))--- >--- > -- (This is an ad-hoc generated binding for C++ callback impl class constructor.)--- > foreign import ccall "genpop__name_impl" name'newCallback--- > :: FunPtr (CInt -> Ptr CChar -> IO CInt)--- > -> FunPtr (FunPtr (IO ()) -> IO ())--- > -> Bool--- > -> IO (CCallback (CInt -> Ptr CChar -> IO CInt))--- >--- > name :: (CInt -> String -> IO CInt) -> IO (CCallback (CInt -> Ptr CChar -> IO CInt))--- > name f = do--- > let cf arg1' arg2' = do--- > arg1 <- return arg1'--- > arg2 <- ...decode the string...--- > f arg1 arg2 >>= return--- > cfp <- name'newFunPtr cf--- > name'newCallback cfp freeHaskellFunPtrFunPtr False------ Only the implementation of bindings that take a callback of this type will--- make use of this @name@ binding; @name@ is not useful to users of bindings.-sayExportCallback :: SayExportMode -> Callback -> Generator ()-sayExportCallback mode cb =- withErrorContext ("generating callback " ++ show (callbackExtName cb)) $ do- let name = callbackExtName cb- paramTypes = callbackParams cb- retType = callbackReturn cb- fnType = callbackToTFn cb- hsFnName = toHsCallbackCtorName cb- hsFnName'newCallback = hsFnName ++ "'newCallback"- hsFnName'newFunPtr = hsFnName ++ "'newFunPtr"-- hsFnCType <- cppTypeToHsTypeAndUse HsCSide fnType- hsFnHsType <- cppTypeToHsTypeAndUse HsHsSide fnType-- let getWholeFnType = do- addImports $ mconcat [hsImportForPrelude, hsImportForRuntime]- return $- HsTyFun hsFnHsType $- HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyP.IO") $- HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyFHR.CCallback") hsFnCType-- case mode of- SayExportForeignImports -> do- addImports $ mconcat [hsImportForForeign, hsImportForPrelude, hsImportForRuntime]- let hsFunPtrType = HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyF.FunPtr") hsFnCType- hsFunPtrImportType =- HsTyFun hsFnCType $- HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyP.IO") hsFunPtrType- hsCallbackCtorImportType =- HsTyFun hsFunPtrType $- HsTyFun (HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyF.FunPtr") $- HsTyFun (HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyF.FunPtr") $- HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyP.IO") $- HsTyCon $ Special HsUnitCon) $- HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyP.IO") $- HsTyCon $ Special HsUnitCon) $- HsTyFun (HsTyCon $ UnQual $ HsIdent "HoppyP.Bool") $- HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyP.IO") $- HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyFHR.CCallback") hsFnCType-- saysLn ["foreign import ccall \"wrapper\" ", hsFnName'newFunPtr, " :: ",- prettyPrint hsFunPtrImportType]- saysLn ["foreign import ccall \"", externalNameToCpp name, "\" ",- hsFnName'newCallback, " :: ", prettyPrint hsCallbackCtorImportType]-- SayExportDecls -> do- addExport hsFnName- wholeFnType <- getWholeFnType- let paramCount = length paramTypes- argNames = map toArgName [1..paramCount]- argNames' = map (++ "'") argNames- ln- saysLn [hsFnName, " :: ", prettyPrint wholeFnType]- saysLn [hsFnName, " f'hs = do"]- indent $ do- sayLet- [do saysLn ["f'c ", unwords argNames, " ="]- indent $ do- forM_ (zip3 paramTypes argNames argNames') $ \(t, argName, argName') ->- sayArgProcessing FromCpp t argName argName'- sayCallAndProcessReturn FromCpp retType $- "f'hs" : map (' ':) argNames']- Nothing- saysLn ["f'p <- ", hsFnName'newFunPtr, " f'c"]- saysLn [hsFnName'newCallback, " f'p HoppyFHR.freeHaskellFunPtrFunPtr HoppyP.False"]-- SayExportBoot -> do- addExport hsFnName- wholeFnType <- getWholeFnType- ln- saysLn [hsFnName, " :: ", prettyPrint wholeFnType]--data CallDirection =- ToCpp -- ^ Haskell code is calling out to C++.- | FromCpp -- ^ C++ is invoking a callback.--sayArgProcessing :: CallDirection -> Type -> String -> String -> Generator ()-sayArgProcessing dir t fromVar toVar =- withErrorContext ("processing argument of type " ++ show t) $- case t of- Internal_TVoid -> throwError $ "TVoid is not a valid argument type"- Internal_TBool -> case dir of- ToCpp -> saysLn ["let ", toVar, " = if ", fromVar, " then 1 else 0 in"]- FromCpp -> do addImports $ hsImport1 "Prelude" "(/=)"- saysLn ["let ", toVar, " = ", fromVar, " /= 0 in"]- Internal_TChar -> noConversion- Internal_TUChar -> noConversion- Internal_TShort -> noConversion- Internal_TUShort -> noConversion- Internal_TInt -> sayCoerceIntegral- Internal_TUInt -> noConversion- Internal_TLong -> noConversion- Internal_TULong -> noConversion- Internal_TLLong -> noConversion- Internal_TULLong -> noConversion- Internal_TFloat -> sayCoerceFloating- Internal_TDouble -> sayCoerceFloating- Internal_TInt8 -> noConversion- Internal_TInt16 -> noConversion- Internal_TInt32 -> noConversion- Internal_TInt64 -> noConversion- Internal_TWord8 -> noConversion- Internal_TWord16 -> noConversion- Internal_TWord32 -> noConversion- Internal_TWord64 -> noConversion- Internal_TPtrdiff -> noConversion- Internal_TSize -> noConversion- Internal_TSSize -> noConversion- Internal_TEnum _ -> do- addImports $ mconcat [hsImport1 "Prelude" "($)", hsImportForPrelude, hsImportForRuntime]- saysLn ["let ", toVar,- -- TODO The coersion here is unnecssary if we replace the C numeric- -- types with their Haskell ones across the board (e.g. CInt ->- -- Int).- case dir of- ToCpp -> " = HoppyFHR.coerceIntegral $ HoppyP.fromEnum "- FromCpp -> " = HoppyP.toEnum $ HoppyFHR.coerceIntegral ",- fromVar, " in"]- Internal_TBitspace b -> do- importHsModuleForExtName $ bitspaceExtName b- saysLn $ concat [ ["let ", toVar, " = "]- , case dir of- ToCpp -> [toHsBitspaceToNumName b, " $ ", toHsBitspaceFromValueName b]- FromCpp -> [toHsBitspaceTypeName b],- [" ", fromVar, " in"]- ]- -- References and pointers are handled equivalently.- Internal_TPtr (Internal_TObj cls) -> do- addImportForClass cls- case dir of- ToCpp -> do- addImports $ mconcat [hsImport1 "Prelude" "($)",- hsImportForRuntime]- saysLn ["HoppyFHR.withCppPtr (", toHsCastMethodName Nonconst cls, " ", fromVar,- ") $ \\", toVar, " ->"]- FromCpp ->- saysLn ["let ", toVar, " = ", toHsDataCtorName Unmanaged Nonconst cls,- " ", fromVar, " in"]- Internal_TPtr (Internal_TConst (Internal_TObj cls)) -> do- addImportForClass cls- case dir of- ToCpp -> do- -- Same as the (TObj _), ToCpp case.- addImports $ mconcat [hsImport1 "Prelude" "($)",- hsImportForPrelude,- hsImportForRuntime]- saysLn [toHsWithValuePtrName cls, " ", fromVar,- " $ HoppyP.flip HoppyFHR.withCppPtr $ \\", toVar, " ->"]- FromCpp ->- saysLn ["let ", toVar, " = ", toHsDataCtorName Unmanaged Const cls,- " ", fromVar, " in"]- Internal_TPtr _ -> noConversion- Internal_TRef t' -> sayArgProcessing dir (ptrT t') fromVar toVar- Internal_TFn {} -> throwError "TFn unimplemented"- Internal_TCallback cb -> case dir of- ToCpp -> do- addImports $ hsImport1 "Prelude" "(>>=)"- importHsModuleForExtName $ callbackExtName cb- saysLn [toHsCallbackCtorName cb, " ", fromVar, " >>= \\", toVar, " ->"]- FromCpp -> throwError "Can't receive a callback from C++"- Internal_TObj cls -> case dir of- ToCpp -> do- -- Same as the (TPtr (TConst (TObj _))), ToPtr case.- addImportForClass cls- addImports $ mconcat [hsImport1 "Prelude" "($)",- hsImportForPrelude,- hsImportForRuntime]- saysLn [toHsWithValuePtrName cls, " ", fromVar,- " $ HoppyP.flip HoppyFHR.withCppPtr $ \\", toVar, " ->"]- FromCpp -> case classHaskellConversion $ classConversion cls of- ClassConversionNone ->- throwError $ concat- ["Can't pass a TObj of ", show cls,- " from C++ to Haskell because no class conversion is defined"]- ClassConversionManual _ -> do- addImportForClass cls- addImports $ mconcat [hsImport1 "Prelude" "(>>=)",- hsImportForRuntime]- saysLn ["HoppyFHR.decode (", toHsDataCtorName Unmanaged Const cls, " ",- fromVar, ") >>= \\", toVar, " ->"]- ClassConversionToHeap -> sayArgProcessing dir (objToHeapT cls) fromVar toVar- ClassConversionToGc -> sayArgProcessing dir (toGcT t) fromVar toVar- Internal_TObjToHeap cls -> case dir of- ToCpp -> throwError $ objToHeapTWrongDirectionErrorMsg Nothing cls- FromCpp -> sayArgProcessing dir (ptrT $ objT cls) fromVar toVar- Internal_TToGc t' -> case dir of- ToCpp -> throwError $ toGcTWrongDirectionErrorMsg Nothing t'- FromCpp -> do- addImports $ mconcat [hsImport1 "Prelude" "(>>=)",- hsImportForRuntime]- saysLn ["HoppyFHR.toGc ", fromVar, " >>= \\", toVar, " ->"]- Internal_TConst t' -> sayArgProcessing dir t' fromVar toVar- where noConversion = saysLn ["let ", toVar, " = ", fromVar, " in"]- sayCoerceIntegral = do- addImports hsImportForRuntime- saysLn ["let ", toVar, " = HoppyFHR.coerceIntegral ", fromVar, " in"]- sayCoerceFloating = do- addImports hsImportForPrelude- saysLn ["let ", toVar, " = HoppyP.realToFrac ", fromVar, " in"]---- | Note that the 'CallDirection' is the direction of the call, not the--- direction of the return. 'ToCpp' means we're returning to the foreign--- language, 'FromCpp' means we're returning from it.-sayCallAndProcessReturn :: CallDirection -> Type -> [String] -> Generator ()-sayCallAndProcessReturn dir t callWords =- withErrorContext ("processing return value of type " ++ show t) $- case t of- Internal_TVoid -> sayCall- Internal_TBool -> do- case dir of- ToCpp -> do addImports $ mconcat [hsImport1 "Prelude" "(/=)", hsImportForPrelude]- sayLn "HoppyP.fmap (/= 0)"- FromCpp -> sayLn "HoppyP.fmap (\\x -> if x then 1 else 0)"- sayCall- Internal_TChar -> sayCall- Internal_TUChar -> sayCall- Internal_TShort -> sayCall- Internal_TUShort -> sayCall- Internal_TInt -> sayCoerceIntegral >> sayCall- Internal_TUInt -> sayCall- Internal_TLong -> sayCall- Internal_TULong -> sayCall- Internal_TLLong -> sayCall- Internal_TULLong -> sayCall- Internal_TFloat -> sayCoerceFloating >> sayCall- Internal_TDouble -> sayCoerceFloating >> sayCall- Internal_TInt8 -> sayCall- Internal_TInt16 -> sayCall- Internal_TInt32 -> sayCall- Internal_TInt64 -> sayCall- Internal_TWord8 -> sayCall- Internal_TWord16 -> sayCall- Internal_TWord32 -> sayCall- Internal_TWord64 -> sayCall- Internal_TPtrdiff -> sayCall- Internal_TSize -> sayCall- Internal_TSSize -> sayCall- Internal_TEnum _ -> do- addImports $ mconcat [hsImport1 "Prelude" "(.)", hsImportForPrelude, hsImportForRuntime]- case dir of- -- TODO The coersion here is unnecssary if we replace the C numeric types- -- with their Haskell ones across the board (e.g. CInt -> Int).- ToCpp -> saysLn ["HoppyP.fmap (HoppyP.toEnum . HoppyFHR.coerceIntegral)"]- FromCpp -> saysLn ["HoppyP.fmap (HoppyFHR.coerceIntegral . HoppyP.fromEnum)"]- sayCall- Internal_TBitspace b -> do- addImports hsImportForPrelude- importHsModuleForExtName $ bitspaceExtName b- saysLn ["HoppyP.fmap ", bitspaceConvFn dir b]- sayCall- -- The same as TPtr (TConst (TObj _)), but nonconst.- Internal_TPtr (Internal_TObj cls) -> do- addImportForClass cls- case dir of- ToCpp -> do- addImports hsImportForPrelude- saysLn ["HoppyP.fmap ", toHsDataCtorName Unmanaged Nonconst cls]- sayCall- FromCpp -> do- addImports $ mconcat [hsImportForPrelude, hsImportForRuntime]- sayLn "HoppyP.fmap HoppyFHR.toPtr"- sayCall- -- The same as TPtr (TConst (TObj _)), but nonconst.- Internal_TPtr (Internal_TConst (Internal_TObj cls)) -> do- addImportForClass cls- case dir of- ToCpp -> do- addImports hsImportForPrelude- saysLn ["HoppyP.fmap ", toHsDataCtorName Unmanaged Const cls]- sayCall- FromCpp -> do- addImports $ mconcat [hsImportForPrelude, hsImportForRuntime]- sayLn "HoppyP.fmap HoppyFHR.toPtr"- sayCall- Internal_TPtr _ -> sayCall- Internal_TRef t' -> sayCallAndProcessReturn dir (ptrT t') callWords- Internal_TFn {} -> throwError "TFn unimplemented"- Internal_TCallback cb -> case dir of- ToCpp -> throwError "Can't receive a callback from C++"- FromCpp -> do- addImports $ hsImport1 "Prelude" "(=<<)"- importHsModuleForExtName $ callbackExtName cb- saysLn [toHsCallbackCtorName cb, "=<<"]- sayCall- Internal_TObj cls -> case dir of- ToCpp -> case classHaskellConversion $ classConversion cls of- ClassConversionNone ->- throwError $ concat- ["Can't return a TObj of ", show cls,- " from C++ to Haskell because no class conversion is defined"]- ClassConversionManual _ -> do- addImportForClass cls- addImports $ mconcat [hsImports "Prelude" ["(.)", "(=<<)"],- hsImportForRuntime]- saysLn ["(HoppyFHR.decodeAndDelete . ", toHsDataCtorName Unmanaged Const cls, ") =<<"]- sayCall- ClassConversionToHeap -> sayCallAndProcessReturn dir (objToHeapT cls) callWords- ClassConversionToGc -> sayCallAndProcessReturn dir (toGcT t) callWords- FromCpp -> do- addImportForClass cls- addImports $ mconcat [hsImports "Prelude" ["(.)", "(=<<)"],- hsImportForPrelude,- hsImportForRuntime]- sayLn "(HoppyP.fmap (HoppyFHR.toPtr) . HoppyFHR.encode) =<<"- sayCall- Internal_TObjToHeap cls -> case dir of- ToCpp -> sayCallAndProcessReturn dir (ptrT $ objT cls) callWords- FromCpp -> throwError $ objToHeapTWrongDirectionErrorMsg Nothing cls- Internal_TToGc t' -> case dir of- ToCpp -> do- addImports $ mconcat [hsImport1 "Prelude" "(=<<)",- hsImportForRuntime]- sayLn "HoppyFHR.toGc =<<"- -- TToGc (TObj _) should create a pointer rather than decoding, so we- -- change the TObj _ into a TPtr (TObj _).- case t' of- Internal_TObj _ -> sayCallAndProcessReturn dir (ptrT t') callWords- _ -> sayCallAndProcessReturn dir t' callWords- FromCpp -> throwError $ toGcTWrongDirectionErrorMsg Nothing t'- Internal_TConst t' -> sayCallAndProcessReturn dir t' callWords- where sayCall = saysLn $ "(" : callWords ++ [")"]- sayCoerceIntegral = do addImports $ mconcat [hsImportForPrelude, hsImportForRuntime]- sayLn "HoppyP.fmap HoppyFHR.coerceIntegral"- sayCoerceFloating = do addImports hsImportForPrelude- sayLn "HoppyP.fmap HoppyP.realToFrac"- bitspaceConvFn dir = case dir of- ToCpp -> toHsBitspaceTypeName- FromCpp -> toHsBitspaceToNumName--sayExportClass :: SayExportMode -> Class -> Generator ()-sayExportClass mode cls = do- case mode of- SayExportForeignImports -> do- sayExportClassHsCtors mode cls-- forM_ (classMethods cls) $ \method ->- (sayExportFn mode <$> getClassyExtName cls <*> pure Nothing <*> methodPurity <*>- pure (getMethodEffectiveParams cls method) <*> methodReturn) method-- SayExportDecls -> do- sayExportClassHsClass True cls Const- sayExportClassHsClass True cls Nonconst-- sayExportClassHsStaticMethods cls-- -- Create a newtype for referencing foreign objects with pointers. The- -- newtype is not used with encodings of value objects.- sayExportClassHsType True cls Const- sayExportClassHsType True cls Nonconst-- sayExportClassHsCtors mode cls-- SayExportBoot -> do- sayExportClassHsClass False cls Const- sayExportClassHsClass False cls Nonconst-- sayExportClassHsType False cls Const- sayExportClassHsType False cls Nonconst-- sayExportClassCastPrimitives mode cls- sayExportClassHsSpecialFns mode cls--sayExportClassHsClass :: Bool -> Class -> Constness -> Generator ()-sayExportClassHsClass doDecls cls cst = do- let hsTypeName = toHsDataTypeName cst cls- hsValueClassName = toHsValueClassName cls- hsWithValuePtrName = toHsWithValuePtrName cls- hsPtrClassName = toHsPtrClassName cst cls- hsCastMethodName = toHsCastMethodName cst cls- supers = classSuperclasses cls-- forM_ supers $ importHsModuleForExtName . classExtName- hsSupers <-- (\x -> if null x- then do addImports hsImportForRuntime- return ["HoppyFHR.CppPtr"]- else return x) $- case cst of- Const -> map (toHsPtrClassName Const) supers- Nonconst -> toHsPtrClassName Const cls : map (toHsPtrClassName Nonconst) supers-- -- Print the value class definition. There is only one of these, and it is- -- spiritually closer to the const version of the pointers for this class, so- -- we emit for the const case only.- when (cst == Const) $ do- addImports hsImportForPrelude- addExport' hsValueClassName- ln- saysLn ["class ", hsValueClassName, " a where"]- indent $- saysLn [hsWithValuePtrName, " :: a -> (", hsTypeName, " -> HoppyP.IO b) -> HoppyP.IO b"]-- -- Generate instances for all pointer subtypes.- ln- saysLn ["#if MIN_VERSION_base(4,8,0)"]- saysLn ["instance {-# OVERLAPPABLE #-} ", hsPtrClassName, " a => ", hsValueClassName, " a",- if doDecls then " where" else ""]- saysLn ["#else"]- saysLn ["instance ", hsPtrClassName, " a => ", hsValueClassName, " a",- if doDecls then " where" else ""]- saysLn ["#endif"]- when doDecls $ do- addImports $ mconcat [hsImports "Prelude" ["($)", "(.)"],- hsImportForPrelude]- indent $ saysLn [hsWithValuePtrName, " = HoppyP.flip ($) . ", hsCastMethodName]-- -- When the class has a native Haskell type, also print an instance for it.- forM_ (getClassHaskellConversion cls) $ \conv -> do- hsType <- classHaskellConversionType conv- ln- saysLn ["#if MIN_VERSION_base(4,8,0)"]- saysLn ["instance {-# OVERLAPPING #-} ", hsValueClassName, " (", prettyPrint hsType, ")",- if doDecls then " where" else ""]- saysLn ["#else"]- saysLn ["instance ", hsValueClassName, " (", prettyPrint hsType, ")",- if doDecls then " where" else ""]- saysLn ["#endif"]- when doDecls $ do- addImports hsImportForRuntime- indent $ saysLn [hsWithValuePtrName, " = HoppyFHR.withCppObj"]-- -- Print the pointer class definition.- addExport' hsPtrClassName- ln- saysLn $- "class (" :- intersperse ", " (map (++ " this") hsSupers) ++- [") => ", hsPtrClassName, " this where"]- indent $ saysLn [hsCastMethodName, " :: this -> ", hsTypeName]-- -- Print the non-static methods.- when doDecls $ do- let methods = filter ((cst ==) . methodConst) $ classMethods cls- forM_ methods $ \method ->- when (methodStatic method == Nonstatic) $- (sayExportFn SayExportDecls <$> getClassyExtName cls <*> pure Nothing <*>- methodPurity <*> pure (getMethodEffectiveParams cls method) <*>- methodReturn) method--sayExportClassHsStaticMethods :: Class -> Generator ()-sayExportClassHsStaticMethods cls =- forM_ (classMethods cls) $ \method ->- when (methodStatic method == Static) $- (sayExportFn SayExportDecls <$> getClassyExtName cls <*> pure Nothing <*> methodPurity <*>- methodParams <*> methodReturn) method--sayExportClassHsType :: Bool -> Class -> Constness -> Generator ()-sayExportClassHsType doDecls cls cst = do- addImports $ mconcat [hsImportForForeign, hsImportForPrelude, hsImportForRuntime]- -- Unfortunately, we must export the data constructor, so that GHC can marshal- -- it in foreign calls in other modules.- addExport' hsTypeName- ln- saysLn ["data ", hsTypeName, " ="]- indent $ do- saysLn [" ", hsCtor, " (HoppyF.Ptr ", hsTypeName, ")"]- saysLn ["| ", hsCtorGc, " (HoppyF.ForeignPtr ()) (HoppyF.Ptr ", hsTypeName, ")"]- when doDecls $ do- addImports $ hsImport1 "Prelude" "(==)"- indent $ sayLn "deriving (HoppyP.Show)"- ln- saysLn ["instance HoppyP.Eq ", hsTypeName, " where"]- indent $ saysLn ["x == y = HoppyFHR.toPtr x == HoppyFHR.toPtr y"]- ln- saysLn ["instance HoppyP.Ord ", hsTypeName, " where"]- indent $ saysLn ["compare x y = HoppyP.compare (HoppyFHR.toPtr x) (HoppyFHR.toPtr y)"]-- -- Generate const_cast functions:- -- castFooToConst :: Foo -> FooConst- -- castFooToNonconst :: FooConst -> Foo- ln- let constCastFnName = toHsConstCastFnName cst cls- addExport constCastFnName- saysLn [constCastFnName, " :: ", toHsDataTypeName (constNegate cst) cls, " -> ", hsTypeName]- when doDecls $ do- addImports $ hsImport1 "Prelude" "($)"- saysLn [constCastFnName, " (", toHsDataCtorName Unmanaged (constNegate cst) cls,- " ptr') = ", hsCtor, " $ HoppyF.castPtr ptr'"]- saysLn [constCastFnName, " (", toHsDataCtorName Managed (constNegate cst) cls,- " fptr' ptr') = ", hsCtorGc, " fptr' $ HoppyF.castPtr ptr'"]-- -- Generate an instance of CppPtr.- ln- if doDecls- then do addImports $ hsImport1 "Prelude" "($)"- saysLn ["instance HoppyFHR.CppPtr ", hsTypeName, " where"]- indent $ do- saysLn ["nullptr = ", toHsDataCtorName Unmanaged cst cls, " HoppyF.nullPtr"]- ln- saysLn ["withCppPtr (", hsCtor, " ptr') f' = f' ptr'"]- saysLn ["withCppPtr (", hsCtorGc,- " fptr' ptr') f' = HoppyF.withForeignPtr fptr' $ \\_ -> f' ptr'"]- ln- saysLn ["toPtr (", hsCtor, " ptr') = ptr'"]- saysLn ["toPtr (", hsCtorGc, " _ ptr') = ptr'"]- ln- saysLn ["touchCppPtr (", hsCtor, " _) = HoppyP.return ()"]- saysLn ["touchCppPtr (", hsCtorGc, " fptr' _) = HoppyF.touchForeignPtr fptr'"]- when (classDtorIsPublic cls) $ do- addImports $ hsImport1 "Prelude" "(==)"- ln- saysLn ["instance HoppyFHR.Deletable ", hsTypeName, " where"]- indent $ do- saysLn $- "delete (" : toHsDataCtorName Unmanaged cst cls : " ptr') = " :- toHsClassDeleteFnName cls :- case cst of- Const -> [" ptr'"]- Nonconst -> [" $ (HoppyF.castPtr ptr' :: HoppyF.Ptr ",- toHsDataTypeName Const cls, ")"]- saysLn ["delete (", toHsDataCtorName Managed cst cls,- " _ _) = HoppyP.fail $ HoppyP.concat ",- "[\"Deletable.delete: Asked to delete a GC-managed \", ",- show hsTypeName, ", \" object.\"]"]- ln- saysLn ["toGc this'@(", hsCtor, " ptr') = ",- -- No sense in creating a ForeignPtr for a null pointer.- "if ptr' == HoppyF.nullPtr then HoppyP.return this' else HoppyP.fmap ",- "(HoppyP.flip ", hsCtorGc, " ptr') $ ",- "HoppyF.newForeignPtr ",- -- The foreign delete function takes a const pointer; we cast it to- -- take a Ptr () to match up with the ForeignPtr () we're creating,- -- assuming that data pointers have the same representation.- "(HoppyF.castFunPtr ", toHsClassDeleteFnPtrName cls,- " :: HoppyF.FunPtr (HoppyF.Ptr () -> HoppyP.IO ())) ",- "(HoppyF.castPtr ptr' :: HoppyF.Ptr ())"]- saysLn ["toGc this'@(", hsCtorGc, " {}) = HoppyP.return this'"]- else do saysLn ["instance HoppyFHR.CppPtr ", hsTypeName]- saysLn ["instance HoppyFHR.Deletable ", hsTypeName]-- -- Generate instances for all superclasses' typeclasses.- genInstances [] cls-- where hsTypeName :: String- hsTypeName = toHsDataTypeName cst cls-- hsCtor :: String- hsCtor = toHsDataCtorName Unmanaged cst cls-- hsCtorGc :: String- hsCtorGc = toHsDataCtorName Managed cst cls-- genInstances :: [Class] -> Class -> Generator ()- genInstances path ancestorCls = do- -- In this example Bar inherits from Foo. We are generating instances- -- either for BarConst or Bar, depending on 'cst'.- --- -- BarConst's instances:- -- instance FooConstPtr BarConst where- -- toFooConst (BarConst ptr') = FooConst $ castBarToFoo ptr'- -- toFooConst (BarConstGc fptr' ptr') = FooConstGc fptr' $ castBarToFoo ptr'- --- -- instance BarConstPtr BarConst where- -- toFooConst = id- --- -- Bar's instances:- -- instance FooConstPtr Bar- -- toFooConst (Bar ptr') =- -- FooConst $ castBarToFoo $ castBarToConst ptr'- -- toFooConst (BarGc fptr' ptr') =- -- FooConstGc fptr' $ castBarToFoo $ castBarToConst ptr'- --- -- instance FooPtr Bar- -- toFoo (Bar ptr') =- -- Foo $ castFooToNonconst $ castBarToFoo $ castBarToConst ptr'- -- toFoo (BarGc fptr' ptr') =- -- FooGc fptr' $ castFooToNonconst $ castBarToFoo $ castBarToConst ptr'- --- -- instance BarConstPtr Bar- -- toBarConst (Bar ptr') = Bar $ castBarToConst ptr'- -- toBarConst (BarGc fptr' ptr') = BarGc fptr' $ castBarToConst ptr'- --- -- instance BarPtr Bar- -- toBar = id- --- -- In all cases, we unwrap the pointer, maybe add const, maybe do an- -- upcast, maybe remove const, then rewrap the pointer. The identity- -- cases are where we just unwrap and wrap again.-- addImportForClass ancestorCls- forM_ (case cst of- Const -> [Const]- Nonconst -> [Const, Nonconst]) $ \typeclassCst -> do- saysLn ["instance ", toHsPtrClassName typeclassCst ancestorCls, " ", hsTypeName,- if doDecls then " where" else ""]- when doDecls $ indent $ do- let castMethodName = toHsCastMethodName typeclassCst ancestorCls- if null path && cst == typeclassCst- then do addImports hsImportForPrelude- saysLn [castMethodName, " = HoppyP.id"]- else do let addConst = cst == Nonconst- removeConst = typeclassCst == Nonconst- when (addConst || removeConst) $- addImports hsImportForForeign- forM_ ([minBound..] :: [Managed]) $ \managed -> do- let ancestorCtor = case managed of- Unmanaged -> [toHsDataCtorName Unmanaged typeclassCst ancestorCls]- Managed -> [toHsDataCtorName Managed typeclassCst ancestorCls,- " fptr'"]- ptrPattern = case managed of- Unmanaged -> [toHsDataCtorName Unmanaged cst cls, " ptr'"]- Managed -> [toHsDataCtorName Managed cst cls, " fptr' ptr'"]- saysLn $ concat- [ [castMethodName, " ("], ptrPattern, [") = "], ancestorCtor- , if removeConst- then [" $ (HoppyF.castPtr :: HoppyF.Ptr ",- toHsDataTypeName Const ancestorCls, " -> HoppyF.Ptr ",- toHsDataTypeName Nonconst ancestorCls, ")"]- else []- , if not $ null path- then [" $ ", toHsCastPrimitiveName cls ancestorCls]- else []- , if addConst- then [" $ (HoppyF.castPtr :: HoppyF.Ptr ",- toHsDataTypeName Nonconst cls, " -> HoppyF.Ptr ",- toHsDataTypeName Const cls, ")"]- else []- , [" ptr'"]- ]-- forM_ (classSuperclasses ancestorCls) $ genInstances $ ancestorCls : path--sayExportClassHsCtors :: SayExportMode -> Class -> Generator ()-sayExportClassHsCtors mode cls =- forM_ (classCtors cls) $ \ctor ->- (sayExportFn mode <$> getClassyExtName cls <*> pure Nothing <*>- pure Nonpure <*> ctorParams <*> pure (ptrT $ objT cls)) ctor--sayExportClassHsSpecialFns :: SayExportMode -> Class -> Generator ()-sayExportClassHsSpecialFns mode cls = do- let typeName = toHsDataTypeName Nonconst cls- typeNameConst = toHsDataTypeName Const cls-- -- Say the delete function.- case mode of- SayExportForeignImports -> when (classDtorIsPublic cls) $ do- addImports $ mconcat [hsImportForForeign, hsImportForPrelude]- saysLn ["foreign import ccall \"", classDeleteFnCppName cls, "\" ",- toHsClassDeleteFnName cls, " :: HoppyF.Ptr ",- toHsDataTypeName Const cls, " -> HoppyP.IO ()"]- saysLn ["foreign import ccall \"&", classDeleteFnCppName cls, "\" ",- toHsClassDeleteFnPtrName cls, " :: HoppyF.FunPtr (HoppyF.Ptr ",- toHsDataTypeName Const cls, " -> HoppyP.IO ())"]- -- The user interface to this is the generic 'delete' function, rendered- -- elsewhere.- SayExportDecls -> return ()- SayExportBoot -> return ()-- case mode of- SayExportForeignImports -> return ()- SayExportDecls -> do- addImports $ mconcat [hsImport1 "Prelude" "($)",- hsImportForForeign,- hsImportForRuntime]- ln- saysLn ["instance HoppyFHR.Assignable (HoppyF.Ptr (HoppyF.Ptr ", typeName, ")) ",- typeName, " where"]- indent $ sayLn "assign ptr' value' = HoppyF.poke ptr' $ HoppyFHR.toPtr value'"- SayExportBoot -> return ()-- -- If the class has an assignment operator that takes its own type, then- -- generate an instance of Assignable.- let assignmentMethods = flip filter (classMethods cls) $ \m ->- methodApplicability m == MNormal &&- (methodParams m == [objT cls] || methodParams m == [refT $ constT $ objT cls]) &&- (case methodImpl m of- RealMethod name -> name == FnOp OpAssign- FnMethod name -> name == FnOp OpAssign)- withAssignmentMethod f = case assignmentMethods of- [] -> return ()- [m] -> f m- _ ->- throwError $ concat- ["Can't determine an Assignable instance to generator for ", show cls,- " because it has multiple assignment operators ", show assignmentMethods]- when (mode == SayExportDecls) $ withAssignmentMethod $ \m -> do- addImports $ mconcat [hsImport1 "Prelude" "(>>)", hsImportForPrelude]- ln- saysLn ["instance ", toHsValueClassName cls, " a => HoppyFHR.Assignable ", typeName,- " a where"]- indent $- saysLn ["assign x' y' = ", toHsFnName $ getClassyExtName cls m,- " x' y' >> HoppyP.return ()"]-- -- A pointer to an object pointer is decodable to an object pointer by peeking- -- at the value, so generate a Decodable instance. You are now a two-star- -- programmer. There is a generic @Ptr (Ptr a)@ to @Ptr a@ instance which- -- handles deeper levels.- case mode of- SayExportForeignImports -> return ()-- SayExportDecls -> do- addImports $ mconcat [hsImport1 "Prelude" "(.)",- hsImportForForeign,- hsImportForPrelude,- hsImportForRuntime]- ln- saysLn ["instance HoppyFHR.Decodable (HoppyF.Ptr (HoppyF.Ptr ",- typeName, ")) ", typeName, " where"]- indent $- saysLn ["decode = HoppyP.fmap ",- toHsDataCtorName Unmanaged Nonconst cls, " . HoppyF.peek"]-- SayExportBoot -> do- addImports $ mconcat [hsImportForForeign, hsImportForRuntime]- ln- -- TODO Encodable.- saysLn ["instance HoppyFHR.Decodable (HoppyF.Ptr (HoppyF.Ptr ", typeName, ")) ", typeName]-- -- Say Encodable and Decodable instances, if the class is encodable and- -- decodable.- forM_ (getClassHaskellConversion cls) $ \conv -> do- hsType <- classHaskellConversionType conv- let hsTypeStr = concat ["(", prettyPrint hsType, ")"]- case mode of- SayExportForeignImports -> return ()-- SayExportDecls -> do- addImports $ mconcat [hsImportForPrelude, hsImportForRuntime]-- -- Say the Encodable instances.- ln- saysLn ["instance HoppyFHR.Encodable ", typeName, " ", hsTypeStr, " where"]- indent $ do- sayLn "encode ="- indent $ classHaskellConversionToCppFn conv- ln- saysLn ["instance HoppyFHR.Encodable ", typeNameConst, " ", hsTypeStr, " where"]- indent $- saysLn ["encode = HoppyP.fmap (", toHsCastMethodName Const cls,- ") . HoppyFHR.encodeAs (HoppyP.undefined :: ", typeName, ")"]-- -- Say the Decodable instances.- ln- saysLn ["instance HoppyFHR.Decodable ", typeName, " ", hsTypeStr, " where"]- indent $- saysLn ["decode = HoppyFHR.decode . ", toHsCastMethodName Const cls]- ln- saysLn ["instance HoppyFHR.Decodable ", typeNameConst, " ", hsTypeStr, " where"]- indent $ do- sayLn "decode ="- indent $ classHaskellConversionFromCppFn conv-- SayExportBoot -> do- addImports hsImportForRuntime- ln- saysLn ["instance HoppyFHR.Encodable ", typeName, " (", hsTypeStr, ")"]- saysLn ["instance HoppyFHR.Encodable ", typeNameConst, " (", hsTypeStr, ")"]- saysLn ["instance HoppyFHR.Decodable ", typeName, " (", hsTypeStr, ")"]- saysLn ["instance HoppyFHR.Decodable ", typeNameConst, " (", hsTypeStr, ")"]--sayExportClassCastPrimitives :: SayExportMode -> Class -> Generator ()-sayExportClassCastPrimitives mode cls = do- let clsType = toHsDataTypeName Const cls- case mode of- SayExportForeignImports ->- forAncestors cls $ \super -> do- let hsCastFnName = toHsCastPrimitiveName cls super- hsDownCastFnName = toHsCastPrimitiveName super cls- superType = toHsDataTypeName Const super- addImports hsImportForForeign- addExport hsCastFnName- saysLn [ "foreign import ccall \"", classCastFnCppName cls super- , "\" ", hsCastFnName, " :: HoppyF.Ptr ", clsType, " -> HoppyF.Ptr ", superType- ]- unless (classIsSubclassOfMonomorphic cls || classIsMonomorphicSuperclass super) $ do- addExport hsDownCastFnName- saysLn [ "foreign import ccall \"", classCastFnCppName super cls- , "\" ", hsDownCastFnName, " :: HoppyF.Ptr ", superType, " -> HoppyF.Ptr ", clsType- ]- return True-- SayExportDecls ->- -- Generate a downcast typeclass and instances for all ancestor classes- -- for the current constness. These don't need to be in the boot file,- -- since they're not used by other generated bindings.- unless (classIsSubclassOfMonomorphic cls) $- forM_ [minBound..] $ \cst -> do- let downCastClassName = toHsDownCastClassName cst cls- downCastMethodName = toHsDownCastMethodName cst cls- addExport' downCastClassName- ln- saysLn ["class ", downCastClassName, " a where"]- indent $ saysLn [downCastMethodName, " :: ",- prettyPrint $ HsTyFun (HsTyVar $ HsIdent "a") $- HsTyCon $ UnQual $ HsIdent $ toHsDataTypeName cst cls]- ln- forAncestors cls $ \super -> case classIsMonomorphicSuperclass super of- True -> return False- False -> do- let superTypeName = toHsDataTypeName cst super- primitiveCastFn = toHsCastPrimitiveName super cls- addImportForClass super- saysLn ["instance ", downCastClassName, " ", superTypeName, " where"]-- -- If Foo is a superclass of Bar:- --- -- instance BarSuper Foo where- -- downToBar castFooToNonconst . downcast' . castFooToConst- -- where downcast' (FooConst ptr') = BarConst $ castFooToBar ptr'- -- downcast' (FooConstGc fptr' ptr') = BarConstGc fptr' $ castFooToBar ptr'- --- -- instance BarSuperConst FooConst where- -- downToBarConst = downcast'- -- where downcast' (FooConst ptr') = BarConst $ castFooToBar ptr'- -- downcast' (FooConstGc fptr' ptr') = BarConstGc fptr' $ castFooToBar ptr'-- indent $ do- saysLn $- downCastMethodName : " = " :- case cst of- Const -> ["cast'"]- Nonconst -> [toHsConstCastFnName Nonconst cls,- " . cast' . ",- toHsConstCastFnName Const super]- indent $ do- sayLn "where"- indent $ do- saysLn ["cast' (", toHsDataCtorName Unmanaged Const super, " ptr') = ",- toHsDataCtorName Unmanaged Const cls, " $ ",- primitiveCastFn, " ptr'"]- saysLn ["cast' (", toHsDataCtorName Managed Const super, " fptr' ptr') = ",- toHsDataCtorName Managed Const cls, " fptr' $ ",- primitiveCastFn, " ptr'"]- return True-- SayExportBoot -> do- forAncestors cls $ \super -> do- let hsCastFnName = toHsCastPrimitiveName cls super- superType = toHsDataTypeName Const super- addImports $ hsImportForForeign- addExport hsCastFnName- saysLn [hsCastFnName, " :: HoppyF.Ptr ", clsType, " -> HoppyF.Ptr ", superType]- return True-- where forAncestors :: Class -> (Class -> Generator Bool) -> Generator ()- forAncestors cls' f = forM_ (classSuperclasses cls') $ \super -> do- recur <- f super- when recur $ forAncestors super f---- | Implements special logic on top of 'cppTypeToHsTypeAndUse', that computes--- the Haskell __qualified__ type for a function, including typeclass--- constraints.-fnToHsTypeAndUse :: HsTypeSide- -> Maybe (Constness, Class)- -> Purity- -> [Type]- -> Type- -> Generator HsQualType-fnToHsTypeAndUse side methodInfo purity paramTypes returnType = do- params <- mapM contextForParam $- (case methodInfo of- Just (cst, cls) -> [("this", case cst of- Nonconst -> ptrT $ objT cls- Const -> ptrT $ constT $ objT cls)]- Nothing -> []) ++- zip (map toArgName [1..]) paramTypes- let context = mapMaybe fst params :: HsContext- hsParams = map snd params-- -- Determine the 'HsHsSide' return type for the function. If the function is- -- returning a 'TObj' of a class that uses 'ClassConversionToHeap' or- -- 'ClassConversionToGc', then first we wrap the return type. Then we do the- -- conversion to a Haskell type, and wrap the result in 'IO' if the function- -- is impure. (HsCSide types always get wrapped in IO.)- returnForGc <- case returnType of- Internal_TObj cls -> case classHaskellConversion $ classConversion cls of- ClassConversionNone ->- throwError $ concat ["Expected ", show cls, " to be returnable from a C++ function"]- ClassConversionManual _ -> return returnType- ClassConversionToHeap -> return $ objToHeapT cls- ClassConversionToGc -> return $ toGcT returnType- _ -> return returnType- hsReturnForGc <- cppTypeToHsTypeAndUse side returnForGc- hsReturnForPurity <- case (purity, side) of- (Pure, HsHsSide) -> return hsReturnForGc- _ -> do- addImports hsImportForPrelude- return $ HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyP.IO") hsReturnForGc-- return $ HsQualType context $ foldr HsTyFun hsReturnForPurity hsParams-- where contextForParam :: (String, Type) -> Generator (Maybe HsAsst, HsType)- contextForParam (s, t) = case t of- Internal_TBitspace b -> receiveBitspace s t b- Internal_TPtr (Internal_TObj cls) -> receivePtr s cls Nonconst- Internal_TPtr (Internal_TConst (Internal_TObj cls)) -> receiveValue s t cls- Internal_TRef (Internal_TObj cls) -> receivePtr s cls Nonconst- Internal_TRef (Internal_TConst (Internal_TObj cls)) -> receiveValue s t cls- Internal_TObj cls -> receiveValue s t cls- Internal_TConst t' -> contextForParam (s, t')- _ -> handoff side t-- -- Use whatever type 'cppTypeToHsTypeAndUse' suggests, with no typeclass- -- constraints.- handoff :: HsTypeSide -> Type -> Generator (Maybe HsAsst, HsType)- handoff side t = (,) Nothing <$> cppTypeToHsTypeAndUse side t-- -- Receives a @IsFooBitspace a => a@.- receiveBitspace s t b = case side of- HsCSide -> handoff side t- HsHsSide -> do- importHsModuleForExtName $ bitspaceExtName b- let t' = HsTyVar $ HsIdent s- return (Just (UnQual $ HsIdent $ toHsBitspaceClassName b, [t']),- t')-- -- Receives a @FooPtr this => this@.- receivePtr :: String -> Class -> Constness -> Generator (Maybe HsAsst, HsType)- receivePtr s cls cst = do- addImportForClass cls- case side of- HsHsSide -> do- let t' = HsTyVar $ HsIdent s- return (Just (UnQual $ HsIdent $ toHsPtrClassName cst cls, [t']),- t')- HsCSide -> do- addImports $ hsImportForForeign- return (Nothing, HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyF.Ptr") $- HsTyVar $ HsIdent $ toHsDataTypeName cst cls)-- -- Receives a @FooValue a => a@.- receiveValue :: String -> Type -> Class -> Generator (Maybe HsAsst, HsType)- receiveValue s t cls = case side of- HsCSide -> handoff side t- HsHsSide -> do- addImports hsImportForRuntime- addImportForClass cls- let t' = HsTyVar $ HsIdent s- return (Just (UnQual $ HsIdent $ toHsValueClassName cls, [t']),- t')--getMethodEffectiveParams :: Class -> Method -> [Type]-getMethodEffectiveParams cls method =- (case methodImpl method of- RealMethod {} -> case methodApplicability method of- MNormal -> (ptrT (objT cls):)- MConst -> (ptrT (constT $ objT cls):)- MStatic -> id- FnMethod {} -> id) $- methodParams method---- | Imports bindings for the given class into the Haskell module.-addImportForClass :: Class -> Generator ()-addImportForClass = importHsModuleForExtName . classExtName+import Control.Arrow ((&&&))+import Control.Monad (forM, unless, when)+#if MIN_VERSION_mtl(2,2,1)+import Control.Monad.Except (throwError)+#else+import Control.Monad.Error (throwError)+#endif+import Control.Monad.Trans (lift)+import Control.Monad.Writer (execWriterT, tell)+import Data.Foldable (forM_)+import Data.Graph (SCC (AcyclicSCC, CyclicSCC), stronglyConnComp)+import Data.List (intersperse)+import qualified Data.Map as M+import Data.Maybe (mapMaybe)+#if !MIN_VERSION_base(4,8,0)+import Data.Monoid (mconcat, mempty)+#endif+import qualified Data.Set as S+import Foreign.Hoppy.Generator.Common+import Foreign.Hoppy.Generator.Spec+import Foreign.Hoppy.Generator.Types+import Foreign.Hoppy.Generator.Language.Cpp (+ classCastFnCppName,+ classDeleteFnCppName,+ externalNameToCpp,+ )+import Foreign.Hoppy.Generator.Language.Haskell+import Language.Haskell.Syntax (+ HsAsst,+ HsContext,+ HsName (HsIdent),+ HsQName (Special, UnQual),+ HsQualType (HsQualType),+ HsSpecialCon (HsUnitCon),+ HsType (HsTyApp, HsTyCon, HsTyFun, HsTyVar),+ )+import System.FilePath ((<.>), pathSeparator)++-- | The in-memory result of generating Haskell code for an interface.+data Generation = Generation+ { generatedFiles :: M.Map FilePath String+ -- ^ A map from paths of generated files to the contents of those files.+ -- The file paths are relative paths below the Haskell generation root.+ }++-- | Runs the C++ code generator against an interface.+generate :: Interface -> Either ErrorMsg Generation+generate iface = do+ -- Build the partial generation of each module.+ modPartials <- forM (M.elems $ interfaceModules iface) $ \m ->+ (,) m <$> execGenerator iface m (generateSource m)++ -- Compute the strongly connected components. If there is a nontrivial SCC,+ -- then there is a module import cycle that we'll have to break with hs-boot+ -- files.+ let partialsByHsName :: M.Map HsModuleName Partial+ partialsByHsName = M.fromList $ map ((partialModuleHsName &&& id) . snd) modPartials++ sccInput :: [((Module, Partial), Partial, [Partial])]+ sccInput = for modPartials $ \x@(_, p) ->+ (x, p,+ mapMaybe (flip M.lookup partialsByHsName . hsImportModule) $+ M.keys $ getHsImportSet $ outputImports $ partialOutput p)++ sccs :: [SCC (Module, Partial)]+ sccs = stronglyConnComp sccInput++ fileContents <- execWriterT $ forM_ sccs $ \scc -> case scc of+ AcyclicSCC (_, p) -> tell [finishPartial p "hs"]+ CyclicSCC mps -> do+ let cycleModNames = S.fromList $ map (partialModuleHsName . snd) mps+ forM_ mps $ \(m, p) -> do+ -- Create a boot partial.+ pBoot <- lift $ execGenerator iface m (generateBootSource m)++ -- Change the source and boot partials so that all imports of modules in+ -- this cycle are {-# SOURCE #-} imports.+ let p' = setSourceImports cycleModNames p+ pBoot' = setSourceImports cycleModNames pBoot++ -- Emit the completed partials.+ tell [finishPartial p' "hs", finishPartial pBoot' "hs-boot"]++ return $ Generation $ M.fromList fileContents++ where finishPartial :: Partial -> String -> (FilePath, String)+ finishPartial p fileExt =+ (listSubst '.' pathSeparator (partialModuleHsName p) <.> fileExt,+ prependExtensions $ renderPartial p)++ setSourceImports :: S.Set HsModuleName -> Partial -> Partial+ setSourceImports modulesToSourceImport p =+ let output = partialOutput p+ imports = outputImports output+ imports' = makeHsImportSet $+ M.mapWithKey (setSourceImportIfIn modulesToSourceImport) $+ getHsImportSet imports+ output' = output { outputImports = imports' }+ in p { partialOutput = output' }++ setSourceImportIfIn :: S.Set HsModuleName -> HsImportKey -> HsImportSpecs -> HsImportSpecs+ setSourceImportIfIn modulesToSourceImport key specs =+ if hsImportModule key `S.member` modulesToSourceImport+ then specs { hsImportSource = True }+ else specs++prependExtensions :: String -> String+prependExtensions = (prependExtensionsPrefix ++)++prependExtensionsPrefix :: String+prependExtensionsPrefix =+ -- MultiParamTypeClasses is necessary for instances of Decodable and+ -- Encodable. FlexibleContexts is needed for the type signature of the+ -- function that wraps the actual callback function in callback creation+ -- functions.+ --+ -- FlexibleInstances and TypeSynonymInstances are enabled to allow conversions+ -- to and from String, which is really [Char].+ --+ -- UndecidableInstances is needed for instances of the form "SomeClassConstPtr+ -- a => SomeClassValue a", and overlapping instances are used for the overlap+ -- between these instances and instances of SomeClassValue for the class's+ -- native Haskell type, when it's convertible. CPP is used for warning-free+ -- compatibility using overlapping instances with both GHC 7.8 and 7.10.+ --+ -- GeneralizedNewtypeDeriving is to enable automatic deriving of+ -- Data.Bits.Bits instances for bitspace newtypes.+ concat+ [ "{-# LANGUAGE CPP, FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving"+ , ", MultiParamTypeClasses, ScopedTypeVariables, TypeSynonymInstances, UndecidableInstances #-}\n"+ , "#if !MIN_VERSION_base(4,8,0)\n"+ , "{-# LANGUAGE OverlappingInstances #-}\n"+ , "#endif\n\n"+ ]++generateSource :: Module -> Generator ()+generateSource m = do+ forM_ (moduleExports m) $ sayExport SayExportForeignImports+ forM_ (moduleExports m) $ sayExport SayExportDecls++ iface <- askInterface+ when (interfaceExceptionSupportModule iface == Just m) $+ sayExceptionSupport True++ addendumHaskell $ getAddendum m++generateBootSource :: Module -> Generator ()+generateBootSource m = do+ forM_ (moduleExports m) $ sayExport SayExportBoot++ iface <- askInterface+ when (interfaceExceptionSupportModule iface == Just m) $+ sayExceptionSupport False++data SayExportMode = SayExportForeignImports | SayExportDecls | SayExportBoot+ deriving (Eq, Show)++sayExport :: SayExportMode -> Export -> Generator ()+sayExport mode export = do+ case export of+ ExportVariable v -> sayExportVar mode v+ ExportEnum enum -> sayExportEnum mode enum+ ExportBitspace bitspace -> sayExportBitspace mode bitspace+ ExportFn fn ->+ (sayExportFn mode <$> fnExtName <*> fnExtName <*> fnPurity <*>+ fnParams <*> fnReturn <*> fnExceptionHandlers) fn+ ExportClass cls -> sayExportClass mode cls+ ExportCallback cb -> sayExportCallback mode cb++ when (mode == SayExportDecls) $+ addendumHaskell $ exportAddendum export++sayExportVar :: SayExportMode -> Variable -> Generator ()+sayExportVar mode v = withErrorContext ("generating variable " ++ show (varExtName v)) $ do+ let getterName = varGetterExtName v+ setterName = varSetterExtName v+ sayExportVar' mode (varType v) Nothing True getterName getterName setterName setterName++sayExportClassVar :: SayExportMode -> Class -> ClassVariable -> Generator ()+sayExportClassVar mode cls v =+ withErrorContext ("generating variable " ++ show (classVarExtName v)) $+ sayExportVar' mode+ (classVarType v)+ (case classVarStatic v of+ Nonstatic -> Just cls+ Static -> Nothing)+ (classVarGettable v)+ (classVarGetterExtName cls v)+ (classVarGetterForeignName cls v)+ (classVarSetterExtName cls v)+ (classVarSetterForeignName cls v)++sayExportVar' :: SayExportMode+ -> Type+ -> Maybe Class+ -> Bool+ -> ExtName+ -> ExtName+ -> ExtName+ -> ExtName+ -> Generator ()+sayExportVar' mode+ t+ classIfNonstatic+ gettable+ getterExtName+ getterForeignName+ setterExtName+ setterForeignName = do+ let (isConst, deconstType) = case t of+ Internal_TConst t -> (True, t)+ t -> (False, t)++ when gettable $+ sayExportFn mode+ getterExtName+ getterForeignName+ Nonpure+ (maybe [] (\cls -> [ptrT $ constT $ objT cls]) classIfNonstatic)+ deconstType+ mempty++ unless isConst $+ sayExportFn mode+ setterExtName+ setterForeignName+ Nonpure+ (maybe [deconstType] (\cls -> [ptrT $ objT cls, deconstType])+ classIfNonstatic)+ voidT+ mempty++sayExportEnum :: SayExportMode -> CppEnum -> Generator ()+sayExportEnum mode enum =+ withErrorContext ("generating enum " ++ show (enumExtName enum)) $+ case mode of+ -- Nothing to import from the C++ side of an enum.+ SayExportForeignImports -> return ()++ SayExportDecls -> do+ hsTypeName <- toHsEnumTypeName enum+ values <- forM (enumValueNames enum) $ \(value, name) -> do+ ctorName <- toHsEnumCtorName enum name+ return (value, ctorName)+ addImports $ mconcat [hsImports "Prelude" ["($)", "(++)"], hsImportForPrelude]++ -- Print out the data declaration.+ ln+ addExport' hsTypeName+ saysLn ["data ", hsTypeName, " ="]+ indent $ do+ forM_ (zip (False:repeat True) values) $ \(cont, (_, hsCtorName)) ->+ saysLn [if cont then "| " else "", hsCtorName]+ sayLn "deriving (HoppyP.Bounded, HoppyP.Eq, HoppyP.Ord, HoppyP.Show)"++ -- Print out the Enum instance.+ ln+ saysLn ["instance HoppyP.Enum ", hsTypeName, " where"]+ indent $ do+ forM_ values $ \(num, hsCtorName) ->+ saysLn ["fromEnum ", hsCtorName, " = ", show num]+ ln+ forM_ values $ \(num, hsCtorName) ->+ saysLn ["toEnum (", show num, ") = ", hsCtorName]+ saysLn ["toEnum n' = HoppyP.error $ ",+ show (concat ["Unknown ", hsTypeName, " numeric value: "]),+ " ++ HoppyP.show n'"]++ SayExportBoot -> do+ hsTypeName <- toHsEnumTypeName enum+ addImports hsImportForPrelude+ addExport hsTypeName+ ln+ saysLn ["data ", hsTypeName]+ saysLn ["instance HoppyP.Bounded ", hsTypeName]+ saysLn ["instance HoppyP.Enum ", hsTypeName]+ saysLn ["instance HoppyP.Eq ", hsTypeName]+ saysLn ["instance HoppyP.Ord ", hsTypeName]+ saysLn ["instance HoppyP.Show ", hsTypeName]++sayExportBitspace :: SayExportMode -> Bitspace -> Generator ()+sayExportBitspace mode bitspace =+ withErrorContext ("generating bitspace " ++ show (bitspaceExtName bitspace)) $ do+ hsTypeName <- toHsBitspaceTypeName bitspace+ fromFnName <- toHsBitspaceToNumName bitspace+ className <- toHsBitspaceClassName bitspace+ toFnName <- toHsBitspaceFromValueName bitspace+ let hsType = HsTyCon $ UnQual $ HsIdent hsTypeName+ case mode of+ -- Nothing to import from the C++ side of a bitspace.+ SayExportForeignImports -> return ()++ SayExportDecls -> do+ values <- forM (bitspaceValueNames bitspace) $ \(value, name) -> do+ bindingName <- toHsBitspaceValueName bitspace name+ return (value, bindingName)++ hsCNumType <- cppTypeToHsTypeAndUse HsCSide $ bitspaceType bitspace+ hsHsNumType <- cppTypeToHsTypeAndUse HsHsSide $ bitspaceType bitspace++ -- Print out the data declaration and conversion functions.+ addImports $ mconcat [hsImportForBits, hsImportForPrelude, hsImportForRuntime]+ addExport' hsTypeName+ addExport' className+ ln+ saysLn ["newtype ", hsTypeName, " = ", hsTypeName, " { ",+ fromFnName, " :: ", prettyPrint hsCNumType, " }"]+ indent $ sayLn "deriving (HoppyDB.Bits, HoppyP.Bounded, HoppyP.Eq, HoppyP.Ord, HoppyP.Show)"+ ln+ saysLn ["class ", className, " a where"]+ indent $ do+ let tyVar = HsTyVar $ HsIdent "a"+ saysLn [toFnName, " :: ", prettyPrint $ HsTyFun tyVar hsType]+ ln+ saysLn ["instance ", className, " (", prettyPrint hsCNumType, ") where"]+ indent $ saysLn [toFnName, " = ", hsTypeName]+ saysLn ["instance ", className, " (", prettyPrint hsHsNumType, ") where"]+ indent $ saysLn [toFnName, " = ", hsTypeName, " . HoppyFHR.coerceIntegral"]+ saysLn ["instance ", className, " ", hsTypeName, " where"]+ indent $ saysLn [toFnName, " = HoppyP.id"]++ -- If the bitspace has an associated enum, then print out a conversion+ -- instance for it as well.+ forM_ (bitspaceEnum bitspace) $ \enum -> do+ enumTypeName <- toHsEnumTypeName enum+ addImports $ mconcat [hsImport1 "Prelude" "(.)", hsImportForPrelude, hsImportForRuntime]+ ln+ saysLn ["instance ", className, " ", enumTypeName, " where"]+ indent $+ saysLn [toFnName, " = ", hsTypeName, " . HoppyFHR.coerceIntegral . HoppyP.fromEnum"]++ -- Print out the constants.+ ln+ forM_ values $ \(num, valueName) -> do+ addExport valueName+ saysLn [valueName, " = ", hsTypeName, " ", show num]++ SayExportBoot -> do+ hsCNumType <- cppTypeToHsTypeAndUse HsCSide $ bitspaceType bitspace+ hsHsNumType <- cppTypeToHsTypeAndUse HsHsSide $ bitspaceType bitspace++ addImports $ mconcat [hsImportForBits, hsImportForPrelude]+ addExport' hsTypeName+ addExport' className+ ln+ saysLn ["newtype ", hsTypeName, " = ", hsTypeName, " { ",+ fromFnName, " :: ", prettyPrint hsCNumType, " }"]+ ln+ saysLn ["instance HoppyDB.Bits ", hsTypeName]+ saysLn ["instance HoppyP.Bounded ", hsTypeName]+ saysLn ["instance HoppyP.Eq ", hsTypeName]+ saysLn ["instance HoppyP.Ord ", hsTypeName]+ saysLn ["instance HoppyP.Show ", hsTypeName]+ ln+ saysLn ["class ", className, " a where"]+ indent $ do+ let tyVar = HsTyVar $ HsIdent "a"+ saysLn [toFnName, " :: ", prettyPrint $ HsTyFun tyVar hsType]+ ln+ saysLn ["instance ", className, " (", prettyPrint hsCNumType, ")"]+ saysLn ["instance ", className, " (", prettyPrint hsHsNumType, ")"]+ saysLn ["instance ", className, " ", hsTypeName]+ forM_ (bitspaceEnum bitspace) $ \enum -> do+ enumTypeName <- toHsEnumTypeName enum+ saysLn ["instance ", className, " ", enumTypeName]++sayExportFn :: SayExportMode+ -> ExtName+ -> ExtName+ -> Purity+ -> [Type]+ -> Type+ -> ExceptionHandlers+ -> Generator ()+sayExportFn mode extName foreignName purity paramTypes retType exceptionHandlers = do+ effectiveHandlers <- getEffectiveExceptionHandlers exceptionHandlers+ let handlerList = exceptionHandlersList effectiveHandlers+ catches = not $ null handlerList++ -- We use the pure version of toHsFnName here; because foreignName isn't an+ -- ExtName present in the interface's lookup table, toHsFnName would bail on+ -- it. Since functions don't reference each other (e.g. we don't put anything+ -- in .hs-boot files for them in circular modules cases), this isn't a problem.+ let hsFnName = toHsFnName' foreignName+ hsFnImportedName = hsFnName ++ "'"++ case mode of+ SayExportForeignImports ->+ withErrorContext ("generating imports for function " ++ show extName) $ do+ -- Print a "foreign import" statement.+ hsCType <- fnToHsTypeAndUse HsCSide purity paramTypes retType effectiveHandlers+ saysLn ["foreign import ccall \"", externalNameToCpp extName, "\" ", hsFnImportedName,+ " :: ", prettyPrint hsCType]++ SayExportDecls -> withErrorContext ("generating function " ++ show extName) $ do+ -- Print the type signature.+ ln+ addExport hsFnName+ hsHsType <- fnToHsTypeAndUse HsHsSide purity paramTypes retType effectiveHandlers+ saysLn [hsFnName, " :: ", prettyPrint hsHsType]++ case purity of+ Nonpure -> return ()+ Pure -> saysLn ["{-# NOINLINE ", hsFnName, " #-}"]++ -- Print the function body.+ let argNames = map toArgName [1..length paramTypes]+ convertedArgNames = map (++ "'") argNames+ -- Operators on this line must bind more weakly than operators used below,+ -- namely ($) and (>>=). (So finish the line with ($).)+ lineEnd <- case purity of+ Nonpure -> return [" ="]+ Pure -> do addImports $ mconcat [hsImport1 "Prelude" "($)", hsImportForUnsafeIO]+ return [" = HoppySIU.unsafePerformIO $"]+ saysLn $ hsFnName : map (' ':) argNames ++ lineEnd+ indent $ do+ forM_ (zip3 paramTypes argNames convertedArgNames) $ \(t, argName, argName') ->+ sayArgProcessing ToCpp t argName argName'++ when catches $ do+ iface <- askInterface+ currentModule <- askModule+ let exceptionSupportModule = interfaceExceptionSupportModule iface+ when (exceptionSupportModule /= Just currentModule) $+ addImports . hsWholeModuleImport . getModuleName iface =<<+ fromMaybeM (throwError "Internal error, an exception support module is not available")+ exceptionSupportModule+ addImports $ mconcat [hsImport1 "Prelude" "($)", hsImportForRuntime]+ sayLn "HoppyFHR.internalHandleExceptions exceptionDb' $"++ let callWords = hsFnImportedName : map (' ':) convertedArgNames+ sayCallAndProcessReturn ToCpp retType callWords++ SayExportBoot ->+ -- Functions (methods included) cannot be referenced from other exports,+ -- so we don't need to emit anything.+ --+ -- If this changes, revisit the comment on hsFnName above.+ return ()++-- | Prints \"foreign import\" statements and an internal callback construction+-- function for a given 'Callback' specification. For example, for a callback+-- of 'HsHsSide' type @Int -> String -> IO Int@, we will generate the following+-- bindings:+--+-- > foreign import ccall "wrapper" name'newFunPtr+-- > :: (CInt -> Ptr CChar -> IO CInt)+-- > -> IO (FunPtr (CInt -> Ptr CChar -> IO CInt))+-- >+-- > -- (This is an ad-hoc generated binding for C++ callback impl class constructor.)+-- > foreign import ccall "genpop__name_impl" name'newCallback+-- > :: FunPtr (CInt -> Ptr CChar -> IO CInt)+-- > -> FunPtr (FunPtr (IO ()) -> IO ())+-- > -> Bool+-- > -> IO (CCallback (CInt -> Ptr CChar -> IO CInt))+-- >+-- > name_newFunPtr :: (Int -> String -> IO Int) -> IO (FunPtr (CInt -> Ptr CChar -> IO CInt))+-- > name_newFunPtr f'hs = name'newFunPtr $ \excIdPtr excPtrPtr arg1 arg2 ->+-- > internalHandleCallbackExceptions excIdPtr excPtrPtr $+-- > coerceIntegral arg1 >>= \arg1' ->+-- > (...decode the C string) >>= \arg2' ->+-- > fmap coerceIntegral+-- > (f'hs arg1' arg2')+-- >+-- > name_new :: (Int -> String -> IO Int) -> IO (CCallback (CInt -> Ptr CChar -> IO CInt))+-- > name_new f = do+-- > f'p <- name_newFunPtr f+-- > name'newCallback f'p freeHaskellFunPtrFunPtr False+sayExportCallback :: SayExportMode -> Callback -> Generator ()+sayExportCallback mode cb =+ withErrorContext ("generating callback " ++ show (callbackExtName cb)) $ do+ let name = callbackExtName cb+ paramTypes = callbackParams cb+ retType = callbackReturn cb+ hsNewFunPtrFnName <- toHsCallbackNewFunPtrFnName cb+ hsCtorName <- toHsCallbackCtorName cb+ let hsCtorName'newCallback = hsCtorName ++ "'newCallback"+ hsCtorName'newFunPtr = hsCtorName ++ "'newFunPtr"++ hsFnCType <- cppTypeToHsTypeAndUse HsCSide =<< callbackToTFn HsCSide cb+ hsFnHsType <- cppTypeToHsTypeAndUse HsHsSide =<< callbackToTFn HsHsSide cb++ let getWholeNewFunPtrFnType = do+ addImports $ mconcat [hsImportForForeign, hsImportForPrelude]+ return $+ HsTyFun hsFnHsType $+ HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyP.IO") $+ HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyF.FunPtr") hsFnCType+ getWholeCtorType = do+ addImports $ mconcat [hsImportForPrelude, hsImportForRuntime]+ return $+ HsTyFun hsFnHsType $+ HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyP.IO") $+ HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyFHR.CCallback") hsFnCType++ case mode of+ SayExportForeignImports -> do+ addImports $ mconcat [hsImportForForeign, hsImportForPrelude, hsImportForRuntime]+ let hsFunPtrType = HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyF.FunPtr") hsFnCType+ hsFunPtrImportType =+ HsTyFun hsFnCType $+ HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyP.IO") hsFunPtrType+ hsCallbackCtorImportType =+ HsTyFun hsFunPtrType $+ HsTyFun (HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyF.FunPtr") $+ HsTyFun (HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyF.FunPtr") $+ HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyP.IO") $+ HsTyCon $ Special HsUnitCon) $+ HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyP.IO") $+ HsTyCon $ Special HsUnitCon) $+ HsTyFun (HsTyCon $ UnQual $ HsIdent "HoppyP.Bool") $+ HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyP.IO") $+ HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyFHR.CCallback") hsFnCType++ saysLn ["foreign import ccall \"wrapper\" ", hsCtorName'newFunPtr, " :: ",+ prettyPrint hsFunPtrImportType]+ saysLn ["foreign import ccall \"", externalNameToCpp name, "\" ",+ hsCtorName'newCallback, " :: ", prettyPrint hsCallbackCtorImportType]++ SayExportDecls -> do+ addExports [hsNewFunPtrFnName, hsCtorName]++ wholeNewFunPtrFnType <- getWholeNewFunPtrFnType+ let paramCount = length paramTypes+ argNames = map toArgName [1..paramCount]+ argNames' = map (++ "'") argNames+ throws <- getEffectiveCallbackThrows cb+ addImports $ mconcat [hsImport1 "Prelude" "($)",+ hsImportForRuntime]+ ln+ saysLn [hsNewFunPtrFnName, " :: ", prettyPrint wholeNewFunPtrFnType]+ saysLn $ hsNewFunPtrFnName : " f'hs = " : hsCtorName'newFunPtr : " $" :+ case (if throws then (["excIdPtr", "excPtrPtr"] ++) else id) argNames of+ [] -> []+ argNames' -> [" \\", unwords argNames', " ->"]+ indent $ do+ when throws $ sayLn "HoppyFHR.internalHandleCallbackExceptions excIdPtr excPtrPtr $"+ forM_ (zip3 paramTypes argNames argNames') $ \(t, argName, argName') ->+ sayArgProcessing FromCpp t argName argName'+ sayCallAndProcessReturn FromCpp retType $+ "f'hs" : map (' ':) argNames'++ wholeCtorType <- getWholeCtorType+ ln+ saysLn [hsCtorName, " :: ", prettyPrint wholeCtorType]+ saysLn [hsCtorName, " f'hs = do"]+ indent $ do+ saysLn ["f'p <- ", hsNewFunPtrFnName, " f'hs"]+ saysLn [hsCtorName'newCallback, " f'p HoppyFHR.freeHaskellFunPtrFunPtr HoppyP.False"]++ SayExportBoot -> do+ addExports [hsNewFunPtrFnName, hsCtorName]+ wholeNewFunPtrFnType <- getWholeNewFunPtrFnType+ wholeCtorType <- getWholeCtorType+ ln+ saysLn [hsNewFunPtrFnName, " :: ", prettyPrint wholeNewFunPtrFnType]+ ln+ saysLn [hsCtorName, " :: ", prettyPrint wholeCtorType]++data CallDirection =+ ToCpp -- ^ Haskell code is calling out to C++.+ | FromCpp -- ^ C++ is invoking a callback.++sayArgProcessing :: CallDirection -> Type -> String -> String -> Generator ()+sayArgProcessing dir t fromVar toVar =+ withErrorContext ("processing argument of type " ++ show t) $+ case t of+ Internal_TVoid -> throwError $ "TVoid is not a valid argument type"+ Internal_TBool -> case dir of+ ToCpp -> saysLn ["let ", toVar, " = if ", fromVar, " then 1 else 0 in"]+ FromCpp -> do addImports $ hsImport1 "Prelude" "(/=)"+ saysLn ["let ", toVar, " = ", fromVar, " /= 0 in"]+ Internal_TChar -> noConversion+ Internal_TUChar -> noConversion+ Internal_TShort -> noConversion+ Internal_TUShort -> noConversion+ Internal_TInt -> sayCoerceIntegral+ Internal_TUInt -> noConversion+ Internal_TLong -> noConversion+ Internal_TULong -> noConversion+ Internal_TLLong -> noConversion+ Internal_TULLong -> noConversion+ Internal_TFloat -> sayCoerceFloating+ Internal_TDouble -> sayCoerceFloating+ Internal_TInt8 -> noConversion+ Internal_TInt16 -> noConversion+ Internal_TInt32 -> noConversion+ Internal_TInt64 -> noConversion+ Internal_TWord8 -> noConversion+ Internal_TWord16 -> noConversion+ Internal_TWord32 -> noConversion+ Internal_TWord64 -> noConversion+ Internal_TPtrdiff -> noConversion+ Internal_TSize -> noConversion+ Internal_TSSize -> noConversion+ Internal_TEnum _ -> do+ addImports $ mconcat [hsImport1 "Prelude" "($)", hsImportForPrelude, hsImportForRuntime]+ saysLn ["let ", toVar,+ -- TODO The coersion here is unnecssary if we replace the C numeric+ -- types with their Haskell ones across the board (e.g. CInt ->+ -- Int).+ case dir of+ ToCpp -> " = HoppyFHR.coerceIntegral $ HoppyP.fromEnum "+ FromCpp -> " = HoppyP.toEnum $ HoppyFHR.coerceIntegral ",+ fromVar, " in"]+ Internal_TBitspace b -> case dir of+ ToCpp -> do+ toNumName <- toHsBitspaceToNumName b+ fromValueName <- toHsBitspaceFromValueName b+ saysLn ["let ", toVar, " = ", toNumName, " $ ", fromValueName, " ", fromVar, " in"]+ FromCpp -> do+ typeName <- toHsBitspaceTypeName b+ saysLn ["let ", toVar, " = " , typeName, " ", fromVar, " in"]+ -- References and pointers are handled equivalently.+ Internal_TPtr (Internal_TObj cls) -> case dir of+ ToCpp -> do+ addImports $ mconcat [hsImport1 "Prelude" "($)",+ hsImportForRuntime]+ castMethodName <- toHsCastMethodName Nonconst cls+ saysLn ["HoppyFHR.withCppPtr (", castMethodName, " ", fromVar,+ ") $ \\", toVar, " ->"]+ FromCpp -> do+ ctorName <- toHsDataCtorName Unmanaged Nonconst cls+ saysLn ["let ", toVar, " = ", ctorName, " ", fromVar, " in"]+ Internal_TPtr (Internal_TConst (Internal_TObj cls)) -> case dir of+ ToCpp -> do+ -- Same as the (TObj _), ToCpp case.+ addImports $ mconcat [hsImport1 "Prelude" "($)",+ hsImportForPrelude,+ hsImportForRuntime]+ withValuePtrName <- toHsWithValuePtrName cls+ saysLn [withValuePtrName, " ", fromVar,+ " $ HoppyP.flip HoppyFHR.withCppPtr $ \\", toVar, " ->"]+ FromCpp -> do+ ctorName <- toHsDataCtorName Unmanaged Const cls+ saysLn ["let ", toVar, " = ", ctorName, " ", fromVar, " in"]+ Internal_TPtr _ -> noConversion+ Internal_TRef t' -> sayArgProcessing dir (ptrT t') fromVar toVar+ Internal_TFn {} -> throwError "TFn unimplemented"+ Internal_TCallback cb -> case dir of+ ToCpp -> do+ addImports $ hsImport1 "Prelude" "(>>=)"+ callbackCtorName <- toHsCallbackCtorName cb+ saysLn [callbackCtorName, " ", fromVar, " >>= \\", toVar, " ->"]+ FromCpp -> throwError "Can't receive a callback from C++"+ Internal_TObj cls -> case dir of+ ToCpp -> do+ -- Same as the (TPtr (TConst (TObj _))), ToPtr case.+ addImports $ mconcat [hsImport1 "Prelude" "($)",+ hsImportForPrelude,+ hsImportForRuntime]+ withValuePtrName <- toHsWithValuePtrName cls+ saysLn [withValuePtrName, " ", fromVar,+ " $ HoppyP.flip HoppyFHR.withCppPtr $ \\", toVar, " ->"]+ FromCpp -> case classHaskellConversionFromCppFn $ getClassHaskellConversion cls of+ Just _ -> do+ addImports $ mconcat [hsImport1 "Prelude" "(>>=)",+ hsImportForRuntime]+ ctorName <- toHsDataCtorName Unmanaged Const cls+ saysLn ["HoppyFHR.decode (", ctorName, " ", fromVar, ") >>= \\", toVar, " ->"]+ Nothing ->+ throwError $ concat+ ["Can't pass a TObj of ", show cls,+ " from C++ to Haskell because no class decode conversion is defined"]+ Internal_TObjToHeap cls -> case dir of+ ToCpp -> throwError $ objToHeapTWrongDirectionErrorMsg Nothing cls+ FromCpp -> sayArgProcessing dir (ptrT $ objT cls) fromVar toVar+ Internal_TToGc t' -> case dir of+ ToCpp -> throwError $ toGcTWrongDirectionErrorMsg Nothing t'+ FromCpp -> do+ addImports $ mconcat [hsImport1 "Prelude" "(>>=)",+ hsImportForRuntime]+ saysLn ["HoppyFHR.toGc ", fromVar, " >>= \\", toVar, " ->"]+ Internal_TConst t' -> sayArgProcessing dir t' fromVar toVar+ where noConversion = saysLn ["let ", toVar, " = ", fromVar, " in"]+ sayCoerceIntegral = do+ addImports hsImportForRuntime+ saysLn ["let ", toVar, " = HoppyFHR.coerceIntegral ", fromVar, " in"]+ sayCoerceFloating = do+ addImports hsImportForPrelude+ saysLn ["let ", toVar, " = HoppyP.realToFrac ", fromVar, " in"]++-- | Note that the 'CallDirection' is the direction of the call, not the+-- direction of the return. 'ToCpp' means we're returning to the foreign+-- language, 'FromCpp' means we're returning from it.+sayCallAndProcessReturn :: CallDirection -> Type -> [String] -> Generator ()+sayCallAndProcessReturn dir t callWords =+ withErrorContext ("processing return value of type " ++ show t) $+ case t of+ Internal_TVoid -> sayCall+ Internal_TBool -> do+ case dir of+ ToCpp -> do addImports $ mconcat [hsImport1 "Prelude" "(/=)", hsImportForPrelude]+ sayLn "HoppyP.fmap (/= 0)"+ FromCpp -> sayLn "HoppyP.fmap (\\x -> if x then 1 else 0)"+ sayCall+ Internal_TChar -> sayCall+ Internal_TUChar -> sayCall+ Internal_TShort -> sayCall+ Internal_TUShort -> sayCall+ Internal_TInt -> sayCoerceIntegral >> sayCall+ Internal_TUInt -> sayCall+ Internal_TLong -> sayCall+ Internal_TULong -> sayCall+ Internal_TLLong -> sayCall+ Internal_TULLong -> sayCall+ Internal_TFloat -> sayCoerceFloating >> sayCall+ Internal_TDouble -> sayCoerceFloating >> sayCall+ Internal_TInt8 -> sayCall+ Internal_TInt16 -> sayCall+ Internal_TInt32 -> sayCall+ Internal_TInt64 -> sayCall+ Internal_TWord8 -> sayCall+ Internal_TWord16 -> sayCall+ Internal_TWord32 -> sayCall+ Internal_TWord64 -> sayCall+ Internal_TPtrdiff -> sayCall+ Internal_TSize -> sayCall+ Internal_TSSize -> sayCall+ Internal_TEnum _ -> do+ addImports $ mconcat [hsImport1 "Prelude" "(.)", hsImportForPrelude, hsImportForRuntime]+ case dir of+ -- TODO The coersion here is unnecssary if we replace the C numeric types+ -- with their Haskell ones across the board (e.g. CInt -> Int).+ ToCpp -> saysLn ["HoppyP.fmap (HoppyP.toEnum . HoppyFHR.coerceIntegral)"]+ FromCpp -> saysLn ["HoppyP.fmap (HoppyFHR.coerceIntegral . HoppyP.fromEnum)"]+ sayCall+ Internal_TBitspace b -> do+ addImports hsImportForPrelude+ convFn <- bitspaceConvFn dir b+ saysLn ["HoppyP.fmap ", convFn]+ sayCall+ -- The same as TPtr (TConst (TObj _)), but nonconst.+ Internal_TPtr (Internal_TObj cls) -> do+ case dir of+ ToCpp -> do+ addImports hsImportForPrelude+ ctorName <- toHsDataCtorName Unmanaged Nonconst cls+ saysLn ["HoppyP.fmap ", ctorName]+ sayCall+ FromCpp -> do+ addImports $ mconcat [hsImportForPrelude, hsImportForRuntime]+ sayLn "HoppyP.fmap HoppyFHR.toPtr"+ sayCall+ -- The same as TPtr (TConst (TObj _)), but nonconst.+ Internal_TPtr (Internal_TConst (Internal_TObj cls)) -> case dir of+ ToCpp -> do+ addImports hsImportForPrelude+ ctorName <- toHsDataCtorName Unmanaged Const cls+ saysLn ["HoppyP.fmap ", ctorName]+ sayCall+ FromCpp -> do+ addImports $ mconcat [hsImportForPrelude, hsImportForRuntime]+ sayLn "HoppyP.fmap HoppyFHR.toPtr"+ sayCall+ Internal_TPtr _ -> sayCall+ Internal_TRef t' -> sayCallAndProcessReturn dir (ptrT t') callWords+ Internal_TFn {} -> throwError "TFn unimplemented"+ Internal_TCallback cb -> case dir of+ ToCpp -> throwError "Can't receive a callback from C++"+ FromCpp -> do+ addImports $ hsImport1 "Prelude" "(=<<)"+ ctorName <- toHsCallbackCtorName cb+ saysLn [ctorName, "=<<"]+ sayCall+ Internal_TObj cls -> case dir of+ ToCpp -> case classHaskellConversionFromCppFn $ getClassHaskellConversion cls of+ Just _ -> do+ addImports $ mconcat [hsImports "Prelude" ["(.)", "(=<<)"],+ hsImportForRuntime]+ ctorName <- toHsDataCtorName Unmanaged Const cls+ saysLn ["(HoppyFHR.decodeAndDelete . ", ctorName, ") =<<"]+ sayCall+ Nothing ->+ throwError $ concat+ ["Can't return a TObj of ", show cls,+ " from C++ to Haskell because no class decode conversion is defined"]+ FromCpp -> do+ addImports $ mconcat [hsImports "Prelude" ["(.)", "(=<<)"],+ hsImportForPrelude,+ hsImportForRuntime]+ sayLn "(HoppyP.fmap (HoppyFHR.toPtr) . HoppyFHR.encode) =<<"+ sayCall+ Internal_TObjToHeap cls -> case dir of+ ToCpp -> sayCallAndProcessReturn dir (ptrT $ objT cls) callWords+ FromCpp -> throwError $ objToHeapTWrongDirectionErrorMsg Nothing cls+ Internal_TToGc t' -> case dir of+ ToCpp -> do+ addImports $ mconcat [hsImport1 "Prelude" "(=<<)",+ hsImportForRuntime]+ sayLn "HoppyFHR.toGc =<<"+ -- TToGc (TObj _) should create a pointer rather than decoding, so we+ -- change the TObj _ into a TPtr (TObj _).+ case t' of+ Internal_TObj _ -> sayCallAndProcessReturn dir (ptrT t') callWords+ _ -> sayCallAndProcessReturn dir t' callWords+ FromCpp -> throwError $ toGcTWrongDirectionErrorMsg Nothing t'+ Internal_TConst t' -> sayCallAndProcessReturn dir t' callWords+ where sayCall = saysLn $ "(" : callWords ++ [")"]+ sayCoerceIntegral = do addImports $ mconcat [hsImportForPrelude, hsImportForRuntime]+ sayLn "HoppyP.fmap HoppyFHR.coerceIntegral"+ sayCoerceFloating = do addImports hsImportForPrelude+ sayLn "HoppyP.fmap HoppyP.realToFrac"+ bitspaceConvFn dir = case dir of+ ToCpp -> toHsBitspaceTypeName+ FromCpp -> toHsBitspaceToNumName++sayExportClass :: SayExportMode -> Class -> Generator ()+sayExportClass mode cls = withErrorContext ("generating class " ++ show (classExtName cls)) $ do+ case mode of+ SayExportForeignImports -> do+ sayExportClassHsVars mode cls+ sayExportClassHsCtors mode cls++ forM_ (classMethods cls) $ \method ->+ (sayExportFn mode <$> classEntityExtName cls <*> classEntityForeignName cls <*>+ methodPurity <*> pure (getMethodEffectiveParams cls method) <*>+ methodReturn <*> methodExceptionHandlers)+ method++ SayExportDecls -> do+ sayExportClassHsClass True cls Const+ sayExportClassHsClass True cls Nonconst++ sayExportClassHsStaticMethods cls++ -- Create a newtype for referencing foreign objects with pointers. The+ -- newtype is not used with encodings of value objects.+ sayExportClassHsType True cls Const+ sayExportClassHsType True cls Nonconst++ sayExportClassExceptionSupport True cls++ sayExportClassHsVars mode cls+ sayExportClassHsCtors mode cls++ SayExportBoot -> do+ sayExportClassHsClass False cls Const+ sayExportClassHsClass False cls Nonconst++ sayExportClassHsType False cls Const+ sayExportClassHsType False cls Nonconst++ sayExportClassExceptionSupport False cls++ sayExportClassHsVars mode cls++ sayExportClassCastPrimitives mode cls+ sayExportClassHsSpecialFns mode cls++sayExportClassHsClass :: Bool -> Class -> Constness -> Generator ()+sayExportClassHsClass doDecls cls cst = withErrorContext "generating Haskell typeclass" $ do+ hsTypeName <- toHsDataTypeName cst cls+ hsValueClassName <- toHsValueClassName cls+ hsWithValuePtrName <- toHsWithValuePtrName cls+ hsPtrClassName <- toHsPtrClassName cst cls+ hsCastMethodName <- toHsCastMethodName cst cls+ let supers = classSuperclasses cls++ hsSupers <-+ (\x -> if null x+ then do addImports hsImportForRuntime+ return ["HoppyFHR.CppPtr"]+ else return x) =<<+ case cst of+ Const -> mapM (toHsPtrClassName Const) supers+ Nonconst ->+ (:) <$> toHsPtrClassName Const cls <*> mapM (toHsPtrClassName Nonconst) supers++ -- Print the value class definition. There is only one of these, and it is+ -- spiritually closer to the const version of the pointers for this class, so+ -- we emit for the const case only.+ when (cst == Const) $ do+ addImports hsImportForPrelude+ addExport' hsValueClassName+ ln+ saysLn ["class ", hsValueClassName, " a where"]+ indent $+ saysLn [hsWithValuePtrName, " :: a -> (", hsTypeName, " -> HoppyP.IO b) -> HoppyP.IO b"]++ -- Generate instances for all pointer subtypes.+ ln+ saysLn ["#if MIN_VERSION_base(4,8,0)"]+ saysLn ["instance {-# OVERLAPPABLE #-} ", hsPtrClassName, " a => ", hsValueClassName, " a",+ if doDecls then " where" else ""]+ saysLn ["#else"]+ saysLn ["instance ", hsPtrClassName, " a => ", hsValueClassName, " a",+ if doDecls then " where" else ""]+ saysLn ["#endif"]+ when doDecls $ do+ addImports $ mconcat [hsImports "Prelude" ["($)", "(.)"],+ hsImportForPrelude]+ indent $ saysLn [hsWithValuePtrName, " = HoppyP.flip ($) . ", hsCastMethodName]++ -- When the class is encodable to a native Haskell type, also print an+ -- instance for it.+ let conv = getClassHaskellConversion cls+ case (classHaskellConversionType conv,+ classHaskellConversionToCppFn conv) of+ (Just hsTypeGen, Just _) -> do+ hsType <- hsTypeGen+ ln+ saysLn ["#if MIN_VERSION_base(4,8,0)"]+ saysLn ["instance {-# OVERLAPPING #-} ", hsValueClassName, " (", prettyPrint hsType, ")",+ if doDecls then " where" else ""]+ saysLn ["#else"]+ saysLn ["instance ", hsValueClassName, " (", prettyPrint hsType, ")",+ if doDecls then " where" else ""]+ saysLn ["#endif"]+ when doDecls $ do+ addImports hsImportForRuntime+ indent $ saysLn [hsWithValuePtrName, " = HoppyFHR.withCppObj"]+ _ -> return ()++ -- Print the pointer class definition.+ addExport' hsPtrClassName+ ln+ saysLn $+ "class (" :+ intersperse ", " (map (++ " this") hsSupers) +++ [") => ", hsPtrClassName, " this where"]+ indent $ saysLn [hsCastMethodName, " :: this -> ", hsTypeName]++ -- Print the non-static methods.+ when doDecls $ do+ let methods = filter ((cst ==) . methodConst) $ classMethods cls+ forM_ methods $ \method ->+ when (methodStatic method == Nonstatic) $+ (sayExportFn SayExportDecls <$> classEntityExtName cls <*> classEntityForeignName cls <*>+ methodPurity <*> pure (getMethodEffectiveParams cls method) <*>+ methodReturn <*> methodExceptionHandlers) method++sayExportClassHsStaticMethods :: Class -> Generator ()+sayExportClassHsStaticMethods cls =+ forM_ (classMethods cls) $ \method ->+ when (methodStatic method == Static) $+ (sayExportFn SayExportDecls <$> classEntityExtName cls <*> classEntityForeignName cls <*>+ methodPurity <*> methodParams <*> methodReturn <*> methodExceptionHandlers) method++sayExportClassHsType :: Bool -> Class -> Constness -> Generator ()+sayExportClassHsType doDecls cls cst = withErrorContext "generating Haskell data types" $ do+ hsTypeName <- toHsDataTypeName cst cls+ hsCtor <- toHsDataCtorName Unmanaged cst cls+ hsCtorGc <- toHsDataCtorName Managed cst cls+ constCastFnName <- toHsConstCastFnName cst cls++ addImports $ mconcat [hsImportForForeign, hsImportForPrelude, hsImportForRuntime]+ -- Unfortunately, we must export the data constructor, so that GHC can marshal+ -- it in foreign calls in other modules.+ addExport' hsTypeName+ ln+ saysLn ["data ", hsTypeName, " ="]+ indent $ do+ saysLn [" ", hsCtor, " (HoppyF.Ptr ", hsTypeName, ")"]+ saysLn ["| ", hsCtorGc, " (HoppyF.ForeignPtr ()) (HoppyF.Ptr ", hsTypeName, ")"]+ when doDecls $ do+ addImports $ hsImport1 "Prelude" "(==)"+ indent $ sayLn "deriving (HoppyP.Show)"+ ln+ saysLn ["instance HoppyP.Eq ", hsTypeName, " where"]+ indent $ saysLn ["x == y = HoppyFHR.toPtr x == HoppyFHR.toPtr y"]+ ln+ saysLn ["instance HoppyP.Ord ", hsTypeName, " where"]+ indent $ saysLn ["compare x y = HoppyP.compare (HoppyFHR.toPtr x) (HoppyFHR.toPtr y)"]++ -- Generate const_cast functions:+ -- castFooToConst :: Foo -> FooConst+ -- castFooToNonconst :: FooConst -> Foo+ hsTypeNameOppConst <- toHsDataTypeName (constNegate cst) cls+ ln+ addExport constCastFnName+ saysLn [constCastFnName, " :: ", hsTypeNameOppConst, " -> ", hsTypeName]+ when doDecls $ do+ addImports $ hsImport1 "Prelude" "($)"+ hsCtorOppConst <- toHsDataCtorName Unmanaged (constNegate cst) cls+ hsCtorGcOppConst <- toHsDataCtorName Managed (constNegate cst) cls+ saysLn [constCastFnName, " (", hsCtorOppConst,+ " ptr') = ", hsCtor, " $ HoppyF.castPtr ptr'"]+ saysLn [constCastFnName, " (", hsCtorGcOppConst,+ " fptr' ptr') = ", hsCtorGc, " fptr' $ HoppyF.castPtr ptr'"]++ -- Generate an instance of CppPtr.+ ln+ if doDecls+ then do addImports $ hsImport1 "Prelude" "($)"+ saysLn ["instance HoppyFHR.CppPtr ", hsTypeName, " where"]+ indent $ do+ saysLn ["nullptr = ", hsCtor, " HoppyF.nullPtr"]+ ln+ saysLn ["withCppPtr (", hsCtor, " ptr') f' = f' ptr'"]+ saysLn ["withCppPtr (", hsCtorGc,+ " fptr' ptr') f' = HoppyF.withForeignPtr fptr' $ \\_ -> f' ptr'"]+ ln+ saysLn ["toPtr (", hsCtor, " ptr') = ptr'"]+ saysLn ["toPtr (", hsCtorGc, " _ ptr') = ptr'"]+ ln+ saysLn ["touchCppPtr (", hsCtor, " _) = HoppyP.return ()"]+ saysLn ["touchCppPtr (", hsCtorGc, " fptr' _) = HoppyF.touchForeignPtr fptr'"]++ when (classDtorIsPublic cls) $ do+ addImports $ hsImport1 "Prelude" "(==)"+ ln+ saysLn ["instance HoppyFHR.Deletable ", hsTypeName, " where"]+ indent $ do+ -- Note, similar "delete" and "toGc" functions are generated for exception+ -- classes' ExceptionClassInfo structures.+ case cst of+ Const ->+ saysLn ["delete (", hsCtor, " ptr') = ", toHsClassDeleteFnName' cls, " ptr'"]+ Nonconst -> do+ constTypeName <- toHsDataTypeName Const cls+ saysLn ["delete (",hsCtor, " ptr') = ", toHsClassDeleteFnName' cls,+ " $ (HoppyF.castPtr ptr' :: HoppyF.Ptr ", constTypeName, ")"]+ saysLn ["delete (", hsCtorGc,+ " _ _) = HoppyP.fail $ HoppyP.concat ",+ "[\"Deletable.delete: Asked to delete a GC-managed \", ",+ show hsTypeName, ", \" object.\"]"]+ ln+ saysLn ["toGc this'@(", hsCtor, " ptr') = ",+ -- No sense in creating a ForeignPtr for a null pointer.+ "if ptr' == HoppyF.nullPtr then HoppyP.return this' else HoppyP.fmap ",+ "(HoppyP.flip ", hsCtorGc, " ptr') $ ",+ "HoppyF.newForeignPtr ",+ -- The foreign delete function takes a const pointer; we cast it to+ -- take a Ptr () to match up with the ForeignPtr () we're creating,+ -- assuming that data pointers have the same representation.+ "(HoppyF.castFunPtr ", toHsClassDeleteFnPtrName' cls,+ " :: HoppyF.FunPtr (HoppyF.Ptr () -> HoppyP.IO ())) ",+ "(HoppyF.castPtr ptr' :: HoppyF.Ptr ())"]+ saysLn ["toGc this'@(", hsCtorGc, " {}) = HoppyP.return this'"]++ forM_ (classFindCopyCtor cls) $ \copyCtor -> do+ copyCtorName <- toHsCtorName cls copyCtor+ ln+ saysLn ["instance HoppyFHR.Copyable ", hsTypeName, " ",+ case cst of+ Nonconst -> hsTypeName+ Const -> hsTypeNameOppConst,+ " where copy = ", copyCtorName]++ else do saysLn ["instance HoppyFHR.CppPtr ", hsTypeName]++ when (classDtorIsPublic cls) $+ saysLn ["instance HoppyFHR.Deletable ", hsTypeName]++ forM_ (classFindCopyCtor cls) $ \_ ->+ saysLn ["instance HoppyFHR.Copyable ", hsTypeName, " ",+ case cst of+ Nonconst -> hsTypeName+ Const -> hsTypeNameOppConst]++ -- Generate instances for all superclasses' typeclasses.+ genInstances hsTypeName [] cls++ where genInstances :: String -> [Class] -> Class -> Generator ()+ genInstances hsTypeName path ancestorCls = do+ -- In this example Bar inherits from Foo. We are generating instances+ -- either for BarConst or Bar, depending on 'cst'.+ --+ -- BarConst's instances:+ -- instance FooConstPtr BarConst where+ -- toFooConst (BarConst ptr') = FooConst $ castBarToFoo ptr'+ -- toFooConst (BarConstGc fptr' ptr') = FooConstGc fptr' $ castBarToFoo ptr'+ --+ -- instance BarConstPtr BarConst where+ -- toFooConst = id+ --+ -- Bar's instances:+ -- instance FooConstPtr Bar+ -- toFooConst (Bar ptr') =+ -- FooConst $ castBarToFoo $ castBarToConst ptr'+ -- toFooConst (BarGc fptr' ptr') =+ -- FooConstGc fptr' $ castBarToFoo $ castBarToConst ptr'+ --+ -- instance FooPtr Bar+ -- toFoo (Bar ptr') =+ -- Foo $ castFooToNonconst $ castBarToFoo $ castBarToConst ptr'+ -- toFoo (BarGc fptr' ptr') =+ -- FooGc fptr' $ castFooToNonconst $ castBarToFoo $ castBarToConst ptr'+ --+ -- instance BarConstPtr Bar+ -- toBarConst (Bar ptr') = Bar $ castBarToConst ptr'+ -- toBarConst (BarGc fptr' ptr') = BarGc fptr' $ castBarToConst ptr'+ --+ -- instance BarPtr Bar+ -- toBar = id+ --+ -- In all cases, we unwrap the pointer, maybe add const, maybe do an+ -- upcast, maybe remove const, then rewrap the pointer. The identity+ -- cases are where we just unwrap and wrap again.++ forM_ (case cst of+ Const -> [Const]+ Nonconst -> [Const, Nonconst]) $ \ancestorCst -> do+ ln+ ancestorPtrClassName <- toHsPtrClassName ancestorCst ancestorCls+ saysLn ["instance ", ancestorPtrClassName, " ", hsTypeName,+ if doDecls then " where" else ""]+ when doDecls $ indent $ do+ -- Unqualified, for Haskell instance methods.+ let castMethodName = toHsCastMethodName' ancestorCst ancestorCls+ if null path && cst == ancestorCst+ then do addImports hsImportForPrelude+ saysLn [castMethodName, " = HoppyP.id"]+ else do let addConst = cst == Nonconst+ removeConst = ancestorCst == Nonconst+ when (addConst || removeConst) $+ addImports hsImportForForeign+ forM_ ([minBound..] :: [Managed]) $ \managed -> do+ ancestorCtor <- case managed of+ Unmanaged -> (\x -> [x]) <$>+ toHsDataCtorName Unmanaged ancestorCst ancestorCls+ Managed -> (\x -> [x, " fptr'"]) <$>+ toHsDataCtorName Managed ancestorCst ancestorCls+ ptrPattern <- case managed of+ Unmanaged -> (\x -> [x, " ptr'"]) <$>+ toHsDataCtorName Unmanaged cst cls+ Managed -> (\x -> [x, " fptr' ptr'"]) <$>+ toHsDataCtorName Managed cst cls+ saysLn . concat =<< sequence+ [ return $+ [castMethodName, " ("] ++ ptrPattern ++ [") = "] ++ ancestorCtor+ , if removeConst+ then do ancestorConstType <- toHsDataTypeName Const ancestorCls+ ancestorNonconstType <- toHsDataTypeName Nonconst ancestorCls+ return [" $ (HoppyF.castPtr :: HoppyF.Ptr ",+ ancestorConstType, " -> HoppyF.Ptr ",+ ancestorNonconstType, ")"]+ else return []+ , if not $ null path+ then do addImports $ hsImport1 "Prelude" "($)"+ castPrimitiveName <- toHsCastPrimitiveName cls cls ancestorCls+ return [" $ ", castPrimitiveName]+ else return []+ , if addConst+ then do addImports $ hsImport1 "Prelude" "($)"+ nonconstTypeName <- toHsDataTypeName Nonconst cls+ constTypeName <- toHsDataTypeName Const cls+ return [" $ (HoppyF.castPtr :: HoppyF.Ptr ",+ nonconstTypeName, " -> HoppyF.Ptr ",+ constTypeName, ")"]+ else return []+ , return [" ptr'"]+ ]++ forM_ (classSuperclasses ancestorCls) $+ genInstances hsTypeName $+ ancestorCls : path++sayExportClassHsVars :: SayExportMode -> Class -> Generator ()+sayExportClassHsVars mode cls =+ forM_ (classVariables cls) $ sayExportClassVar mode cls++sayExportClassHsCtors :: SayExportMode -> Class -> Generator ()+sayExportClassHsCtors mode cls =+ withErrorContext "generating constructors" $+ forM_ (classCtors cls) $ \ctor ->+ (sayExportFn mode <$> classEntityExtName cls <*> classEntityForeignName cls <*>+ pure Nonpure <*> ctorParams <*> pure (ptrT $ objT cls) <*>+ ctorExceptionHandlers) ctor++sayExportClassHsSpecialFns :: SayExportMode -> Class -> Generator ()+sayExportClassHsSpecialFns mode cls = do+ typeName <- toHsDataTypeName Nonconst cls+ typeNameConst <- toHsDataTypeName Const cls++ -- Say the delete function.+ withErrorContext "generating delete bindings" $+ case mode of+ SayExportForeignImports -> when (classDtorIsPublic cls) $ do+ addImports $ mconcat [hsImportForForeign, hsImportForPrelude]+ saysLn ["foreign import ccall \"", classDeleteFnCppName cls, "\" ",+ toHsClassDeleteFnName' cls, " :: HoppyF.Ptr ",+ typeNameConst, " -> HoppyP.IO ()"]+ saysLn ["foreign import ccall \"&", classDeleteFnCppName cls, "\" ",+ toHsClassDeleteFnPtrName' cls, " :: HoppyF.FunPtr (HoppyF.Ptr ",+ typeNameConst, " -> HoppyP.IO ())"]+ -- The user interface to this is the generic 'delete' function, rendered+ -- elsewhere.+ SayExportDecls -> return ()+ SayExportBoot -> return ()++ withErrorContext "generating pointer Assignable instance" $+ case mode of+ SayExportForeignImports -> return ()+ SayExportDecls -> do+ addImports $ mconcat [hsImport1 "Prelude" "($)",+ hsImportForForeign,+ hsImportForRuntime]+ ln+ saysLn ["instance HoppyFHR.Assignable (HoppyF.Ptr (HoppyF.Ptr ", typeName, ")) ",+ typeName, " where"]+ indent $ sayLn "assign ptr' value' = HoppyF.poke ptr' $ HoppyFHR.toPtr value'"+ SayExportBoot -> return ()++ -- If the class has an assignment operator that takes its own type, then+ -- generate an instance of Assignable.+ withErrorContext "generating Assignable instance" $ do+ let assignmentMethods = flip filter (classMethods cls) $ \m ->+ methodApplicability m == MNormal &&+ (methodParams m == [objT cls] || methodParams m == [refT $ constT $ objT cls]) &&+ (case methodImpl m of+ RealMethod name -> name == FnOp OpAssign+ FnMethod name -> name == FnOp OpAssign)+ withAssignmentMethod f = case assignmentMethods of+ [] -> return ()+ [m] -> f m+ _ ->+ throwError $ concat+ ["Can't determine an Assignable instance to generator for ", show cls,+ " because it has multiple assignment operators ", show assignmentMethods]+ when (mode == SayExportDecls) $ withAssignmentMethod $ \m -> do+ addImports $ mconcat [hsImport1 "Prelude" "(>>)", hsImportForPrelude]+ valueClassName <- toHsValueClassName cls+ assignmentMethodName <- toHsMethodName cls m+ ln+ saysLn ["instance ", valueClassName, " a => HoppyFHR.Assignable ", typeName, " a where"]+ indent $+ saysLn ["assign x' y' = ", assignmentMethodName, " x' y' >> HoppyP.return ()"]++ -- A pointer to an object pointer is decodable to an object pointer by peeking+ -- at the value, so generate a Decodable instance. You are now a two-star+ -- programmer. There is a generic @Ptr (Ptr a)@ to @Ptr a@ instance which+ -- handles deeper levels.+ withErrorContext "generating pointer Decodable instance" $ do+ case mode of+ SayExportForeignImports -> return ()++ SayExportDecls -> do+ addImports $ mconcat [hsImport1 "Prelude" "(.)",+ hsImportForForeign,+ hsImportForPrelude,+ hsImportForRuntime]+ ln+ saysLn ["instance HoppyFHR.Decodable (HoppyF.Ptr (HoppyF.Ptr ",+ typeName, ")) ", typeName, " where"]+ indent $ do+ ctorName <- toHsDataCtorName Unmanaged Nonconst cls+ saysLn ["decode = HoppyP.fmap ", ctorName, " . HoppyF.peek"]++ SayExportBoot -> do+ addImports $ mconcat [hsImportForForeign, hsImportForRuntime]+ ln+ -- TODO Encodable.+ saysLn ["instance HoppyFHR.Decodable (HoppyF.Ptr (HoppyF.Ptr ", typeName, ")) ", typeName]++ -- Say Encodable and Decodable instances, if the class is encodable and+ -- decodable.+ withErrorContext "generating Encodable/Decodable instances" $ do+ let conv = getClassHaskellConversion cls+ forM_ (classHaskellConversionType conv) $ \hsTypeGen -> do+ let hsTypeStrGen = hsTypeGen >>= \hsType -> return $ "(" ++ prettyPrint hsType ++ ")"++ case mode of+ SayExportForeignImports -> return ()++ SayExportDecls -> do+ -- Say the Encodable instances.+ forM_ (classHaskellConversionToCppFn conv) $ \toCppFnGen -> do+ hsTypeStr <- hsTypeStrGen+ addImports $ mconcat [hsImportForPrelude, hsImportForRuntime]+ castMethodName <- toHsCastMethodName Const cls++ ln+ saysLn ["instance HoppyFHR.Encodable ", typeName, " ", hsTypeStr, " where"]+ indent $ do+ sayLn "encode ="+ indent toCppFnGen+ ln+ saysLn ["instance HoppyFHR.Encodable ", typeNameConst, " ", hsTypeStr, " where"]+ indent $+ saysLn ["encode = HoppyP.fmap (", castMethodName,+ ") . HoppyFHR.encodeAs (HoppyP.undefined :: ", typeName, ")"]++ -- Say the Decodable instances.+ forM_ (classHaskellConversionFromCppFn conv) $ \fromCppFnGen -> do+ hsTypeStr <- hsTypeStrGen+ addImports hsImportForRuntime+ castMethodName <- toHsCastMethodName Const cls++ ln+ saysLn ["instance HoppyFHR.Decodable ", typeName, " ", hsTypeStr, " where"]+ indent $+ saysLn ["decode = HoppyFHR.decode . ", castMethodName]+ ln+ saysLn ["instance HoppyFHR.Decodable ", typeNameConst, " ", hsTypeStr, " where"]+ indent $ do+ sayLn "decode ="+ indent fromCppFnGen++ SayExportBoot -> do+ -- Say the Encodable instances.+ forM_ (classHaskellConversionToCppFn conv) $ \_ -> do+ hsTypeStr <- hsTypeStrGen+ addImports hsImportForRuntime+ ln+ saysLn ["instance HoppyFHR.Encodable ", typeName, " (", hsTypeStr, ")"]+ saysLn ["instance HoppyFHR.Encodable ", typeNameConst, " (", hsTypeStr, ")"]++ -- Say the Decodable instances.+ forM_ (classHaskellConversionFromCppFn conv) $ \_ -> do+ hsTypeStr <- hsTypeStrGen+ addImports hsImportForRuntime+ ln+ saysLn ["instance HoppyFHR.Decodable ", typeName, " (", hsTypeStr, ")"]+ saysLn ["instance HoppyFHR.Decodable ", typeNameConst, " (", hsTypeStr, ")"]++-- | Generates a non-const @CppException@ instance if the class is an exception+-- class.+sayExportClassExceptionSupport :: Bool -> Class -> Generator ()+sayExportClassExceptionSupport doDecls cls =+ when (classIsException cls) $+ withErrorContext "generating exception support" $ do+ typeName <- toHsDataTypeName Nonconst cls+ typeNameConst <- toHsDataTypeName Const cls++ -- Generate a non-const CppException instance.+ exceptionId <- getClassExceptionId cls+ addImports hsImportForRuntime+ ln+ saysLn ["instance HoppyFHR.CppException ", typeName,+ if doDecls then " where" else ""]+ when doDecls $ indent $ do+ ctorName <- toHsDataCtorName Unmanaged Nonconst cls+ ctorGcName <- toHsDataCtorName Managed Nonconst cls+ addImports $ mconcat [hsImports "Prelude" ["($)", "(.)", "(=<<)"],+ hsImportForForeign,+ hsImportForMap,+ hsImportForPrelude]+ sayLn "cppExceptionInfo _ ="+ indent $ do+ saysLn ["HoppyFHR.ExceptionClassInfo (HoppyFHR.ExceptionId ",+ show $ getExceptionId exceptionId, ") ", show typeName,+ " upcasts' delete' copy' toGc'"]++ -- Note, similar "delete" and "toGc" functions are generated for the class's+ -- Deletable instance.+ saysLn ["where delete' ptr' = ", toHsClassDeleteFnName' cls,+ " (HoppyF.castPtr ptr' :: HoppyF.Ptr ", typeNameConst, ")"]++ indentSpaces 6 $ do+ ctorName <- toHsDataCtorName Unmanaged Nonconst cls+ ln+ saysLn ["copy' = HoppyP.fmap (HoppyF.castPtr . HoppyFHR.toPtr) . HoppyFHR.copy . ",+ ctorName, " . HoppyF.castPtr"]++ ln+ saysLn ["toGc' ptr' = HoppyF.newForeignPtr ",+ -- The foreign delete function takes a const pointer; we cast it to+ -- take a Ptr () to match up with the ForeignPtr () we're creating,+ -- assuming that data pointers have the same representation.+ "(HoppyF.castFunPtr ", toHsClassDeleteFnPtrName' cls,+ " :: HoppyF.FunPtr (HoppyF.Ptr () -> HoppyP.IO ())) ",+ "ptr'"]++ sayLn "upcasts' = HoppyDM.fromList"+ indent $ case classSuperclasses cls of+ [] -> sayLn "[]"+ _ -> do+ let genCast :: Bool -> [Class] -> Class -> Generator ()+ genCast first path ancestorCls =+ when (classIsException ancestorCls) $ do+ let path' = ancestorCls : path+ ancestorId <- getClassExceptionId ancestorCls+ ancestorCastChain <- forM (zip path' $ drop 1 path') $ \(from, to) ->+ -- We're upcasting, so 'from' is the subclass.+ toHsCastPrimitiveName from to from+ saysLn $ concat [ [if first then "[" else ",",+ " ( HoppyFHR.ExceptionId ",+ show $ getExceptionId ancestorId,+ ", \\(e' :: HoppyF.Ptr ()) -> "]+ , intersperse " $ " $+ "HoppyF.castPtr" :+ ancestorCastChain +++ ["HoppyF.castPtr e' :: HoppyF.Ptr ()"]+ , [")"]+ ]+ forM_ (classSuperclasses ancestorCls) $ genCast False path'++ forM_ (zip (classSuperclasses cls) (True : repeat False)) $+ \(ancestorCls, first) -> genCast first [cls] ancestorCls+ sayLn "]"++ ln+ saysLn ["cppExceptionBuild fptr' ptr' = ", ctorGcName,+ " fptr' (HoppyF.castPtr ptr' :: HoppyF.Ptr ", typeName, ")"]+ ln+ saysLn ["cppExceptionBuildToGc ptr' = HoppyFHR.toGc $ ", ctorName,+ " (HoppyF.castPtr ptr' :: HoppyF.Ptr ", typeName, ")"]++ -- Generate a const CppException instance that piggybacks off of the+ -- non-const implementation.+ ln+ saysLn ["instance HoppyFHR.CppException ", typeNameConst,+ if doDecls then " where" else ""]+ when doDecls $ indent $ do+ addImports $ mconcat [hsImport1 "Prelude" "(.)",+ hsImportForPrelude]+ constCastFnName <- toHsConstCastFnName Const cls+ saysLn ["cppExceptionInfo _ = HoppyFHR.cppExceptionInfo (HoppyP.undefined :: ",+ typeName, ")"]+ saysLn ["cppExceptionBuild = (", constCastFnName,+ " .) . HoppyFHR.cppExceptionBuild"]+ saysLn ["cppExceptionBuildToGc = HoppyP.fmap ", constCastFnName,+ " . HoppyFHR.cppExceptionBuildToGc"]++ -- Generate a non-const CppThrowable instance.+ ln+ saysLn ["instance HoppyFHR.CppThrowable ", typeName,+ if doDecls then " where" else ""]+ when doDecls $ indent $ do+ ctorName <- toHsDataCtorName Unmanaged Nonconst cls+ ctorGcName <- toHsDataCtorName Managed Nonconst cls+ addImports $ mconcat [hsImportForForeign,+ hsImportForPrelude]+ saysLn ["toSomeCppException this'@(", ctorName, " ptr') = HoppyFHR.SomeCppException ",+ "(HoppyFHR.cppExceptionInfo this') HoppyP.Nothing (HoppyF.castPtr ptr')"]+ saysLn ["toSomeCppException this'@(", ctorGcName, " fptr' ptr') = HoppyFHR.SomeCppException ",+ "(HoppyFHR.cppExceptionInfo this') (HoppyP.Just fptr') (HoppyF.castPtr ptr')"]++sayExportClassCastPrimitives :: SayExportMode -> Class -> Generator ()+sayExportClassCastPrimitives mode cls = withErrorContext "generating cast primitives" $ do+ clsType <- toHsDataTypeName Const cls+ case mode of+ SayExportForeignImports ->+ forAncestors cls $ \super -> do+ hsCastFnName <- toHsCastPrimitiveName cls cls super+ hsDownCastFnName <- toHsCastPrimitiveName cls super cls+ superType <- toHsDataTypeName Const super+ addImports hsImportForForeign+ addExport hsCastFnName+ saysLn [ "foreign import ccall \"", classCastFnCppName cls super+ , "\" ", hsCastFnName, " :: HoppyF.Ptr ", clsType, " -> HoppyF.Ptr ", superType+ ]+ unless (classIsSubclassOfMonomorphic cls || classIsMonomorphicSuperclass super) $ do+ addExport hsDownCastFnName+ saysLn [ "foreign import ccall \"", classCastFnCppName super cls+ , "\" ", hsDownCastFnName, " :: HoppyF.Ptr ", superType, " -> HoppyF.Ptr ", clsType+ ]+ return True++ SayExportDecls ->+ -- Generate a downcast typeclass and instances for all ancestor classes+ -- for the current constness. These don't need to be in the boot file,+ -- since they're not used by other generated bindings.+ unless (classIsSubclassOfMonomorphic cls) $+ forM_ [minBound..] $ \cst -> do+ downCastClassName <- toHsDownCastClassName cst cls+ downCastMethodName <- toHsDownCastMethodName cst cls+ typeName <- toHsDataTypeName cst cls+ addExport' downCastClassName+ ln+ saysLn ["class ", downCastClassName, " a where"]+ indent $ saysLn [downCastMethodName, " :: ",+ prettyPrint $ HsTyFun (HsTyVar $ HsIdent "a") $+ HsTyCon $ UnQual $ HsIdent typeName]+ ln+ forAncestors cls $ \super -> case classIsMonomorphicSuperclass super of+ True -> return False+ False -> do+ superTypeName <- toHsDataTypeName cst super+ primitiveCastFn <- toHsCastPrimitiveName cls super cls+ saysLn ["instance ", downCastClassName, " ", superTypeName, " where"]++ -- If Foo is a superclass of Bar:+ --+ -- instance BarSuper Foo where+ -- downToBar castFooToNonconst . downcast' . castFooToConst+ -- where downcast' (FooConst ptr') = BarConst $ castFooToBar ptr'+ -- downcast' (FooConstGc fptr' ptr') = BarConstGc fptr' $ castFooToBar ptr'+ --+ -- instance BarSuperConst FooConst where+ -- downToBarConst = downcast'+ -- where downcast' (FooConst ptr') = BarConst $ castFooToBar ptr'+ -- downcast' (FooConstGc fptr' ptr') = BarConstGc fptr' $ castFooToBar ptr'++ indent $ do+ case cst of+ Const -> saysLn [downCastMethodName, " = cast'"]+ Nonconst -> do+ addImports $ hsImport1 "Prelude" "(.)"+ castClsToNonconst <- toHsConstCastFnName Nonconst cls+ castSuperToConst <- toHsConstCastFnName Const super+ saysLn [downCastMethodName, " = ", castClsToNonconst, " . cast' . ",+ castSuperToConst]+ indent $ do+ sayLn "where"+ indent $ do+ clsCtorName <- toHsDataCtorName Unmanaged Const cls+ clsCtorGcName <- toHsDataCtorName Managed Const cls+ superCtorName <- toHsDataCtorName Unmanaged Const super+ superCtorGcName <- toHsDataCtorName Managed Const super+ saysLn ["cast' (", superCtorName, " ptr') = ",+ clsCtorName, " $ ", primitiveCastFn, " ptr'"]+ saysLn ["cast' (", superCtorGcName, " fptr' ptr') = ",+ clsCtorGcName , " fptr' $ ", primitiveCastFn, " ptr'"]+ return True++ SayExportBoot -> do+ forAncestors cls $ \super -> do+ hsCastFnName <- toHsCastPrimitiveName cls cls super+ superType <- toHsDataTypeName Const super+ addImports $ hsImportForForeign+ addExport hsCastFnName+ saysLn [hsCastFnName, " :: HoppyF.Ptr ", clsType, " -> HoppyF.Ptr ", superType]+ return True++ where forAncestors :: Class -> (Class -> Generator Bool) -> Generator ()+ forAncestors cls' f = forM_ (classSuperclasses cls') $ \super -> do+ recur <- f super+ when recur $ forAncestors super f++-- | Outputs the @ExceptionDb@ needed by all Haskell gateway functions that deal+-- with exceptions.+sayExceptionSupport :: Bool -> Generator ()+sayExceptionSupport doDecls = do+ iface <- askInterface+ addExport "exceptionDb'"+ addImports hsImportForRuntime+ ln+ sayLn "exceptionDb' :: HoppyFHR.ExceptionDb"+ when doDecls $ do+ addImports $ mconcat [hsImport1 "Prelude" "($)",+ hsImportForMap]+ sayLn "exceptionDb' = HoppyFHR.ExceptionDb $ HoppyDM.fromList"+ indent $ do+ let classes = interfaceAllExceptionClasses iface+ case classes of+ [] -> sayLn "[]"+ _ -> do+ addImports hsImportForPrelude+ forM_ (zip classes (True : repeat False)) $ \(cls, first) -> do+ exceptionId <-+ fromMaybeM (throwError $ "sayExceptionSupport: Internal error, " ++ show cls +++ " has no exception ID.") $+ interfaceExceptionClassId iface cls+ typeName <- toHsDataTypeName Nonconst cls+ saysLn [if first then "[ (" else ", (",+ "HoppyFHR.ExceptionId ", show $ getExceptionId exceptionId,+ ", HoppyFHR.cppExceptionInfo (HoppyP.undefined :: ",+ typeName, "))"]+ sayLn "]"++-- | Implements special logic on top of 'cppTypeToHsTypeAndUse', that computes+-- the Haskell __qualified__ type for a function, including typeclass+-- constraints.+fnToHsTypeAndUse :: HsTypeSide+ -> Purity+ -> [Type]+ -> Type+ -> ExceptionHandlers+ -> Generator HsQualType+fnToHsTypeAndUse side purity paramTypes returnType exceptionHandlers = do+ let catches = not $ null $ exceptionHandlersList exceptionHandlers++ params <- mapM contextForParam $+ (if catches && side == HsCSide+ then (++ [("excId", ptrT intT), ("excPtr", ptrT $ ptrT voidT)])+ else id) $+ zip (map toArgName [1..]) paramTypes+ let context = mapMaybe fst params :: HsContext+ hsParams = map snd params++ -- Determine the 'HsHsSide' return type for the function. Do the conversion+ -- to a Haskell type, and wrap the result in 'IO' if the function is impure.+ -- (HsCSide types always get wrapped in IO.)+ hsReturnInitial <- cppTypeToHsTypeAndUse side returnType+ hsReturnForPurity <- case (purity, side) of+ (Pure, HsHsSide) -> return hsReturnInitial+ _ -> do+ addImports hsImportForPrelude+ return $ HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyP.IO") hsReturnInitial++ return $ HsQualType context $ foldr HsTyFun hsReturnForPurity hsParams++ where contextForParam :: (String, Type) -> Generator (Maybe HsAsst, HsType)+ contextForParam (s, t) = case t of+ Internal_TBitspace b -> receiveBitspace s t b+ Internal_TPtr (Internal_TObj cls) -> receivePtr s cls Nonconst+ Internal_TPtr (Internal_TConst (Internal_TObj cls)) -> receiveValue s t cls+ Internal_TRef (Internal_TObj cls) -> receivePtr s cls Nonconst+ Internal_TRef (Internal_TConst (Internal_TObj cls)) -> receiveValue s t cls+ Internal_TObj cls -> receiveValue s t cls+ Internal_TConst t' -> contextForParam (s, t')+ _ -> handoff side t++ -- Use whatever type 'cppTypeToHsTypeAndUse' suggests, with no typeclass+ -- constraints.+ handoff :: HsTypeSide -> Type -> Generator (Maybe HsAsst, HsType)+ handoff side t = (,) Nothing <$> cppTypeToHsTypeAndUse side t++ -- Receives a @IsFooBitspace a => a@.+ receiveBitspace s t b = case side of+ HsCSide -> handoff side t+ HsHsSide -> do+ bitspaceClassName <- toHsBitspaceClassName b+ let t' = HsTyVar $ HsIdent s+ return (Just (UnQual $ HsIdent bitspaceClassName, [t']),+ t')++ -- Receives a @FooPtr this => this@.+ receivePtr :: String -> Class -> Constness -> Generator (Maybe HsAsst, HsType)+ receivePtr s cls cst = case side of+ HsHsSide -> do+ ptrClassName <- toHsPtrClassName cst cls+ let t' = HsTyVar $ HsIdent s+ return (Just (UnQual $ HsIdent ptrClassName, [t']),+ t')+ HsCSide -> do+ addImports $ hsImportForForeign+ typeName <- toHsDataTypeName cst cls+ return (Nothing, HsTyApp (HsTyCon $ UnQual $ HsIdent "HoppyF.Ptr") $+ HsTyVar $ HsIdent typeName)++ -- Receives a @FooValue a => a@.+ receiveValue :: String -> Type -> Class -> Generator (Maybe HsAsst, HsType)+ receiveValue s t cls = case side of+ HsCSide -> handoff side t+ HsHsSide -> do+ addImports hsImportForRuntime+ valueClassName <- toHsValueClassName cls+ let t' = HsTyVar $ HsIdent s+ return (Just (UnQual $ HsIdent valueClassName, [t']),+ t')++getMethodEffectiveParams :: Class -> Method -> [Type]+getMethodEffectiveParams cls method =+ (case methodImpl method of+ RealMethod {} -> case methodApplicability method of+ MNormal -> (ptrT (objT cls):)+ MConst -> (ptrT (constT $ objT cls):)+ MStatic -> id+ FnMethod {} -> id) $+ methodParams method++getEffectiveExceptionHandlers :: ExceptionHandlers -> Generator ExceptionHandlers+getEffectiveExceptionHandlers handlers = do+ ifaceHandlers <- interfaceExceptionHandlers <$> askInterface+ moduleHandlers <- getExceptionHandlers <$> askModule+ -- Exception handlers declared lower in the hierarchy take precedence over+ -- those in the hierarchy; ExceptionHandlers is a left-biased monoid.+ return $ mconcat [handlers, moduleHandlers, ifaceHandlers]++getEffectiveCallbackThrows :: Callback -> Generator Bool+getEffectiveCallbackThrows cb = case callbackThrows cb of+ Just b -> return b+ Nothing -> moduleCallbacksThrow <$> askModule >>= \case+ Just b -> return b+ Nothing -> interfaceCallbacksThrow <$> askInterface++getClassExceptionId :: Class -> Generator ExceptionId+getClassExceptionId cls = do+ iface <- askInterface+ fromMaybeM (throwError $ concat+ ["Internal error, exception class ", show cls, " doesn't have an exception ID"]) $+ interfaceExceptionClassId iface cls
src/Foreign/Hoppy/Generator/Spec.hs view
@@ -1,1877 +1,32 @@ -- This file is part of Hoppy. ----- Copyright 2015-2016 Bryan Gardiner <bog@khumba.net>------ This program is free software: you can redistribute it and/or modify--- it under the terms of the GNU Affero General Public License as published by--- the Free Software Foundation, either version 3 of the License, or--- (at your option) any later version.------ This program is distributed in the hope that it will be useful,--- but WITHOUT ANY WARRANTY; without even the implied warranty of--- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the--- GNU Affero General Public License for more details.------ You should have received a copy of the GNU Affero General Public License--- along with this program. If not, see <http://www.gnu.org/licenses/>.--{-# LANGUAGE CPP #-}---- | The primary data types for specifying C++ interfaces.------ 'Show' instances in this module produce strings of the form @\"\<TypeOfObject--- nameOfObject otherInfo...\>\"@. They can be used in error messages without--- specifying a noun separately, i.e. write @show cls@ instead of @\"the class--- \" ++ show cls@.-module Foreign.Hoppy.Generator.Spec (- -- * Interfaces- Interface,- ErrorMsg,- interface,- interfaceName,- interfaceModules,- interfaceNamesToModules,- interfaceHaskellModuleBase,- interfaceDefaultHaskellModuleBase,- interfaceAddHaskellModuleBase,- -- * C++ includes- Include,- includeStd,- includeLocal,- includeToString,- -- * Modules- Module,- moduleName,- moduleHppPath,- moduleCppPath,- moduleExports,- moduleReqs,- moduleHaskellName,- makeModule,- moduleModify,- moduleModify',- moduleSetHppPath,- moduleSetCppPath,- moduleAddExports,- moduleAddHaskellName,- -- * Requirements- Reqs,- reqsIncludes,- reqInclude,- HasReqs (..),- addReqs,- addReqIncludes,- -- * Names and exports- ExtName,- toExtName,- fromExtName,- FnName (..),- IsFnName (..),- Operator (..),- OperatorType (..),- operatorPreferredExtName,- operatorPreferredExtName',- operatorType,- Export (..),- exportExtName,- exportAddendum,- Identifier,- identifierParts,- IdPart,- idPartBase,- idPartArgs,- ident, ident', ident1, ident2, ident3, ident4, ident5,- identT, identT', ident1T, ident2T, ident3T, ident4T, ident5T,- -- * Basic types- Type (..),- normalizeType,- stripConst,- -- ** Variables- Variable, makeVariable, varIdentifier, varExtName, varType, varReqs,- varIsConst, varGetterExtName, varSetterExtName,- -- ** Enums- CppEnum, makeEnum, enumIdentifier, enumExtName, enumValueNames, enumReqs,- -- ** Bitspaces- Bitspace, makeBitspace, bitspaceExtName, bitspaceType, bitspaceValueNames, bitspaceEnum,- bitspaceAddEnum, bitspaceCppTypeIdentifier, bitspaceFromCppValueFn, bitspaceToCppValueFn,- bitspaceAddCppType, bitspaceReqs,- -- ** Functions- Purity (..),- Function, makeFn, fnCName, fnExtName, fnPurity, fnParams, fnReturn, fnReqs,- -- ** Classes- Class, makeClass, classIdentifier, classExtName, classSuperclasses, classCtors, classDtorIsPublic,- classMethods, classConversion, classReqs, classAddCtors, classSetDtorPrivate, classAddMethods,- classIsMonomorphicSuperclass, classSetMonomorphicSuperclass,- classIsSubclassOfMonomorphic, classSetSubclassOfMonomorphic,- HasClassyExtName (..),- Ctor, makeCtor, mkCtor, ctorExtName, ctorParams,- Method,- MethodImpl (..),- MethodApplicability (..),- Constness (..),- constNegate,- Staticness (..),- makeMethod, makeFnMethod, mkMethod, mkMethod', mkConstMethod, mkConstMethod',- mkStaticMethod, mkStaticMethod',- mkProps, mkProp, mkStaticProp, mkBoolIsProp, mkBoolHasProp,- methodImpl, methodExtName, methodApplicability, methodPurity, methodParams,- methodReturn, methodConst, methodStatic,- -- *** Conversion to and from foreign values- ClassConversion (..),- ClassConversionMode (..),- classConversionNone,- classModifyConversion,- classSetConversion,- classSetConversionToHeap,- classSetConversionToGc,- classSetHaskellConversion,- ClassHaskellConversion (..),- -- ** Callbacks- Callback, makeCallback, callbackExtName, callbackParams, callbackReturn, callbackReqs,- -- * Addenda- Addendum (..),- HasAddendum,- addAddendumHaskell,- -- * Haskell imports- HsModuleName, HsImportSet, HsImportKey (..), HsImportSpecs (..), HsImportName, HsImportVal (..),- hsWholeModuleImport, hsQualifiedImport, hsImport1, hsImport1', hsImports, hsImports',- hsImportSetMakeSource,- -- * Internal to Hoppy- stringOrIdentifier,- callbackToTFn,- -- ** Haskell imports- makeHsImportSet,- getHsImportSet,- hsImportForBits,- hsImportForInt,- hsImportForWord,- hsImportForForeign,- hsImportForForeignC,- hsImportForPrelude,- hsImportForRuntime,- hsImportForSystemPosixTypes,- hsImportForUnsafeIO,- -- ** Error messages- objToHeapTWrongDirectionErrorMsg,- tToGcInvalidFormErrorMessage,- toGcTWrongDirectionErrorMsg,- ) where--#if !MIN_VERSION_base(4,8,0)-import Control.Applicative ((<$>), (<*>))-#endif-import Control.Arrow ((&&&))-import Control.Monad (liftM2, unless)-#if MIN_VERSION_mtl(2,2,1)-import Control.Monad.Except (MonadError, throwError)-#else-import Control.Monad.Error (MonadError, throwError)-#endif-import Control.Monad.State (MonadState, StateT, execStateT, get, modify)-import Data.Char (isAlpha, isAlphaNum, toUpper)-import Data.Function (on)-import Data.List (intercalate, intersperse)-import qualified Data.Map as M-import Data.Maybe (fromMaybe)-#if !MIN_VERSION_base(4,8,0)-import Data.Monoid (Monoid, mappend, mconcat, mempty)-#endif-import qualified Data.Set as S-import {-# SOURCE #-} qualified Foreign.Hoppy.Generator.Language.Haskell as Haskell-import Language.Haskell.Syntax (HsType)---- | Indicates strings that are error messages.-type ErrorMsg = String---- | A complete specification of a C++ API. Generators for different languages,--- including the binding generator for C++, use these to produce their output.-data Interface = Interface- { interfaceName :: String- -- ^ The textual name of the interface.- , interfaceModules :: M.Map String Module- -- ^ All of the individual modules, by 'moduleName'.- , interfaceNamesToModules :: M.Map ExtName Module- -- ^ Maps each 'ExtName' exported by some module to the module that exports- -- the name.- , interfaceHaskellModuleBase' :: Maybe [String]- -- ^ See 'interfaceHaskellModuleBase'.- }--instance Show Interface where- show iface = concat ["<Interface ", show (interfaceName iface), ">"]---- | Constructs an 'Interface' from the required parts. Some validation is--- performed; if the resulting interface would be invalid, an error message is--- returned instead.-interface :: String -- ^ 'interfaceName'- -> [Module] -- ^ 'interfaceModules'- -> Either ErrorMsg Interface-interface ifName modules = do- -- TODO Check for duplicate module names.- -- TODO Check for duplicate module file paths.-- -- Check for multiple modules exporting an ExtName.- let extNamesToModules :: M.Map ExtName [Module]- extNamesToModules =- M.unionsWith (++) $- map (\m -> const [m] <$> moduleExports m) modules-- extNamesInMultipleModules :: [(ExtName, [Module])]- extNamesInMultipleModules =- M.toList $- M.filter (\modules -> case modules of- _:_:_ -> True- _ -> False)- extNamesToModules-- unless (null extNamesInMultipleModules) $- Left $ unlines $- "Some external name(s) are exported by multiple modules:" :- map (\(extName, modules) ->- concat $ "- " : show extName : ": " : intersperse ", " (map show modules))- extNamesInMultipleModules-- return Interface- { interfaceName = ifName- , interfaceModules = M.fromList $ map (moduleName &&& id) modules- , interfaceNamesToModules = M.map (\[x] -> x) extNamesToModules- , interfaceHaskellModuleBase' = Nothing- }---- | The name of the parent Haskell module under which a Haskell module will be--- generated for a Hoppy 'Module'. This is a list of Haskell module path--- components, in other words, @'Data.List.intercalate' "."@ on the list--- produces a Haskell module name. Defaults to--- 'interfaceDefaultHaskellModuleBase', and may be overridden with--- 'interfaceAddHaskellModuleBase'.-interfaceHaskellModuleBase :: Interface -> [String]-interfaceHaskellModuleBase =- fromMaybe interfaceDefaultHaskellModuleBase . interfaceHaskellModuleBase'---- | The default Haskell module under which Hoppy modules will be generated.--- This is @Foreign.Hoppy.Generated@, that is:------ > ["Foreign", "Hoppy", "Generated"]-interfaceDefaultHaskellModuleBase :: [String]-interfaceDefaultHaskellModuleBase = ["Foreign", "Hoppy", "Generated"]---- | Sets an interface to generate all of its modules under the given Haskell--- module prefix. See 'interfaceHaskellModuleBase'.-interfaceAddHaskellModuleBase :: [String] -> Interface -> Either String Interface-interfaceAddHaskellModuleBase modulePath iface = case interfaceHaskellModuleBase' iface of- Nothing -> Right iface { interfaceHaskellModuleBase' = Just modulePath }- Just existingPath ->- Left $ concat- [ "addInterfaceHaskellModuleBase: Trying to add Haskell module base "- , intercalate "." modulePath, " to ", show iface- , " which already has a module base ", intercalate "." existingPath- ]---- | An @#include@ directive in a C++ file.-data Include = Include- { includeToString :: String- -- ^ Returns the complete @#include ...@ line for an include, including- -- trailing newline.- } deriving (Eq, Ord, Show)---- | Creates an @#include \<...\>@ directive.-includeStd :: String -> Include-includeStd path = Include $ "#include <" ++ path ++ ">\n"---- | Creates an @#include "..."@ directive.-includeLocal :: String -> Include-includeLocal path = Include $ "#include \"" ++ path ++ "\"\n"---- | A portion of functionality in a C++ API. An 'Interface' is composed of--- multiple modules. A module will generate a single compilation unit--- containing bindings for all of the module's exports. The C++ code for a--- generated module will @#include@ everything necessary for what is written to--- the header and source files separately. You can declare include dependencies--- with e.g. 'addReqIncludes', either for individual exports or at the module--- level. Dependencies between modules are handled automatically, and--- circularity is supported to a certain extent. See the documentation for the--- individual language modules for further details.-data Module = Module- { moduleName :: String- -- ^ The module's name. A module name must identify a unique module within- -- an 'Interface'.- , moduleHppPath :: String- -- ^ A relative path under a C++ sources root to which the generator will- -- write a header file for the module's C++ bindings.- , moduleCppPath :: String- -- ^ A relative path under a C++ sources root to which the generator will- -- write a source file for the module's C++ bindings.- , moduleExports :: M.Map ExtName Export- -- ^ All of the exports in a module.- , moduleReqs :: Reqs- -- ^ Module-level requirements.- , moduleHaskellName :: Maybe [String]- -- ^ The generated Haskell module name, underneath the- -- 'interfaceHaskellModuleBase'. If absent (by default), the 'moduleName'- -- is used. May be modified with 'moduleAddHaskellName'.- }--instance Eq Module where- (==) = (==) `on` moduleName--instance Ord Module where- compare = compare `on` moduleName--instance Show Module where- show m = concat ["<Module ", moduleName m, ">"]--instance HasReqs Module where- getReqs = moduleReqs- setReqs reqs m = m { moduleReqs = reqs }---- | Creates an empty module, ready to be configured with 'moduleModify'.-makeModule :: String -- ^ 'moduleName'- -> String -- ^ 'moduleHppPath'- -> String -- ^ 'moduleCppPath'- -> Module-makeModule name hppPath cppPath = Module- { moduleName = name- , moduleHppPath = hppPath- , moduleCppPath = cppPath- , moduleExports = M.empty- , moduleReqs = mempty- , moduleHaskellName = Nothing- }---- | Extends a module. To be used with the module state-monad actions in this--- package.-moduleModify :: Module -> StateT Module (Either String) () -> Either ErrorMsg Module-moduleModify = flip execStateT---- | Same as 'moduleModify', but calls 'error' in the case of failure, which is--- okay in for a generator which would abort in this case anyway.-moduleModify' :: Module -> StateT Module (Either String) () -> Module-moduleModify' m action = case moduleModify m action of- Left errorMsg ->- error $ concat- ["moduleModify' failed to modify ", show m, ": ", errorMsg]- Right m' -> m'---- | Replaces a module's 'moduleHppPath'.-moduleSetHppPath :: MonadState Module m => String -> m ()-moduleSetHppPath path = modify $ \m -> m { moduleHppPath = path }---- | Replaces a module's 'moduleCppPath'.-moduleSetCppPath :: MonadState Module m => String -> m ()-moduleSetCppPath path = modify $ \m -> m { moduleCppPath = path }---- | Adds exports to a module. An export must only be added to any module at--- most once, and must not be added to multiple modules.-moduleAddExports :: (MonadError String m, MonadState Module m) => [Export] -> m ()-moduleAddExports exports = do- m <- get- let existingExports = moduleExports m- newExports = M.fromList $ map (exportExtName &&& id) exports- duplicateNames = (S.intersection `on` M.keysSet) existingExports newExports- if S.null duplicateNames- then modify $ \m -> m { moduleExports = existingExports `mappend` newExports }- else throwError $ concat- ["moduleAddExports: ", show m, " defines external names multiple times: ",- show duplicateNames]---- | Changes a module's 'moduleHaskellName' from the default. This can only be--- called once on a module.-moduleAddHaskellName :: (MonadError String m, MonadState Module m) => [String] -> m ()-moduleAddHaskellName name = do- m <- get- case moduleHaskellName m of- Nothing -> modify $ \m -> m { moduleHaskellName = Just name }- Just name' ->- throwError $ concat- ["moduleAddHaskellName: ", show m, " already has Haskell name ",- show name', "; trying to add name ", show name, "."]---- | A set of requirements of needed to use an identifier in C++ (function,--- type, etc.), via a set of 'Include's. The monoid instance has 'mempty' as an--- empty set of includes, and 'mappend' unions two include sets.-data Reqs = Reqs- { reqsIncludes :: S.Set Include- -- ^ The includes specified by a 'Reqs'.- } deriving (Show)--instance Monoid Reqs where- mempty = Reqs mempty-- mappend (Reqs incl) (Reqs incl') = Reqs $ mappend incl incl'-- mconcat reqs = Reqs $ mconcat $ map reqsIncludes reqs---- | Creates a 'Reqs' that contains the given include.-reqInclude :: Include -> Reqs-reqInclude include = mempty { reqsIncludes = S.singleton include }---- | C++ types that have requirements in order to use them in generated--- bindings.-class HasReqs a where- {-# MINIMAL getReqs, (setReqs | modifyReqs) #-}-- -- | Returns an object's requirements.- getReqs :: a -> Reqs-- -- | Replaces an object's requirements with new ones.- setReqs :: Reqs -> a -> a- setReqs = modifyReqs . const-- -- | Modifies an object's requirements.- modifyReqs :: (Reqs -> Reqs) -> a -> a- modifyReqs f x = setReqs (f $ getReqs x) x---- | Adds to a object's requirements.-addReqs :: HasReqs a => Reqs -> a -> a-addReqs reqs = modifyReqs $ mappend reqs---- | Adds a list of includes to the requirements of an object.-addReqIncludes :: HasReqs a => [Include] -> a -> a-addReqIncludes includes =- modifyReqs $ mappend mempty { reqsIncludes = S.fromList includes }---- | An external name is a string that generated bindings use to uniquely--- identify an object at runtime. An external name must start with an--- alphabetic character, and may only contain alphanumeric characters and @'_'@.--- You are free to use whatever naming style you like; case conversions will be--- performed automatically when required. Hoppy does make use of some--- conventions though, for example with 'Operator's and in the provided bindings--- for the C++ standard library.------ External names must be unique within an interface. They may not be reused--- between modules. This assumption is used for symbol naming in compiled--- shared objects and to freely import modules in Haskell bindings.-newtype ExtName = ExtName- { fromExtName :: String- -- ^ Returns the string an an 'ExtName' contains.- } deriving (Eq, Ord)--instance Show ExtName where- show extName = concat ["$\"", fromExtName extName, "\"$"]---- | Creates an 'ExtName' that contains the given string, erroring if the string--- is an invalid 'ExtName'.-toExtName :: String -> ExtName-toExtName str = case str of- [] -> error "An ExtName cannot be empty."- c:cs -> if isAlpha c && all ((||) <$> isAlphaNum <*> (== '_')) cs- then ExtName str- else error $- "An ExtName must start with a letter and only contain letters, numbers, and '_': " ++- show str---- | Generates an 'ExtName' from an 'Identifier', if the given name is absent.-extNameOrIdentifier :: Identifier -> Maybe ExtName -> ExtName-extNameOrIdentifier ident = fromMaybe $ case identifierParts ident of- [] -> error "extNameOrIdentifier: Invalid empty identifier."- parts -> toExtName $ idPartBase $ last parts---- | Like 'extNameOrIdentifier', but works with strings rather than 'ExtName's.-stringOrIdentifier :: Identifier -> Maybe String -> String-stringOrIdentifier ident = fromMaybe $ case identifierParts ident of- [] -> error "stringOrIdentifier: Invalid empty identifier."- parts -> idPartBase $ last parts---- | Generates an 'ExtName' from an @'FnName' 'Identifier'@, if the given name--- is absent.-extNameOrFnIdentifier :: FnName Identifier -> Maybe ExtName -> ExtName-extNameOrFnIdentifier name =- fromMaybe $ case name of- FnName identifier -> case identifierParts identifier of- [] -> error "extNameOrFnIdentifier: Empty idenfitier."- parts -> toExtName $ idPartBase $ last parts- FnOp op -> operatorPreferredExtName op---- | The C++ name of a function or method.-data FnName name =- FnName name- -- ^ A regular, \"alphanumeric\" name. The exact type depends on what kind of- -- object is being named.- | FnOp Operator- -- ^ An operator name.- deriving (Eq, Ord)--instance Show name => Show (FnName name) where- show (FnName name) = concat ["<FnName ", show name, ">"]- show (FnOp op) = concat ["<FnOp ", show op, ">"]---- | Enables implementing automatic conversions to a @'FnName' t@.-class IsFnName t a where- toFnName :: a -> FnName t--instance IsFnName t (FnName t) where- toFnName = id--instance IsFnName t t where- toFnName = FnName--instance IsFnName t Operator where- toFnName = FnOp---- | Overloadable C++ operators.-data Operator =- OpCall -- ^ @x(...)@- | OpComma -- ^ @x, y@- | OpAssign -- ^ @x = y@- | OpArray -- ^ @x[y]@- | OpDeref -- ^ @*x@- | OpAddress -- ^ @&x@- | OpAdd -- ^ @x + y@- | OpAddAssign -- ^ @x += y@- | OpSubtract -- ^ @x - y@- | OpSubtractAssign -- ^ @x -= y@- | OpMultiply -- ^ @x * y@- | OpMultiplyAssign -- ^ @x *= y@- | OpDivide -- ^ @x / y@- | OpDivideAssign -- ^ @x /= y@- | OpModulo -- ^ @x % y@- | OpModuloAssign -- ^ @x %= y@- | OpPlus -- ^ @+x@- | OpMinus -- ^ @-x@- | OpIncPre -- ^ @++x@- | OpIncPost -- ^ @x++@- | OpDecPre -- ^ @--x@- | OpDecPost -- ^ @x--@- | OpEq -- ^ @x == y@- | OpNe -- ^ @x != y@- | OpLt -- ^ @x < y@- | OpLe -- ^ @x <= y@- | OpGt -- ^ @x > y@- | OpGe -- ^ @x >= y@- | OpNot -- ^ @!x@- | OpAnd -- ^ @x && y@- | OpOr -- ^ @x || y@- | OpBitNot -- ^ @~x@- | OpBitAnd -- ^ @x & y@- | OpBitAndAssign -- ^ @x &= y@- | OpBitOr -- ^ @x | y@- | OpBitOrAssign -- ^ @x |= y@- | OpBitXor -- ^ @x ^ y@- | OpBitXorAssign -- ^ @x ^= y@- | OpShl -- ^ @x << y@- | OpShlAssign -- ^ @x <<= y@- | OpShr -- ^ @x >> y@- | OpShrAssign -- ^ @x >>= y@- deriving (Bounded, Enum, Eq, Ord, Show)---- | The arity and syntax of an operator.-data OperatorType =- UnaryPrefixOperator String -- ^ Prefix unary operators. Examples: @!x@, @*x@, @++x@.- | UnaryPostfixOperator String -- ^ Postfix unary operators. Examples: @x--, x++@.- | BinaryOperator String -- ^ Infix binary operators. Examples: @x * y@, @x >>= y@.- | CallOperator -- ^ @x(...)@ with arbitrary arity.- | ArrayOperator -- ^ @x[y]@, a binary operator with non-infix syntax.--data OperatorInfo = OperatorInfo- { operatorPreferredExtName'' :: ExtName- , operatorType' :: OperatorType- }--makeOperatorInfo :: String -> OperatorType -> OperatorInfo-makeOperatorInfo = OperatorInfo . toExtName---- | Returns a conventional string to use for the 'ExtName' of an operator.-operatorPreferredExtName :: Operator -> ExtName-operatorPreferredExtName op = case M.lookup op operatorInfo of- Just info -> operatorPreferredExtName'' info- Nothing ->- error $ concat- ["operatorPreferredExtName: Internal error, missing info for operator ", show op, "."]---- | Returns a conventional name for an operator, as with--- 'operatorPreferredExtName', but as a string.-operatorPreferredExtName' :: Operator -> String-operatorPreferredExtName' = fromExtName . operatorPreferredExtName---- | Returns the type of an operator.-operatorType :: Operator -> OperatorType-operatorType op = case M.lookup op operatorInfo of- Just info -> operatorType' info- Nothing ->- error $ concat- ["operatorType: Internal error, missing info for operator ", show op, "."]---- | Metadata for operators.------ TODO Test out this missing data.-operatorInfo :: M.Map Operator OperatorInfo-operatorInfo =- let input =- [ (OpCall, makeOperatorInfo "CALL" CallOperator)- , (OpComma, makeOperatorInfo "COMMA" $ BinaryOperator ",")- , (OpAssign, makeOperatorInfo "ASSIGN" $ BinaryOperator "=")- , (OpArray, makeOperatorInfo "ARRAY" ArrayOperator)- , (OpDeref, makeOperatorInfo "DEREF" $ UnaryPrefixOperator "*")- , (OpAddress, makeOperatorInfo "ADDRESS" $ UnaryPrefixOperator "&")- , (OpAdd, makeOperatorInfo "ADD" $ BinaryOperator "+")- , (OpAddAssign, makeOperatorInfo "ADDA" $ BinaryOperator "+=")- , (OpSubtract, makeOperatorInfo "SUB" $ BinaryOperator "-")- , (OpSubtractAssign, makeOperatorInfo "SUBA" $ BinaryOperator "-=")- , (OpMultiply, makeOperatorInfo "MUL" $ BinaryOperator "*")- , (OpMultiplyAssign, makeOperatorInfo "MULA" $ BinaryOperator "*=")- , (OpDivide, makeOperatorInfo "DIV" $ BinaryOperator "/")- , (OpDivideAssign, makeOperatorInfo "DIVA" $ BinaryOperator "/=")- , (OpModulo, makeOperatorInfo "MOD" $ BinaryOperator "%")- , (OpModuloAssign, makeOperatorInfo "MODA" $ BinaryOperator "%=")- , (OpPlus, makeOperatorInfo "PLUS" $ UnaryPrefixOperator "+")- , (OpMinus, makeOperatorInfo "NEG" $ UnaryPrefixOperator "-")- , (OpIncPre, makeOperatorInfo "INC" $ UnaryPrefixOperator "++")- , (OpIncPost, makeOperatorInfo "INCPOST" $ UnaryPostfixOperator "++")- , (OpDecPre, makeOperatorInfo "DEC" $ UnaryPrefixOperator "--")- , (OpDecPost, makeOperatorInfo "DECPOST" $ UnaryPostfixOperator "--")- , (OpEq, makeOperatorInfo "EQ" $ BinaryOperator "==")- , (OpNe, makeOperatorInfo "NE" $ BinaryOperator "!=")- , (OpLt, makeOperatorInfo "LT" $ BinaryOperator "<")- , (OpLe, makeOperatorInfo "LE" $ BinaryOperator "<=")- , (OpGt, makeOperatorInfo "GT" $ BinaryOperator ">")- , (OpGe, makeOperatorInfo "GE" $ BinaryOperator ">=")- , (OpNot, makeOperatorInfo "NOT" $ UnaryPrefixOperator "!")- , (OpAnd, makeOperatorInfo "AND" $ BinaryOperator "&&")- , (OpOr, makeOperatorInfo "OR" $ BinaryOperator "||")- , (OpBitNot, makeOperatorInfo "BNOT" $ UnaryPrefixOperator "~")- , (OpBitAnd, makeOperatorInfo "BAND" $ BinaryOperator "&")- , (OpBitAndAssign, makeOperatorInfo "BANDA" $ BinaryOperator "&=")- , (OpBitOr, makeOperatorInfo "BOR" $ BinaryOperator "|")- , (OpBitOrAssign, makeOperatorInfo "BORA" $ BinaryOperator "|=")- , (OpBitXor, makeOperatorInfo "BXOR" $ BinaryOperator "^")- , (OpBitXorAssign, makeOperatorInfo "BXORA" $ BinaryOperator "^=")- , (OpShl, makeOperatorInfo "SHL" $ BinaryOperator "<<")- , (OpShlAssign, makeOperatorInfo "SHLA" $ BinaryOperator "<<=")- , (OpShr, makeOperatorInfo "SHR" $ BinaryOperator ">>")- , (OpShrAssign, makeOperatorInfo "SHR" $ BinaryOperator ">>=")- ]- in if map fst input == [minBound..]- then M.fromList input- else error "operatorInfo: Operator info list is out of sync with Operator data type."---- | Specifies some C++ object (function or class) to give access to.-data Export =- ExportVariable Variable -- ^ Exports a variable.- | ExportEnum CppEnum -- ^ Exports an enum.- | ExportBitspace Bitspace -- ^ Exports a bitspace.- | ExportFn Function -- ^ Exports a function.- | ExportClass Class -- ^ Exports a class with all of its contents.- | ExportCallback Callback -- ^ Exports a callback.- deriving (Show)---- | Returns the external name of an export.-exportExtName :: Export -> ExtName-exportExtName export = case export of- ExportVariable v -> varExtName v- ExportEnum e -> enumExtName e- ExportBitspace b -> bitspaceExtName b- ExportFn f -> fnExtName f- ExportClass c -> classExtName c- ExportCallback cb -> callbackExtName cb---- | Returns the export's addendum. 'Export' doesn't have a 'HasAddendum'--- instance because you normally wouldn't want to modify the addendum of one.-exportAddendum export = case export of- ExportVariable v -> getAddendum v- ExportEnum e -> getAddendum e- ExportBitspace bs -> getAddendum bs- ExportFn f -> getAddendum f- ExportClass cls -> getAddendum cls- ExportCallback cb -> getAddendum cb---- | A path to some C++ object, including namespaces. An identifier consists of--- multiple parts separated by @\"::\"@. Each part has a name string followed--- by an optional template argument list, where each argument gets rendered from--- a 'Type' (non-type arguments for template metaprogramming are not supported).-newtype Identifier = Identifier- { identifierParts :: [IdPart]- -- ^ The separate parts of the identifier, between @::@s.- } deriving (Eq)--instance Show Identifier where- show ident =- (\words -> concat $ "<Identifier " : words ++ [">"]) $- intersperse "::" $- map (\part -> case idPartArgs part of- Nothing -> idPartBase part- Just args ->- concat $- idPartBase part : "<" :- intersperse ", " (map show args) ++ [">"]) $- identifierParts ident---- | A single component of an 'Identifier', between @::@s.-data IdPart = IdPart- { idPartBase :: String- -- ^ The name within the enclosing scope.- , idPartArgs :: Maybe [Type]- -- ^ Template arguments, if present.- } deriving (Eq, Show)---- | Creates an identifier of the form @a@.-ident :: String -> Identifier-ident a = Identifier [IdPart a Nothing]---- | Creates an identifier of the form @a1::a2::...::aN@.-ident' :: [String] -> Identifier-ident' = Identifier . map (\x -> IdPart x Nothing)---- | Creates an identifier of the form @a::b@.-ident1 :: String -> String -> Identifier-ident1 a b = ident' [a, b]---- | Creates an identifier of the form @a::b::c@.-ident2 :: String -> String -> String -> Identifier-ident2 a b c = ident' [a, b, c]---- | Creates an identifier of the form @a::b::c::d@.-ident3 :: String -> String -> String -> String -> Identifier-ident3 a b c d = ident' [a, b, c, d]---- | Creates an identifier of the form @a::b::c::d::e@.-ident4 :: String -> String -> String -> String -> String -> Identifier-ident4 a b c d e = ident' [a, b, c, d, e]---- | Creates an identifier of the form @a::b::c::d::e::f@.-ident5 :: String -> String -> String -> String -> String -> String -> Identifier-ident5 a b c d e f = ident' [a, b, c, d, e, f]---- | Creates an identifier of the form @a\<...\>@.-identT :: String -> [Type] -> Identifier-identT a ts = Identifier [IdPart a $ Just ts]---- | Creates an identifier with arbitrary many templated and non-templated--- parts.-identT' :: [(String, Maybe [Type])] -> Identifier-identT' = Identifier . map (uncurry IdPart)---- | Creates an identifier of the form @a::b\<...\>@.-ident1T :: String -> String -> [Type] -> Identifier-ident1T a b ts = Identifier [IdPart a Nothing, IdPart b $ Just ts]---- | Creates an identifier of the form @a::b::c\<...\>@.-ident2T :: String -> String -> String -> [Type] -> Identifier-ident2T a b c ts = Identifier [IdPart a Nothing, IdPart b Nothing, IdPart c $ Just ts]---- | Creates an identifier of the form @a::b::c::d\<...\>@.-ident3T :: String -> String -> String -> String -> [Type] -> Identifier-ident3T a b c d ts =- Identifier [IdPart a Nothing, IdPart b Nothing, IdPart c Nothing,- IdPart d $ Just ts]---- | Creates an identifier of the form @a::b::c::d::e\<...\>@.-ident4T :: String -> String -> String -> String -> String -> [Type] -> Identifier-ident4T a b c d e ts =- Identifier [IdPart a Nothing, IdPart b Nothing, IdPart c Nothing,- IdPart d Nothing, IdPart e $ Just ts]---- | Creates an identifier of the form @a::b::c::d::e::f\<...\>@.-ident5T :: String -> String -> String -> String -> String -> String -> [Type] -> Identifier-ident5T a b c d e f ts =- Identifier [IdPart a Nothing, IdPart b Nothing, IdPart c Nothing,- IdPart d Nothing, IdPart e Nothing, IdPart f $ Just ts]---- | A concrete C++ type. Use the bindings in "Foreign.Hoppy.Generator.Types"--- for values of this type; these data constructors are subject to change--- without notice.-data Type =- Internal_TVoid- | Internal_TBool- | Internal_TChar- | Internal_TUChar- | Internal_TShort- | Internal_TUShort- | Internal_TInt- | Internal_TUInt- | Internal_TLong- | Internal_TULong- | Internal_TLLong- | Internal_TULLong- | Internal_TFloat- | Internal_TDouble- | Internal_TInt8- | Internal_TInt16- | Internal_TInt32- | Internal_TInt64- | Internal_TWord8- | Internal_TWord16- | Internal_TWord32- | Internal_TWord64- | Internal_TPtrdiff- | Internal_TSize- | Internal_TSSize- | Internal_TEnum CppEnum- | Internal_TBitspace Bitspace- | Internal_TPtr Type- | Internal_TRef Type- | Internal_TFn [Type] Type- | Internal_TCallback Callback- | Internal_TObj Class- | Internal_TObjToHeap Class- | Internal_TToGc Type- | Internal_TConst Type- deriving (Eq, Show)---- | Canonicalizes a 'Type' without changing its meaning. Multiple nested--- 'Internal_TConst's are collapsed into a single one.-normalizeType :: Type -> Type-normalizeType t = case t of- Internal_TVoid -> t- Internal_TBool -> t- Internal_TChar -> t- Internal_TUChar -> t- Internal_TShort -> t- Internal_TUShort -> t- Internal_TInt -> t- Internal_TUInt -> t- Internal_TLong -> t- Internal_TULong -> t- Internal_TLLong -> t- Internal_TULLong -> t- Internal_TFloat -> t- Internal_TDouble -> t- Internal_TInt8 -> t- Internal_TInt16 -> t- Internal_TInt32 -> t- Internal_TInt64 -> t- Internal_TWord8 -> t- Internal_TWord16 -> t- Internal_TWord32 -> t- Internal_TWord64 -> t- Internal_TPtrdiff -> t- Internal_TSize -> t- Internal_TSSize -> t- Internal_TEnum _ -> t- Internal_TBitspace _ -> t- Internal_TPtr t' -> Internal_TPtr $ normalizeType t'- Internal_TRef t' -> Internal_TRef $ normalizeType t'- Internal_TFn paramTypes retType ->- Internal_TFn (map normalizeType paramTypes) $ normalizeType retType- Internal_TCallback _ -> t- Internal_TObj _ -> t- Internal_TObjToHeap _ -> t- Internal_TToGc _ -> t- Internal_TConst (Internal_TConst t') -> normalizeType $ Internal_TConst t'- Internal_TConst _ -> t---- | Strips leading 'Internal_TConst's off of a type.-stripConst :: Type -> Type-stripConst t = case t of- Internal_TConst t' -> stripConst t'- _ -> t---- | A C++ variable.-data Variable = Variable- { varIdentifier :: Identifier- -- ^ The identifier used to refer to the variable.- , varExtName :: ExtName- -- ^ The variable's external name.- , varType :: Type- -- ^ The type of the variable. This may be- -- 'Foreign.Hoppy.Generator.Types.constT' to indicate that the variable is- -- read-only.- , varReqs :: Reqs- -- ^ Requirements for bindings to use this variable.- , varAddendum :: Addendum- -- ^ The variable's addendum.- }--instance Eq Variable where- (==) = (==) `on` varIdentifier--instance Show Variable where- show v = concat ["<Variable ", show (varExtName v), " ", show (varType v), ">"]--instance HasReqs Variable where- getReqs = varReqs- setReqs reqs v = v { varReqs = reqs }--instance HasAddendum Variable where- getAddendum = varAddendum- setAddendum addendum v = v { varAddendum = addendum }---- | Creates a binding for a C++ variable.-makeVariable :: Identifier -> Maybe ExtName -> Type -> Variable-makeVariable identifier maybeExtName t =- Variable identifier (extNameOrIdentifier identifier maybeExtName) t mempty mempty---- | Returns whether the variable is constant, i.e. whether its type is--- @'Foreign.Hoppy.Generator.Types.constT' ...@.-varIsConst :: Variable -> Bool-varIsConst v = case varType v of- Internal_TConst _ -> True- _ -> False---- | Returns the external name of the getter function for the variable.-varGetterExtName :: Variable -> ExtName-varGetterExtName = toExtName . (++ "_get") . fromExtName . varExtName---- | Returns the external name of the setter function for the variable.-varSetterExtName :: Variable -> ExtName-varSetterExtName = toExtName . (++ "_set") . fromExtName . varExtName---- | A C++ enum declaration. An enum should actually be enumerable (in the--- sense of Haskell's 'Enum'); if it's not, consider using a 'Bitspace' instead.-data CppEnum = CppEnum- { enumIdentifier :: Identifier- -- ^ The identifier used to refer to the enum.- , enumExtName :: ExtName- -- ^ The enum's external name.- , enumValueNames :: [(Int, [String])]- -- ^ The numeric values and names of the enum values. A single value's name- -- is broken up into words. How the words and ext name get combined to make- -- a name in a particular foreign language depends on the language.- , enumReqs :: Reqs- -- ^ Requirements for a 'Type' to reference this enum.- , enumAddendum :: Addendum- -- ^ The enum's addendum.- }--instance Eq CppEnum where- (==) = (==) `on` enumIdentifier--instance Show CppEnum where- show e = concat ["<Enum ", show (enumExtName e), " ", show (enumIdentifier e), ">"]--instance HasReqs CppEnum where- getReqs = enumReqs- setReqs reqs e = e { enumReqs = reqs }--instance HasAddendum CppEnum where- getAddendum = enumAddendum- setAddendum addendum e = e { enumAddendum = addendum }---- | Creates a binding for a C++ enum.-makeEnum :: Identifier -- ^ 'enumIdentifier'- -> Maybe ExtName- -- ^ An optional external name; will be automatically derived from- -- the identifier if absent.- -> [(Int, [String])] -- ^ 'enumValueNames'- -> CppEnum-makeEnum identifier maybeExtName valueNames =- CppEnum identifier (extNameOrIdentifier identifier maybeExtName) valueNames mempty mempty---- | A C++ numeric space with bitwise operations. This is similar to a--- 'CppEnum', but in addition to the extra operations, this differs in that--- these values aren't enumerable.------ Additionally, as a kludge for Qtah, a bitspace may have a C++ type--- ('bitspaceCppTypeIdentifier') separate from its numeric type--- ('bitspaceType'). Qt bitspaces aren't raw numbers but are instead type-safe--- @QFlags@ objects that don't implicitly convert from integers, so we need a--- means to do so manually. Barring general ad-hoc argument and return value--- conversion support, we allow this as follows: when given a C++ type, then a--- bitspace may also have a conversion function between the numeric and C++--- type, in each direction. If a conversion function is present, it will be--- used for conversions in its respective direction. The C++ type is not a full--- 'Type', but only an 'Identifier', since additional information is not needed.--- See 'bitspaceAddCppType'.-data Bitspace = Bitspace- { bitspaceExtName :: ExtName- -- ^ The bitspace's external name.- , bitspaceType :: Type- -- ^ The C++ type used for bits values. This should be a primitive numeric- -- type, usually 'Foreign.Hoppy.Generator.Types.intT'.- , bitspaceValueNames :: [(Int, [String])]- -- ^ The numeric values and names of the bitspace values. See- -- 'enumValueNames'.- , bitspaceEnum :: Maybe CppEnum- -- ^ An associated enum, whose values may be converted to values in the- -- bitspace.- , bitspaceCppTypeIdentifier :: Maybe Identifier- -- ^ The optional C++ type for a bitspace.- , bitspaceToCppValueFn :: Maybe String- -- ^ The name of a C++ function to convert from 'bitspaceType' to the- -- bitspace's C++ type.- , bitspaceFromCppValueFn :: Maybe String- -- ^ The name of a C++ function to convert from the bitspace's C++ type to- -- 'bitspaceType'.- , bitspaceReqs :: Reqs- -- ^ Requirements for emitting the bindings for a bitspace, i.e. what's- -- necessary to reference 'bitspaceCppTypeIdentifier',- -- 'bitspaceFromCppValueFn', and 'bitspaceToCppValueFn'. 'bitspaceType' can- -- take some numeric types that require includes as well, but you don't need- -- to list these here.- , bitspaceAddendum :: Addendum- -- ^ The bitspace's addendum.- }--instance Eq Bitspace where- (==) = (==) `on` bitspaceExtName--instance Show Bitspace where- show e = concat ["<Bitspace ", show (bitspaceExtName e), " ", show (bitspaceType e), ">"]--instance HasReqs Bitspace where- getReqs = bitspaceReqs- setReqs reqs b = b { bitspaceReqs = reqs }--instance HasAddendum Bitspace where- getAddendum = bitspaceAddendum- setAddendum addendum bs = bs { bitspaceAddendum = addendum }---- | Creates a binding for a C++ bitspace.-makeBitspace :: ExtName -- ^ 'bitspaceExtName'- -> Type -- ^ 'bitspaceType'- -> [(Int, [String])] -- ^ 'bitspaceValueNames'- -> Bitspace-makeBitspace extName t valueNames =- Bitspace extName t valueNames Nothing Nothing Nothing Nothing mempty mempty---- | Associates an enum with the bitspace. See 'bitspaceEnum'.-bitspaceAddEnum :: CppEnum -> Bitspace -> Bitspace-bitspaceAddEnum enum bitspace = case bitspaceEnum bitspace of- Just enum' ->- error $ concat- ["bitspaceAddEnum: Adding ", show enum, " to ", show bitspace,- ", but it already has ", show enum', "."]- Nothing ->- if bitspaceValueNames bitspace /= enumValueNames enum- then error $ concat- ["bitspaceAddEnum: Trying to add ", show enum, " to ", show bitspace,- ", but the values aren't equal.\nBitspace values: ", show $ bitspaceValueNames bitspace,- "\n Enum values: ", show $ enumValueNames enum]- else bitspace { bitspaceEnum = Just enum }---- | @bitspaceAddCppType cppTypeIdentifier toCppValueFn fromCppValueFn@--- associates a C++ type (plus optional conversion functions) with a bitspace.--- At least one conversion should be specified, otherwise adding the C++ type--- will mean nothing. You should also add use requirements to the bitspace for--- all of these arguments; see 'HasReqs'.-bitspaceAddCppType :: Identifier -> Maybe String -> Maybe String -> Bitspace -> Bitspace-bitspaceAddCppType cppTypeId toCppValueFnMaybe fromCppValueFnMaybe b =- case bitspaceCppTypeIdentifier b of- Just cppTypeId' ->- error $ concat- ["bitspaceAddCppType: Adding C++ type ", show cppTypeId,- " to ", show b, ", but it already has ", show cppTypeId', "."]- Nothing ->- b { bitspaceCppTypeIdentifier = Just cppTypeId- , bitspaceToCppValueFn = toCppValueFnMaybe- , bitspaceFromCppValueFn = fromCppValueFnMaybe- }---- | Whether or not a function may cause side-effects.------ Haskell bindings for pure functions will not be in 'IO', and calls to pure--- functions will be executed non-strictly. Calls to impure functions will--- execute in the IO monad.------ Member functions for mutable classes should not be made pure, because it is--- difficult in general to control when the call will be made.-data Purity = Nonpure -- ^ Side-affects are possible.- | Pure -- ^ Side-affects will not happen.- deriving (Eq, Show)---- | A C++ function declaration.-data Function = Function- { fnCName :: FnName Identifier- -- ^ The identifier used to call the function.- , fnExtName :: ExtName- -- ^ The function's external name.- , fnPurity :: Purity- -- ^ Whether the function is pure.- , fnParams :: [Type]- -- ^ The function's parameter types.- , fnReturn :: Type- -- ^ The function's return type.- , fnReqs :: Reqs- -- ^ Requirements for a binding to call the function.- , fnAddendum :: Addendum- -- ^ The function's addendum.- }--instance Show Function where- show fn =- concat ["<Function ", show (fnExtName fn), " ", show (fnCName fn),- show (fnParams fn), " ", show (fnReturn fn), ">"]--instance HasReqs Function where- getReqs = fnReqs- setReqs reqs fn = fn { fnReqs = reqs }--instance HasAddendum Function where- getAddendum = fnAddendum- setAddendum addendum fn = fn { fnAddendum = addendum }---- | Creates a binding for a C++ function.-makeFn :: IsFnName Identifier name- => name- -> Maybe ExtName- -- ^ An optional external name; will be automatically derived from- -- the identifier if absent.- -> Purity- -> [Type] -- ^ Parameter types.- -> Type -- ^ Return type.- -> Function-makeFn cName maybeExtName purity paramTypes retType =- let fnName = toFnName cName- in Function fnName- (extNameOrFnIdentifier fnName maybeExtName)- purity paramTypes retType mempty mempty---- | A C++ class declaration. A class's external name is automatically combined--- with the external names of things inside the class, by way of--- 'HasClassyExtName'.-data Class = Class- { classIdentifier :: Identifier- -- ^ The identifier used to refer to the class.- , classExtName :: ExtName- -- ^ The class's external name.- , classSuperclasses :: [Class]- -- ^ The class's public superclasses.- , classCtors :: [Ctor]- -- ^ The class's constructors.- , classDtorIsPublic :: Bool- -- ^ Whether the class's destructor has public visibility.- , classMethods :: [Method]- -- ^ The class's methods.- , classConversion :: ClassConversion- -- ^ Behaviour for converting objects to and from foriegn values.- , classReqs :: Reqs- -- ^ Requirements for a 'Type' to reference this class.- , classAddendum :: Addendum- -- ^ The class's addendum.- , classIsMonomorphicSuperclass :: Bool- -- ^ This is true for classes passed through- -- 'classSetMonomorphicSuperclass'.- , classIsSubclassOfMonomorphic :: Bool- -- ^ This is true for classes passed through- -- 'classSetSubclassOfMonomorphic'.- }--instance Eq Class where- (==) = (==) `on` classIdentifier--instance Show Class where- show cls =- concat ["<Class ", show (classExtName cls), " ", show (classIdentifier cls), ">"]--instance HasReqs Class where- getReqs = classReqs- setReqs reqs cls = cls { classReqs = reqs }--instance HasAddendum Class where- getAddendum = classAddendum- setAddendum addendum cls = cls { classAddendum = addendum }---- | Creates a binding for a C++ class and its contents.-makeClass :: Identifier- -> Maybe ExtName- -- ^ An optional external name; will be automatically derived from the- -- identifier if absent.- -> [Class] -- ^ Superclasses.- -> [Ctor]- -> [Method]- -> Class-makeClass identifier maybeExtName supers ctors methods = Class- { classIdentifier = identifier- , classExtName = extNameOrIdentifier identifier maybeExtName- , classSuperclasses = supers- , classCtors = ctors- , classDtorIsPublic = True- , classMethods = methods- , classConversion = classConversionNone- , classReqs = mempty- , classAddendum = mempty- , classIsMonomorphicSuperclass = False- , classIsSubclassOfMonomorphic = False- }---- | Adds constructors to a class.-classAddCtors :: [Ctor] -> Class -> Class-classAddCtors ctors cls =- if null ctors then cls else cls { classCtors = classCtors cls ++ ctors }---- | Marks a class's destructor as private, so that a binding for it won't be--- generated.-classSetDtorPrivate :: Class -> Class-classSetDtorPrivate cls = cls { classDtorIsPublic = False }---- | Explicitly marks a class as being monomorphic (i.e. not having any--- virtual methods or destructors). By default, Hoppy assumes that a class that--- is derived is also polymorphic, but it can happen that this is not the case.--- Downcasting with @dynamic_cast@ from such classes is not available. See also--- 'classSetSubclassOfMonomorphic'.-classSetMonomorphicSuperclass :: Class -> Class-classSetMonomorphicSuperclass cls = cls { classIsMonomorphicSuperclass = True }---- | Marks a class as being derived from some monomorphic superclass. This--- prevents any downcasting to this class. Generally it is better to use--- 'classSetMonomorphicSuperclass' on the specific superclasses that are--- monomorphic, but in cases where this is not possible, this function can be--- applied to the subclass instead.-classSetSubclassOfMonomorphic :: Class -> Class-classSetSubclassOfMonomorphic cls = cls { classIsSubclassOfMonomorphic = True }---- | Adds methods to a class.-classAddMethods :: [Method] -> Class -> Class-classAddMethods methods cls =- if null methods then cls else cls { classMethods = classMethods cls ++ methods }---- | When a class object is returned from a function or taken as a parameter by--- value (i.e. with 'Foreign.Hoppy.Generator.Types.objT'), it will be converted--- to or from a foreign (non-C++) object. Conversion may also be performed--- explicitly. This data type describes how to perform those conversions. A--- class may or may not support conversion, for any particular foreign language;--- what is said below only applies to classes that are convertible for a--- language.------ When converting between a C++ value and a foreign value, a pointer to the--- object is passed between C++ and the foreign language. Then, for each--- foreign language, a binding author can provide pieces of code in that--- language to translate between the pointer and a foreign value (usually by--- invoking the FFI functions generated by Hoppy), and generated bindings will--- perform these conversions automatically. The code supplied to convert in--- each direction should leave the original object unchanged (and alive, in case--- of manual memory management). (Internally, during a function call in either--- direction, the side that creates a value is in charge of its lifetime, but--- this is managed by Hoppy.)------ In foreign code, foreign values can be explicitly converted to new C++ (heap)--- objects, and C++ object pointers can be explicitly converted to foreign--- values, via special functions generated for the class.-data ClassConversion = ClassConversion- { classHaskellConversion :: ClassConversionMode ClassHaskellConversion- -- ^ Conversions to and from Haskell.-- -- NOTE! When adding new languages here, add the language to- -- 'classSetConversionToHeap', and 'classSetConversionToGc' as well if the- -- language supports garbage collection.- }---- | Specifies whether (and if so, how) objects of a class get converted to and--- from values in a specific foreign language.-data ClassConversionMode a =- ClassConversionNone- -- ^ Indicates that a class __is not__ convertible for a language. Passing- -- raw 'Foreign.Hoppy.Generator.Types.objT' values into and out of C++ is- -- not allowed.- | ClassConversionManual a- -- ^ Indicates that a class __is__ convertible for a language. Passing raw- -- 'Foreign.Hoppy.Generator.Types.objT' values into and out of C++ is- -- allowed, and the attached structure describes how to perform the- -- conversions.- | ClassConversionToHeap- -- ^ Indicates that a class __is not__ convertible for a language.- -- Nevertheless, passing an object from C++ to the foreign language via a- -- type of @'Foreign.Hoppy.Generator.Types.objT' cls@ is allowed, and- -- behaves as though the type were- -- @'Foreign.Hoppy.Generator.Types.objToHeapT' cls@ instead.- | ClassConversionToGc- -- ^ Indicates that a class __is not__ convertible for a language.- -- Nevertheless, passing an object from C++ to the foreign language via a- -- type of @'Foreign.Hoppy.Generator.Types.objT' cls@ is allowed, and- -- behaves as though the type were @'Foreign.Hoppy.Generator.Types.toGcT'- -- ('Foreign.Hoppy.Generator.Types.objT' cls)@ instead.- --- -- This should be used for value objects so that you can simply use- -- @'Foreign.Hoppy.Generator.Types.objT' cls@ in return types, and also- -- write on @'mkProp' "..." ('Foreign.Hoppy.Generator.Types.objT' cls)@.---- | Encoding parameters for a class that is not encodable or decodable.-classConversionNone :: ClassConversion-classConversionNone = ClassConversion ClassConversionNone---- | Modifies a class's 'ClassConversion' structure with a given function.-classModifyConversion :: (ClassConversion -> ClassConversion) -> Class -> Class-classModifyConversion f cls = cls { classConversion = f $ classConversion cls }---- | Replaces a class's 'ClassConversion' structure.-classSetConversion :: ClassConversion -> Class -> Class-classSetConversion c cls = cls { classConversion = c }---- | Modifies a class's 'ClassConversion' structure by setting all languages--- to use 'ClassConversionToHeap'.-classSetConversionToHeap :: Class -> Class-classSetConversionToHeap cls = flip classModifyConversion cls $ \c ->- c { classHaskellConversion = ClassConversionToHeap- }---- | Modifies a class's 'ClassConversion' structure by setting all languages--- that support garbage collection to use 'ClassConversionToGc'.-classSetConversionToGc :: Class -> Class-classSetConversionToGc cls = flip classModifyConversion cls $ \c ->- c { classHaskellConversion = ClassConversionToGc- }---- | Replaces a class's 'classHaskellConversion' with a given value.-classSetHaskellConversion :: ClassHaskellConversion -> Class -> Class-classSetHaskellConversion conv = classModifyConversion $ \c ->- c { classHaskellConversion = ClassConversionManual conv }---- | Controls how conversions between C++ objects and Haskell values happen in--- Haskell bindings.-data ClassHaskellConversion = ClassHaskellConversion- { classHaskellConversionType :: Haskell.Generator HsType- -- ^ Produces the Haskell type that represents a value of the corresponding- -- C++ class. This generator may add imports, but must not output code or- -- add exports.- , classHaskellConversionToCppFn :: Haskell.Generator ()- -- ^ Produces a Haskell expression that evaluates to a function that takes- -- an object of the type that 'classHaskellConversionType' generates, and- -- returns a pointer to a new non-const C++ class object in IO. The- -- generator must output code and may add imports, but must not add exports.- , classHaskellConversionFromCppFn :: Haskell.Generator ()- -- ^ Produces a Haskell expression that evaluates to a function that takes a- -- pointer to a const C++ class object, and returns an object of the type- -- that 'classHaskellConversionType' generates, in IO. The generator must- -- output code and may add imports, but must not add exports.- }---- | Things that live inside of a class, and have the class's external name--- prepended to their own in generated code. With an external name of @\"bar\"@--- and a class with external name @\"foo\"@, the resulting name will be--- @\"foo_bar\"@.-class HasClassyExtName a where- -- | Extracts the external name of the object, without the class name added.- getClassyExtNameSuffix :: a -> ExtName-- -- | Computes the external name to use in generated code, containing both the- -- class's and object's external names.- --- -- See also 'Foreign.Hoppy.Generator.Language.Haskell.General.toHsMethodName'.- getClassyExtName :: Class -> a -> ExtName- getClassyExtName cls x =- toExtName $ concat [fromExtName $ classExtName cls, "_", fromExtName $ getClassyExtNameSuffix x]---- | A C++ class constructor declaration.-data Ctor = Ctor- { ctorExtName :: ExtName- -- ^ The constructor's external name.- , ctorParams :: [Type]- -- ^ The constructor's parameter types.- }--instance Show Ctor where- show ctor = concat ["<Ctor ", show (ctorExtName ctor), " ", show (ctorParams ctor), ">"]--instance HasClassyExtName Ctor where- getClassyExtNameSuffix = ctorExtName---- | Creates a 'Ctor' with full generality.-makeCtor :: ExtName- -> [Type] -- ^ Parameter types.- -> Ctor-makeCtor = Ctor---- | @mkCtor name@ creates a 'Ctor' whose external name is @className_name@.-mkCtor :: String- -> [Type] -- ^ Parameter types.- -> Ctor-mkCtor = makeCtor . toExtName---- | A C++ class method declaration.------ Any operator function that can be written as a method may have its binding be--- written either as part of the associated class or as a separate entity,--- independently of how the function is declared in C++.-data Method = Method- { methodImpl :: MethodImpl- -- ^ The underlying code that the binding calls.- , methodExtName :: ExtName- -- ^ The method's external name.- , methodApplicability :: MethodApplicability- -- ^ How the method is associated to its class.- , methodPurity :: Purity- -- ^ Whether the method is pure.- , methodParams :: [Type]- -- ^ The method's parameter types.- , methodReturn :: Type- -- ^ The method's return type.- }--instance Show Method where- show method =- concat ["<Method ", show (methodExtName method), " ",- case methodImpl method of- RealMethod name -> show name- FnMethod name -> show name, " ",- show (methodApplicability method), " ",- show (methodPurity method), " ",- show (methodParams method), " ",- show (methodReturn method), ">"]--instance HasClassyExtName Method where- getClassyExtNameSuffix = methodExtName---- | The C++ code to which a 'Method' is bound.-data MethodImpl =- RealMethod (FnName String)- -- ^ The 'Method' is bound to an actual class method.- | FnMethod (FnName Identifier)- -- ^ The 'Method' is bound to a wrapper function. When wrapping a method- -- with another function, this is preferrable to just using a 'Function'- -- binding because a method will still appear to be part of the class in- -- foreign bindings.- deriving (Eq, Show)---- | How a method is associated to its class. A method may be static, const, or--- neither (a regular method).-data MethodApplicability = MNormal | MStatic | MConst- deriving (Bounded, Enum, Eq, Show)---- | Whether or not a method is const.-data Constness = Nonconst | Const- deriving (Bounded, Enum, Eq, Show)---- | Returns the opposite constness value.-constNegate :: Constness -> Constness-constNegate Nonconst = Const-constNegate Const = Nonconst---- | Whether or not a method is static.-data Staticness = Nonstatic | Static- deriving (Bounded, Enum, Eq, Show)---- | Returns the constness of a method, based on its 'methodApplicability'.-methodConst :: Method -> Constness-methodConst method = case methodApplicability method of- MConst -> Const- _ -> Nonconst---- | Returns the staticness of a method, based on its 'methodApplicability'.-methodStatic :: Method -> Staticness-methodStatic method = case methodApplicability method of- MStatic -> Static- _ -> Nonstatic---- | Creates a 'Method' with full generality and manual name specification.-makeMethod :: IsFnName String name- => name -- ^ The C++ name of the method.- -> ExtName -- ^ The external name of the method.- -> MethodApplicability- -> Purity- -> [Type] -- ^ Parameter types.- -> Type -- ^ Return type.- -> Method-makeMethod name = Method $ RealMethod $ toFnName name---- | Creates a 'Method' that is in fact backed by a C++ non-member function (a--- la 'makeFn'), but appears to be a regular method. This is useful for--- wrapping a method on the C++ side when its arguments aren't right for binding--- directly.------ A @this@ pointer parameter is __not__ automatically added to the parameter--- list for non-static methods created with @makeFnMethod@.-makeFnMethod :: IsFnName Identifier name- => name- -> String- -> MethodApplicability- -> Purity- -> [Type]- -> Type- -> Method-makeFnMethod cName foreignName = Method (FnMethod $ toFnName cName) (toExtName foreignName)---- | This function is internal.------ Creates a method similar to 'makeMethod', but with automatic naming. The--- method's external name will be @className ++ \"_\" ++ cppMethodName@. If the--- method name is a 'FnOp' then the 'operatorPreferredExtName' will be appeneded--- to the class name.------ For creating multiple bindings to a method, see 'makeMethod''.-makeMethod' :: IsFnName String name- => name -- ^ The C++ name of the method.- -> MethodApplicability- -> Purity- -> [Type] -- ^ Parameter types.- -> Type -- ^ Return type.- -> Method-makeMethod' name = makeMethod''' (toFnName name) Nothing---- | This function is internal.------ Creates a method similar to 'makeMethod'', but with an custom string that--- will be appended to the class name to form the method's external name. This--- is useful for making multiple bindings to a method, e.g. for overloading and--- optional arguments.-makeMethod'' :: IsFnName String name- => name -- ^ The C++ name of the method.- -> String -- ^ A foreign name for the method.- -> MethodApplicability- -> Purity- -> [Type] -- ^ Parameter types.- -> Type -- ^ Return type.- -> Method-makeMethod'' name foreignName = makeMethod''' (toFnName name) $ Just foreignName---- | The implementation of 'makeMethod'' and 'makeMethod'''.-makeMethod''' :: FnName String -- ^ The C++ name of the method.- -> Maybe String -- ^ A foreign name for the method.- -> MethodApplicability- -> Purity- -> [Type] -- ^ Parameter types.- -> Type -- ^ Return type.- -> Method-makeMethod''' (FnName "") maybeForeignName _ _ paramTypes retType =- error $ concat ["makeMethod''': Given an empty method name with foreign name ",- show maybeForeignName, ", parameter types ", show paramTypes,- ", and return type ", show retType, "."]-makeMethod''' name (Just "") _ _ paramTypes retType =- error $ concat ["makeMethod''': Given an empty foreign name with method ",- show name, ", parameter types ", show paramTypes, ", and return type ",- show retType, "."]-makeMethod''' name maybeForeignName appl purity paramTypes retType =- let extName = flip fromMaybe (toExtName <$> maybeForeignName) $ case name of- FnName s -> toExtName s- FnOp op -> operatorPreferredExtName op- in makeMethod name extName appl purity paramTypes retType---- | Creates a nonconst, nonstatic 'Method' for @class::methodName@ and whose--- external name is @class_methodName@. If the name is an operator, then the--- 'operatorPreferredExtName' will be used in the external name.------ For creating multiple bindings to a method, see 'mkMethod''.-mkMethod :: IsFnName String name- => name -- ^ The C++ name of the method.- -> [Type] -- ^ Parameter types.- -> Type -- ^ Return type.- -> Method-mkMethod name = makeMethod' name MNormal Nonpure---- | Creates a nonconst, nonstatic 'Method' for method @class::methodName@ and--- whose external name is @class_methodName@. This enables multiple 'Method's--- with different foreign names (and hence different external names) to bind to--- the same method, e.g. to make use of optional arguments or overloading. See--- 'mkMethod' for a simpler form.-mkMethod' :: IsFnName String name- => name -- ^ The C++ name of the method.- -> String -- ^ A foreign name for the method.- -> [Type] -- ^ Parameter types.- -> Type -- ^ Return type.- -> Method-mkMethod' cName foreignName = makeMethod'' cName foreignName MNormal Nonpure---- | Same as 'mkMethod', but returns an 'MConst' method.-mkConstMethod :: IsFnName String name => name -> [Type] -> Type -> Method-mkConstMethod name = makeMethod' name MConst Nonpure---- | Same as 'mkMethod'', but returns an 'MConst' method.-mkConstMethod' :: IsFnName String name => name -> String -> [Type] -> Type -> Method-mkConstMethod' cName foreignName = makeMethod'' cName foreignName MConst Nonpure---- | Same as 'mkMethod', but returns an 'MStatic' method.-mkStaticMethod :: IsFnName String name => name -> [Type] -> Type -> Method-mkStaticMethod name = makeMethod' name MStatic Nonpure---- | Same as 'mkMethod'', but returns an 'MStatic' method.-mkStaticMethod' :: IsFnName String name => name -> String -> [Type] -> Type -> Method-mkStaticMethod' cName foreignName = makeMethod'' cName foreignName MStatic Nonpure---- | Used in conjunction with 'mkProp' and friends, this creates a list of--- 'Method's for binding to getter/setter method pairs. This can be used as--- follows:------ > myClass =--- > makeClass ... $--- > [ methods... ] ++--- > mkProps--- > [ mkBoolIsProp myClass "adjustable"--- > , mkProp myClass "maxWidth" intT--- > ]-mkProps :: [[Method]] -> [Method]-mkProps = concat---- | Creates a getter/setter binding pair for methods:------ > T getFoo() const--- > void setFoo(T)-mkProp :: String -> Type -> [Method]-mkProp name t =- let c:cs = name- setName = 's' : 'e' : 't' : toUpper c : cs- in [ mkConstMethod name [] t- , mkMethod setName [t] Internal_TVoid- ]---- | Creates a getter/setter binding pair for static methods:------ > static T getFoo() const--- > static void setFoo(T)-mkStaticProp :: String -> Type -> [Method]-mkStaticProp name t =- let c:cs = name- setName = 's' : 'e' : 't' : toUpper c : cs- in [ mkStaticMethod name [] t- , mkStaticMethod setName [t] Internal_TVoid- ]---- | Creates a getter/setter binding pair for boolean methods, where the getter--- is prefixed with @is@:------ > bool isFoo() const--- > void setFoo(bool)-mkBoolIsProp :: String -> [Method]-mkBoolIsProp name =- let c:cs = name- name' = toUpper c : cs- isName = 'i':'s':name'- setName = 's':'e':'t':name'- in [ mkConstMethod isName [] Internal_TBool- , mkMethod setName [Internal_TBool] Internal_TVoid- ]---- | Creates a getter/setter binding pair for boolean methods, where the getter--- is prefixed with @has@:------ > bool hasFoo() const--- > void setFoo(bool)-mkBoolHasProp :: String -> [Method]-mkBoolHasProp name =- let c:cs = name- name' = toUpper c : cs- hasName = 'h':'a':'s':name'- setName = 's':'e':'t':name'- in [ mkConstMethod hasName [] Internal_TBool- , mkMethod setName [Internal_TBool] Internal_TVoid- ]---- | A non-C++ function that can be invoked via a C++ functor.-data Callback = Callback- { callbackExtName :: ExtName- -- ^ The callback's external name.- , callbackParams :: [Type]- -- ^ The callback's parameter types.- , callbackReturn :: Type- -- ^ The callback's return type.- , callbackReqs :: Reqs- -- ^ Requirements for the callback.- , callbackAddendum :: Addendum- -- ^ The callback's addendum.- }--instance Eq Callback where- (==) = (==) `on` callbackExtName--instance Show Callback where- show cb =- concat ["<Callback ", show (callbackExtName cb), " ", show (callbackParams cb), " ",- show (callbackReturn cb)]--instance HasReqs Callback where- getReqs = callbackReqs- setReqs reqs cb = cb { callbackReqs = reqs }--instance HasAddendum Callback where- getAddendum = callbackAddendum- setAddendum addendum cb = cb { callbackAddendum = addendum }---- | Creates a binding for constructing callbacks into foreign code.-makeCallback :: ExtName- -> [Type] -- ^ Parameter types.- -> Type -- ^ Return type.- -> Callback-makeCallback extName paramTypes retType = Callback extName paramTypes retType mempty mempty---- | Creates a 'Foreign.Hoppy.Generator.Types.fnT' from a callback's parameter--- and return types.-callbackToTFn :: Callback -> Type-callbackToTFn = Internal_TFn <$> callbackParams <*> callbackReturn---- | A collection of imports for a Haskell module. This is a monoid: import--- Statements are merged to give the union of imported bindings.------ This structure supports two specific types of imports:--- - @import Foo (...)@--- - @import qualified Foo as Bar@--- Imports with @as@ but without @qualified@, and @qualified@ imports with a--- spec list, are not supported. This satisfies the needs of the code--- generator, and keeps the merging logic simple.-newtype HsImportSet = HsImportSet- { getHsImportSet :: M.Map HsImportKey HsImportSpecs- -- ^ Returns the import set's internal map from module names to imported- -- bindings.- } deriving (Show)--instance Monoid HsImportSet where- mempty = HsImportSet M.empty-- mappend (HsImportSet m) (HsImportSet m') =- HsImportSet $ M.unionWith mergeImportSpecs m m'-- mconcat sets =- HsImportSet $ M.unionsWith mergeImportSpecs $ map getHsImportSet sets---- | A literal piece of code that will be inserted into a generated source file--- after the regular binding glue. The 'Monoid' instance concatenates code--- (actions).-data Addendum = Addendum- { addendumHaskell :: Haskell.Generator ()- -- ^ Code to be output into the Haskell binding. May also add imports and- -- exports.- }--instance Monoid Addendum where- mempty = Addendum $ return ()- mappend (Addendum a) (Addendum b) = Addendum $ a >> b---- | A typeclass for types that have an addendum.-class HasAddendum a where- {-# MINIMAL getAddendum, (setAddendum | modifyAddendum) #-}-- -- | Returns an object's addendum.- getAddendum :: a -> Addendum-- -- | Replaces and object's addendum with another.- setAddendum :: Addendum -> a -> a- setAddendum addendum = modifyAddendum $ const addendum-- -- | Modified an object's addendum.- modifyAddendum :: (Addendum -> Addendum) -> a -> a- modifyAddendum f x = setAddendum (f $ getAddendum x) x---- | Adds a Haskell addendum to an object.-addAddendumHaskell :: HasAddendum a => Haskell.Generator () -> a -> a-addAddendumHaskell gen = modifyAddendum $ \addendum ->- addendum `mappend` mempty { addendumHaskell = gen }---- | Constructor for an import set.-makeHsImportSet :: M.Map HsImportKey HsImportSpecs -> HsImportSet-makeHsImportSet = HsImportSet---- | Sets all of the import specifications in an import set to be--- @{-#SOURCE#-}@ imports.-hsImportSetMakeSource :: HsImportSet -> HsImportSet-hsImportSetMakeSource (HsImportSet m) =- HsImportSet $ M.map (\specs -> specs { hsImportSource = True }) m---- | A Haskell module name.-type HsModuleName = String---- | References an occurrence of an import statement, under which bindings can--- be imported. Only imported specs under equal 'HsImportKey's may be merged.-data HsImportKey = HsImportKey- { hsImportModule :: HsModuleName- , hsImportQualifiedName :: Maybe HsModuleName- } deriving (Eq, Ord, Show)---- | A specification of bindings to import from a module. If 'Nothing', then--- the entire module is imported. If @'Just' 'M.empty'@, then only instances--- are imported.-data HsImportSpecs = HsImportSpecs- { getHsImportSpecs :: Maybe (M.Map HsImportName HsImportVal)- , hsImportSource :: Bool- } deriving (Show)---- | Combines two 'HsImportSpecs's into one that imports everything that the two--- did separately.-mergeImportSpecs :: HsImportSpecs -> HsImportSpecs -> HsImportSpecs-mergeImportSpecs (HsImportSpecs mm s) (HsImportSpecs mm' s') =- HsImportSpecs (liftM2 mergeMaps mm mm') (s || s')- where mergeMaps = M.unionWith mergeValues- mergeValues v v' = case (v, v') of- (HsImportValAll, _) -> HsImportValAll- (_, HsImportValAll) -> HsImportValAll- (HsImportValSome s, HsImportValSome s') -> HsImportValSome $ s ++ s'- (x@(HsImportValSome _), _) -> x- (_, x@(HsImportValSome _)) -> x- (HsImportVal, HsImportVal) -> HsImportVal---- | An identifier that can be imported from a module. Symbols may be used here--- when surrounded by parentheses. Examples are @\"fmap\"@ and @\"(++)\"@.-type HsImportName = String---- | Specifies how a name is imported.-data HsImportVal =- HsImportVal- -- ^ The name is imported, and nothing underneath it is.- | HsImportValSome [HsImportName]- -- ^ The name is imported, as are specific names underneath it. This is a- -- @X (a, b, c)@ import.- | HsImportValAll- -- ^ The name is imported, along with all names underneath it. This is a @X- -- (..)@ import.- deriving (Show)---- | An import for the entire contents of a Haskell module.-hsWholeModuleImport :: HsModuleName -> HsImportSet-hsWholeModuleImport moduleName =- HsImportSet $ M.singleton (HsImportKey moduleName Nothing) $- HsImportSpecs Nothing False---- | A qualified import of a Haskell module.-hsQualifiedImport :: HsModuleName -> HsModuleName -> HsImportSet-hsQualifiedImport moduleName qualifiedName =- HsImportSet $ M.singleton (HsImportKey moduleName $ Just qualifiedName) $- HsImportSpecs Nothing False---- | An import of a single name from a Haskell module.-hsImport1 :: HsModuleName -> HsImportName -> HsImportSet-hsImport1 moduleName valueName = hsImport1' moduleName valueName HsImportVal---- | A detailed import of a single name from a Haskell module.-hsImport1' :: HsModuleName -> HsImportName -> HsImportVal -> HsImportSet-hsImport1' moduleName valueName valueType =- HsImportSet $ M.singleton (HsImportKey moduleName Nothing) $- HsImportSpecs (Just $ M.singleton valueName valueType) False---- | An import of multiple names from a Haskell module.-hsImports :: HsModuleName -> [HsImportName] -> HsImportSet-hsImports moduleName names =- hsImports' moduleName $ map (\name -> (name, HsImportVal)) names---- | A detailed import of multiple names from a Haskell module.-hsImports' :: HsModuleName -> [(HsImportName, HsImportVal)] -> HsImportSet-hsImports' moduleName values =- HsImportSet $ M.singleton (HsImportKey moduleName Nothing) $- HsImportSpecs (Just $ M.fromList values) False---- | Imports "Data.Bits" qualified as @HoppyDB@.-hsImportForBits :: HsImportSet-hsImportForBits = hsQualifiedImport "Data.Bits" "HoppyDB"---- | Imports "Data.Int" qualified as @HoppyDI@.-hsImportForInt :: HsImportSet-hsImportForInt = hsQualifiedImport "Data.Int" "HoppyDI"---- | Imports "Data.Word" qualified as @HoppyDW@.-hsImportForWord :: HsImportSet-hsImportForWord = hsQualifiedImport "Data.Word" "HoppyDW"---- | Imports "Foreign" qualified as @HoppyF@.-hsImportForForeign :: HsImportSet-hsImportForForeign = hsQualifiedImport "Foreign" "HoppyF"---- | Imports "Foreign.C" qualified as @HoppyFC@.-hsImportForForeignC :: HsImportSet-hsImportForForeignC = hsQualifiedImport "Foreign.C" "HoppyFC"---- | Imports "Prelude" qualified as @HoppyP@.-hsImportForPrelude :: HsImportSet-hsImportForPrelude = hsQualifiedImport "Prelude" "HoppyP"---- | Imports "Foreign.Hoppy.Runtime" qualified as @HoppyFHR@.-hsImportForRuntime :: HsImportSet-hsImportForRuntime = hsQualifiedImport "Foreign.Hoppy.Runtime" "HoppyFHR"---- | Imports "System.Posix.Types" qualified as @HoppySPT@.-hsImportForSystemPosixTypes :: HsImportSet-hsImportForSystemPosixTypes = hsQualifiedImport "System.Posix.Types" "HoppySPT"---- | Imports "System.IO.Unsafe" qualified as @HoppySIU@.-hsImportForUnsafeIO :: HsImportSet-hsImportForUnsafeIO = hsQualifiedImport "System.IO.Unsafe" "HoppySIU"---- | Returns an error message indicating that--- 'Foreign.Hoppy.Generator.Types.objToHeapT' is used where data is going from a--- foreign language into C++.-objToHeapTWrongDirectionErrorMsg :: Maybe String -> Class -> String-objToHeapTWrongDirectionErrorMsg maybeCaller cls =- concat [maybe "" (++ ": ") maybeCaller,- "(TObjToHeap ", show cls, ") cannot be passed into C++",- maybe "" (const ".") maybeCaller]---- | Returns an error message indicating that--- 'Foreign.Hoppy.Generator.Types.objToHeapT' is used where data is going from a--- foreign language into C++.-tToGcInvalidFormErrorMessage :: Maybe String -> Type -> String-tToGcInvalidFormErrorMessage maybeCaller typeArg =- concat [maybe "" (++ ": ") maybeCaller,- "(", show (Internal_TToGc typeArg), ") is an invalid form for TToGc.",- maybe "" (const ".") maybeCaller]---- | Returns an error message indicating that--- 'Foreign.Hoppy.Generator.Types.toGcT' is used where data is going from a--- foreign language into C++.-toGcTWrongDirectionErrorMsg :: Maybe String -> Type -> String-toGcTWrongDirectionErrorMsg maybeCaller typeArg =- concat [maybe "" (++ ": ") maybeCaller,- "(", show (Internal_TToGc typeArg), ") cannot be passed into C++",- maybe "" (const ".") maybeCaller]+-- Copyright 2016 Bryan Gardiner <bog@khumba.net>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++-- | The primary data types for specifying C++ interfaces.+--+-- 'Show' instances in this module produce strings of the form @\"\<TypeOfObject+-- nameOfObject otherInfo...\>\"@. They can be used in error messages without+-- specifying a noun separately, i.e. write @show cls@ instead of @\"the class+-- \" ++ show cls@.+module Foreign.Hoppy.Generator.Spec (+ module Foreign.Hoppy.Generator.Spec.Base,+ module Foreign.Hoppy.Generator.Spec.Conversion,+ module Foreign.Hoppy.Generator.Spec.ClassFeature,+ ) where++import Foreign.Hoppy.Generator.Spec.Base+import Foreign.Hoppy.Generator.Spec.ClassFeature+import Foreign.Hoppy.Generator.Spec.Conversion
+ src/Foreign/Hoppy/Generator/Spec/Base.hs view
@@ -0,0 +1,2484 @@+-- This file is part of Hoppy.+--+-- Copyright 2015-2016 Bryan Gardiner <bog@khumba.net>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++{-# LANGUAGE CPP, GeneralizedNewtypeDeriving #-}++module Foreign.Hoppy.Generator.Spec.Base (+ -- * Interfaces+ Interface,+ ErrorMsg,+ InterfaceOptions (..),+ defaultInterfaceOptions,+ interface,+ interface',+ interfaceName,+ interfaceModules,+ interfaceNamesToModules,+ interfaceHaskellModuleBase,+ interfaceDefaultHaskellModuleBase,+ interfaceAddHaskellModuleBase,+ interfaceHaskellModuleImportNames,+ interfaceExceptionHandlers,+ interfaceCallbacksThrow,+ interfaceSetCallbacksThrow,+ interfaceExceptionClassId,+ interfaceExceptionSupportModule,+ interfaceSetExceptionSupportModule,+ -- * C++ includes+ Include,+ includeStd,+ includeLocal,+ includeToString,+ -- * Modules+ Module,+ moduleName,+ moduleHppPath,+ moduleCppPath,+ moduleExports,+ moduleReqs,+ moduleExceptionHandlers,+ moduleCallbacksThrow,+ moduleSetCallbacksThrow,+ moduleAddendum,+ moduleHaskellName,+ makeModule,+ moduleModify,+ moduleModify',+ moduleSetHppPath,+ moduleSetCppPath,+ moduleAddExports,+ moduleAddHaskellName,+ -- * Requirements+ Reqs,+ reqsIncludes,+ reqInclude,+ HasReqs (..),+ addReqs,+ addReqIncludes,+ -- * Names and exports+ ExtName,+ toExtName,+ isValidExtName,+ fromExtName,+ HasExtNames (..),+ getAllExtNames,+ FnName (..),+ IsFnName (..),+ Operator (..),+ OperatorType (..),+ operatorPreferredExtName,+ operatorPreferredExtName',+ operatorType,+ Export (..),+ exportAddendum,+ Identifier,+ identifierParts,+ IdPart,+ idPartBase,+ idPartArgs,+ ident, ident', ident1, ident2, ident3, ident4, ident5,+ identT, identT', ident1T, ident2T, ident3T, ident4T, ident5T,+ -- * Basic types+ Type (..),+ normalizeType,+ stripConst,+ -- ** Variables+ Variable, makeVariable, varIdentifier, varExtName, varType, varReqs,+ varIsConst, varGetterExtName, varSetterExtName,+ -- ** Enums+ CppEnum, makeEnum, enumIdentifier, enumExtName, enumValueNames, enumReqs,+ enumValuePrefix, enumSetValuePrefix,+ -- ** Bitspaces+ Bitspace, makeBitspace, bitspaceExtName, bitspaceType, bitspaceValueNames, bitspaceEnum,+ bitspaceAddEnum, bitspaceCppTypeIdentifier, bitspaceFromCppValueFn, bitspaceToCppValueFn,+ bitspaceAddCppType, bitspaceReqs,+ bitspaceValuePrefix, bitspaceSetValuePrefix,+ -- ** Functions+ Purity (..),+ Function, makeFn, fnCName, fnExtName, fnPurity, fnParams, fnReturn, fnReqs, fnExceptionHandlers,+ -- ** Classes+ Class, makeClass, classIdentifier, classExtName, classSuperclasses,+ classEntities, classAddEntities, classVariables, classCtors, classMethods,+ classDtorIsPublic, classSetDtorPrivate,+ classConversion, classReqs, classEntityPrefix, classSetEntityPrefix,+ classIsMonomorphicSuperclass, classSetMonomorphicSuperclass,+ classIsSubclassOfMonomorphic, classSetSubclassOfMonomorphic,+ classIsException, classMakeException,+ ClassEntity (..),+ IsClassEntity (..), classEntityExtName, classEntityForeignName, classEntityForeignName',+ ClassVariable,+ makeClassVariable, makeClassVariable_,+ mkClassVariable, mkClassVariable_,+ mkStaticClassVariable, mkStaticClassVariable_,+ classVarCName, classVarExtName, classVarType, classVarStatic, classVarGettable,+ classVarGetterExtName, classVarGetterForeignName,+ classVarSetterExtName, classVarSetterForeignName,+ Ctor, makeCtor, makeCtor_, mkCtor, mkCtor_, ctorExtName, ctorParams, ctorExceptionHandlers,+ Method,+ MethodImpl (..),+ MethodApplicability (..),+ Constness (..),+ constNegate,+ Staticness (..),+ makeMethod, makeMethod_,+ makeFnMethod, makeFnMethod_,+ mkMethod, mkMethod_, mkMethod', mkMethod'_,+ mkConstMethod, mkConstMethod_, mkConstMethod', mkConstMethod'_,+ mkStaticMethod, mkStaticMethod_, mkStaticMethod', mkStaticMethod'_,+ Prop, -- The data constructor is private.+ mkProp, mkProp_,+ mkStaticProp, mkStaticProp_,+ mkBoolIsProp, mkBoolIsProp_,+ mkBoolHasProp, mkBoolHasProp_,+ methodImpl, methodExtName, methodApplicability, methodPurity, methodParams,+ methodReturn, methodExceptionHandlers, methodConst, methodStatic,+ -- *** Conversion to and from foreign values+ ClassConversion (..),+ classConversionNone,+ classModifyConversion,+ classSetConversion,+ ClassHaskellConversion (..),+ classHaskellConversionNone,+ classSetHaskellConversion,+ -- ** Callbacks+ Callback, makeCallback,+ callbackExtName, callbackParams, callbackReturn, callbackThrows, callbackReqs,+ callbackSetThrows,+ -- * Exceptions+ ExceptionId (..),+ exceptionCatchAllId,+ ExceptionHandler (..),+ ExceptionHandlers (..),+ HandlesExceptions (getExceptionHandlers),+ handleExceptions,+ -- * Addenda+ Addendum (..),+ HasAddendum (..),+ addAddendumHaskell,+ -- * Haskell imports+ HsModuleName, HsImportSet, HsImportKey (..), HsImportSpecs (..), HsImportName, HsImportVal (..),+ hsWholeModuleImport, hsQualifiedImport, hsImport1, hsImport1', hsImports, hsImports',+ hsImportSetMakeSource,+ -- * Internal to Hoppy+ interfaceAllExceptionClasses,+ classFindCopyCtor,+ -- ** Haskell imports+ makeHsImportSet,+ getHsImportSet,+ hsImportForBits,+ hsImportForException,+ hsImportForInt,+ hsImportForWord,+ hsImportForForeign,+ hsImportForForeignC,+ hsImportForMap,+ hsImportForPrelude,+ hsImportForRuntime,+ hsImportForSystemPosixTypes,+ hsImportForTypeable,+ hsImportForUnsafeIO,+ -- ** Error messages+ objToHeapTWrongDirectionErrorMsg,+ tToGcInvalidFormErrorMessage,+ toGcTWrongDirectionErrorMsg,+ ) where++#if !MIN_VERSION_base(4,8,0)+import Control.Applicative ((<$>), (<*>))+#endif+import Control.Arrow ((&&&))+import Control.Monad (liftM2, unless)+#if MIN_VERSION_mtl(2,2,1)+import Control.Monad.Except (MonadError, throwError)+#else+import Control.Monad.Error (MonadError, throwError)+#endif+import Control.Monad.State (MonadState, StateT, execStateT, get, modify)+import Data.Char (isAlpha, isAlphaNum, toUpper)+import Data.Function (on)+import Data.List (intercalate, intersperse)+import qualified Data.Map as M+import Data.Maybe (catMaybes, fromMaybe, isJust, isNothing, mapMaybe)+#if !MIN_VERSION_base(4,8,0)+import Data.Monoid (Monoid, mappend, mconcat, mempty)+#endif+import qualified Data.Set as S+import Foreign.Hoppy.Generator.Common+import {-# SOURCE #-} qualified Foreign.Hoppy.Generator.Language.Haskell as Haskell+import Language.Haskell.Syntax (HsType)++-- | Indicates strings that are error messages.+type ErrorMsg = String++-- | A complete specification of a C++ API. Generators for different languages,+-- including the binding generator for C++, use these to produce their output.+--+-- 'Interface' does not have a 'HandlesExceptions' instance because+-- 'modifyExceptionHandlers' does not work for it (handled exceptions cannot be+-- modified after an 'Interface' is constructed).+data Interface = Interface+ { interfaceName :: String+ -- ^ The textual name of the interface.+ , interfaceModules :: M.Map String Module+ -- ^ All of the individual modules, by 'moduleName'.+ , interfaceNamesToModules :: M.Map ExtName Module+ -- ^ Maps each 'ExtName' exported by some module to the module that exports+ -- the name.+ , interfaceHaskellModuleBase' :: Maybe [String]+ -- ^ See 'interfaceHaskellModuleBase'.+ , interfaceHaskellModuleImportNames :: M.Map Module String+ -- ^ Short qualified module import names that generated modules use to refer+ -- to each other tersely.+ , interfaceExceptionHandlers :: ExceptionHandlers+ -- ^ Exceptions that all functions in the interface may throw.+ , interfaceCallbacksThrow :: Bool+ -- ^ Whether callbacks within the interface support throwing C++ exceptions+ -- from Haskell into C++ during their execution. This may be overridden by+ -- 'moduleCallbacksThrow' and 'callbackThrows'.+ , interfaceExceptionNamesToIds :: M.Map ExtName ExceptionId+ -- ^ Maps from external names of exception classes to their exception IDs.+ , interfaceExceptionSupportModule :: Maybe Module+ -- ^ When an interface uses C++ exceptions, then one module needs to+ -- manually be selected to contain some interface-specific runtime support.+ -- This is the selected module.+ }++instance Show Interface where+ show iface = concat ["<Interface ", show (interfaceName iface), ">"]++-- | Optional parameters when constructing an 'Interface' with 'interface'.+data InterfaceOptions = InterfaceOptions+ { interfaceOptionsExceptionHandlers :: ExceptionHandlers+ }++-- | Options used by 'interface'. This contains no exception handlers.+defaultInterfaceOptions :: InterfaceOptions+defaultInterfaceOptions = InterfaceOptions mempty++-- | Constructs an 'Interface' from the required parts. Some validation is+-- performed; if the resulting interface would be invalid, an error message is+-- returned instead.+--+-- This function passes 'defaultInterfaceOptions' to 'interface''.+interface :: String -- ^ 'interfaceName'+ -> [Module] -- ^ 'interfaceModules'+ -> Either ErrorMsg Interface+interface ifName modules = interface' ifName modules defaultInterfaceOptions++-- | Same as 'interface', but accepts some optional arguments.+interface' :: String -- ^ 'interfaceName'+ -> [Module] -- ^ 'interfaceModules'+ -> InterfaceOptions+ -> Either ErrorMsg Interface+interface' ifName modules options = do+ -- TODO Check for duplicate module names.+ -- TODO Check for duplicate module file paths.++ -- Check for multiple modules exporting an ExtName.+ let extNamesToModules :: M.Map ExtName [Module]+ extNamesToModules =+ M.unionsWith (++) $+ for modules $ \mod ->+ let extNames = concatMap getAllExtNames $ M.elems $ moduleExports mod+ in M.fromList $ zip extNames $ repeat [mod]++ extNamesInMultipleModules :: [(ExtName, [Module])]+ extNamesInMultipleModules =+ M.toList $+ M.filter (\modules -> case modules of+ _:_:_ -> True+ _ -> False)+ extNamesToModules++ unless (null extNamesInMultipleModules) $+ Left $ unlines $+ "Some external name(s) are exported by multiple modules:" :+ map (\(extName, modules) ->+ concat $ "- " : show extName : ": " : intersperse ", " (map show modules))+ extNamesInMultipleModules++ let haskellModuleImportNames =+ M.fromList $+ (\a b f -> zipWith f a b) modules [1..] $+ \mod index -> (mod, 'M' : show index)++ -- Generate a unique exception ID integer for each exception class. IDs 0 and+ -- 1 are reserved.+ let exceptionNamesToIds =+ M.fromList $+ zip (map classExtName $ interfaceAllExceptionClasses' modules)+ (map ExceptionId [exceptionFirstFreeId..])++ return Interface+ { interfaceName = ifName+ , interfaceModules = M.fromList $ map (moduleName &&& id) modules+ , interfaceNamesToModules = M.map (\[x] -> x) extNamesToModules+ , interfaceHaskellModuleBase' = Nothing+ , interfaceHaskellModuleImportNames = haskellModuleImportNames+ , interfaceExceptionHandlers = interfaceOptionsExceptionHandlers options+ , interfaceCallbacksThrow = False+ , interfaceExceptionNamesToIds = exceptionNamesToIds+ , interfaceExceptionSupportModule = Nothing+ }++-- | The name of the parent Haskell module under which a Haskell module will be+-- generated for a Hoppy 'Module'. This is a list of Haskell module path+-- components, in other words, @'Data.List.intercalate' "."@ on the list+-- produces a Haskell module name. Defaults to+-- 'interfaceDefaultHaskellModuleBase', and may be overridden with+-- 'interfaceAddHaskellModuleBase'.+interfaceHaskellModuleBase :: Interface -> [String]+interfaceHaskellModuleBase =+ fromMaybe interfaceDefaultHaskellModuleBase . interfaceHaskellModuleBase'++-- | The default Haskell module under which Hoppy modules will be generated.+-- This is @Foreign.Hoppy.Generated@, that is:+--+-- > ["Foreign", "Hoppy", "Generated"]+interfaceDefaultHaskellModuleBase :: [String]+interfaceDefaultHaskellModuleBase = ["Foreign", "Hoppy", "Generated"]++-- | Sets an interface to generate all of its modules under the given Haskell+-- module prefix. See 'interfaceHaskellModuleBase'.+interfaceAddHaskellModuleBase :: [String] -> Interface -> Either String Interface+interfaceAddHaskellModuleBase modulePath iface = case interfaceHaskellModuleBase' iface of+ Nothing -> Right iface { interfaceHaskellModuleBase' = Just modulePath }+ Just existingPath ->+ Left $ concat+ [ "addInterfaceHaskellModuleBase: Trying to add Haskell module base "+ , intercalate "." modulePath, " to ", show iface+ , " which already has a module base ", intercalate "." existingPath+ ]++-- | Returns the the exception ID for a class in an interface, if it has one+-- (i.e. if it's been marked as an exception class with 'classMakeException').+interfaceExceptionClassId :: Interface -> Class -> Maybe ExceptionId+interfaceExceptionClassId iface cls =+ M.lookup (classExtName cls) $ interfaceExceptionNamesToIds iface++-- | Returns all of the exception classes in an interface.+interfaceAllExceptionClasses :: Interface -> [Class]+interfaceAllExceptionClasses = interfaceAllExceptionClasses' . M.elems . interfaceModules++interfaceAllExceptionClasses' :: [Module] -> [Class]+interfaceAllExceptionClasses' modules =+ flip concatMap modules $ \mod ->+ catMaybes $+ for (M.elems $ moduleExports mod) $ \export -> case export of+ ExportClass cls | classIsException cls -> Just cls+ _ -> Nothing++-- | Changes 'callbackThrows' for all callbacks in an interface that don't have it+-- set explicitly at the module or callback level.+interfaceSetCallbacksThrow :: Bool -> Interface -> Interface+interfaceSetCallbacksThrow b iface = iface { interfaceCallbacksThrow = b }++-- | Sets an interface's exception support module, for interfaces that use+-- exceptions.+interfaceSetExceptionSupportModule :: Module -> Interface -> Interface+interfaceSetExceptionSupportModule mod iface = case interfaceExceptionSupportModule iface of+ Nothing -> iface { interfaceExceptionSupportModule = Just mod }+ Just existingMod ->+ if mod == existingMod+ then iface+ else error $ "interfaceSetExceptionSupportModule: " ++ show iface +++ " already has exception support module " ++ show existingMod +++ ", trying to set " ++ show mod ++ "."++-- | An @#include@ directive in a C++ file.+data Include = Include+ { includeToString :: String+ -- ^ Returns the complete @#include ...@ line for an include, including+ -- trailing newline.+ } deriving (Eq, Ord, Show)++-- | Creates an @#include \<...\>@ directive.+includeStd :: String -> Include+includeStd path = Include $ "#include <" ++ path ++ ">\n"++-- | Creates an @#include "..."@ directive.+includeLocal :: String -> Include+includeLocal path = Include $ "#include \"" ++ path ++ "\"\n"++-- | A portion of functionality in a C++ API. An 'Interface' is composed of+-- multiple modules. A module will generate a single compilation unit+-- containing bindings for all of the module's exports. The C++ code for a+-- generated module will @#include@ everything necessary for what is written to+-- the header and source files separately. You can declare include dependencies+-- with e.g. 'addReqIncludes', either for individual exports or at the module+-- level. Dependencies between modules are handled automatically, and+-- circularity is supported to a certain extent. See the documentation for the+-- individual language modules for further details.+data Module = Module+ { moduleName :: String+ -- ^ The module's name. A module name must identify a unique module within+ -- an 'Interface'.+ , moduleHppPath :: String+ -- ^ A relative path under a C++ sources root to which the generator will+ -- write a header file for the module's C++ bindings.+ , moduleCppPath :: String+ -- ^ A relative path under a C++ sources root to which the generator will+ -- write a source file for the module's C++ bindings.+ , moduleExports :: M.Map ExtName Export+ -- ^ All of the exports in a module.+ , moduleReqs :: Reqs+ -- ^ Module-level requirements.+ , moduleHaskellName :: Maybe [String]+ -- ^ The generated Haskell module name, underneath the+ -- 'interfaceHaskellModuleBase'. If absent (by default), the 'moduleName'+ -- is used. May be modified with 'moduleAddHaskellName'.+ , moduleExceptionHandlers :: ExceptionHandlers+ -- ^ Exceptions that all functions in the module may throw.+ , moduleCallbacksThrow :: Maybe Bool+ -- ^ Whether callbacks exported from the module support exceptions being+ -- thrown during their execution. When present, this overrides+ -- 'interfaceCallbacksThrow'. This maybe overridden by 'callbackThrows'.+ , moduleAddendum :: Addendum+ -- ^ The module's addendum.+ }++instance Eq Module where+ (==) = (==) `on` moduleName++instance Ord Module where+ compare = compare `on` moduleName++instance Show Module where+ show m = concat ["<Module ", moduleName m, ">"]++instance HasReqs Module where+ getReqs = moduleReqs+ setReqs reqs m = m { moduleReqs = reqs }++instance HasAddendum Module where+ getAddendum = moduleAddendum+ setAddendum addendum m = m { moduleAddendum = addendum }++instance HandlesExceptions Module where+ getExceptionHandlers = moduleExceptionHandlers+ modifyExceptionHandlers f m = m { moduleExceptionHandlers = f $ moduleExceptionHandlers m }++-- | Creates an empty module, ready to be configured with 'moduleModify'.+makeModule :: String -- ^ 'moduleName'+ -> String -- ^ 'moduleHppPath'+ -> String -- ^ 'moduleCppPath'+ -> Module+makeModule name hppPath cppPath = Module+ { moduleName = name+ , moduleHppPath = hppPath+ , moduleCppPath = cppPath+ , moduleExports = M.empty+ , moduleReqs = mempty+ , moduleHaskellName = Nothing+ , moduleExceptionHandlers = mempty+ , moduleCallbacksThrow = Nothing+ , moduleAddendum = mempty+ }++-- | Extends a module. To be used with the module state-monad actions in this+-- package.+moduleModify :: Module -> StateT Module (Either String) () -> Either ErrorMsg Module+moduleModify = flip execStateT++-- | Same as 'moduleModify', but calls 'error' in the case of failure, which is+-- okay in for a generator which would abort in this case anyway.+moduleModify' :: Module -> StateT Module (Either String) () -> Module+moduleModify' m action = case moduleModify m action of+ Left errorMsg ->+ error $ concat+ ["moduleModify' failed to modify ", show m, ": ", errorMsg]+ Right m' -> m'++-- | Replaces a module's 'moduleHppPath'.+moduleSetHppPath :: MonadState Module m => String -> m ()+moduleSetHppPath path = modify $ \m -> m { moduleHppPath = path }++-- | Replaces a module's 'moduleCppPath'.+moduleSetCppPath :: MonadState Module m => String -> m ()+moduleSetCppPath path = modify $ \m -> m { moduleCppPath = path }++-- | Adds exports to a module. An export must only be added to any module at+-- most once, and must not be added to multiple modules.+moduleAddExports :: (MonadError String m, MonadState Module m) => [Export] -> m ()+moduleAddExports exports = do+ m <- get+ let existingExports = moduleExports m+ newExports = M.fromList $ map (getPrimaryExtName &&& id) exports+ duplicateNames = (S.intersection `on` M.keysSet) existingExports newExports+ if S.null duplicateNames+ then modify $ \m -> m { moduleExports = existingExports `mappend` newExports }+ else throwError $ concat+ ["moduleAddExports: ", show m, " defines external names multiple times: ",+ show duplicateNames]++-- | Changes a module's 'moduleHaskellName' from the default. This can only be+-- called once on a module.+moduleAddHaskellName :: (MonadError String m, MonadState Module m) => [String] -> m ()+moduleAddHaskellName name = do+ m <- get+ case moduleHaskellName m of+ Nothing -> modify $ \m -> m { moduleHaskellName = Just name }+ Just name' ->+ throwError $ concat+ ["moduleAddHaskellName: ", show m, " already has Haskell name ",+ show name', "; trying to add name ", show name, "."]++-- | Changes 'callbackThrows' for all callbacks in a module that don't have it+-- set explicitly.+moduleSetCallbacksThrow :: MonadState Module m => Maybe Bool -> m ()+moduleSetCallbacksThrow b = modify $ \m -> m { moduleCallbacksThrow = b }++-- | A set of requirements of needed to use an identifier in C++ (function,+-- type, etc.), via a set of 'Include's. The monoid instance has 'mempty' as an+-- empty set of includes, and 'mappend' unions two include sets.+data Reqs = Reqs+ { reqsIncludes :: S.Set Include+ -- ^ The includes specified by a 'Reqs'.+ } deriving (Show)++instance Monoid Reqs where+ mempty = Reqs mempty++ mappend (Reqs incl) (Reqs incl') = Reqs $ mappend incl incl'++ mconcat reqs = Reqs $ mconcat $ map reqsIncludes reqs++-- | Creates a 'Reqs' that contains the given include.+reqInclude :: Include -> Reqs+reqInclude include = mempty { reqsIncludes = S.singleton include }++-- | C++ types that have requirements in order to use them in generated+-- bindings.+class HasReqs a where+ {-# MINIMAL getReqs, (setReqs | modifyReqs) #-}++ -- | Returns an object's requirements.+ getReqs :: a -> Reqs++ -- | Replaces an object's requirements with new ones.+ setReqs :: Reqs -> a -> a+ setReqs = modifyReqs . const++ -- | Modifies an object's requirements.+ modifyReqs :: (Reqs -> Reqs) -> a -> a+ modifyReqs f x = setReqs (f $ getReqs x) x++-- | Adds to a object's requirements.+addReqs :: HasReqs a => Reqs -> a -> a+addReqs reqs = modifyReqs $ mappend reqs++-- | Adds a list of includes to the requirements of an object.+addReqIncludes :: HasReqs a => [Include] -> a -> a+addReqIncludes includes =+ modifyReqs $ mappend mempty { reqsIncludes = S.fromList includes }++-- | An external name is a string that generated bindings use to uniquely+-- identify an object at runtime. An external name must start with an+-- alphabetic character, and may only contain alphanumeric characters and @'_'@.+-- You are free to use whatever naming style you like; case conversions will be+-- performed automatically when required. Hoppy does make use of some+-- conventions though, for example with 'Operator's and in the provided bindings+-- for the C++ standard library.+--+-- External names must be unique within an interface. They may not be reused+-- between modules. This assumption is used for symbol naming in compiled+-- shared objects and to freely import modules in Haskell bindings.+newtype ExtName = ExtName+ { fromExtName :: String+ -- ^ Returns the string an an 'ExtName' contains.+ } deriving (Eq, Monoid, Ord)++instance Show ExtName where+ show extName = concat ["$\"", fromExtName extName, "\"$"]++-- | Creates an 'ExtName' that contains the given string, erroring if the string+-- is an invalid 'ExtName'.+toExtName :: String -> ExtName+toExtName str = case str of+ -- Keep this logic in sync with isValidExtName.+ [] -> error "An ExtName cannot be empty."+ _ -> if isValidExtName str+ then ExtName str+ else error $+ "An ExtName must start with a letter and only contain letters, numbers, and '_': " +++ show str++-- | Returns true if the given string is represents a valid 'ExtName'.+isValidExtName :: String -> Bool+isValidExtName str = case str of+ -- Keep this logic in sync with toExtName.+ [] -> False+ c:cs -> isAlpha c && all ((||) <$> isAlphaNum <*> (== '_')) cs++-- | Generates an 'ExtName' from an 'Identifier', if the given name is absent.+extNameOrIdentifier :: Identifier -> Maybe ExtName -> ExtName+extNameOrIdentifier ident = fromMaybe $ case identifierParts ident of+ [] -> error "extNameOrIdentifier: Invalid empty identifier."+ parts -> toExtName $ idPartBase $ last parts++-- | Generates an 'ExtName' from an @'FnName' 'Identifier'@, if the given name+-- is absent.+extNameOrFnIdentifier :: FnName Identifier -> Maybe ExtName -> ExtName+extNameOrFnIdentifier name =+ fromMaybe $ case name of+ FnName identifier -> case identifierParts identifier of+ [] -> error "extNameOrFnIdentifier: Empty idenfitier."+ parts -> toExtName $ idPartBase $ last parts+ FnOp op -> operatorPreferredExtName op++-- | Generates an 'ExtName' from a string, if the given name is absent.+extNameOrString :: String -> Maybe ExtName -> ExtName+extNameOrString str = fromMaybe $ toExtName str++-- | Types that have an external name, and also optionally have nested entities+-- with external names as well. See 'getAllExtNames'.+class HasExtNames a where+ -- | Returns the external name by which a given entity is referenced.+ getPrimaryExtName :: a -> ExtName++ -- | Returns external names nested within the given entity. Does not include+ -- the primary external name.+ getNestedExtNames :: a -> [ExtName]+ getNestedExtNames _ = []++-- | Returns a list of all of the external names an entity contains. This+-- combines both 'getPrimaryExtName' and 'getNestedExtNames'.+getAllExtNames :: HasExtNames a => a -> [ExtName]+getAllExtNames x = getPrimaryExtName x : getNestedExtNames x++-- | The C++ name of a function or method.+data FnName name =+ FnName name+ -- ^ A regular, \"alphanumeric\" name. The exact type depends on what kind of+ -- object is being named.+ | FnOp Operator+ -- ^ An operator name.+ deriving (Eq, Ord)++instance Show name => Show (FnName name) where+ show (FnName name) = concat ["<FnName ", show name, ">"]+ show (FnOp op) = concat ["<FnOp ", show op, ">"]++-- | Enables implementing automatic conversions to a @'FnName' t@.+class IsFnName t a where+ toFnName :: a -> FnName t++instance IsFnName t (FnName t) where+ toFnName = id++instance IsFnName t t where+ toFnName = FnName++instance IsFnName t Operator where+ toFnName = FnOp++-- | Overloadable C++ operators.+data Operator =+ OpCall -- ^ @x(...)@+ | OpComma -- ^ @x, y@+ | OpAssign -- ^ @x = y@+ | OpArray -- ^ @x[y]@+ | OpDeref -- ^ @*x@+ | OpAddress -- ^ @&x@+ | OpAdd -- ^ @x + y@+ | OpAddAssign -- ^ @x += y@+ | OpSubtract -- ^ @x - y@+ | OpSubtractAssign -- ^ @x -= y@+ | OpMultiply -- ^ @x * y@+ | OpMultiplyAssign -- ^ @x *= y@+ | OpDivide -- ^ @x / y@+ | OpDivideAssign -- ^ @x /= y@+ | OpModulo -- ^ @x % y@+ | OpModuloAssign -- ^ @x %= y@+ | OpPlus -- ^ @+x@+ | OpMinus -- ^ @-x@+ | OpIncPre -- ^ @++x@+ | OpIncPost -- ^ @x++@+ | OpDecPre -- ^ @--x@+ | OpDecPost -- ^ @x--@+ | OpEq -- ^ @x == y@+ | OpNe -- ^ @x != y@+ | OpLt -- ^ @x < y@+ | OpLe -- ^ @x <= y@+ | OpGt -- ^ @x > y@+ | OpGe -- ^ @x >= y@+ | OpNot -- ^ @!x@+ | OpAnd -- ^ @x && y@+ | OpOr -- ^ @x || y@+ | OpBitNot -- ^ @~x@+ | OpBitAnd -- ^ @x & y@+ | OpBitAndAssign -- ^ @x &= y@+ | OpBitOr -- ^ @x | y@+ | OpBitOrAssign -- ^ @x |= y@+ | OpBitXor -- ^ @x ^ y@+ | OpBitXorAssign -- ^ @x ^= y@+ | OpShl -- ^ @x << y@+ | OpShlAssign -- ^ @x <<= y@+ | OpShr -- ^ @x >> y@+ | OpShrAssign -- ^ @x >>= y@+ deriving (Bounded, Enum, Eq, Ord, Show)++-- | The arity and syntax of an operator.+data OperatorType =+ UnaryPrefixOperator String -- ^ Prefix unary operators. Examples: @!x@, @*x@, @++x@.+ | UnaryPostfixOperator String -- ^ Postfix unary operators. Examples: @x--, x++@.+ | BinaryOperator String -- ^ Infix binary operators. Examples: @x * y@, @x >>= y@.+ | CallOperator -- ^ @x(...)@ with arbitrary arity.+ | ArrayOperator -- ^ @x[y]@, a binary operator with non-infix syntax.++data OperatorInfo = OperatorInfo+ { operatorPreferredExtName'' :: ExtName+ , operatorType' :: OperatorType+ }++makeOperatorInfo :: String -> OperatorType -> OperatorInfo+makeOperatorInfo = OperatorInfo . toExtName++-- | Returns a conventional string to use for the 'ExtName' of an operator.+operatorPreferredExtName :: Operator -> ExtName+operatorPreferredExtName op = case M.lookup op operatorInfo of+ Just info -> operatorPreferredExtName'' info+ Nothing ->+ error $ concat+ ["operatorPreferredExtName: Internal error, missing info for operator ", show op, "."]++-- | Returns a conventional name for an operator, as with+-- 'operatorPreferredExtName', but as a string.+operatorPreferredExtName' :: Operator -> String+operatorPreferredExtName' = fromExtName . operatorPreferredExtName++-- | Returns the type of an operator.+operatorType :: Operator -> OperatorType+operatorType op = case M.lookup op operatorInfo of+ Just info -> operatorType' info+ Nothing ->+ error $ concat+ ["operatorType: Internal error, missing info for operator ", show op, "."]++-- | Metadata for operators.+--+-- TODO Test out this missing data.+operatorInfo :: M.Map Operator OperatorInfo+operatorInfo =+ let input =+ [ (OpCall, makeOperatorInfo "CALL" CallOperator)+ , (OpComma, makeOperatorInfo "COMMA" $ BinaryOperator ",")+ , (OpAssign, makeOperatorInfo "ASSIGN" $ BinaryOperator "=")+ , (OpArray, makeOperatorInfo "ARRAY" ArrayOperator)+ , (OpDeref, makeOperatorInfo "DEREF" $ UnaryPrefixOperator "*")+ , (OpAddress, makeOperatorInfo "ADDRESS" $ UnaryPrefixOperator "&")+ , (OpAdd, makeOperatorInfo "ADD" $ BinaryOperator "+")+ , (OpAddAssign, makeOperatorInfo "ADDA" $ BinaryOperator "+=")+ , (OpSubtract, makeOperatorInfo "SUB" $ BinaryOperator "-")+ , (OpSubtractAssign, makeOperatorInfo "SUBA" $ BinaryOperator "-=")+ , (OpMultiply, makeOperatorInfo "MUL" $ BinaryOperator "*")+ , (OpMultiplyAssign, makeOperatorInfo "MULA" $ BinaryOperator "*=")+ , (OpDivide, makeOperatorInfo "DIV" $ BinaryOperator "/")+ , (OpDivideAssign, makeOperatorInfo "DIVA" $ BinaryOperator "/=")+ , (OpModulo, makeOperatorInfo "MOD" $ BinaryOperator "%")+ , (OpModuloAssign, makeOperatorInfo "MODA" $ BinaryOperator "%=")+ , (OpPlus, makeOperatorInfo "PLUS" $ UnaryPrefixOperator "+")+ , (OpMinus, makeOperatorInfo "NEG" $ UnaryPrefixOperator "-")+ , (OpIncPre, makeOperatorInfo "INC" $ UnaryPrefixOperator "++")+ , (OpIncPost, makeOperatorInfo "INCPOST" $ UnaryPostfixOperator "++")+ , (OpDecPre, makeOperatorInfo "DEC" $ UnaryPrefixOperator "--")+ , (OpDecPost, makeOperatorInfo "DECPOST" $ UnaryPostfixOperator "--")+ , (OpEq, makeOperatorInfo "EQ" $ BinaryOperator "==")+ , (OpNe, makeOperatorInfo "NE" $ BinaryOperator "!=")+ , (OpLt, makeOperatorInfo "LT" $ BinaryOperator "<")+ , (OpLe, makeOperatorInfo "LE" $ BinaryOperator "<=")+ , (OpGt, makeOperatorInfo "GT" $ BinaryOperator ">")+ , (OpGe, makeOperatorInfo "GE" $ BinaryOperator ">=")+ , (OpNot, makeOperatorInfo "NOT" $ UnaryPrefixOperator "!")+ , (OpAnd, makeOperatorInfo "AND" $ BinaryOperator "&&")+ , (OpOr, makeOperatorInfo "OR" $ BinaryOperator "||")+ , (OpBitNot, makeOperatorInfo "BNOT" $ UnaryPrefixOperator "~")+ , (OpBitAnd, makeOperatorInfo "BAND" $ BinaryOperator "&")+ , (OpBitAndAssign, makeOperatorInfo "BANDA" $ BinaryOperator "&=")+ , (OpBitOr, makeOperatorInfo "BOR" $ BinaryOperator "|")+ , (OpBitOrAssign, makeOperatorInfo "BORA" $ BinaryOperator "|=")+ , (OpBitXor, makeOperatorInfo "BXOR" $ BinaryOperator "^")+ , (OpBitXorAssign, makeOperatorInfo "BXORA" $ BinaryOperator "^=")+ , (OpShl, makeOperatorInfo "SHL" $ BinaryOperator "<<")+ , (OpShlAssign, makeOperatorInfo "SHLA" $ BinaryOperator "<<=")+ , (OpShr, makeOperatorInfo "SHR" $ BinaryOperator ">>")+ , (OpShrAssign, makeOperatorInfo "SHR" $ BinaryOperator ">>=")+ ]+ in if map fst input == [minBound..]+ then M.fromList input+ else error "operatorInfo: Operator info list is out of sync with Operator data type."++-- | Specifies some C++ object (function or class) to give access to.+data Export =+ ExportVariable Variable -- ^ Exports a variable.+ | ExportEnum CppEnum -- ^ Exports an enum.+ | ExportBitspace Bitspace -- ^ Exports a bitspace.+ | ExportFn Function -- ^ Exports a function.+ | ExportClass Class -- ^ Exports a class with all of its contents.+ | ExportCallback Callback -- ^ Exports a callback.+ deriving (Show)++instance HasExtNames Export where+ getPrimaryExtName x = case x of+ ExportVariable v -> getPrimaryExtName v+ ExportEnum e -> getPrimaryExtName e+ ExportBitspace b -> getPrimaryExtName b+ ExportFn f -> getPrimaryExtName f+ ExportClass cls -> getPrimaryExtName cls+ ExportCallback cb -> getPrimaryExtName cb++ getNestedExtNames x = case x of+ ExportVariable v -> getNestedExtNames v+ ExportEnum e -> getNestedExtNames e+ ExportBitspace b -> getNestedExtNames b+ ExportFn f -> getNestedExtNames f+ ExportClass cls -> getNestedExtNames cls+ ExportCallback cb -> getNestedExtNames cb++-- | Returns the export's addendum. 'Export' doesn't have a 'HasAddendum'+-- instance because you normally wouldn't want to modify the addendum of one.+exportAddendum export = case export of+ ExportVariable v -> getAddendum v+ ExportEnum e -> getAddendum e+ ExportBitspace bs -> getAddendum bs+ ExportFn f -> getAddendum f+ ExportClass cls -> getAddendum cls+ ExportCallback cb -> getAddendum cb++-- | A path to some C++ object, including namespaces. An identifier consists of+-- multiple parts separated by @\"::\"@. Each part has a name string followed+-- by an optional template argument list, where each argument gets rendered from+-- a 'Type' (non-type arguments for template metaprogramming are not supported).+newtype Identifier = Identifier+ { identifierParts :: [IdPart]+ -- ^ The separate parts of the identifier, between @::@s.+ } deriving (Eq)++instance Show Identifier where+ show ident =+ (\words -> concat $ "<Identifier " : words ++ [">"]) $+ intersperse "::" $+ map (\part -> case idPartArgs part of+ Nothing -> idPartBase part+ Just args ->+ concat $+ idPartBase part : "<" :+ intersperse ", " (map show args) ++ [">"]) $+ identifierParts ident++-- | A single component of an 'Identifier', between @::@s.+data IdPart = IdPart+ { idPartBase :: String+ -- ^ The name within the enclosing scope.+ , idPartArgs :: Maybe [Type]+ -- ^ Template arguments, if present.+ } deriving (Eq, Show)++-- | Creates an identifier of the form @a@.+ident :: String -> Identifier+ident a = Identifier [IdPart a Nothing]++-- | Creates an identifier of the form @a1::a2::...::aN@.+ident' :: [String] -> Identifier+ident' = Identifier . map (\x -> IdPart x Nothing)++-- | Creates an identifier of the form @a::b@.+ident1 :: String -> String -> Identifier+ident1 a b = ident' [a, b]++-- | Creates an identifier of the form @a::b::c@.+ident2 :: String -> String -> String -> Identifier+ident2 a b c = ident' [a, b, c]++-- | Creates an identifier of the form @a::b::c::d@.+ident3 :: String -> String -> String -> String -> Identifier+ident3 a b c d = ident' [a, b, c, d]++-- | Creates an identifier of the form @a::b::c::d::e@.+ident4 :: String -> String -> String -> String -> String -> Identifier+ident4 a b c d e = ident' [a, b, c, d, e]++-- | Creates an identifier of the form @a::b::c::d::e::f@.+ident5 :: String -> String -> String -> String -> String -> String -> Identifier+ident5 a b c d e f = ident' [a, b, c, d, e, f]++-- | Creates an identifier of the form @a\<...\>@.+identT :: String -> [Type] -> Identifier+identT a ts = Identifier [IdPart a $ Just ts]++-- | Creates an identifier with arbitrary many templated and non-templated+-- parts.+identT' :: [(String, Maybe [Type])] -> Identifier+identT' = Identifier . map (uncurry IdPart)++-- | Creates an identifier of the form @a::b\<...\>@.+ident1T :: String -> String -> [Type] -> Identifier+ident1T a b ts = Identifier [IdPart a Nothing, IdPart b $ Just ts]++-- | Creates an identifier of the form @a::b::c\<...\>@.+ident2T :: String -> String -> String -> [Type] -> Identifier+ident2T a b c ts = Identifier [IdPart a Nothing, IdPart b Nothing, IdPart c $ Just ts]++-- | Creates an identifier of the form @a::b::c::d\<...\>@.+ident3T :: String -> String -> String -> String -> [Type] -> Identifier+ident3T a b c d ts =+ Identifier [IdPart a Nothing, IdPart b Nothing, IdPart c Nothing,+ IdPart d $ Just ts]++-- | Creates an identifier of the form @a::b::c::d::e\<...\>@.+ident4T :: String -> String -> String -> String -> String -> [Type] -> Identifier+ident4T a b c d e ts =+ Identifier [IdPart a Nothing, IdPart b Nothing, IdPart c Nothing,+ IdPart d Nothing, IdPart e $ Just ts]++-- | Creates an identifier of the form @a::b::c::d::e::f\<...\>@.+ident5T :: String -> String -> String -> String -> String -> String -> [Type] -> Identifier+ident5T a b c d e f ts =+ Identifier [IdPart a Nothing, IdPart b Nothing, IdPart c Nothing,+ IdPart d Nothing, IdPart e Nothing, IdPart f $ Just ts]++-- | A concrete C++ type. Use the bindings in "Foreign.Hoppy.Generator.Types"+-- for values of this type; these data constructors are subject to change+-- without notice.+data Type =+ Internal_TVoid+ | Internal_TBool+ | Internal_TChar+ | Internal_TUChar+ | Internal_TShort+ | Internal_TUShort+ | Internal_TInt+ | Internal_TUInt+ | Internal_TLong+ | Internal_TULong+ | Internal_TLLong+ | Internal_TULLong+ | Internal_TFloat+ | Internal_TDouble+ | Internal_TInt8+ | Internal_TInt16+ | Internal_TInt32+ | Internal_TInt64+ | Internal_TWord8+ | Internal_TWord16+ | Internal_TWord32+ | Internal_TWord64+ | Internal_TPtrdiff+ | Internal_TSize+ | Internal_TSSize+ | Internal_TEnum CppEnum+ | Internal_TBitspace Bitspace+ | Internal_TPtr Type+ | Internal_TRef Type+ | Internal_TFn [Type] Type+ | Internal_TCallback Callback+ | Internal_TObj Class+ | Internal_TObjToHeap Class+ | Internal_TToGc Type+ | Internal_TConst Type+ deriving (Eq, Show)++-- | Canonicalizes a 'Type' without changing its meaning. Multiple nested+-- 'Internal_TConst's are collapsed into a single one.+normalizeType :: Type -> Type+normalizeType t = case t of+ Internal_TVoid -> t+ Internal_TBool -> t+ Internal_TChar -> t+ Internal_TUChar -> t+ Internal_TShort -> t+ Internal_TUShort -> t+ Internal_TInt -> t+ Internal_TUInt -> t+ Internal_TLong -> t+ Internal_TULong -> t+ Internal_TLLong -> t+ Internal_TULLong -> t+ Internal_TFloat -> t+ Internal_TDouble -> t+ Internal_TInt8 -> t+ Internal_TInt16 -> t+ Internal_TInt32 -> t+ Internal_TInt64 -> t+ Internal_TWord8 -> t+ Internal_TWord16 -> t+ Internal_TWord32 -> t+ Internal_TWord64 -> t+ Internal_TPtrdiff -> t+ Internal_TSize -> t+ Internal_TSSize -> t+ Internal_TEnum _ -> t+ Internal_TBitspace _ -> t+ Internal_TPtr t' -> Internal_TPtr $ normalizeType t'+ Internal_TRef t' -> Internal_TRef $ normalizeType t'+ Internal_TFn paramTypes retType ->+ Internal_TFn (map normalizeType paramTypes) $ normalizeType retType+ Internal_TCallback _ -> t+ Internal_TObj _ -> t+ Internal_TObjToHeap _ -> t+ Internal_TToGc _ -> t+ Internal_TConst (Internal_TConst t') -> normalizeType $ Internal_TConst t'+ Internal_TConst _ -> t++-- | Strips leading 'Internal_TConst's off of a type.+stripConst :: Type -> Type+stripConst t = case t of+ Internal_TConst t' -> stripConst t'+ _ -> t++-- | A C++ variable.+data Variable = Variable+ { varIdentifier :: Identifier+ -- ^ The identifier used to refer to the variable.+ , varExtName :: ExtName+ -- ^ The variable's external name.+ , varType :: Type+ -- ^ The variable's type. This may be+ -- 'Foreign.Hoppy.Generator.Types.constT' to indicate that the variable is+ -- read-only.+ , varReqs :: Reqs+ -- ^ Requirements for bindings to use this variable.+ , varAddendum :: Addendum+ -- ^ The variable's addendum.+ }++instance Eq Variable where+ (==) = (==) `on` varExtName++instance Show Variable where+ show v = concat ["<Variable ", show (varExtName v), " ", show (varType v), ">"]++instance HasExtNames Variable where+ getPrimaryExtName = varExtName+ getNestedExtNames v = [varGetterExtName v, varSetterExtName v]++instance HasReqs Variable where+ getReqs = varReqs+ setReqs reqs v = v { varReqs = reqs }++instance HasAddendum Variable where+ getAddendum = varAddendum+ setAddendum addendum v = v { varAddendum = addendum }++-- | Creates a binding for a C++ variable.+makeVariable :: Identifier -> Maybe ExtName -> Type -> Variable+makeVariable identifier maybeExtName t =+ Variable identifier (extNameOrIdentifier identifier maybeExtName) t mempty mempty++-- | Returns whether the variable is constant, i.e. whether its type is+-- @'Foreign.Hoppy.Generator.Types.constT' ...@.+varIsConst :: Variable -> Bool+varIsConst v = case varType v of+ Internal_TConst _ -> True+ _ -> False++-- | Returns the external name of the getter function for the variable.+varGetterExtName :: Variable -> ExtName+varGetterExtName = toExtName . (++ "_get") . fromExtName . varExtName++-- | Returns the external name of the setter function for the variable.+varSetterExtName :: Variable -> ExtName+varSetterExtName = toExtName . (++ "_set") . fromExtName . varExtName++-- | A C++ enum declaration. An enum should actually be enumerable (in the+-- sense of Haskell's 'Enum'); if it's not, consider using a 'Bitspace' instead.+data CppEnum = CppEnum+ { enumIdentifier :: Identifier+ -- ^ The identifier used to refer to the enum.+ , enumExtName :: ExtName+ -- ^ The enum's external name.+ , enumValueNames :: [(Int, [String])]+ -- ^ The numeric values and names of the enum values. A single value's name+ -- is broken up into words. How the words and ext name get combined to make+ -- a name in a particular foreign language depends on the language.+ , enumReqs :: Reqs+ -- ^ Requirements for a 'Type' to reference this enum.+ , enumAddendum :: Addendum+ -- ^ The enum's addendum.+ , enumValuePrefix :: String+ -- ^ The prefix applied to value names ('enumValueNames') when determining+ -- the names of values in foreign languages. This defaults to the external+ -- name of the enum, plus an underscore.+ --+ -- See 'enumSetValuePrefix'.+ }++instance Eq CppEnum where+ (==) = (==) `on` enumExtName++instance Show CppEnum where+ show e = concat ["<Enum ", show (enumExtName e), " ", show (enumIdentifier e), ">"]++instance HasExtNames CppEnum where+ getPrimaryExtName = enumExtName++instance HasReqs CppEnum where+ getReqs = enumReqs+ setReqs reqs e = e { enumReqs = reqs }++instance HasAddendum CppEnum where+ getAddendum = enumAddendum+ setAddendum addendum e = e { enumAddendum = addendum }++-- | Creates a binding for a C++ enum.+makeEnum :: Identifier -- ^ 'enumIdentifier'+ -> Maybe ExtName+ -- ^ An optional external name; will be automatically derived from+ -- the identifier if absent.+ -> [(Int, [String])] -- ^ 'enumValueNames'+ -> CppEnum+makeEnum identifier maybeExtName valueNames =+ let extName = extNameOrIdentifier identifier maybeExtName+ in CppEnum+ identifier+ extName+ valueNames+ mempty+ mempty+ (fromExtName extName ++ "_")++-- | Sets the prefix applied to the names of enum values' identifiers in foreign+-- languages.+--+-- See 'enumValuePrefix'.+enumSetValuePrefix :: String -> CppEnum -> CppEnum+enumSetValuePrefix prefix enum = enum { enumValuePrefix = prefix }++-- | A C++ numeric space with bitwise operations. This is similar to a+-- 'CppEnum', but in addition to the extra operations, this differs in that+-- these values aren't enumerable.+--+-- Additionally, as a kludge for Qtah, a bitspace may have a C++ type+-- ('bitspaceCppTypeIdentifier') separate from its numeric type+-- ('bitspaceType'). Qt bitspaces aren't raw numbers but are instead type-safe+-- @QFlags@ objects that don't implicitly convert from integers, so we need a+-- means to do so manually. Barring general ad-hoc argument and return value+-- conversion support, we allow this as follows: when given a C++ type, then a+-- bitspace may also have a conversion function between the numeric and C+++-- type, in each direction. If a conversion function is present, it will be+-- used for conversions in its respective direction. The C++ type is not a full+-- 'Type', but only an 'Identifier', since additional information is not needed.+-- See 'bitspaceAddCppType'.+data Bitspace = Bitspace+ { bitspaceExtName :: ExtName+ -- ^ The bitspace's external name.+ , bitspaceType :: Type+ -- ^ The C++ type used for bits values. This should be a primitive numeric+ -- type, usually 'Foreign.Hoppy.Generator.Types.intT'.+ , bitspaceValueNames :: [(Int, [String])]+ -- ^ The numeric values and names of the bitspace values. See+ -- 'enumValueNames'.+ , bitspaceEnum :: Maybe CppEnum+ -- ^ An associated enum, whose values may be converted to values in the+ -- bitspace.+ , bitspaceCppTypeIdentifier :: Maybe Identifier+ -- ^ The optional C++ type for a bitspace.+ , bitspaceToCppValueFn :: Maybe String+ -- ^ The name of a C++ function to convert from 'bitspaceType' to the+ -- bitspace's C++ type.+ , bitspaceFromCppValueFn :: Maybe String+ -- ^ The name of a C++ function to convert from the bitspace's C++ type to+ -- 'bitspaceType'.+ , bitspaceReqs :: Reqs+ -- ^ Requirements for emitting the bindings for a bitspace, i.e. what's+ -- necessary to reference 'bitspaceCppTypeIdentifier',+ -- 'bitspaceFromCppValueFn', and 'bitspaceToCppValueFn'. 'bitspaceType' can+ -- take some numeric types that require includes as well, but you don't need+ -- to list these here.+ , bitspaceAddendum :: Addendum+ -- ^ The bitspace's addendum.+ , bitspaceValuePrefix :: String+ -- ^ The prefix applied to value names ('bitspaceValueNames') when+ -- determining the names of values in foreign languages. This defaults to+ -- the external name of the bitspace, plus an underscore.+ --+ -- See 'bitspaceSetValuePrefix'.+ }++instance Eq Bitspace where+ (==) = (==) `on` bitspaceExtName++instance Show Bitspace where+ show e = concat ["<Bitspace ", show (bitspaceExtName e), " ", show (bitspaceType e), ">"]++instance HasExtNames Bitspace where+ getPrimaryExtName = bitspaceExtName++instance HasReqs Bitspace where+ getReqs = bitspaceReqs+ setReqs reqs b = b { bitspaceReqs = reqs }++instance HasAddendum Bitspace where+ getAddendum = bitspaceAddendum+ setAddendum addendum bs = bs { bitspaceAddendum = addendum }++-- | Creates a binding for a C++ bitspace.+makeBitspace :: ExtName -- ^ 'bitspaceExtName'+ -> Type -- ^ 'bitspaceType'+ -> [(Int, [String])] -- ^ 'bitspaceValueNames'+ -> Bitspace+makeBitspace extName t valueNames =+ Bitspace extName t valueNames Nothing Nothing Nothing Nothing mempty mempty+ (fromExtName extName ++ "_")++-- | Sets the prefix applied to the names of enum values' identifiers in foreign+-- languages.+--+-- See 'enumValuePrefix'.+bitspaceSetValuePrefix :: String -> Bitspace -> Bitspace+bitspaceSetValuePrefix prefix bitspace = bitspace { bitspaceValuePrefix = prefix }++-- | Associates an enum with the bitspace. See 'bitspaceEnum'.+bitspaceAddEnum :: CppEnum -> Bitspace -> Bitspace+bitspaceAddEnum enum bitspace = case bitspaceEnum bitspace of+ Just enum' ->+ error $ concat+ ["bitspaceAddEnum: Adding ", show enum, " to ", show bitspace,+ ", but it already has ", show enum', "."]+ Nothing ->+ if bitspaceValueNames bitspace /= enumValueNames enum+ then error $ concat+ ["bitspaceAddEnum: Trying to add ", show enum, " to ", show bitspace,+ ", but the values aren't equal.\nBitspace values: ", show $ bitspaceValueNames bitspace,+ "\n Enum values: ", show $ enumValueNames enum]+ else bitspace { bitspaceEnum = Just enum }++-- | @bitspaceAddCppType cppTypeIdentifier toCppValueFn fromCppValueFn@+-- associates a C++ type (plus optional conversion functions) with a bitspace.+-- At least one conversion should be specified, otherwise adding the C++ type+-- will mean nothing. You should also add use requirements to the bitspace for+-- all of these arguments; see 'HasReqs'.+bitspaceAddCppType :: Identifier -> Maybe String -> Maybe String -> Bitspace -> Bitspace+bitspaceAddCppType cppTypeId toCppValueFnMaybe fromCppValueFnMaybe b =+ case bitspaceCppTypeIdentifier b of+ Just cppTypeId' ->+ error $ concat+ ["bitspaceAddCppType: Adding C++ type ", show cppTypeId,+ " to ", show b, ", but it already has ", show cppTypeId', "."]+ Nothing ->+ b { bitspaceCppTypeIdentifier = Just cppTypeId+ , bitspaceToCppValueFn = toCppValueFnMaybe+ , bitspaceFromCppValueFn = fromCppValueFnMaybe+ }++-- | Whether or not a function may cause side-effects.+--+-- Haskell bindings for pure functions will not be in 'IO', and calls to pure+-- functions will be executed non-strictly. Calls to impure functions will+-- execute in the IO monad.+--+-- Member functions for mutable classes should not be made pure, because it is+-- difficult in general to control when the call will be made.+data Purity = Nonpure -- ^ Side-affects are possible.+ | Pure -- ^ Side-affects will not happen.+ deriving (Eq, Show)++-- | A C++ function declaration.+data Function = Function+ { fnCName :: FnName Identifier+ -- ^ The identifier used to call the function.+ , fnExtName :: ExtName+ -- ^ The function's external name.+ , fnPurity :: Purity+ -- ^ Whether the function is pure.+ , fnParams :: [Type]+ -- ^ The function's parameter types.+ , fnReturn :: Type+ -- ^ The function's return type.+ , fnReqs :: Reqs+ -- ^ Requirements for a binding to call the function.+ , fnExceptionHandlers :: ExceptionHandlers+ -- ^ Exceptions that the function might throw.+ , fnAddendum :: Addendum+ -- ^ The function's addendum.+ }++instance Show Function where+ show fn =+ concat ["<Function ", show (fnExtName fn), " ", show (fnCName fn),+ show (fnParams fn), " ", show (fnReturn fn), ">"]++instance HasExtNames Function where+ getPrimaryExtName = fnExtName++instance HasReqs Function where+ getReqs = fnReqs+ setReqs reqs fn = fn { fnReqs = reqs }++instance HandlesExceptions Function where+ getExceptionHandlers = fnExceptionHandlers+ modifyExceptionHandlers f fn = fn { fnExceptionHandlers = f $ fnExceptionHandlers fn }++instance HasAddendum Function where+ getAddendum = fnAddendum+ setAddendum addendum fn = fn { fnAddendum = addendum }++-- | Creates a binding for a C++ function.+makeFn :: IsFnName Identifier name+ => name+ -> Maybe ExtName+ -- ^ An optional external name; will be automatically derived from+ -- the identifier if absent.+ -> Purity+ -> [Type] -- ^ Parameter types.+ -> Type -- ^ Return type.+ -> Function+makeFn cName maybeExtName purity paramTypes retType =+ let fnName = toFnName cName+ in Function fnName+ (extNameOrFnIdentifier fnName maybeExtName)+ purity paramTypes retType mempty mempty mempty++-- | A C++ class declaration. See 'IsClassEntity' for more information about the+-- interaction between a class's names and the names of entities within the+-- class.+data Class = Class+ { classIdentifier :: Identifier+ -- ^ The identifier used to refer to the class.+ , classExtName :: ExtName+ -- ^ The class's external name.+ , classSuperclasses :: [Class]+ -- ^ The class's public superclasses.+ , classEntities :: [ClassEntity]+ -- ^ The class's entities.+ , classDtorIsPublic :: Bool+ -- ^ The class's methods.+ , classConversion :: ClassConversion+ -- ^ Behaviour for converting objects to and from foriegn values.+ , classReqs :: Reqs+ -- ^ Requirements for a 'Type' to reference this class.+ , classAddendum :: Addendum+ -- ^ The class's addendum.+ , classIsMonomorphicSuperclass :: Bool+ -- ^ This is true for classes passed through+ -- 'classSetMonomorphicSuperclass'.+ , classIsSubclassOfMonomorphic :: Bool+ -- ^ This is true for classes passed through+ -- 'classSetSubclassOfMonomorphic'.+ , classIsException :: Bool+ -- ^ Whether to support using the class as a C++ exception.+ , classEntityPrefix :: String+ -- ^ The prefix applied to the external names of entities (methods, etc.)+ -- within this class when determining the names of foreign languages'+ -- corresponding bindings. This defaults to the external name of the class,+ -- plus an underscore. Changing this allows you to potentially have+ -- entities with the same foreign name in separate modules. This may be the+ -- empty string, in which case the foreign name will simply be the external+ -- name of the entity.+ --+ -- This does __not__ affect the things' external names themselves; external+ -- names must still be unique in an interface. For instance, a method with+ -- external name @bar@ in a class with external name @Flab@ and prefix+ -- @Flob_@ will use the effective external name @Flab_bar@, but the+ -- generated name in say Haskell would be @Flob_bar@.+ --+ -- See 'IsClassEntity' and 'classSetEntityPrefix'.+ }++instance Eq Class where+ (==) = (==) `on` classExtName++instance Ord Class where+ compare = compare `on` classExtName++instance Show Class where+ show cls =+ concat ["<Class ", show (classExtName cls), " ", show (classIdentifier cls), ">"]++instance HasExtNames Class where+ getPrimaryExtName = classExtName++ getNestedExtNames cls = concatMap (classEntityExtNames cls) $ classEntities cls++instance HasReqs Class where+ getReqs = classReqs+ setReqs reqs cls = cls { classReqs = reqs }++instance HasAddendum Class where+ getAddendum = classAddendum+ setAddendum addendum cls = cls { classAddendum = addendum }++-- | Creates a binding for a C++ class and its contents.+makeClass :: Identifier+ -> Maybe ExtName+ -- ^ An optional external name; will be automatically derived from the+ -- identifier if absent.+ -> [Class] -- ^ Superclasses.+ -> [ClassEntity]+ -> Class+makeClass identifier maybeExtName supers entities =+ let extName = extNameOrIdentifier identifier maybeExtName+ in Class+ { classIdentifier = identifier+ , classExtName = extName+ , classSuperclasses = supers+ , classEntities = entities+ , classDtorIsPublic = True+ , classConversion = classConversionNone+ , classReqs = mempty+ , classAddendum = mempty+ , classIsMonomorphicSuperclass = False+ , classIsSubclassOfMonomorphic = False+ , classIsException = False+ , classEntityPrefix = fromExtName extName ++ "_"+ }++-- | Sets the prefix applied to foreign languages' entities generated from+-- methods, etc. within the class.+--+-- See 'IsClassEntity' and 'classEntityPrefix'.+classSetEntityPrefix :: String -> Class -> Class+classSetEntityPrefix prefix cls = cls { classEntityPrefix = prefix }++-- | Adds constructors to a class.+classAddEntities :: [ClassEntity] -> Class -> Class+classAddEntities ents cls =+ if null ents then cls else cls { classEntities = classEntities cls ++ ents }++-- | Returns all of the class's variables.+classVariables :: Class -> [ClassVariable]+classVariables = mapMaybe pickVar . classEntities+ where pickVar ent = case ent of+ CEVar v -> Just v+ CECtor _ -> Nothing+ CEMethod _ -> Nothing+ CEProp _ -> Nothing++-- | Returns all of the class's constructors.+classCtors :: Class -> [Ctor]+classCtors = mapMaybe pickCtor . classEntities+ where pickCtor ent = case ent of+ CEVar _ -> Nothing+ CECtor ctor -> Just ctor+ CEMethod _ -> Nothing+ CEProp _ -> Nothing++-- | Returns all of the class's methods, including methods generated from+-- 'Prop's.+classMethods :: Class -> [Method]+classMethods = concatMap pickMethods . classEntities+ where pickMethods ent = case ent of+ CEVar _ -> []+ CECtor _ -> []+ CEMethod m -> [m]+ CEProp (Prop ms) -> ms++-- | Marks a class's destructor as private, so that a binding for it won't be+-- generated.+classSetDtorPrivate :: Class -> Class+classSetDtorPrivate cls = cls { classDtorIsPublic = False }++-- | Explicitly marks a class as being monomorphic (i.e. not having any+-- virtual methods or destructors). By default, Hoppy assumes that a class that+-- is derived is also polymorphic, but it can happen that this is not the case.+-- Downcasting with @dynamic_cast@ from such classes is not available. See also+-- 'classSetSubclassOfMonomorphic'.+classSetMonomorphicSuperclass :: Class -> Class+classSetMonomorphicSuperclass cls = cls { classIsMonomorphicSuperclass = True }++-- | Marks a class as being derived from some monomorphic superclass. This+-- prevents any downcasting to this class. Generally it is better to use+-- 'classSetMonomorphicSuperclass' on the specific superclasses that are+-- monomorphic, but in cases where this is not possible, this function can be+-- applied to the subclass instead.+classSetSubclassOfMonomorphic :: Class -> Class+classSetSubclassOfMonomorphic cls = cls { classIsSubclassOfMonomorphic = True }++-- | Marks a class as being used as an exception. This makes the class+-- throwable and catchable.+classMakeException :: Class -> Class+classMakeException cls = case classIsException cls of+ False -> cls { classIsException = True }+ True -> cls++-- | Separately from passing object handles between C++ and foreign languages,+-- objects can also be made to implicitly convert to native values in foreign+-- languages. A single such type may be associated with any C++ class for each+-- foreign language. The foreign type and the conversion process in each+-- direction are specified using this object. Converting a C++ object to a+-- foreign value is also called decoding, and vice versa is called encoding. A+-- class may be convertible in one direction and not the other.+--+-- To use these implicit conversions, instead of specifying an object handle+-- type such as+-- @'Foreign.Hoppy.Generator.Types.ptrT' . 'Foreign.Hoppy.Generator.Types.objT'@+-- or+-- @'Foreign.Hoppy.Generator.Types.refT' . 'Foreign.Hoppy.Generator.Types.objT'@,+-- use 'Foreign.Hoppy.Generator.Types.objT' directly.+--+-- The subfields in this object specify how to do conversions between C++ and+-- foreign languages.+data ClassConversion = ClassConversion+ { classHaskellConversion :: ClassHaskellConversion+ -- ^ Conversions to and from Haskell.++ -- NOTE! When adding new languages here, add the language to+ -- 'classSetConversionToHeap', and 'classSetConversionToGc' as well if the+ -- language supports garbage collection.+ }++-- | Conversion behaviour for a class that is not convertible.+classConversionNone :: ClassConversion+classConversionNone = ClassConversion classHaskellConversionNone++-- | Modifies a class's 'ClassConversion' structure with a given function.+classModifyConversion :: (ClassConversion -> ClassConversion) -> Class -> Class+classModifyConversion f cls =+ let cls' = cls { classConversion = f $ classConversion cls }+ conv = classConversion cls'+ haskellConv = classHaskellConversion conv+ in case undefined of+ _ | (isJust (classHaskellConversionToCppFn haskellConv) ||+ isJust (classHaskellConversionFromCppFn haskellConv)) &&+ isNothing (classHaskellConversionType haskellConv) ->+ error $ "classModifyConversion: " ++ show cls' +++ " was given a Haskell-to-C++ or C++-to-Haskell conversion function" +++ " but no Haskell type. Please provide a classHaskellConversionType."+ _ -> cls'++-- | Replaces a class's 'ClassConversion' structure.+classSetConversion :: ClassConversion -> Class -> Class+classSetConversion c = classModifyConversion $ const c++-- | Controls how conversions between C++ objects and Haskell values happen in+-- Haskell bindings.+data ClassHaskellConversion = ClassHaskellConversion+ { classHaskellConversionType :: Maybe (Haskell.Generator HsType)+ -- ^ Produces the Haskell type that represents a value of the corresponding+ -- C++ class. This generator may add imports, but must not output code or+ -- add exports.+ , classHaskellConversionToCppFn :: Maybe (Haskell.Generator ())+ -- ^ Produces a Haskell expression that evaluates to a function that takes+ -- an object of the type that 'classHaskellConversionType' generates, and+ -- returns a non-const handle for a new C++ object in IO. The generator+ -- must output code and may add imports, but must not add exports.+ --+ -- If this field is present, then 'classHaskellConversionType' must also be+ -- present.+ , classHaskellConversionFromCppFn :: Maybe (Haskell.Generator ())+ -- ^ Produces a Haskell expression that evaluates to a function that takes a+ -- const handle for a C++ object, and returns an value of the type that+ -- 'classHaskellConversionType' generates, in IO. The generator must output+ -- code and may add imports, but must not add exports.+ --+ -- If this field is present, then 'classHaskellConversionType' must also be+ -- present.+ }++-- | Conversion behaviour for a class that is not convertible to or from+-- Haskell.+classHaskellConversionNone :: ClassHaskellConversion+classHaskellConversionNone =+ ClassHaskellConversion+ { classHaskellConversionType = Nothing+ , classHaskellConversionToCppFn = Nothing+ , classHaskellConversionFromCppFn = Nothing+ }++-- | Replaces a class's 'classHaskellConversion' with a given value.+classSetHaskellConversion :: ClassHaskellConversion -> Class -> Class+classSetHaskellConversion conv = classModifyConversion $ \c ->+ c { classHaskellConversion = conv }++-- | Things that live inside of a class, and have the class's external name+-- prepended to their own in generated code. With an external name of @\"bar\"@+-- and a class with external name @\"foo\"@, the resulting name will be+-- @\"foo_bar\"@.+--+-- See 'classEntityPrefix' and 'classSetEntityPrefix'.+class IsClassEntity a where+ -- | Extracts the external name of the object, without the class name added.+ classEntityExtNameSuffix :: a -> ExtName++-- | Computes the external name to use in generated code, containing both the+-- class's and object's external names. This is the concatenation of the+-- class's and entity's external names, separated by an underscore.+classEntityExtName :: IsClassEntity a => Class -> a -> ExtName+classEntityExtName cls x =+ toExtName $ fromExtName (classExtName cls) ++ "_" ++ fromExtName (classEntityExtNameSuffix x)++-- | Computes the name under which a class entity is to be exposed in foreign+-- languages. This is the concatenation of a class's entity prefix, and the+-- external name of the entity.+classEntityForeignName :: IsClassEntity a => Class -> a -> ExtName+classEntityForeignName cls x =+ classEntityForeignName' cls $ classEntityExtNameSuffix x++-- | Computes the name under which a class entity is to be exposed in foreign+-- languages, given a class and an entity's external name. The result is the+-- concatenation of a class's entity prefix, and the external name of the+-- entity.+classEntityForeignName' :: Class -> ExtName -> ExtName+classEntityForeignName' cls extName =+ toExtName $ classEntityPrefix cls ++ fromExtName extName++-- | A C++ entity that belongs to a class.+data ClassEntity =+ CEVar ClassVariable+ | CECtor Ctor+ | CEMethod Method+ | CEProp Prop++-- | Returns all of the names in a 'ClassEntity' within the corresponding+-- 'Class'.+classEntityExtNames :: Class -> ClassEntity -> [ExtName]+classEntityExtNames cls ent = case ent of+ CEVar v -> [classEntityExtName cls v]+ CECtor ctor -> [classEntityExtName cls ctor]+ CEMethod m -> [classEntityExtName cls m]+ CEProp (Prop methods) -> map (classEntityExtName cls) methods++-- | A C++ member variable.+data ClassVariable = ClassVariable+ { classVarCName :: String+ -- ^ The variable's C++ name.+ , classVarExtName :: ExtName+ -- ^ The variable's external name.+ , classVarType :: Type+ -- ^ The variable's type. This may be+ -- 'Foreign.Hoppy.Generator.Types.constT' to indicate that the variable is+ -- read-only.+ , classVarStatic :: Staticness+ -- ^ Whether the variable is static (i.e. whether it exists once in the+ -- class itself and not in each instance).+ , classVarGettable :: Bool+ -- ^ Whether the variable should have an accompanying getter. Note this+ -- exists only for disabling getters on callback variables - as there is+ -- currently no functionality to pass callbacks out of c+++ }++instance Show ClassVariable where+ show v =+ concat ["<ClassVariable ",+ show $ classVarCName v, " ",+ show $ classVarExtName v, " ",+ show $ classVarStatic v, " ",+ show $ classVarType v, ">"]++instance IsClassEntity ClassVariable where+ classEntityExtNameSuffix = classVarExtName++-- | Creates a 'ClassVariable' with full generality and manual name specification.+--+-- The result is wrapped in a 'CEVar'. For an unwrapped value, use+-- 'makeClassVariable_'.+makeClassVariable :: String -> Maybe ExtName -> Type -> Staticness -> Bool -> ClassEntity+makeClassVariable cName maybeExtName tp static gettable =+ CEVar $ makeClassVariable_ cName maybeExtName tp static gettable++-- | The unwrapped version of 'makeClassVariable'.+makeClassVariable_ :: String -> Maybe ExtName -> Type -> Staticness -> Bool -> ClassVariable+makeClassVariable_ cName maybeExtName =+ ClassVariable cName $ extNameOrString cName maybeExtName++-- | Creates a 'ClassVariable' for a nonstatic class variable for+-- @class::varName@ whose external name is @class_varName@.+--+-- The result is wrapped in a 'CEVar'. For an unwrapped value, use+-- 'mkClassVariable_'.+mkClassVariable :: String -> Type -> ClassEntity+mkClassVariable = (CEVar .) . mkClassVariable_++-- | The unwrapped version of 'mkClassVariable'.+mkClassVariable_ :: String -> Type -> ClassVariable+mkClassVariable_ cName t = makeClassVariable_ cName Nothing t Nonstatic True++-- | Same as 'mkClassVariable', but returns a static variable instead.+--+-- The result is wrapped in a 'CEVar'. For an unwrapped value, use+-- 'mkStaticClassVariable_'.+mkStaticClassVariable :: String -> Type -> ClassEntity+mkStaticClassVariable = (CEVar .) . mkStaticClassVariable_++-- | The unwrapped version of 'mkStaticClassVariable'.+mkStaticClassVariable_ :: String -> Type -> ClassVariable+mkStaticClassVariable_ cName t = makeClassVariable_ cName Nothing t Static True++-- | Returns the external name of the getter function for the class variable.+classVarGetterExtName :: Class -> ClassVariable -> ExtName+classVarGetterExtName cls v =+ toExtName $ fromExtName (classEntityExtName cls v) ++ "_get"++-- | Returns the foreign name of the getter function for the class variable.+classVarGetterForeignName :: Class -> ClassVariable -> ExtName+classVarGetterForeignName cls v =+ toExtName $ fromExtName (classEntityForeignName cls v) ++ "_get"++-- | Returns the external name of the setter function for the class variable.+classVarSetterExtName :: Class -> ClassVariable -> ExtName+classVarSetterExtName cls v =+ toExtName $ fromExtName (classEntityExtName cls v) ++ "_set"++-- | Returns the foreign name of the setter function for the class variable.+classVarSetterForeignName :: Class -> ClassVariable -> ExtName+classVarSetterForeignName cls v =+ toExtName $ fromExtName (classEntityForeignName cls v) ++ "_set"++-- | A C++ class constructor declaration.+data Ctor = Ctor+ { ctorExtName :: ExtName+ -- ^ The constructor's external name.+ , ctorParams :: [Type]+ -- ^ The constructor's parameter types.+ , ctorExceptionHandlers :: ExceptionHandlers+ -- ^ Exceptions that the constructor may throw.+ }++instance Show Ctor where+ show ctor = concat ["<Ctor ", show (ctorExtName ctor), " ", show (ctorParams ctor), ">"]++instance HandlesExceptions Ctor where+ getExceptionHandlers = ctorExceptionHandlers+ modifyExceptionHandlers f ctor = ctor { ctorExceptionHandlers = f $ ctorExceptionHandlers ctor }++instance IsClassEntity Ctor where+ classEntityExtNameSuffix = ctorExtName++-- | Creates a 'Ctor' with full generality.+--+-- The result is wrapped in a 'CECtor'. For an unwrapped value, use+-- 'makeCtor_'.+makeCtor :: ExtName+ -> [Type] -- ^ Parameter types.+ -> ClassEntity+makeCtor = (CECtor .) . makeCtor_++-- | The unwrapped version of 'makeCtor'.+makeCtor_ :: ExtName -> [Type] -> Ctor+makeCtor_ extName paramTypes = Ctor extName paramTypes mempty++-- | @mkCtor name@ creates a 'Ctor' whose external name is @className_name@.+--+-- The result is wrapped in a 'CECtor'. For an unwrapped value, use+-- 'makeCtor_'.+mkCtor :: String+ -> [Type] -- ^ Parameter types.+ -> ClassEntity+mkCtor = (CECtor .) . mkCtor_++-- | The unwrapped version of 'mkCtor'.+mkCtor_ :: String -> [Type] -> Ctor+mkCtor_ = makeCtor_ . toExtName++-- | Searches a class for a copy constructor, returning it if found.+classFindCopyCtor :: Class -> Maybe Ctor+classFindCopyCtor cls = case mapMaybe check $ classEntities cls of+ [ctor] -> Just ctor+ _ -> Nothing+ where check entity = case entity of+ CECtor ctor ->+ let params = map (stripConst . normalizeType) (ctorParams ctor)+ in if params == [Internal_TObj cls] ||+ params == [Internal_TRef $ Internal_TConst $ Internal_TObj cls]+ then Just ctor+ else Nothing+ _ -> Nothing++-- | A C++ class method declaration.+--+-- Any operator function that can be written as a method may have its binding be+-- written either as part of the associated class or as a separate entity,+-- independently of how the function is declared in C++.+data Method = Method+ { methodImpl :: MethodImpl+ -- ^ The underlying code that the binding calls.+ , methodExtName :: ExtName+ -- ^ The method's external name.+ , methodApplicability :: MethodApplicability+ -- ^ How the method is associated to its class.+ , methodPurity :: Purity+ -- ^ Whether the method is pure.+ , methodParams :: [Type]+ -- ^ The method's parameter types.+ , methodReturn :: Type+ -- ^ The method's return type.+ , methodExceptionHandlers :: ExceptionHandlers+ -- ^ Exceptions that the method might throw.+ }++instance Show Method where+ show method =+ concat ["<Method ", show (methodExtName method), " ",+ case methodImpl method of+ RealMethod name -> show name+ FnMethod name -> show name, " ",+ show (methodApplicability method), " ",+ show (methodPurity method), " ",+ show (methodParams method), " ",+ show (methodReturn method), ">"]++instance HandlesExceptions Method where+ getExceptionHandlers = methodExceptionHandlers++ modifyExceptionHandlers f method =+ method { methodExceptionHandlers = f $ methodExceptionHandlers method }++instance IsClassEntity Method where+ classEntityExtNameSuffix = methodExtName++-- | The C++ code to which a 'Method' is bound.+data MethodImpl =+ RealMethod (FnName String)+ -- ^ The 'Method' is bound to an actual class method.+ | FnMethod (FnName Identifier)+ -- ^ The 'Method' is bound to a wrapper function. When wrapping a method+ -- with another function, this is preferrable to just using a 'Function'+ -- binding because a method will still appear to be part of the class in+ -- foreign bindings.+ deriving (Eq, Show)++-- | How a method is associated to its class. A method may be static, const, or+-- neither (a regular method).+data MethodApplicability = MNormal | MStatic | MConst+ deriving (Bounded, Enum, Eq, Show)++-- | Whether or not a method is const.+data Constness = Nonconst | Const+ deriving (Bounded, Enum, Eq, Show)++-- | Returns the opposite constness value.+constNegate :: Constness -> Constness+constNegate Nonconst = Const+constNegate Const = Nonconst++-- | Whether or not a method is static.+data Staticness = Nonstatic | Static+ deriving (Bounded, Enum, Eq, Show)++-- | Returns the constness of a method, based on its 'methodApplicability'.+methodConst :: Method -> Constness+methodConst method = case methodApplicability method of+ MConst -> Const+ _ -> Nonconst++-- | Returns the staticness of a method, based on its 'methodApplicability'.+methodStatic :: Method -> Staticness+methodStatic method = case methodApplicability method of+ MStatic -> Static+ _ -> Nonstatic++-- | Creates a 'Method' with full generality and manual name specification.+--+-- The result is wrapped in a 'CEMethod'. For an unwrapped value, use+-- 'makeMethod_'.+makeMethod :: IsFnName String name+ => name -- ^ The C++ name of the method.+ -> ExtName -- ^ The external name of the method.+ -> MethodApplicability+ -> Purity+ -> [Type] -- ^ Parameter types.+ -> Type -- ^ Return type.+ -> ClassEntity+makeMethod = (((((CEMethod .) .) .) .) .) . makeMethod_++-- | The unwrapped version of 'makeMethod'.+makeMethod_ :: IsFnName String name+ => name+ -> ExtName+ -> MethodApplicability+ -> Purity+ -> [Type]+ -> Type+ -> Method+makeMethod_ cName extName appl purity paramTypes retType =+ Method (RealMethod $ toFnName cName) extName appl purity paramTypes retType mempty++-- | Creates a 'Method' that is in fact backed by a C++ non-member function (a+-- la 'makeFn'), but appears to be a regular method. This is useful for+-- wrapping a method on the C++ side when its arguments aren't right for binding+-- directly.+--+-- A @this@ pointer parameter is __not__ automatically added to the parameter+-- list for non-static methods created with @makeFnMethod@.+--+-- The result is wrapped in a 'CEMethod'. For an unwrapped value, use+-- 'makeFnMethod_'.+makeFnMethod :: IsFnName Identifier name+ => name+ -> String+ -> MethodApplicability+ -> Purity+ -> [Type]+ -> Type+ -> ClassEntity+makeFnMethod = (((((CEMethod .) .) .) .) .) . makeFnMethod_++-- | The unwrapped version of 'makeFnMethod'.+makeFnMethod_ :: IsFnName Identifier name+ => name+ -> String+ -> MethodApplicability+ -> Purity+ -> [Type]+ -> Type+ -> Method+makeFnMethod_ cName foreignName appl purity paramTypes retType =+ Method (FnMethod $ toFnName cName) (toExtName foreignName)+ appl purity paramTypes retType mempty++-- | This function is internal.+--+-- Creates a method similar to 'makeMethod', but with automatic naming. The+-- method's external name will be @className ++ \"_\" ++ cppMethodName@. If the+-- method name is a 'FnOp' then the 'operatorPreferredExtName' will be appeneded+-- to the class name.+--+-- For creating multiple bindings to a method, see 'makeMethod''.+makeMethod' :: IsFnName String name+ => name -- ^ The C++ name of the method.+ -> MethodApplicability+ -> Purity+ -> [Type] -- ^ Parameter types.+ -> Type -- ^ Return type.+ -> Method+makeMethod' name = makeMethod''' (toFnName name) Nothing++-- | This function is internal.+--+-- Creates a method similar to 'makeMethod'', but with an custom string that+-- will be appended to the class name to form the method's external name. This+-- is useful for making multiple bindings to a method, e.g. for overloading and+-- optional arguments.+makeMethod'' :: IsFnName String name+ => name -- ^ The C++ name of the method.+ -> String -- ^ A foreign name for the method.+ -> MethodApplicability+ -> Purity+ -> [Type] -- ^ Parameter types.+ -> Type -- ^ Return type.+ -> Method+makeMethod'' name foreignName = makeMethod''' (toFnName name) $ Just foreignName++-- | The implementation of 'makeMethod'' and 'makeMethod'''.+makeMethod''' :: FnName String -- ^ The C++ name of the method.+ -> Maybe String -- ^ A foreign name for the method.+ -> MethodApplicability+ -> Purity+ -> [Type] -- ^ Parameter types.+ -> Type -- ^ Return type.+ -> Method+makeMethod''' (FnName "") maybeForeignName _ _ paramTypes retType =+ error $ concat ["makeMethod''': Given an empty method name with foreign name ",+ show maybeForeignName, ", parameter types ", show paramTypes,+ ", and return type ", show retType, "."]+makeMethod''' name (Just "") _ _ paramTypes retType =+ error $ concat ["makeMethod''': Given an empty foreign name with method ",+ show name, ", parameter types ", show paramTypes, ", and return type ",+ show retType, "."]+makeMethod''' name maybeForeignName appl purity paramTypes retType =+ let extName = flip fromMaybe (toExtName <$> maybeForeignName) $ case name of+ FnName s -> toExtName s+ FnOp op -> operatorPreferredExtName op+ in makeMethod_ name extName appl purity paramTypes retType++-- | Creates a nonconst, nonstatic 'Method' for @class::methodName@ and whose+-- external name is @class_methodName@. If the name is an operator, then the+-- 'operatorPreferredExtName' will be used in the external name.+--+-- For creating multiple bindings to a method, see 'mkMethod''.+--+-- The result is wrapped in a 'CEMethod'. For an unwrapped value, use+-- 'mkMethod_'.+mkMethod :: IsFnName String name+ => name -- ^ The C++ name of the method.+ -> [Type] -- ^ Parameter types.+ -> Type -- ^ Return type.+ -> ClassEntity+mkMethod = ((CEMethod .) .) . mkMethod_++-- | The unwrapped version of 'mkMethod'.+mkMethod_ :: IsFnName String name+ => name+ -> [Type]+ -> Type+ -> Method+mkMethod_ name = makeMethod' name MNormal Nonpure++-- | Creates a nonconst, nonstatic 'Method' for method @class::methodName@ and+-- whose external name is @class_methodName@. This enables multiple 'Method's+-- with different foreign names (and hence different external names) to bind to+-- the same method, e.g. to make use of optional arguments or overloading. See+-- 'mkMethod' for a simpler form.+--+-- The result is wrapped in a 'CEMethod'. For an unwrapped value, use+-- 'mkMethod'_'.+mkMethod' :: IsFnName String name+ => name -- ^ The C++ name of the method.+ -> String -- ^ A foreign name for the method.+ -> [Type] -- ^ Parameter types.+ -> Type -- ^ Return type.+ -> ClassEntity+mkMethod' = (((CEMethod .) .) .) . mkMethod'_++-- | The unwrapped version of 'mkMethod''.+mkMethod'_ :: IsFnName String name+ => name+ -> String+ -> [Type]+ -> Type+ -> Method+mkMethod'_ cName foreignName = makeMethod'' cName foreignName MNormal Nonpure++-- | Same as 'mkMethod', but returns an 'MConst' method.+--+-- The result is wrapped in a 'CEMethod'. For an unwrapped value, use+-- 'mkConstMethod_'.+mkConstMethod :: IsFnName String name => name -> [Type] -> Type -> ClassEntity+mkConstMethod = ((CEMethod .) .) . mkConstMethod_++-- | The unwrapped version of 'mkConstMethod'.+mkConstMethod_ :: IsFnName String name => name -> [Type] -> Type -> Method+mkConstMethod_ name = makeMethod' name MConst Nonpure++-- | Same as 'mkMethod'', but returns an 'MConst' method.+--+-- The result is wrapped in a 'CEMethod'. For an unwrapped value, use+-- 'mkConstMethod'_'.+mkConstMethod' :: IsFnName String name => name -> String -> [Type] -> Type -> ClassEntity+mkConstMethod' = (((CEMethod .) .) .) . mkConstMethod'_++-- | The unwrapped version of 'mkConstMethod''.+mkConstMethod'_ :: IsFnName String name => name -> String -> [Type] -> Type -> Method+mkConstMethod'_ cName foreignName = makeMethod'' cName foreignName MConst Nonpure++-- | Same as 'mkMethod', but returns an 'MStatic' method.+--+-- The result is wrapped in a 'CEMethod'. For an unwrapped value, use+-- 'mkStaticMethod_'.+mkStaticMethod :: IsFnName String name => name -> [Type] -> Type -> ClassEntity+mkStaticMethod = ((CEMethod .) .) . mkStaticMethod_++-- | The unwrapped version of 'mkStaticMethod'.+mkStaticMethod_ :: IsFnName String name => name -> [Type] -> Type -> Method+mkStaticMethod_ name = makeMethod' name MStatic Nonpure++-- | Same as 'mkMethod'', but returns an 'MStatic' method.+--+-- The result is wrapped in a 'CEMethod'. For an unwrapped value, use+-- 'mkStaticMethod'_'.+mkStaticMethod' :: IsFnName String name => name -> String -> [Type] -> Type -> ClassEntity+mkStaticMethod' = (((CEMethod .) .) .) . mkStaticMethod'_++-- | The unwrapped version of 'mkStaticMethod''.+mkStaticMethod'_ :: IsFnName String name => name -> String -> [Type] -> Type -> Method+mkStaticMethod'_ cName foreignName = makeMethod'' cName foreignName MStatic Nonpure++-- | A \"property\" getter/setter pair.+newtype Prop = Prop [Method]++-- | Creates a getter/setter binding pair for methods:+--+-- > T foo() const+-- > void setFoo(T)+--+-- The result is wrapped in a 'CEProp'. For an unwrapped value, use+-- 'mkProp_'.+mkProp :: String -> Type -> ClassEntity+mkProp = (CEProp .) . mkProp_++-- | The unwrapped version of 'mkProp'.+mkProp_ :: String -> Type -> Prop+mkProp_ name t =+ let c:cs = name+ setName = 's' : 'e' : 't' : toUpper c : cs+ in Prop [ mkConstMethod_ name [] t+ , mkMethod_ setName [t] Internal_TVoid+ ]++-- | Creates a getter/setter binding pair for static methods:+--+-- > static T foo() const+-- > static void setFoo(T)+mkStaticProp :: String -> Type -> ClassEntity+mkStaticProp = (CEProp .) . mkStaticProp_++-- | The unwrapped version of 'mkStaticProp'.+mkStaticProp_ :: String -> Type -> Prop+mkStaticProp_ name t =+ let c:cs = name+ setName = 's' : 'e' : 't' : toUpper c : cs+ in Prop [ mkStaticMethod_ name [] t+ , mkStaticMethod_ setName [t] Internal_TVoid+ ]++-- | Creates a getter/setter binding pair for boolean methods, where the getter+-- is prefixed with @is@:+--+-- > bool isFoo() const+-- > void setFoo(bool)+--+-- The result is wrapped in a 'CEProp'. For an unwrapped value, use+-- 'mkBoolIsProp_'.+mkBoolIsProp :: String -> ClassEntity+mkBoolIsProp = CEProp . mkBoolIsProp_++-- | The unwrapped version of 'mkBoolIsProp'.+mkBoolIsProp_ :: String -> Prop+mkBoolIsProp_ name =+ let c:cs = name+ name' = toUpper c : cs+ isName = 'i':'s':name'+ setName = 's':'e':'t':name'+ in Prop [ mkConstMethod_ isName [] Internal_TBool+ , mkMethod_ setName [Internal_TBool] Internal_TVoid+ ]++-- | Creates a getter/setter binding pair for boolean methods, where the getter+-- is prefixed with @has@:+--+-- > bool hasFoo() const+-- > void setFoo(bool)+--+-- The result is wrapped in a 'CEProp'. For an unwrapped value, use+-- 'mkBoolHasProp_'.+mkBoolHasProp :: String -> ClassEntity+mkBoolHasProp = CEProp . mkBoolHasProp_++-- | The unwrapped version of 'mkBoolHasProp'.+mkBoolHasProp_ :: String -> Prop+mkBoolHasProp_ name =+ let c:cs = name+ name' = toUpper c : cs+ hasName = 'h':'a':'s':name'+ setName = 's':'e':'t':name'+ in Prop [ mkConstMethod_ hasName [] Internal_TBool+ , mkMethod_ setName [Internal_TBool] Internal_TVoid+ ]++-- | A non-C++ function that can be invoked via a C++ functor or function+-- pointer.+data Callback = Callback+ { callbackExtName :: ExtName+ -- ^ The callback's external name.+ , callbackParams :: [Type]+ -- ^ The callback's parameter types.+ , callbackReturn :: Type+ -- ^ The callback's return type.+ , callbackThrows :: Maybe Bool+ -- ^ Whether the callback supports throwing C++ exceptions from Haskell into+ -- C++ during its execution. When absent, the value is inherited from+ -- 'moduleCallbacksThrow' and 'interfaceCallbacksThrow'.+ , callbackReqs :: Reqs+ -- ^ Requirements for the callback.+ , callbackAddendum :: Addendum+ -- ^ The callback's addendum.+ }++instance Eq Callback where+ (==) = (==) `on` callbackExtName++instance Show Callback where+ show cb =+ concat ["<Callback ", show (callbackExtName cb), " ", show (callbackParams cb), " ",+ show (callbackReturn cb)]++instance HasExtNames Callback where+ getPrimaryExtName = callbackExtName++instance HasReqs Callback where+ getReqs = callbackReqs+ setReqs reqs cb = cb { callbackReqs = reqs }++instance HasAddendum Callback where+ getAddendum = callbackAddendum+ setAddendum addendum cb = cb { callbackAddendum = addendum }++-- | Creates a binding for constructing callbacks into foreign code.+makeCallback :: ExtName+ -> [Type] -- ^ Parameter types.+ -> Type -- ^ Return type.+ -> Callback+makeCallback extName paramTypes retType =+ Callback extName paramTypes retType Nothing mempty mempty++-- | Sets whether a callback supports handling thrown C++ exceptions and passing+-- them into C++.+callbackSetThrows :: Bool -> Callback -> Callback+callbackSetThrows value cb = cb { callbackThrows = Just value }++-- | Each exception class has a unique exception ID.+newtype ExceptionId = ExceptionId+ { getExceptionId :: Int -- ^ Internal.+ } deriving (Eq, Show)++-- | The exception ID that represents the catch-all type.+exceptionCatchAllId :: ExceptionId+exceptionCatchAllId = ExceptionId 1++-- | The lowest exception ID to be used for classes.+exceptionFirstFreeId :: Int+exceptionFirstFreeId = getExceptionId exceptionCatchAllId + 1++-- | Indicates the ability to handle a certain type of C++ exception.+data ExceptionHandler =+ CatchClass Class+ -- ^ Indicates that instances of the given class are handled (including+ -- derived types).+ | CatchAll+ -- ^ Indicates that all C++ exceptions are handled, i.e. @catch (...)@.+ deriving (Eq, Ord)++-- | Represents a list of exception handlers to be used for a body of code.+-- Order is important; a 'CatchAll' will prevent all subsequent handlers from+-- being invoked.+data ExceptionHandlers = ExceptionHandlers+ { exceptionHandlersList :: [ExceptionHandler]+ -- ^ Extracts the list of exception handlers.+ }++instance Monoid ExceptionHandlers where+ mempty = ExceptionHandlers []++ mappend e1 e2 =+ ExceptionHandlers+ (S.toList $ S.fromList $ exceptionHandlersList e1 ++ exceptionHandlersList e2)++-- | Types that can handle exceptions.+class HandlesExceptions a where+ -- | Extracts the exception handlers for an object.+ getExceptionHandlers :: a -> ExceptionHandlers++ -- | Modifies an object's exception handlers with a given function.+ modifyExceptionHandlers :: (ExceptionHandlers -> ExceptionHandlers) -> a -> a++-- | Appends additional exception handlers to an object.+handleExceptions :: HandlesExceptions a => [ExceptionHandler] -> a -> a+handleExceptions classes =+ modifyExceptionHandlers $ mappend mempty {exceptionHandlersList = classes}++-- | A collection of imports for a Haskell module. This is a monoid: import+-- Statements are merged to give the union of imported bindings.+--+-- This structure supports two specific types of imports:+-- - @import Foo (...)@+-- - @import qualified Foo as Bar@+-- Imports with @as@ but without @qualified@, and @qualified@ imports with a+-- spec list, are not supported. This satisfies the needs of the code+-- generator, and keeps the merging logic simple.+newtype HsImportSet = HsImportSet+ { getHsImportSet :: M.Map HsImportKey HsImportSpecs+ -- ^ Returns the import set's internal map from module names to imported+ -- bindings.+ } deriving (Show)++instance Monoid HsImportSet where+ mempty = HsImportSet M.empty++ mappend (HsImportSet m) (HsImportSet m') =+ HsImportSet $ M.unionWith mergeImportSpecs m m'++ mconcat sets =+ HsImportSet $ M.unionsWith mergeImportSpecs $ map getHsImportSet sets++-- | A literal piece of code that will be inserted into a generated source file+-- after the regular binding glue. The 'Monoid' instance concatenates code+-- (actions).+data Addendum = Addendum+ { addendumHaskell :: Haskell.Generator ()+ -- ^ Code to be output into the Haskell binding. May also add imports and+ -- exports.+ }++instance Monoid Addendum where+ mempty = Addendum $ return ()+ mappend (Addendum a) (Addendum b) = Addendum $ a >> b++-- | A typeclass for types that have an addendum.+class HasAddendum a where+ {-# MINIMAL getAddendum, (setAddendum | modifyAddendum) #-}++ -- | Returns an object's addendum.+ getAddendum :: a -> Addendum++ -- | Replaces and object's addendum with another.+ setAddendum :: Addendum -> a -> a+ setAddendum addendum = modifyAddendum $ const addendum++ -- | Modified an object's addendum.+ modifyAddendum :: (Addendum -> Addendum) -> a -> a+ modifyAddendum f x = setAddendum (f $ getAddendum x) x++-- | Adds a Haskell addendum to an object.+addAddendumHaskell :: HasAddendum a => Haskell.Generator () -> a -> a+addAddendumHaskell gen = modifyAddendum $ \addendum ->+ addendum `mappend` mempty { addendumHaskell = gen }++-- | Constructor for an import set.+makeHsImportSet :: M.Map HsImportKey HsImportSpecs -> HsImportSet+makeHsImportSet = HsImportSet++-- | Sets all of the import specifications in an import set to be+-- @{-#SOURCE#-}@ imports.+hsImportSetMakeSource :: HsImportSet -> HsImportSet+hsImportSetMakeSource (HsImportSet m) =+ HsImportSet $ M.map (\specs -> specs { hsImportSource = True }) m++-- | A Haskell module name.+type HsModuleName = String++-- | References an occurrence of an import statement, under which bindings can+-- be imported. Only imported specs under equal 'HsImportKey's may be merged.+data HsImportKey = HsImportKey+ { hsImportModule :: HsModuleName+ , hsImportQualifiedName :: Maybe HsModuleName+ } deriving (Eq, Ord, Show)++-- | A specification of bindings to import from a module. If 'Nothing', then+-- the entire module is imported. If @'Just' 'M.empty'@, then only instances+-- are imported.+data HsImportSpecs = HsImportSpecs+ { getHsImportSpecs :: Maybe (M.Map HsImportName HsImportVal)+ , hsImportSource :: Bool+ } deriving (Show)++-- | Combines two 'HsImportSpecs's into one that imports everything that the two+-- did separately.+mergeImportSpecs :: HsImportSpecs -> HsImportSpecs -> HsImportSpecs+mergeImportSpecs (HsImportSpecs mm s) (HsImportSpecs mm' s') =+ HsImportSpecs (liftM2 mergeMaps mm mm') (s || s')+ where mergeMaps = M.unionWith mergeValues+ mergeValues v v' = case (v, v') of+ (HsImportValAll, _) -> HsImportValAll+ (_, HsImportValAll) -> HsImportValAll+ (HsImportValSome s, HsImportValSome s') -> HsImportValSome $ s ++ s'+ (x@(HsImportValSome _), _) -> x+ (_, x@(HsImportValSome _)) -> x+ (HsImportVal, HsImportVal) -> HsImportVal++-- | An identifier that can be imported from a module. Symbols may be used here+-- when surrounded by parentheses. Examples are @\"fmap\"@ and @\"(++)\"@.+type HsImportName = String++-- | Specifies how a name is imported.+data HsImportVal =+ HsImportVal+ -- ^ The name is imported, and nothing underneath it is.+ | HsImportValSome [HsImportName]+ -- ^ The name is imported, as are specific names underneath it. This is a+ -- @X (a, b, c)@ import.+ | HsImportValAll+ -- ^ The name is imported, along with all names underneath it. This is a @X+ -- (..)@ import.+ deriving (Show)++-- | An import for the entire contents of a Haskell module.+hsWholeModuleImport :: HsModuleName -> HsImportSet+hsWholeModuleImport moduleName =+ HsImportSet $ M.singleton (HsImportKey moduleName Nothing) $+ HsImportSpecs Nothing False++-- | A qualified import of a Haskell module.+hsQualifiedImport :: HsModuleName -> HsModuleName -> HsImportSet+hsQualifiedImport moduleName qualifiedName =+ HsImportSet $ M.singleton (HsImportKey moduleName $ Just qualifiedName) $+ HsImportSpecs Nothing False++-- | An import of a single name from a Haskell module.+hsImport1 :: HsModuleName -> HsImportName -> HsImportSet+hsImport1 moduleName valueName = hsImport1' moduleName valueName HsImportVal++-- | A detailed import of a single name from a Haskell module.+hsImport1' :: HsModuleName -> HsImportName -> HsImportVal -> HsImportSet+hsImport1' moduleName valueName valueType =+ HsImportSet $ M.singleton (HsImportKey moduleName Nothing) $+ HsImportSpecs (Just $ M.singleton valueName valueType) False++-- | An import of multiple names from a Haskell module.+hsImports :: HsModuleName -> [HsImportName] -> HsImportSet+hsImports moduleName names =+ hsImports' moduleName $ map (\name -> (name, HsImportVal)) names++-- | A detailed import of multiple names from a Haskell module.+hsImports' :: HsModuleName -> [(HsImportName, HsImportVal)] -> HsImportSet+hsImports' moduleName values =+ HsImportSet $ M.singleton (HsImportKey moduleName Nothing) $+ HsImportSpecs (Just $ M.fromList values) False++-- | Imports "Data.Bits" qualified as @HoppyDB@.+hsImportForBits :: HsImportSet+hsImportForBits = hsQualifiedImport "Data.Bits" "HoppyDB"++-- | Imports "Control.Exception" qualified as @HoppyCE@.+hsImportForException :: HsImportSet+hsImportForException = hsQualifiedImport "Control.Exception" "HoppyCE"++-- | Imports "Data.Int" qualified as @HoppyDI@.+hsImportForInt :: HsImportSet+hsImportForInt = hsQualifiedImport "Data.Int" "HoppyDI"++-- | Imports "Data.Word" qualified as @HoppyDW@.+hsImportForWord :: HsImportSet+hsImportForWord = hsQualifiedImport "Data.Word" "HoppyDW"++-- | Imports "Foreign" qualified as @HoppyF@.+hsImportForForeign :: HsImportSet+hsImportForForeign = hsQualifiedImport "Foreign" "HoppyF"++-- | Imports "Foreign.C" qualified as @HoppyFC@.+hsImportForForeignC :: HsImportSet+hsImportForForeignC = hsQualifiedImport "Foreign.C" "HoppyFC"++-- | Imports "Data.Map" qualified as @HoppyDM@.+hsImportForMap :: HsImportSet+hsImportForMap = hsQualifiedImport "Data.Map" "HoppyDM"++-- | Imports "Prelude" qualified as @HoppyP@.+hsImportForPrelude :: HsImportSet+hsImportForPrelude = hsQualifiedImport "Prelude" "HoppyP"++-- | Imports "Foreign.Hoppy.Runtime" qualified as @HoppyFHR@.+hsImportForRuntime :: HsImportSet+hsImportForRuntime = hsQualifiedImport "Foreign.Hoppy.Runtime" "HoppyFHR"++-- | Imports "System.Posix.Types" qualified as @HoppySPT@.+hsImportForSystemPosixTypes :: HsImportSet+hsImportForSystemPosixTypes = hsQualifiedImport "System.Posix.Types" "HoppySPT"++-- | Imports "Data.Typeable" qualified as @HoppyDT@.+hsImportForTypeable :: HsImportSet+hsImportForTypeable = hsQualifiedImport "Data.Typeable" "HoppyDT"++-- | Imports "System.IO.Unsafe" qualified as @HoppySIU@.+hsImportForUnsafeIO :: HsImportSet+hsImportForUnsafeIO = hsQualifiedImport "System.IO.Unsafe" "HoppySIU"++-- | Returns an error message indicating that+-- 'Foreign.Hoppy.Generator.Types.objToHeapT' is used where data is going from a+-- foreign language into C++.+objToHeapTWrongDirectionErrorMsg :: Maybe String -> Class -> String+objToHeapTWrongDirectionErrorMsg maybeCaller cls =+ concat [maybe "" (++ ": ") maybeCaller,+ "(TObjToHeap ", show cls, ") cannot be passed into C++",+ maybe "" (const ".") maybeCaller]++-- | Returns an error message indicating that+-- 'Foreign.Hoppy.Generator.Types.objToHeapT' is used where data is going from a+-- foreign language into C++.+tToGcInvalidFormErrorMessage :: Maybe String -> Type -> String+tToGcInvalidFormErrorMessage maybeCaller typeArg =+ concat [maybe "" (++ ": ") maybeCaller,+ "(", show (Internal_TToGc typeArg), ") is an invalid form for TToGc.",+ maybe "" (const ".") maybeCaller]++-- | Returns an error message indicating that+-- 'Foreign.Hoppy.Generator.Types.toGcT' is used where data is going from a+-- foreign language into C++.+toGcTWrongDirectionErrorMsg :: Maybe String -> Type -> String+toGcTWrongDirectionErrorMsg maybeCaller typeArg =+ concat [maybe "" (++ ": ") maybeCaller,+ "(", show (Internal_TToGc typeArg), ") cannot be passed into C++",+ maybe "" (const ".") maybeCaller]
src/Foreign/Hoppy/Generator/Spec/ClassFeature.hs view
@@ -19,6 +19,7 @@ -- | Bindings for common class operations, such as copy construction. module Foreign.Hoppy.Generator.Spec.ClassFeature (+ -- * Class features ClassFeature (..), classAddFeatures, ) where@@ -26,7 +27,7 @@ #if !MIN_VERSION_base(4,8,0) import Data.Monoid (mempty) #endif-import Foreign.Hoppy.Generator.Spec+import Foreign.Hoppy.Generator.Spec.Base import Foreign.Hoppy.Generator.Types -- | Sets of functionality that can be stamped onto a class with@@ -45,41 +46,37 @@ -- Foo::operator==(const Foo&)@. deriving (Eq, Show) -featureContents :: ClassFeature -> Class -> ([Ctor], [Method], Reqs)+featureContents :: ClassFeature -> Class -> ([ClassEntity], Reqs) featureContents feature cls = case feature of Assignable -> assignableContents cls Comparable -> comparableContents cls Copyable -> copyableContents cls Equatable -> equatableContents cls -assignableContents :: Class -> ([Ctor], [Method], Reqs)+assignableContents :: Class -> ([ClassEntity], Reqs) assignableContents cls =- ([],- [ mkMethod OpAssign [refT $ constT $ objT cls] $ refT $ objT cls+ ([ mkMethod OpAssign [refT $ constT $ objT cls] $ refT $ objT cls ], mempty) -comparableContents :: Class -> ([Ctor], [Method], Reqs)+comparableContents :: Class -> ([ClassEntity], Reqs) comparableContents cls =- ([],- [ mkConstMethod OpLt [refT $ constT $ objT cls] boolT+ ([ mkConstMethod OpLt [refT $ constT $ objT cls] boolT , mkConstMethod OpLe [refT $ constT $ objT cls] boolT , mkConstMethod OpGt [refT $ constT $ objT cls] boolT , mkConstMethod OpGe [refT $ constT $ objT cls] boolT ], mempty) -copyableContents :: Class -> ([Ctor], [Method], Reqs)+copyableContents :: Class -> ([ClassEntity], Reqs) copyableContents cls = ([ mkCtor "newCopy" [objT cls] ],- [], mempty) -equatableContents :: Class -> ([Ctor], [Method], Reqs)+equatableContents :: Class -> ([ClassEntity], Reqs) equatableContents cls =- ([],- [ mkConstMethod OpEq [objT cls] boolT+ ([ mkConstMethod OpEq [objT cls] boolT , mkConstMethod OpNe [objT cls] boolT ], mempty)@@ -89,9 +86,8 @@ classAddFeatures :: [ClassFeature] -> Class -> Class classAddFeatures features cls = foldr (\feature cls' ->- let (ctors, methods, reqs) = featureContents feature cls'+ let (entities, reqs) = featureContents feature cls' in addReqs reqs $- classAddCtors ctors $- classAddMethods methods cls')+ classAddEntities entities cls') cls features
+ src/Foreign/Hoppy/Generator/Spec/Conversion.hs view
@@ -0,0 +1,77 @@+-- This file is part of Hoppy.+--+-- Copyright 2015-2016 Bryan Gardiner <bog@khumba.net>+--+-- This program is free software: you can redistribute it and/or modify+-- it under the terms of the GNU Affero General Public License as published by+-- the Free Software Foundation, either version 3 of the License, or+-- (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+-- GNU Affero General Public License for more details.+--+-- You should have received a copy of the GNU Affero General Public License+-- along with this program. If not, see <http://www.gnu.org/licenses/>.++{-# LANGUAGE CPP #-}++-- | The primary data types for specifying C++ interfaces.+--+-- 'Show' instances in this module produce strings of the form @\"\<TypeOfObject+-- nameOfObject otherInfo...\>\"@. They can be used in error messages without+-- specifying a noun separately, i.e. write @show cls@ instead of @\"the class+-- \" ++ show cls@.+module Foreign.Hoppy.Generator.Spec.Conversion (+ -- * Advanced class conversions+ classSetConversionToHeap,+ classSetConversionToGc,+ ) where++#if !MIN_VERSION_base(4,8,0)+import Data.Monoid (mconcat)+#endif+import Foreign.Hoppy.Generator.Language.Haskell+import Foreign.Hoppy.Generator.Spec.Base+import Foreign.Hoppy.Generator.Types++-- | Modifies a class's 'ClassConversion' structure by setting all languages+-- to use 'ClassConversionToHeap'.+classSetConversionToHeap :: Class -> Class+classSetConversionToHeap cls = case classFindCopyCtor cls of+ Just _ ->+ flip classModifyConversion cls $ \c ->+ c { classHaskellConversion = classHaskellConversionToHeap cls+ }+ Nothing -> error $ "classSetConversionToHeap: " ++ show cls ++ " must be copyable."++-- | Modifies a class's 'ClassConversion' structure by setting all languages+-- that support garbage collection to use 'ClassConversionToGc'.+classSetConversionToGc :: Class -> Class+classSetConversionToGc cls = case classFindCopyCtor cls of+ Just _ ->+ flip classModifyConversion cls $ \c ->+ c { classHaskellConversion = classHaskellConversionToGc cls+ }+ Nothing -> error $ "classSetConversionToGc: " ++ show cls ++ " must be copyable."++classHaskellConversionToHeap :: Class -> ClassHaskellConversion+classHaskellConversionToHeap cls =+ ClassHaskellConversion+ { classHaskellConversionType = Just $ cppTypeToHsTypeAndUse HsHsSide $ ptrT $ objT cls+ , classHaskellConversionToCppFn = Nothing+ , classHaskellConversionFromCppFn = Just $ do+ addImports hsImportForRuntime+ sayLn "HoppyFHR.copy"+ }++classHaskellConversionToGc :: Class -> ClassHaskellConversion+classHaskellConversionToGc cls =+ ClassHaskellConversion+ { classHaskellConversionType = Just $ cppTypeToHsTypeAndUse HsHsSide $ ptrT $ objT cls+ , classHaskellConversionToCppFn = Nothing+ , classHaskellConversionFromCppFn = Just $ do+ addImports $ mconcat [hsImport1 "Control.Monad" "(>=>)", hsImportForRuntime]+ sayLn "HoppyFHR.copy >=> HoppyFHR.toGc"+ }
src/Foreign/Hoppy/Generator/Types.hs view
@@ -58,7 +58,7 @@ constT, ) where -import Foreign.Hoppy.Generator.Spec+import Foreign.Hoppy.Generator.Spec.Base -- | C++ @void@, Haskell @()@. voidT = Internal_TVoid