Chinaunix首页 | 论坛 | 博客
  • 博客访问: 8136443
  • 博文数量: 594
  • 博客积分: 13065
  • 博客等级: 上将
  • 技术积分: 10324
  • 用 户 组: 普通用户
  • 注册时间: 2008-03-26 16:44
个人简介

推荐: blog.csdn.net/aquester https://github.com/eyjian https://www.cnblogs.com/aquester http://blog.chinaunix.net/uid/20682147.html

文章分类

全部博文(594)

分类: C/C++

2015-01-20 20:19:41

IDL编译器实现入门.pdf

目录

目录 1
1. 前言 1
2. 目标(example.idl) 1
3. 功能 2
4. 文件构成 2
5. flex词法文件(mooon.l) 3
6. bison语法文件(mooon.y) 5
7. service_info.h 8
8. service_info.cpp 11
9. main.cpp 13
10. Makefile 13
11. 运行示例 14
附:完整源代码包 15

1.       前言

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

2.       目标(example.idl

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

 

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

// 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;

}

 

运行效果如下:

request ==>

        int16 aaa (0, 2015)

        string bbb (0, 32)

response ==>

        int32 xxx (, )

        string zzz (, )

3.       功能

request

表示为请求

response

表示为响应

optional

表示字段是可选的

required

表示字段必须存在

int16

支持的数据类型,还包括stringint32int64

(0, 2015)

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

aaa

为字段名称,其它如bbbxxxzzz等也是字段名称

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的词法文件:

 // 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的语法文件:

 // 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

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

// 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;      // 字段的名称,如示例中的aaabbbxxxzzz

    std::string type_name; // 字段的数据类型,如int16string

 

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

    // 针对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的实现:

// 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存储的信息即可生成目标代码。

// 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

# 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,即可以观察到屏幕输出如下:

request ==>

        int16 aaa (0, 2015)

        string bbb (0, 32)

response ==>

        int32 xxx (, )

        string zzz (, )

附:完整源代码包

idl_compiler.zip

阅读(7864) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~