1 引言
由于之前读linux和ubuntu的时候,出现scripts/kconfig/conf程序用于配置整个工程,出现一个yyparse函数,数千行,后来的之是flex+bison生成的。接下来就是做一个程序,简单了解写flex+bison。程序主要是为了读取一个my_config文件。my_config如下:
config"FIRST"
type 0
value 999
help "first help"
CONFIG"SECOND"
type 0
value 2
help "second help"
config"THIRD"
type 0
value "I am the Third!"
depend "FIRST" "SECOND"
help "third help"
为了简便,这里仅仅设置两者类型,整形和字符型,对于所有字符型数均采用加””方式识别。
笔者指向了解一下flex+bison,仅列出实验过程。理论主要来源入如下三面文章:
(1)《flex和bison的用于加载和解析配置文件(参考freeDiameter用法)》
http://blog.csdn.net/ncepubdtb/article/details/42707131
(2)《写编译器:学习GNU Flex,写一个词法分析器》
http://xiaoxia.org/2011/10/24/writing-a-compiler-learning-gnu-flex-write-a-lexical-analyzer/
(3)《 语法分析器 BISON》
http://blog.sina.com.cn/s/blog_aed82f6f01019fp6.html
2 代码
源码包括conf.l,conf.y,conf.h,main.c,Makfile还有一个要处理的配置文件my_config。
2.1 Makefile
.PHONY = all
all:
flex-o lex.yy.cpp conf.l
bison-d -o conf.tab.cpp conf.y
g++-o main main.cpp lex.yy.cpp conf.tab.cpp -I./
clean:
rm-rf lex.yy.cpp main conf.tab.cpp conf.tab.hpp
2.2 main.c
#include "config.h"
#include "stdio.h"
#include <string.h>
using std::cout;
using std::endl;
struct symbol * g_conf_list;
extern int yyparse(char * file);
void dump_conf(struct symbol * conf)
{
cout<<"config:"<<conf->name<<endl;
cout<<"type:"<<conf->type<<endl;
cout<<"value:"<<conf->type<<endl;
cout<<"dep:";
for(inti=0;i<conf->dep_num;i++)
cout<<conf->dep[i]<<"";
cout<<"\n";
cout<<"help:"<<conf->help<<endl;
cout<<"\n";
}
int main()
{
char* file_name = "my_config";
externFILE * yyin; int ret;
yyin= fopen(file_name, "r");
if(yyin == NULL)
{
cout<<"my_configdoes not exist!"<<endl;
}
cout<<"parsemy_config..."<<endl;
yyparse(file_name);
cout<<"endparse my_config"<<endl;
cout<<"\n";
structsymbol * cur_conf_list = g_conf_list;
if(cur_conf_list== NULL)
cout<<"isNULL"<<endl;
while(NULL!= cur_conf_list)
{
dump_conf(cur_conf_list);
cur_conf_list = cur_conf_list->next;
}
return1;
}
2.3 config.h
#ifndef CONFIG_H
#define CONFIG_H
#include <iostream>
#include <string>
struct symbol_value {
std::stringstr;
int number;
};
#define S_DEF_COUNT 10
struct symbol {
structsymbol * next;
std::stringname;
inttype; //0表示int,1表示string
structsymbol_value value;
intdep_num;
std::stringdep[S_DEF_COUNT];
std::stringhelp;
};
#endif
2.4 conf.l
%{
#include "conf.tab.hpp"
#include <iostream>
using std::cout;
using std::endl;
%}
%option noyywrap
digit [0-9]
number {digit}+
%%
(?i:config) {
/*读到config表示一个新的symbol开始, i表示不区分大小写 */
returnCONFIG_START;
}
(?i:type) {
/* 读到type表示即将设置symbole类型symbol_typer,并且后面的值便是设置值 */
returnCONFIG_TYPE;
}
(?i:value) {
/* 读到value表示即将设置symbole类型symbol_value的str或number,并且后面的值便是设置值*/
returnCONFIG_VALUE;
}
(?i:depend) {
/* 读到depend on, 表示设置依赖列表,并且后面的值便是依赖列表(是字符串型)*/
returnCONFIG_DEPEND;
}
(?i:help) {
/* 读到help, 表示设置帮助,并且后面的值便是以来(是字符串型)*/
returnCONFIG_HELP;
}
\"[^\"]+\" {
/* 字符串,不支持"" */
//yytext对应读取的这个内容的指针,yyleng为其长度。yylval对应.y文件的union
//这里有一个问题,不知道在那里free的,暂时不去深究。
yylval.string= (char * )malloc(512);
memset(yylval.string,0,512);
memcpy(yylval.string,yytext,yyleng);
returnSET_STRING;
}
[0-9]+ {
/* 数字,仅支持正整数 */
//yytext对应读取的这个内容的指针,yylval对应.y文件的union
intret = sscanf(yytext, "%i", &(yylval.integer));
if (ret != 1)
{returnLEX_ERROR;}
returnSET_UINT;
}
[\t] {/*其中'\t'和'\n'以及空格都是合法的*/}
[\n] {/*其中'\t'和'\n'以及空格都是合法的*/}
[ ] {/*其中'\t'和'\n'以及空格都是合法的*/}
. {
printf("illegalword!\n");
returnLEX_ERROR;
}
%%
2.5 conf.y
%parse-param {char * conffile}
%{
#include "config.h"
#include <string>
#include <string.h>
extern struct symbol * g_conf_list;
struct symbol * current_conf_list;
int config_enable = 0; //只有分配内存之后才能配置
/* flex的解析函数 */
int yylex (void);
/* bison的默认解析函数, Forwarddeclaration */
int yyparse(char * file);
using std::cout;
using std::endl;
void yyerror(char * conffile, charconst *s)
{
}
void init_symbol(struct symbol * ptr)
{
ptr->next= NULL;
ptr->name= "";
ptr->name.clear();
ptr->type= 0;
memset(&(ptr->value),0,sizeof(structsymbol_value));
ptr->dep_num= 0;
for(inti=0;i<S_DEF_COUNT;i++)
{
ptr->name= "";
ptr->dep[i].clear();
}
ptr->help= "";
ptr->help.clear();
}
%}
//对应于yylval这个变量,联合体应该包括所有可能的类型
%union {
int integer; /* 保存整型数 */
char* string; /* 保存字符串 */
}
/* 错误标识 */
%token LEX_ERROR
/* 配置标志 */
%token CONFIG_START
%token CONFIG_TYPE
%token CONFIG_VALUE
%token CONFIG_DEPEND
%token CONFIG_HELP
/* 配置值 */
%token <string> SET_STRING
%token <integer> SET_UINT
%%
/*语法定义*/
grammar: /* empty grammar is OK */
|grammar after_read_config
|grammar after_read_type
|grammar after_read_value_uint
|grammar after_read_value_string
|grammar after_read_help
|grammar after_read_depend
|grammar after_read_depend_append
;
//读取config之后
after_read_config: CONFIG_STARTSET_STRING
{
if(current_conf_list== NULL)
{
//current_conf_list= (struct symbol *)malloc(sizeof(struct symbol));
g_conf_list= new struct symbol;
current_conf_list= g_conf_list;
}
else
{
//current_conf_list->next= (struct symbol *)malloc(sizeof(struct symbol));
current_conf_list->next= new struct symbol;
current_conf_list= current_conf_list->next;
}
//memset(current_conf_list,0,sizeof(structsymbol));
//因为有结构题,所以不能使用memset
init_symbol(current_conf_list);
current_conf_list->name= $2;
config_enable= 1;
}
;
//读type行
after_read_type: CONFIG_TYPE SET_UINT
{
if((current_conf_list!= NULL)&&(config_enable==1))
{
current_conf_list->type= $2;
}
}
;
//读value行,确保value在type下面设置才能保证有效
after_read_value_uint: CONFIG_VALUESET_UINT
{
if((current_conf_list!= NULL)&&(current_conf_list->type ==0)&&(config_enable==1))
{
current_conf_list->value.number= $2;
}
}
;
after_read_value_string: CONFIG_VALUESET_STRING
{
if((current_conf_list!= NULL)&&(current_conf_list->type ==1)&&(config_enable==1))
{
current_conf_list->value.str= $2;
}
}
;
//读help行
after_read_help: CONFIG_HELP SET_STRING
{
if((current_conf_list!= NULL)&&(config_enable==1))
{
current_conf_list->help= $2;
}
}
;
//读depend行,读其中第一个依赖
after_read_depend: CONFIG_DEPEND SET_STRING
{
if((current_conf_list!= NULL)&&(config_enable==1))
{
current_conf_list->dep[0]= $2;
current_conf_list->dep_num= 1;
}
}
after_read_depend_append: after_read_depend SET_STRING
{
if((current_conf_list!=NULL)&&(config_enable==1)&&(current_conf_list->dep_num<S_DEF_COUNT))
{
current_conf_list->dep[current_conf_list->dep_num++]= $2;
}
}
3 实验
程序主要是读取my_config文件到类型为symbol的链表中,并打印出来。
(1) make
(2)./main
会出现如下结果: