-- This file is part of Hoppy. -- -- Copyright 2015-2020 Bryan Gardiner -- -- 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 . {-# LANGUAGE CPP #-} -- | Internal portion of the C++ code generator. module Foreign.Hoppy.Generator.Language.Cpp.Internal ( Generation, generate, generatedFiles, ) where #if !MIN_VERSION_base(4,8,0) import Control.Applicative ((<$>)) #endif import Control.Monad (when) import Control.Monad.Writer (execWriterT, tell) import Control.Monad.Trans (lift) import Data.Foldable (forM_) #if !MIN_VERSION_base(4,8,0) import Data.Functor ((<$)) #endif import qualified Data.Map as M #if !MIN_VERSION_base(4,8,0) import Data.Monoid (mappend, mconcat, mempty) #endif import Foreign.Hoppy.Generator.Common import Foreign.Hoppy.Generator.Language.Cpp import Foreign.Hoppy.Generator.Spec import Foreign.Hoppy.Generator.Types -- | The in-memory result of generating C++ code for an interface. newtype 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 C++ generation root. } -- | Runs the C++ code generator against an interface. generate :: Interface -> Either ErrorMsg Generation generate iface = fmap (Generation . M.fromList) $ execWriterT $ forM_ (M.elems $ interfaceModules iface) $ \m -> do let headerGuard = concat ["HOPPY_MODULE_", interfaceName iface, "_", moduleName m] header <- lift $ execGenerator iface m (Just headerGuard) sayModuleHeader tell [(moduleHppPath m, header)] source <- lift $ execGenerator iface m Nothing sayModuleSource tell [(moduleCppPath m, source)] sayModuleHeader :: Generator () sayModuleHeader = do m <- askModule addReqsM $ moduleReqs m mapM_ (sayExportCpp SayHeader) $ M.elems $ moduleExports m iface <- askInterface when (interfaceExceptionSupportModule iface == Just m) $ sayExceptionSupport False sayModuleSource :: Generator () sayModuleSource = do m <- askModule addInclude $ includeLocal $ moduleHppPath m mapM_ (sayExportCpp SaySource) $ M.elems $ moduleExports m iface <- askInterface when (interfaceExceptionSupportModule iface == Just m) $ sayExceptionSupport True -- | 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"]