IDL编译器系列-入门篇

IDL系列文章


文章目录

  • IDL系列文章
  • 前言
  • 一、目标
  • 二、功能
  • 二、文件构成
  • 三、flex 词法文件( mooon.l )
  • 四、bison 语法文件( mooon.y )
  • 五、service_info.h
  • 六、service_info.cpp
  • 七、main.cpp
  • 八、 Makefile
  • 九、运行示例
  • 完整源代码包


前言

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


一、目标

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

// Author: sky、木鱼
// Date: 2022/11/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 (, ) 

二、功能

request 表示为请求
response 表示为响应
optional 表示字段是可选的
required 表示字段必须存在
int16 支持的数据类型,还包括 string 、 int32 和 int64 等
(0 , 2015) 这个也是可选的,表示取值范围,对于整数则表示最小值和最大值,对于字符串则表示最小长度和最大长度
aaa 为字段名称,其它如 bbb 、 xxx 和 zzz 等也是字段名称

二、文件构成

文件名 文件说明
example.idl 演示用 IDL 文件
mooon.l 词法文件
mooon.y 语法文件
service_info.h 定义存储元数据的结构
service_info.cpp 对 service_info.h 的实现
main.cpp main() 函数所在文件,调用解析器,并生成目标代码(本文为简单,并没有生成目标代码,而只是在屏幕上输出)
Makefile 编译脚本,成功后生成编译工具 idl_compiler

三、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;
} 

四、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;

}

;

%% 

五、service_info.h

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

 // 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<:string struct fieldinfo> 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

return ss.str();

}



extern int yylineno;

extern char* yytext;

extern int yylex(void);

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

#endif // SERVICE_INFO_H 

六、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)\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<:iterator bool> 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);

} 

七、main.cpp

yyparse () 会将解析结果存储在 g_request_info 和 g_response_info 这两个全局对象中,根据 g_request_info 和 g_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;

} 

八、 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 

九、运行示例

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

 request ==>

int16 aaa (0, 2015)

string bbb (0, 32)

response ==>

int32 xxx (, )

string zzz (, ) 

完整源代码包

你可能感兴趣的:(java,前端,javascript)