{-# LANGUAGE QuasiQuotes, RecordWildCards, OverloadedStrings #-} module Language.MessagePack.IDL.CodeGen.Cpp ( Config(..), generate, ) where import Data.Char import Data.List import qualified Data.Text as T import qualified Data.Text.Lazy as LT import qualified Data.Text.Lazy.IO as LT import System.FilePath import Text.Shakespeare.Text import Language.MessagePack.IDL.Syntax data Config = Config { configFilePath :: FilePath , configNameSpace :: String , configPFICommon :: Bool } deriving (Show, Eq) generate:: Config -> Spec -> IO () generate Config {..} spec = do let name = takeBaseName configFilePath once = map toUpper name ns = LT.splitOn "::" $ LT.pack configNameSpace typeHeader | configPFICommon = [lt|#include |] | otherwise = [lt|#include |] serverHeader | configPFICommon = [lt|#include #include |] | otherwise = [lt|#include |] clientHeader | configPFICommon = [lt|#include |] | otherwise = [lt|#include |] LT.writeFile (name ++ "_types.hpp") $ templ configFilePath ns once "TYPES" [lt| #include #include #include #include #include #{typeHeader} #{genNameSpace ns $ LT.concat $ map (genTypeDecl name) spec } |] LT.writeFile (name ++ "_server.hpp") $ templ configFilePath (snoc ns "server") once "SERVER" [lt| #include "#{name}_types.hpp" #{serverHeader} #{genNameSpace (snoc ns "server") $ LT.concat $ map (genServer configPFICommon) spec} |] LT.writeFile (name ++ "_client.hpp") $ templ configFilePath (snoc ns "client") once "CLIENT" [lt| #include "#{name}_types.hpp" #{clientHeader} #{genNameSpace (snoc ns "client") $ LT.concat $ map (genClient configPFICommon) spec} |] genTypeDecl :: String -> Decl -> LT.Text genTypeDecl _ MPMessage {..} = genMsg msgName msgFields False genTypeDecl _ MPException {..} = genMsg excName excFields True genTypeDecl _ MPType { .. } = [lt| typedef #{genType tyType} #{tyName}; |] genTypeDecl _ _ = "" genMsg name flds isExc = let fields = map f flds fs = map (maybe undefined fldName) $ sortField flds in [lt| struct #{name}#{e} { public: #{destructor} MSGPACK_DEFINE(#{T.intercalate ", " fs}); #{LT.concat fields} }; |] where e = if isExc then [lt| : public std::exception|] else "" destructor = if isExc then [lt|~#{name}() throw() {} |] else "" f Field {..} = [lt| #{genType fldType} #{fldName};|] sortField flds = flip map [0 .. maximum $ [-1] ++ map fldId flds] $ \ix -> find ((==ix). fldId) flds genServer :: Bool -> Decl -> LT.Text genServer False MPService {..} = [lt| template class #{serviceName} : public msgpack::rpc::server::base { public: void dispatch(msgpack::rpc::request req) { try { std::string method; req.method().convert(&method); #{LT.concat $ map genMethodDispatch serviceMethods} } catch (const msgpack::type_error& e) { req.error(msgpack::rpc::ARGUMENT_ERROR); } catch (const std::exception& e) { req.error(std::string(e.what())); } } }; |] where genMethodDispatch Function {..} = -- TODO: FIX IT! let typs = map (genRetType . maybe Nothing (Just . fldType)) $ sortField methodArgs in let params = map g methodArgs in case params of [] -> [lt| if (method == "#{methodName}") { req.result<#{genRetType methodRetType} >(static_cast(this)->#{methodName}()); return; } |] _ -> [lt| if (method == "#{methodName}") { msgpack::type::tuple<#{LT.intercalate ", " typs} > params; req.params().convert(¶ms); req.result<#{genRetType methodRetType} >(static_cast(this)->#{methodName}(#{LT.intercalate ", " params})); return; } |] where g fld = [lt|params.get<#{show $ fldId fld}>()|] genMethodDispatch _ = "" genServer True MPService {..} = [lt| template class #{serviceName} : public pfi::network::mprpc::rpc_server { public: #{serviceName}(double timeout_sec): rpc_server(timeout_sec) { #{LT.concat $ map genSetMethod serviceMethods} } }; |] where genSetMethod Function {..} = let typs = map (genRetType . maybe Nothing (Just . fldType)) $ sortField methodArgs sign = [lt|#{genRetType methodRetType}(#{LT.intercalate ", " typs})|] phs = LT.concat $ [[lt|, pfi::lang::_#{show ix}|] | ix <- [1 .. length (typs)]] in [lt| rpc_server::add<#{sign} >("#{methodName}", pfi::lang::bind(&Impl::#{methodName}, static_cast(this)#{phs}));|] genSetMethod _ = "" genServer _ _ = "" genClient :: Bool -> Decl -> LT.Text genClient False MPService {..} = [lt| class #{serviceName} { public: #{serviceName}(const std::string &host, uint64_t port) : c_(host, port) {} #{LT.concat $ map genMethodCall serviceMethods} private: msgpack::rpc::client c_; }; |] where genMethodCall Function {..} = let args = LT.intercalate ", " $ map arg methodArgs in let vals = LT.concat $ map val methodArgs in case methodRetType of Nothing -> [lt| void #{methodName}(#{args}) { c_.call("#{methodName}"#{vals}); } |] Just typ -> [lt| #{genType typ} #{methodName}(#{args}) { return c_.call("#{methodName}"#{vals}).get<#{genType typ} >(); } |] where arg Field {..} = [lt|#{genType fldType} #{fldName}|] val Field {..} = [lt|, #{fldName}|] genMethodCall _ = "" genClient True MPService {..} = [lt| class #{serviceName} : public pfi::network::mprpc::rpc_client { public: #{serviceName}(const std::string &host, uint64_t port, double timeout_sec) : rpc_client(host, port, timeout_sec) {} #{LT.concat $ map genMethodCall serviceMethods} private: }; |] where genMethodCall Function {..} = let typs = map (genRetType . maybe Nothing (\f -> Just (fldType f))) $ sortField methodArgs sign = [lt|#{genRetType methodRetType}(#{LT.intercalate ", " typs})|] args = LT.intercalate ", " $ map arg methodArgs vals = LT.intercalate ", " $ map val methodArgs in case methodRetType of Nothing -> [lt| void #{methodName}(#{args}) { call<#{sign}>("#{methodName}")(#{vals}); } |] Just t -> [lt| #{genType t} #{methodName}(#{args}) { return call<#{sign}>("#{methodName}")(#{vals}); } |] where arg Field {..} = [lt|#{genType fldType} #{fldName}|] val Field {..} = [lt|#{fldName}|] genMethodCall _ = "" genClient _ _ = "" genType :: Type -> LT.Text genType (TInt sign bits) = let base = if sign then "int" else "uint" :: LT.Text in [lt|#{base}#{show bits}_t|] genType (TFloat False) = [lt|float|] genType (TFloat True) = [lt|double|] genType TBool = [lt|bool|] genType TRaw = [lt|std::string|] genType TString = [lt|std::string|] genType (TList typ) = [lt|std::vector<#{genType typ} >|] genType (TMap typ1 typ2) = [lt|std::map<#{genType typ1}, #{genType typ2} >|] genType (TUserDef className params) = [lt|#{className}|] genType (TTuple ts) = -- TODO: FIX foldr1 (\t1 t2 -> [lt|std::pair<#{t1}, #{t2} >|]) $ map genType ts genType TObject = [lt|msgpack::object|] genRetType :: Maybe Type -> LT.Text genRetType Nothing = [lt|void|] genRetType (Just t) = genType t templ :: FilePath -> [LT.Text] -> String -> String -> LT.Text -> LT.Text templ filepath ns once name content = [lt| // This file is auto-generated from #{filepath} // *** DO NOT EDIT *** #ifndef #{namespace}_#{once}_#{name}_HPP_ #define #{namespace}_#{once}_#{name}_HPP_ #{content} #endif // #{namespace}_#{once}_#{name}_HPP_ |] where namespace = LT.intercalate "_" $ map LT.toUpper ns genNameSpace :: [LT.Text] -> LT.Text -> LT.Text genNameSpace namespace content = f namespace where f [] = [lt|#{content}|] f (n:ns) = [lt| namespace #{n} { #{f ns} } // namespace #{n} |] snoc xs x = xs ++ [x]