C++中消息自动派发之二 About IDL解析器

  前一篇blog中讲了如何在C++中实现消息的自动派发,而关键点在于如何实现通过IDL文件自动生成msg_dispatcher模板类。有几个网友提醒我idl解析器会比较难写,事实却是如此。我第一个版本的idl解析器本来只是想做demo只用。花了一个晚上时间拼凑了几个python函数,msg_dispatcher类倒是能生成,但解析器的代码太混乱了,简直毫无结构可言。说实话,这个消息自动派发框架我还要深入的开发、扩展、优化,所以还是像模像样的搞一个解析器吧。于是果断扔掉第一版本的解析器代码,重新实现之。仍然采用Python实现,目前只完成了cpp代码生成器,并且只能支持消息体的decode,不支持encode,语法报错机制也没有加入。随未完美,但是毕竟开了个好头,这里讲一下解析器的实现。

完整示例代码 svn co http://ffown.googlecode.com/svn/trunk/fflib/lib/generator/

示例idl 文件:svn co http://ffown.googlecode.com/svn/trunk/fflib/lib/generator/example.idl

struct student_t
{
    struct book_t
    {
        int16       pages;
    };
   string           age;
};

 1. 词法分析

    用Python的好处是解析字符串非常方便,首先要把idl源文件解析成单个的单词。我定义了一个src_parser_t类实现此功能。解析分如下几步:

  1> 读入idl 源文件内容

  2> 把源文件内容分隔成单个行,只需将file_content_str.split('\n')即可。

  3> 在把每行按空格分隔成单个单词 split(' ')即可

  4> 如果有单词最后一个字符有分号去掉。

解析代码如下(只有80行):

from pylib.inc import *

class src_parser_t:
    def __init__(self, file):
        self.file = file
        self.struct_def_mgr = struct_def_mgr_t()
        self.file_content = ''
        self.all_words    = []
        f = open(file)
        self.file_content = f.read()
        f.close()
    def get_struct_def_mgr(self):
        return self.struct_def_mgr
    def parse_to_words(self):
        all_line = self.file_content.split('\n')
        for line in all_line:
            words = line.split(' ')
            for w in words:
                w = w.strip()
                if w != '':
                    self.all_words.append(w)

    def build_struct_relation(self):
        struct_stack = []
        index = 0
        while index < len(self.all_words):
            if len(struct_stack) < 1:
                struct_stack.append(self.struct_def_mgr)

            parent_struct = struct_stack[len(struct_stack) - 1]
            cur_word = self.all_words[index]
            if cur_word == 'struct':
                struct_def = struct_def_t(self.all_words[index + 1])
                parent_struct.add_struct(struct_def)
                struct_stack.append(struct_def)
                index = index + 1
            elif cur_word == '}' or cur_word == '};':
                struct_stack.pop()
            elif cur_word == 'int8' or cur_word == 'int16' or cur_word == 'int32' or \
               cur_word == 'float' or cur_word == 'string':
                field_name = self.all_words[index + 1].split(';')[0]
                field = field_def_t(field_name, cur_word, '', '')
                parent_struct.add_field(field)
                index = index + 1
            else:
                if -1 == cur_word.find('dictionary') and  -1 == cur_word.find('{') and -1 == cur_word.find('array') :
                    field_name = self.all_words[index + 1].split(';')[0]
                    field = field_def_t(field_name, cur_word, '', '')
                    parent_struct.add_field(field)
                    index = index + 1
                else:
                    field_type = ''
                    field_name = ''
                    key_type   = ''
                    val_type   = ''
                    if -1 != cur_word.find('array'):
                        field_name = self.all_words[index + 1].split(';')[0]
                        word_split = cur_word.split('<')
                        field_type = word_split[0]
                        key_type = word_split[1].split('>')[0]
                        field = field_def_t(field_name, field_type, key_type, '')
                        parent_struct.add_field(field)
                        index = index + 1
                    elif -1 != cur_word.find('dictionary'):
                        field_name = self.all_words[index + 1].split(';')[0]
                        word_split = cur_word.split('<')
                        field_type = word_split[0]
                        key_val_type = word_split[1].split('>')
                        key_type = key_val_type[0].split(',')[0]
                        val_type = key_val_type[0].split(',')[1]
                        field = field_def_t(field_name, field_type, key_type, val_type)
                        parent_struct.add_field(field)
                        index = index + 1
            index = index + 1
            
    def exe(self):
        self.parse_to_words()
        self.build_struct_relation()
        

2. 语法分析

  idl 文件语法规则非常简单,遍历所有单词,依次做如下判断:

  1> 如果当前单词为struct, 那么下一个单词即为新的消息体名称,当然也有可能是子消息体,无需担心,只需将新创建的消息体对象添加到特定栈的的栈顶struct对象中,默认struct_def_mgr存在于栈中。并把新的消息体压入栈中。

  2> 如果为int/string/float/array/dictionary,那么下一个单词即为消息体的字段名称。把新字段对象add到栈顶的struct_def对象中

  3> 遇到‘}’ 代表当前struct的解析完成。pop 弹出栈顶struct_def 对象。

  4> 其他字段忽略

3. 消息体结构管理

  1> field_def_t 描述消息体字段信息,包括字段的名称、类型、key_type、val_type、父消息体对象。如array<int>那么key_type为int,如果dictionary<int,string> 那么key_type为int, val_type为string

  2> struct_def_t 描述单个消息体的信息,包括消息体名称、子消息集合、字段对象集合。

  3> struct_def_mgr_t 维护所有的消息体集合。

代码即注释:

class field_def_t:
    def __init__(self, name, type, key_type, val_type_):
        self.name       = name
        self.parent     = None
        self.type       = type
        self.key_type   = key_type
        self.val_type   = val_type_
    def get_name(self):
        return self.name
    def get_parent(self):
        return self.parent
    def set_parent(self, p):
        self.parent = p
    def get_type(self):
        return self.type
    def get_key_type(self):
        return self.key_type
    def get_val_type(self):
        return self.val_type
    def dump(self, prefix = ''):
        print(prefix, self.name, self.type, self.key_type, self.val_type)

class struct_def_t:
    def __init__(self, name, parent = None):
        self.name       = name
        self.parent     = parent
        self.all_fields = {}
        self.sub_struct = []

    def get_name(self):
        return self.name
    def get_parent(self):
        return self.parent
    def set_parent(self, parent):
        self.parent = parent    
    def add_field(self, field_def_):
        self.all_fields[field_def_.get_name()] = field_def_
        field_def_.set_parent(self)
    def add_struct(self, struct_def_):
        self.sub_struct.append(struct_def_)
        struct_def_.set_parent(self)
    def get_all_struct(self):
        return self.sub_struct
    def get_all_field(self):
         return self.all_fields
    def get_parent(self):
        return self.parent
    def has_field(self, name):
        if None == self.all_fields.get(name):
            return False
        return True
    def dump(self, prefix = ''):
        print(prefix, self.name, 'include struct:')
        for struct in self.sub_struct:
            struct.dump(prefix + "")
        print(prefix, self.name, "include fields:")
        for field in self.all_fields:
            self.all_fields[field].dump(prefix + "")

        
class struct_def_mgr_t:
    def __init__(self):
        self.all_struct = {}
    def get_name(self):
        return ''
    def add_struct(self, struct_def_):
        self.all_struct[struct_def_.get_name()] = struct_def_
        struct_def_.set_parent(None)
    def get_all_struct(self):
        return self.all_struct
    def get_struct(self, name):
        return self.all_struct[name]
    def get_parent(self):
        return ''
    def dump(self):
        for name in self.all_struct:
            self.all_struct[name].dump()

4. 中间代码生成

  代码是由code_generator_t 类实现的。考虑到未来需要支持多语言,中间代码生成采用策略模式。当前只实现了cpp的code_generator。如果要增强其他语言,只需再编写一个特定的code_generator_t类即可。

5. TODO

  1> struct 消息体只支持decode from json,还不能支持encode to json,也不能支持decode from Bin or encode to Bin。

  2> 语法报错不够友好。

  3> 多语言支持。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏ccylovehs

JavaScript异步编程

平时开发经常会用到js异步编程,由于前端展示页面都是基于网络机顶盒(IPTV的一般性能不太好,OTT较好),目前公司主要采取的异步编程的方式有setTimeou...

562
来自专栏编程

Golang中defer 的五个坑-第三部分

译注:全文总共有四篇,本文为同系列文章的第三篇 本文将侧重于讲解使用 defer 的一些技巧 如果你对 defer 的基本操作还没有清晰的认识,请先阅读这篇文章...

1925
来自专栏张善友的专栏

MongoDB 聚合管道(Aggregation Pipeline)

管道概念 POSIX多线程的使用方式中, 有一种很重要的方式-----流水线(亦称为“管道”)方式,“数据元素”流串行地被一组线程按顺序执行。它的使用架构可参考...

17510
来自专栏阮一峰的网络日志

Node 定时器详解

JavaScript 是单线程运行,异步操作特别重要。 只要用到引擎之外的功能,就需要跟外部交互,从而形成异步操作。由于异步操作实在太多,JavaScript ...

3295
来自专栏滕先生的博客

runtime官方文档翻译版本通过OC源代码通过NSObject中定义的方法直接调用运行时的函数消息传递机制使用隐藏参数获取方法地址动态方法解析动态加载消息转发转发和多继承代理对象转发和继承类型编码声

2657
来自专栏JetpropelledSnake

Python面试题之回调函数

编程分为两类:系统编程(system programming)和应用编程(application programming)。所谓系统编程,简单来说,就是编写库;...

662
来自专栏安恒网络空间安全讲武堂

Thinkphp5实现安全数据库操作以及部分运行流程分析

文章的灵感来自于此文ThinkPHP3.2.3框架实现安全数据库操作分→https://xz.aliyun.com/t/79

1112
来自专栏GreenLeaves

C# Encoding

之前做公司项目的时候,对于C#编码这块总是一知半解,所以打算通过这篇笔记对C#编码(Encoding)进行彻底的扫盲,关于编码和字符集的基础知识,请参考字符集和...

2277
来自专栏祝威廉

Spark Tungsten in-heap / off-heap 内存管理机制前言

分析方式基本是自下而上,也就是我们分析的线路会从最基础内存申请到上层的使用。我们假设你对sun.misc.Unsafe 的API有一些最基本的了解。

753
来自专栏Java帮帮-微信公众号-技术文章全总结

JavaWeb12-JSP, EL表达式,JSTL标签

JSP & EL & JSTL 一.JSP 1. jsp回顾 jsp作用 jsp全称java server pages(java服务器页面),本质是一个serv...

3579

扫描关注云+社区