前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >IDL编译器实现入门

IDL编译器实现入门

作者头像
一见
发布2018-08-10 17:28:30
2.7K0
发布2018-08-10 17:28:30
举报
文章被收录于专栏:蓝天蓝天

IDL编译器实现入门.pdf

1. 前言

本文不对词法和语法、以及flex和bison进行介绍,如有需要,可以阅读《RPC的实现》。本文试图用直接的方式,以最短的篇幅介绍一个最简单的IDL编译器实现。

2. 目标(example.idl)

本文介绍的IDL编译器,能够解析如下所示的IDL文件,但限于篇幅,生成C++代码部分省略掉,只介绍到对下述内容的解析,以便控制篇幅和复杂度。

下面这个表格为示例IDL文件example.idl的内容:

代码语言:javascript
复制
// Author: yijian

// Date: 2015/01/20

// 运行示例:./idl_compiler < example.idl

request

{

    optional aaa: int16(0, 2015);

    required bbb: string(0, 32);

}

 

response

{

    required xxx: int32;

    required zzz: string;

}

运行效果如下:

代码语言:javascript
复制
request ==>

        int16 aaa (0, 2015)

        string bbb (0, 32)

response ==>

        int32 xxx (, )

        string zzz (, )

3. 功能

request

表示为请求

response

表示为响应

optional

表示字段是可选的

required

表示字段必须存在

int16

支持的数据类型,还包括string、int32和int64等

(0, 2015)

这个也是可选的,表示取值范围,对于整数则表示最小值和最大值,对于字符串则表示最小长度和最大长度

aaa

为字段名称,其它如bbb、xxx和zzz等也是字段名称

4. 文件构成

文件名

文件说明

example.idl

演示用IDL文件

mooon.l

词法文件

mooon.y

语法文件

service_info.h

定义存储元数据的结构

service_info.cpp

对service_info.h的实现

main.cpp

main()函数所在文件,调用解析器,并生成目标代码(本文为简单,并没有生成目标代码,而只是在屏幕上输出)

Makefile

编译脚本,成功后生成编译工具idl_compiler

5. flex词法文件(mooon.l)

定义example.idl的词法文件:

代码语言:javascript
复制
// Author: yijian

 // Date: 2015/01/20

%option yylineno

 // flex mooon.l

 

%{

 

#include "mooon.tab.h" // bison编译mooon.y时生成的文件

#include "service_info.h"

 

// 定义保留关键词

void reserved_keyword(const char* keyword)

{

    yyerror("Cannot use reserved language keyword: \"%s\"\n", keyword);

    exit(1);

}

 

// 整数大小溢出判断

void integer_overflow(const char* text)

{

    yyerror("This integer is too big: \"%s\"\n", text);

    exit(1);

}

 

// 无效的单词

void unexpected_token(const char* text)

{

    yyerror("Unexpected token: \"%s\"\n", text);

    exit(1);

}

 

%}

 

 // 下面的定义,类似于C/C++语言中的宏定义,另外请注意本区域内的注释不能顶格

intconstant   ([+-]?[0-9]+)

dubconstant   ([+-]?[0-9]*(\.[0-9]+)?([eE][+-]?[0-9]+)?)

identifier    ([a-zA-Z_][\.a-zA-Z_0-9]*)

whitespace    ([ \t\r\n]*)

sillycomm     ("/*""*"*"*/")

multicomm     ("/*"[^*]"/"*([^*/]|[^*]"/"|"*"[^/])*"*"*"*/")

comment       ("//"[^\n]*)

unixcomment   ("#"[^\n]*)

symbol        ([:;\,\{\}\(\)\=<>\[\]])

 

%%

 

 // 下面是模式,以及模式对应的动作,也请注意本区域内的注释不能顶格

{whitespace}         { }

{sillycomm}          { }

{multicomm}          { }

{comment}            { }

{unixcomment}        { }

 

"request"            { yylval.sval = strdup(yytext); return tok_request;    }

"response"           { yylval.sval = strdup(yytext); return tok_response;   }

"string"             { yylval.sval = strdup(yytext); return tok_string;     }

"bool"               { yylval.sval = strdup(yytext); return tok_bool;       }

"int16"              { yylval.sval = strdup(yytext); return tok_int16;      }

"int32"              { yylval.sval = strdup(yytext); return tok_int32;      }

"int64"              { yylval.sval = strdup(yytext); return tok_int64;      }

"double"             { yylval.sval = strdup(yytext); return tok_double;     }

"required"           { yylval.sval = strdup(yytext); return tok_required;   }

"optional"           { yylval.sval = strdup(yytext); return tok_optional;   }

 

{symbol}             { return yytext[0];                                    }

 

"service"            { reserved_keyword(yytext); }

"struct"             { reserved_keyword(yytext); }

 

{intconstant}        {

    errno = 0;

    yylval.iconst = strtoll(yytext, NULL, 10);

 

    if (ERANGE == errno)

    {

        integer_overflow(yytext);

    }

   

    return tok_int_constant;

}

 

{dubconstant}        {

    yylval.dconst = atof(yytext);

    return tok_dub_constant;

}

 

{identifier}         { yylval.sval = strdup(yytext); return tok_identifier; }

.                    { unexpected_token(yytext);                            }

 

%%

 

// 如果定义了选项“%option noyywrap”,则不需要下面的yywrap()函数

int yywrap()

{

    return 1;

}

6. bison语法文件(mooon.y)

定义example.idl的语法文件:

代码语言:javascript
复制
// Author: yijian

 // Date: 2015/01/20

%{

// bison -d mooon.y

 

#include "service_info.h"

 

static struct FieldInfo g_field_info;

static FieldInfoTable g_field_info_table(false);

 

%}

 

%union

{

    int ival;

    char* sval;

    int64_t iconst;

    double dconst;

}

 

// 为各token指定使用union的哪个成员来存储它的值

%token tok_int_constant

%token tok_dub_constant

%token tok_request

%token tok_response

%token tok_string

%token tok_bool

%token tok_int16

%token tok_int32

%token tok_int64

%token tok_double

%token tok_identifier

%token tok_required

%token tok_optional

 

%%

 

// 本区域的内容是语法解析定义

ServiceDef:

    | RequestDef

    | ResponseDef

    | RequestDef ResponseDef

    | ResponseDef RequestDef

    ;

 

RequestDef: tok_request '{' FieldDefList '}'

    {

        g_request_info.swap(g_field_info_table);

    }

    ;

 

ResponseDef: tok_response '{' FieldDefList '}'

    {

        g_response_info.swap(g_field_info_table);

    }

    ;

 

FieldDefList: FieldDef

    | FieldDefList FieldDef

    {

    }

    ;

 

FieldDef: FieldRequiredness tok_identifier ':' FieldType Attributes ';'

    {

        g_field_info.name = $2;

        g_field_info_table.add_field_info(g_field_info);

       

        g_field_info.reset();

    }

    ;

 

FieldRequiredness: tok_required

    {

        g_field_info.optional = false;

    }

    | tok_optional

    {

        g_field_info.optional= true;

    }

 

FieldType: tok_string

    {

        g_field_info.type_name = $1;

    }

    | tok_bool

    {

        g_field_info.type_name = $1;

    }

    | tok_int16

    {

        g_field_info.type_name = $1;

    }

    | tok_int32

    {

        g_field_info.type_name = $1;

    }

    | tok_int64

    {

        g_field_info.type_name = $1;

    }

    | tok_double

    {

        g_field_info.type_name = $1;

    }

    ;

   

Attributes:

    | '(' MinValue ',' MaxValue ')'

    {

    }

   

MinValue: tok_int_constant

    {

        g_field_info.limit_type = LIMIT_INT;

        g_field_info.limit.int_limit.max = $1;

    }

    | tok_dub_constant

    {

        g_field_info.limit_type = LIMIT_DOUBLE;

        g_field_info.limit.dou_limit.max = $1;

    }

    ;

 

MaxValue: tok_int_constant

    {

        g_field_info.limit_type = LIMIT_INT;

        g_field_info.limit.int_limit.max = $1;

    }

    | tok_dub_constant

    {

        g_field_info.limit_type = LIMIT_DOUBLE;

        g_field_info.limit.dou_limit.max = $1;

    }

    ;

 

%%

7. service_info.h

定义了FieldInfo和FieldInfoTalbe,以供mooon.y使用。另外,main.cpp需要根据FieldInfo和FieldInfoTalbe来生成目标代码:

代码语言:javascript
复制
// Author: yijian

// Date: 2015/01/20

#ifndef SERVICE_INFO_H

#define SERVICE_INFO_H

 

#include

#include

#include

#include

#include

#include

#include

 

// limit类型

// 针对optional aaa: int16(0, 2015);

// 如果int16后没有(0, 2015),则为LIMIT_NONE

// 对于string和各种int,则为LIMIT_INT,如int16(0, 2015)即为LIMIT_INT

// 对于double则要使用LIMIT_DOUBLE

enum LimitType

{

    LIMIT_NONE,

    LIMIT_INT,

    LIMIT_DOUBLE

};

 

// 用来存储字段信息

struct FieldInfo

{

    bool optional;         // 表示字段是否为可选字段

    std::string name;      // 字段的名称,如示例中的aaa、bbb、xxx和zzz

    std::string type_name; // 字段的数据类型,如int16、string等

 

    // 最大值(对于整数值)或最大长度(对于字符串值)

    // 针对int16(0, 2015)中的(0, 2015)

    enum LimitType limit_type;

    union

    {

        struct

        {

            int64_t max;

            int64_t min;

        } int_limit;

 

        struct

        {

            double max;

            double min;

        } dou_limit;

    } limit;

 

    FieldInfo()

    {

        reset();

    }

 

    void reset()

    {

        limit_type = LIMIT_NONE;

    }

};

 

// 字段信息表,用来存储一个request或一个response中所有字段的FieldInfo

class FieldInfoTable

{

public:

    typedef std::map FieldInfoMap;

 

public:

    FieldInfoTable(bool is_request);

 

    void reset();

    void show();

    void swap(FieldInfoTable& another);

 

    const FieldInfoMap& get_field_info_map() const;

    void add_field_info(const struct FieldInfo& field_info);

 

private:

    bool _is_request;

    FieldInfoMap _field_info_map;

};

 

extern FieldInfoTable g_request_info;

extern FieldInfoTable g_response_info;

 

template

std::string any2string(T t)

{

    std::stringstream ss;

    ss << t;

    return ss.str();

}

 

////////////////////////////////////////////////////////////////////////////////

extern int yylineno;

extern char* yytext;

extern int yylex(void);

extern void yyerror(const char* format, ...);

 

#endif // SERVICE_INFO_H

8. service_info.cpp

以下内容是针对service_info.h的实现:

代码语言:javascript
复制
// Author: yijian

// Date: 2015/01/20

#include "service_info.h"

 

FieldInfoTable g_request_info(true);

FieldInfoTable g_response_info(false);

 

FieldInfoTable::FieldInfoTable(bool is_request)

    : _is_request(is_request)

{

}

 

void FieldInfoTable::reset()

{

    _field_info_map.clear();

}

 

void FieldInfoTable::show()

{

    if (_field_info_map.empty())

    {

        printf("empty\n");

    }

    else

    {

        std::string type = _is_request? "request": "response";

        printf("%s ==> \n", type.c_str());

 

        for (FieldInfoMap::iterator iter=_field_info_map.begin();

                iter!=_field_info_map.end(); ++iter)

        {

            const struct FieldInfo& field_info = iter->second;

            std::string tag = field_info.optional? "optional": "required";

 

            std::string min, max;

            if (LIMIT_INT == field_info.limit_type)

            {

                min = any2string(field_info.limit.int_limit.min);

                max = any2string(field_info.limit.int_limit.max);

            }

            else if (LIMIT_DOUBLE == field_info.limit_type)

            {

                min = any2string(field_info.limit.dou_limit.min);

                max = any2string(field_info.limit.dou_limit.max);

            }

 

            printf("\t<%s> %s %s (%s, %s)\n",

                    tag.c_str(),

                    field_info.type_name.c_str(), field_info.name.c_str(),

                    min.c_str(), max.c_str());

        }

    }

}

 

void FieldInfoTable::swap(FieldInfoTable& another)

{

    const FieldInfoMap& field_info_map = another.get_field_info_map();

 

    for (FieldInfoMap::const_iterator iter=field_info_map.begin();

            iter!=field_info_map.end(); ++iter)

    {

        const struct FieldInfo& field_info = iter->second;

        add_field_info(field_info);

    }

 

    another.reset();

}

 

const FieldInfoTable::FieldInfoMap& FieldInfoTable::get_field_info_map() const

{

    return _field_info_map;

}

 

void FieldInfoTable::add_field_info(const struct FieldInfo& field_info)

{

    std::pair ret =

            _field_info_map.insert(std::make_pair(field_info.name, field_info));

 

    if (!ret.second)

    {

        yyerror("Syntax error: repeat field: in request\n", field_info.name.c_str());

        exit(1);

    }

}

 

////////////////////////////////////////////////////////////////////////////////

void yyerror(const char* format, ...)

{

    fprintf(stderr, "[ERROR:%d] (last token was '%s')\n", yylineno, yytext);

 

    va_list args;

    va_start(args, format);

    vfprintf(stderr, format, args);

    va_end(args);

}

9. main.cpp

yyparse()会将解析结果存储在g_request_infog_response_info这两个全局对象中,根据g_request_infog_response_info存储的信息即可生成目标代码。

代码语言:javascript
复制
// Author: yijian

// Date: 2015/01/20

#include "service_info.h"

 

extern int yyparse();

 

int main(int argc, char* argv[])

{

    yyparse();

 

    g_request_info.show();

    g_response_info.show();

 

    return 0;

}

10. Makefile

编译脚本,运行成本后生成IDL编译器idl_compiler

代码语言:javascript
复制
# Author: yijian

# Date: 2015/01/20

all: idl_compiler

 

objs=service_info.o mooon.tab.o lex.yy.o main.o

idl_compiler: $(objs)

     g++ -g -o $@ $(objs)

 

service_info.o: service_info.h service_info.cpp

     g++ -g -c service_info.cpp -I.

 

mooon.tab.o: mooon.tab.c

     g++ -g -c mooon.tab.c -I.

 

mooon.tab.c: mooon.y

     bison -d mooon.y

 

lex.yy.o: lex.yy.c mooon.tab.h

     g++ -g -c lex.yy.c -I.

 

lex.yy.c: mooon.l

     flex mooon.l

 

main.o: main.cpp service_info.h mooon.l mooon.y

     g++ -g -c main.cpp -I.

 

.PHONY: clean

 

clean:

     rm -f $(objs)

     rm -f mooon.tab.c

     rm -f lex.yy.c

     rm -f idl_compiler

11. 运行示例

执行make编译,成功后运行:./idl_compiler < example.idl,即可以观察到屏幕输出如下:

代码语言:javascript
复制
request ==>

        int16 aaa (0, 2015)

        string bbb (0, 32)

response ==>

        int32 xxx (, )

        string zzz (, )

附:完整源代码包

idl_compiler.zip

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2015-01-20 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 前言
  • 2. 目标(example.idl)
  • 3. 功能
  • 4. 文件构成
  • 5. flex词法文件(mooon.l)
  • 6. bison语法文件(mooon.y)
  • 7. service_info.h
  • 8. service_info.cpp
  • 9. main.cpp
  • 10. Makefile
  • 11. 运行示例
  • 附:完整源代码包
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档