分类: C/C++
2016-09-19 09:40:33
原文地址:IDL编译器实现入门 作者:Aquester
目录 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
本文不对词法和语法、以及flex和bison进行介绍,如有需要,可以阅读《RPC的实现》。本文试图用直接的方式,以最短的篇幅介绍一个最简单的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 ==>
response ==>
|
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 |
定义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; } |
定义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
%token
%token
%token
%token
%token
%token
%token
%token
%token
%token %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; } ;
%% |
定义了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
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
|
以下内容是针对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 _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); } |
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; } |
编译脚本,运行成本后生成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 ==>
response ==>
|