flex是一个在unix/linux下经典的词法分析程序,能够灵活运用这个软件,会是你在linux下编程提供方便。我前篇blog简单的谈了一下用flex生成一个c++分析程序,还记得我们生成的默认的分析类是xxFlexLexer,可是如果我们在某个程序中需要两个这样的不同的类该怎么办呢(这两个类的功能也不同)!!,总不能是两个类都用同样的xxFlexLexer这个名字吧!!,我花了半天的功夫总算弄得半个明白,下面就将我的成果和大家分享一下,希望对大家有所帮助。
下面是count.l的词法分析描述文件,这个文件中描述了一个用于计数字符个数和文件行数的规则:
%{
#include "data.h"
extern data newData;
%}
%option nounput
%option prefix="xx"
%option outfile="hostParser.cc"
%%
/n newData.incNumLines(); newData.incNumChars();
. newData.incNumChars();
%%
int yywrap()
{
return 1;
}
注意%prefix="xx"标识了我们要生成的类的前缀是xx。而yywrap这个函数是必须有的,这个在这里就不解释了,只要
记得每个.l文件都要有他就行了。
下面是infor文件,这个文件能够区分你输入的是数字还是字符,他也是一个词法描述文件
%{
#include<iostream>
using std::cout;
int mylineno = 0;
%}
%option nounput
%option prefix="zz"
%option outfile="info.cc"
string /"[^/n"]+/"
ws [ /t]+
alpha [A-Za-z]
dig [0-9]
name ({alpha}|{dig}|/$)({alpha}|{dig}|[_./-/$])*
num1 [-+]?{dig}+/.?([eE][-+]?{dig}+)?
num2 [-+]?{dig}*/.{dig}+([eE][-+]?{dig}+)?
number {num1}|{num2}
%%
{ws} /* skip blanks and tabs */
"/*" {
int c;
while((c = yyinput()) != 0)
{
if(c == '/n')
++mylineno;
else if(c == '*')
{
if((c = yyinput()) == '/')
break;
else
unput(c);
}
}
}
{number} cout << "number " << YYText() << '/n';
/n mylineno++;
{name} cout << "name " << YYText() << '/n';
{string} cout << "string " << YYText() << '/n';
%%
int yywrap()
{
return 1;
}
注意到%option prefix="zz",他告诉我们,我们要生成的类前缀是zz
下面是一个简单的 c++文件,在这里并不是主角,相信大家都能看懂。
data.h文件
#ifndef __DATA_H
#define __DATA_H
class data{
protected:
int num_lines;
int num_chars;
public:
void incNumLines();
void incNumChars();
int getNumLines();
int getNumChars();
data();
};
#endif
data.cpp文件
#include "data.h"
data::data()
{
num_lines = 0;
num_chars = 0;
}
void data::incNumLines()
{
num_lines++;
}
void data::incNumChars()
{
num_chars++;
}
int data::getNumLines()
{
return num_lines;
}
int data::getNumChars()
{
return num_chars;
}
information.h文件
#ifndef __INFORMATION
#define __INFORMATION
class information
{
public:
void getInfor();
};
#endif
information.cpp文件
#include "information.h"
#undef yyFlexLexer
#define yyFlexLexer zzFlexLexer
#include <FlexLexer.h>
void information::getInfor()
{
FlexLexer* lexer = new zzFlexLexer;
while(lexer->yylex() != 0)
;
}
最后给出main.cpp文件:
#include <fstream.h>
#include <string.h>
#include <stream.h>
#include "data.h"
#include "information.h"
#undef yyFlexLexer
#define yyFlexLexer xxFlexLexer
#include <FlexLexer.h>
data newData;
int main( int /* argc */, char* argv[] )
{
ifstream* pmyFile = new ifstream();
pmyFile->open(argv[1],ios::out);
FlexLexer* lexer = new xxFlexLexer(pmyFile);
while(lexer->yylex()!=0);
printf("total lines:%d/ntotal chars:%d/n",newData.getNumLines(), newData.getNumChars());
information infor ;
infor.getInfor();
delete pmyFile;
delete lexer;
return 0;
}
我们需要说明的是,在main.cpp和information.cpp文件中分别有如下预处理:
information.cpp
#undef yyFlexLexer
#define yyFlexLexer zzFlexLexer
#include <FlexLexer.h>
main.cpp
#undef yyFlexLexer
#define yyFlexLexer xxFlexLexer
#include <FlexLexer.h>
这使得两个文件中使用了不同的词法分析类:
information.cpp
FlexLexer* lexer = new zzFlexLexer;
while(lexer->yylex() != 0;
main.cpp
FlexLexer* lexer = new xxFlexLexer(pmyFile);
while(lexer->yylex()!=0);
这样程序就会调用不同的分析方法来进行分析了。
这里的while循环是必须的,这个体空的while循环完成了对文件的一次扫描。
有人会问,为什么要定义一个information.h呢!!!,能不能直接在main函数中使用两个不同的分析方法呢?
答案是很遗憾,目前还不支持这重方法。
还有一个问题就是外部变量无法直接传入到词法分析类中,词法分析类必须通过extern来引用外部变量,这个在前面的例子中我们可以看到。
最后一个问题是语法分析程序(bison),对c++并没有很好的支持,所以我们可以通过flex直接完成语法分析部分
也是可以的,相关例子可一在网上找到,这里就不累述。
最后给一个简单的makefile以供参考:
main: main.o data.o information.o
flex -+ -Pxx count.l
flex -+ -Pyy info.l
g++ main.cpp data.cpp information.cpp hostParser.cc info.cc -o main
clear:
rm -rf *~ *.cc *.o main
如果有什么意见和看法,可以给我留言,大家一起探讨学习。
本文是由作者个人所写,如有转载请标明出处。