zoukankan      html  css  js  c++  java
  • juggle dsl语法介绍及codegen浅析

    juggle语法规范如下:

    类型:

    bool         ->     in cpp bool
    int          ->     in cpp int64
    float        ->     in cpp double
    string       ->     in cpp std::string
    array        ->     in cpp std::vector
    struct       ->     in cpp object

    函数的定义则同c语言:void rpctest1(int argv1, bool argv2, string argv3, float argv4, array<int> argv5);

    整体的juggle语法如下:

    module juggle{
        void rpctest1(int argv1, bool argv2, string argv3, float argv4, array<int> argv5);
    
        void rpctest2(int argv1, bool argv2, string argv3, float argv4, array<int> argv5);
    
    }

    其中module对应c++中的class,并且在服务器端会被codegen实现为一个单件,无需用户定义句柄有codegen生成对应的create代码。

    codegen会依据module中函数定义,生成如下代码:

    #include <juggle.h>
    
    class juggle: public module{
    public:
      juggle() : module(ch, juggleuuid::UUID()){
           _service_handle->register_module_method(juggle_rpctest1,boost::bind(&juggle::call_rpctest1, this, _1));
           _service_handle->register_module_method(juggle_rpctest2,boost::bind(&juggle::call_rpctest2, this, _1));
        }
    
        ~juggle(){
        }
        virtual void rpctest1(int64_t argv1,bool argv2,std::string argv3,double argv4,std::vector<int64_t>  argv5) = 0;
    
        void call_rpctest1(boost::shared_ptr<channel> ch, boost::shared_ptr<object> v){
            auto argv1 = (*v)["argv1"].asint();
            auto argv2 = (*v)["argv2"].asbool();
            auto argv3 = (*v)["argv3"].asstring();
    	auto argv4 = (*v)["argv4"].asfloat();
    	std::vector<int64_t> argv5;
    	for(int i = 0; i < (*v)["argv5"].size(); i++){
              v.push_back((*v)["argv5"][i].asint());
         }
            auto ret = rpctest1(argv1, argv2, argv3, argv4, argv5);
    	boost::shared_ptr<object> r = boost::make_shared<object>();
    	(*r)["suuid"] = (*v)["suuid"];
    	(*r)["method"] = (*value)["method"];
    	 
            (*r)["ret"] = ret;
    	ch->push(r);
        }
    	 
        virtual void rpctest2(int64_t argv1,bool argv2,std::string argv3,double argv4,std::vector<int64_t>  argv5) = 0;
    	 
        void call_rpctest2(boost::shared_ptr<channel> ch, boost::shared_ptr<object> v){
            auto argv1 = (*v)["argv1"].asint();
            auto argv2 = (*v)["argv2"].asbool();
            auto argv3 = (*v)["argv3"].asstring();
    	auto argv4 = (*v)["argv4"].asfloat();
    	std::vector<int64_t> argv5;
    	for(int i = 0; i < (*v)["argv5"].size(); i++){
                v.push_back((*v)["argv5"][i].asint());
    	}
    	auto ret = rpctest2(argv1, argv2, argv3, argv4, argv5);
    	boost::shared_ptr<object> r = boost::make_shared<object>();
    	(*r)["suuid"] = (*v)["suuid"];
    	(*r)["method"] = (*value)["method"];
    
    	(*r)["ret"] = ret;
    	ch->push(r);
      } };

    可以看到,codegen实现了网络层面的消息响应、协议pack/unpack以及对rpc函数的调用,返回值封包发送的代码。用户只需要继承module并实现对应的rpc函数。

    其中对于obejct的定义见 https://github.com/NetEase/fossilizid/blob/master/juggle/interface/object.h

    我定义了一个纯虚类,用于规范一个通信协议参数入栈和访问的接口

    然后定义了一个channel https://github.com/NetEase/fossilizid/blob/master/juggle/interface/channel.h

    用于规范通信的接口

    对于通信而言,push/pop是非常上层的一个接口,但是这样的设计目的在于提供一个宽泛的抽象,这里通信的可以是一个消息队列,一个基于共享内存的本地跨进程通信,同样也可以是socket。

    btw:另一个原因是我自己封装的网络库的长相是这样的 https://github.com/NetEase/fossilizid/tree/master/remoteq, remotoq提供的通信句柄正是channel,而提供的访问接口则是push/pop。并且通过模板参数配置了网络协议的pack/unpack。我这么实现是为了方便代码复用。

    然后是对dsl语言的编译:

    juggle的语法定义的关键字,除了变量类型,就只有module和struct。对于一个module的定义,在module之后是是这个module的命名,之后是'{'表示此module定义开始,至'}'表示此module定义结束。module的分析代码如下:

    class module(object):
        def __init__(self):
            self.keyworld = ''
            self.name = ''
            self.module = []
            self.machine = None
     
            def push(self, ch):
                if ch == '}':
                    self.machine = None
    	        return True
     
                if self.machine is not None:
                    if self.machine.push(ch):
                        self.module.append(self.machine.func)
    	            self.machine.clear()
                else:
                    if ch == '{':
                        self.name = deleteNoneSpacelstrip(self.keyworld)
                        self.keyworld = ''
                        self.machine = func()
                        return False
    
                self.keyworld += ch
    
                return False
                               

    在检索到'{'之后开始对module定义的分析,至'}'结束这个module的定义。

    因为dsl语言本身的特性,module中只有函数定义,struct中变量定义。所以在module中,只需要分析函数定义。

    self.machine = func(),对函数分析器的定义如下:

    class func(object):
        def __init__(self):
            self.keyworld = ''
            self.func = []
            self.argvtuple = None
    
        def clear(self):
            self.keyworld = ''
            self.func = []
            self.argvtuple = None
    
        def push(self, ch):
            if ch == ' ' or ch == '':
                self.keyworld = deleteNoneSpacelstrip(self.keyworld)
                if self.keyworld != '':
                    if self.argvtuple is None:
                        self.func.append(self.keyworld)
                    else:
                        self.argvtuple.append(self.keyworld)
                    self.keyworld = ''
                    return False
    
            if ch == ',':
                if self.keyworld != '':
                    self.argvtuple.append(deleteNoneSpacelstrip(self.keyworld))
                    self.func.append(self.argvtuple)
                    self.keyworld = ''
                    self.argvtuple = []
                    return False
    
            if ch == '(':
                self.func.append(deleteNoneSpacelstrip(self.keyworld))
                self.argvtuple = []
                self.keyworld = ''
                return False
    
            if ch == ')':
                if self.keyworld != '':
                    self.argvtuple.append(deleteNoneSpacelstrip(self.keyworld))
                    self.func.append(self.argvtuple)
                    self.keyworld = ''
                    return False
    
            if ch == ';':
                return True
    
            self.keyworld += ch
    
            return False

    因为无需考虑其他的语法要素的区分,函数定义的分析只需要考虑依次提取返回值类型,函数名,(,参数定义,),;函数定义结束。符号表示如下:

    rettype funcname(argvlist...);

    之后是对struct的分析,与module类似,在struct之后的既是struct name的定义,之后是'{'开始struct的定义,之'}'结束此struct的定义,代码如下:

    class struct(object):
        def __init__(self):
            self.keyworld = ''
            self.name = ''
            self.struct = []
            self.argvdef = []
    
        def push(self, ch):
            if ch == ' ' or ch == '':
                if self.keyworld != '':
                    self.argvdef.append(self.keyworld)
    
            if ch == '{':
                self.name = deleteNoneSpacelstrip(self.keyworld)
                self.keyworld = ''
                return False
    
            if ch == ';':
                self.struct.append(self.argvdef)
                self.argvdef = []
    
            if ch == '}':
                return True
    
            self.keyworld += ch
    
            return False

    对于struct中的变量定义,同样以'type name;'的方式直接分割。

    之后是对jeggle文件的整体分析:

    class statemachine(object):
        Moduledefine = 0
        Funcdefine = 1
    
        def __init__(self):
            self.keyworld = ''
            self.module = {}
            self.struct = {}
            self.machine = None
    
        def push(self, ch):
            if self.machine is not None:
                if self.machine.push(ch):
                    if isinstance(self.machine, module):
                        self.module[self.machine.name] = self.machine.module
                        self.machine = None
                    if isinstance(self.machine, struct):
                        self.struct[self.machine.name] = self.machine.struct
                        self.machine = None
            else:
                self.keyworld += ch
    
                if self.keyworld == 'module':
                    self.machine = module()
                    self.keyworld = ''
    
                if self.keyworld == 'struct':
                    self.machine = struct()
                    self.keyworld = ''
    
        def getmodule(self):
            return self.module
    
        def getstruct(self):
            return self.struct
    
        def syntaxanalysis(self, genfilestr):
            for str in genfilestr:
                for ch in str:
                    self.push(ch)

    检索到module和struct之后分别进入对应分支。

    之后是codegen的代码见:

    https://github.com/NetEase/fossilizid/blob/master/juggle/rpcmake/codegen.py

    和之前的http://www.cnblogs.com/qianqians/p/4184441.html对比可以看到精简之后的dsl语法要方便分析许多,实作代码也要清晰不少。

    和之前为c++添加rpccall的计划相比,现在的dsl语言便于提供其他语言的扩展,同时编译器也会好些很多。

    btw:现在的dsl语法非常之强类型,尤其是带模板参数的array<int>,有用过protobuf和thrift的同学应该可以对比去其中的区别,希望大家能对如何设计一个好用的dsl展开讨论。

  • 相关阅读:
    mongoGridFS
    ModelClientValidationRule同时存在多个位置解决方案
    C# 使用 SocketAsyncEventArgs 实现UdpSocket系列
    如何把握机会
    c#异步socket
    服务器×××上的MSDTC不可用解决办法
    分享.net常见的内存泄露及解决方法
    如何处理几十万条并发数据
    iphone开发,模拟器生成文件路径
    Iphone隐藏和显示TabBar的方法
  • 原文地址:https://www.cnblogs.com/qianqians/p/4255034.html
Copyright © 2011-2022 走看看