http://gmd20.blog.163.com/blog/static/16843923201331664345310/
freeDiameter的主配置文件还有那些extension的配置文件,都是用flex 和bison来做解析的。
#log enabled log_enabled = true; # l;og_level LOG_LEVEL = 2 ; #aop name app_name = "my application name "; #数组的输入 a = [1, -2, 3,4, 5, 6, 7, 8, 90];
#include <iostream> #include <vector> #include <string> struct app_conf { bool log_enabled; int log_level; std::string app_name; std::vector<int> array; void init() { log_enabled = false; log_level = 0; app_name.clear(); array.clear(); } }; extern struct app_conf my_app_conf;
#include "app_conf.h" struct app_conf my_app_conf; extern int load_conf(char * conffile); int main () { if (0 == load_conf("app_conf.txt")) { std::cout << "log_enabled = " << my_app_conf.log_enabled << std::endl; std::cout << "log_level = " << my_app_conf.log_level<< std::endl; std::cout << "app_name = " << my_app_conf.app_name<< std::endl; std::cout << "array = "; for (std::vector<int>::iterator it = my_app_conf.array.begin() ; it != my_app_conf.array.end(); ++it) { std::cout << ' ' << *it; } std::cout << '\n'; } return 0; }
%{ #include <iostream> #include <string> #include <vector> // bison 不会把 c++ 的头文件加到下面这个头文件里面去, // 所以包含这个头文件直接,只要要手工在前面加上c++的头文件 /* bision生成的头文件包含bison文件定义的token 常量, Include yacc tokens definitions */ #include "app_conf_parse.hpp" #include <stdio.h> /* Update the column information */ #define YY_USER_ACTION { \ yylloc->first_column = yylloc->last_column + 1; \ yylloc->last_column = yylloc->first_column + yyleng - 1; \ } %} /* flex的选项设置 yywrap函数在默认yyin输入文件读到结尾之后调用,用于加载多个yyin文件。一般都不会用这个 所以下面noyywrap禁用了这个功能了。 --bison-bridge 告诉flex生成和bison兼容的api接口 所有参数说明都可以在 这里找到 http://flex.sourceforge.net/manual/Index-of-Scanner-Options.html#Index-of-Scanner-Options */ %option bison-bridge bison-locations %option noyywrap %option nounput %% /* 记录行数 */ \n { yylloc->first_line++; yylloc->last_line++; yylloc->last_column=0; } ([[:space:]]{-}[\n])+ { /* 忽略所有的空格 如果这里使用[ \t]的话,windows平台的文件 回车\r会导致匹配失败*/ } /* Eat all spaces but not new lines 忽略空格的另一种写法 ([[:space:]]{-}[\n])+ ; {-} 表示所有在前面的集合 [[:space:]] 里面又不在后面集合 [\n] 里面的字符 {+} 类似,表示集合的并集的意思,在任何前面后面的集合里面出现 参见 http://flex.sourceforge.net/manual/Patterns.html#Patterns */ /*忽略所有的注释*/ #.*$ ; /*匹配数字的另一种写法 [-]?[[:digit:]]+ 参考内置的class 的定义 http://flex.sourceforge.net/manual/Patterns.html#Patterns */ [-]?[0-9]+ { int ret=0; ret = sscanf(yytext, "%i", &yylval->integer); if (ret != 1) {return LEX_ERROR;} return INTEGER; } /* Recognize quoted strings -- we do not support escaped \" in the string currently. */ \"[^\"]+\" { /*yytext 是匹配的字符串开始的指针,yyleng是长度 */ yylval->string = new std::string (yytext+1, yyleng - 2); return QSTRING; } /* ?i 忽略大小写的写法 参考 http://flex.sourceforge.net/manual/Patterns.html#Patterns 的说明 */ (?i:"app_name") { return APP_NAME; } (?i:"log_enabled") { return LOG_ENABLED; } true|false { yylval->integer = strcmp(yytext, "true"); return LOG_STATE; } (?i:"log_level") { return LOG_LEVEL; } [a-zA-Z][a-zA-Z0-9]* { yylval->string = new std::string (yytext, yyleng); return STRING; } /* 认为是合法的单个字符 */ [=;,\[\]] {return yytext[0];} /* 除了上面列出的匹配模式之外,其他的都当作错误返回 * 如果是自己的和自己的程序集成的话,这里也可以使用自己的log函数打印错误, * LEX_ERROR 为自定义表示错误的token,yyerror 可以重写的吧 */ . { printf("不能识别的模式:在 %d 行 %d 列, %c\n", yylloc->first_line, yylloc->first_column, *yytext); return LEX_ERROR; } %%
/* For development only : */ %debug %error-verbose /* The parser receives the configuration file filename as parameter */ /* 这个设置之后,yyerror等函数就会多一个参数 */ %parse-param {char * conffile} /* Keep track of location */ %locations %pure-parser %{ #include "app_conf.h" //自定义的app_conf 结构 #include <errno.h> #include <stdio.h> #include <iostream> #include <vector> #include <string> // bison 不会把 c++ 的头文件加到下面这个头文件里面去, // 所以包含这个头文件直接,只要要手工在前面加上c++的头文件 #include "app_conf_parse.hpp" /* bison的默认解析函数, Forward declaration */ int yyparse(char * conffile); /* 提供给其他模块使用的加载设置的函数 */ int load_conf(char * conffile) { // flex默认会从这个yyin文件指针读取输入 // 如果没有修改flex生成的前缀,应该是这个名字 // 不过可以通过给flex指定参数,修改yy为自己想要的前缀名字
// 除了这个yyin的用法,flex的api也有直接从内存数组里面加载要解析的内容的。 extern FILE * yyin; int ret; yyin = fopen(conffile, "r"); if (yyin == NULL) { ret = errno; return ret; } my_app_conf.init(); ret = yyparse(conffile); fclose(yyin); return ret; } /* The Lex parser prototype */ int yylex(YYSTYPE *lvalp, YYLTYPE *llocp); /* Function to report the errors */ void yyerror (YYLTYPE *ploc, char * conffile, char const *s) { if (ploc->first_line != ploc->last_line) printf("%s:%d.%d-%d.%d : %s\n", conffile, ploc->first_line, ploc->first_column, ploc->last_line, ploc->last_column, s); else if (ploc->first_column != ploc->last_column) printf("%s:%d.%d-%d : %s\n", conffile, ploc->first_line, ploc->first_column, ploc->last_column, s); else printf("%s:%d.%d : %s\n", conffile, ploc->first_line, ploc->first_column, s); } %} /* Values returned by lex for token */ %union { int integer; /* 保存整型数 */ std::string *string; /* 保存字符串 */ std::vector<int> *array_data; /*保存数组的内容*/ } /* In case of error in the lexical analysis */ %token LEX_ERROR /* Key words */ %token LOG_ENABLED %token LOG_LEVEL %token APP_NAME /* 有返回值的,类型相关的定义 */ %token <string> QSTRING %token <string> STRING %token <integer> INTEGER %token <integer> LOG_STATE /* 声明语法树节点的类型,对应 %unino 里面的哪一种类型 */ %type <array_data> array_items; /* ----------------------- 开始语法定义--------------------------------- */ %% conffile: /* empty grammar is OK */ | conffile log_enabled | conffile log_level | conffile app_name | conffile array ; log_enabled: LOG_ENABLED '=' LOG_STATE ';' { my_app_conf.log_enabled = (bool) $3; } ; log_level: LOG_LEVEL '=' INTEGER ';' { my_app_conf.log_level = $3; } ; app_name: APP_NAME '=' QSTRING ';' { my_app_conf.app_name = *$3; } ; array: STRING '=' '[' array_items ']'';' { std::cout << "array name = " << *$1 << std::endl; my_app_conf.array = * $4; } ; array_items: INTEGER { // $$表示当前冒号左边的节点, $1 $2 ... 表示 冒号右边的第几个节点。 $$ = new std::vector<int> (); $$->push_back($1); } | array_items ',' INTEGER { $$ = new std::vector<int> (); * $$ = * $1; $$->push_back($3); } ;
[root@LINUX-01 bright]# bison -d -o app_conf_parse.cpp app_conf.y [root@LINUX-01 bright]# ls app_conf.l app_conf.y app_conf_parse.hpp app_conf.txt app_conf_parse.cpp main.cpp [root@LINUX-01 bright]# flex -o app_conf_lex.cpp app_conf.l [root@LINUX-01 bright]# ls app_conf.l app_conf.y app_conf_parse.cpp main.cpp app_conf.txt app_conf_lex.cpp app_conf_parse.hpp g++ -g -o my_app app_conf_parse.cpp app_conf_lex.cpp main.cpp [root@LINUX-01 bright]# ./my_app array name = a log_enabled = 0 log_level = 2 app_name = my application name array = 1 -2 3 4 5 6 7 8 90