flex学习 - 产生C++扫描器
重要提示:扫描类的当前形式是实验性的,在主要版本之间可能会有很大的变化。
flex提供了两种不同的方法来生成用于C++的扫描器。第一种方法是使用C++编译器而不是C编译器编译flex生成的扫描器。你不应该遇到任何编译错误(请查看报告bug)。然后,您可以在规则的操作中使用C++代码而不是C代码。注意,扫描器的默认输入源仍然是yyin,默认回显仍然是yyout。这两个都是FILE 变量,而不是C++流。
你也可以使用flex来生成一个C++扫描器类,使用’-+’选项(或等价的,%option c++),如果flex可执行文件的名字以’+’结尾,比如’flex++’,这个选项是自动指定的。当使用此选项时,flex默认将扫描器生成为文件lex.yy.cc而不是lex.yy.c。生成的扫描器包括头文件FlexLexer.h,它定义了两个C++类的接口。
FlexLexer.h中的第一个类FlexLexer,提供了一个抽象基类,定义了通用扫描器类接口。它提供了下面的成员函数:
const char YYText()
返回大部分当前匹配的标记文本,等效于yytext
int YYLeng()
返回大部分当前匹配的标记的长度,等效于yyleng
int lineno() const
返回当前的输入行号(查看%option yylineno),或如果%option yylineno不被使用返回1
void set_debug( int flag )
设置扫描器的调试标志,等效于分配到yy_flex_debug(查看扫描器选项)。注意你必须使用包括调试信息的%option debug建立扫描器。
int debug() const
返回当前设置的调试标志
还提供了相当于yy_switch_to_buffer(),yy_create_buffer()等效的成员函数(虽然第一个参数是一个istream&引用目标且不是一个FILE ),yy_flush_buffer(),yy_delete_buffer()和yyrestart()(再次,第一个参数是istream&目标引用)。
在FlexLexer.h中第二个定义的类是yyFlexLexer,它派生自FlexLexer。它定义了下面的额外的成员函数:
yyFlexLexer( istream arg_yyin = 0, ostream* arg_yyout = 0 )
yyFlexLexer( istream& arg_yyin, ostream& arg_yyout )
使用给定的输入输出流构造yyFlexLexer对象。如果没有指定,流默认分别是cin和cout。yyFlexLexer不承担其流参数的所有权。由用户来确保指向的流至少与yyFlexLexer实例一样长。
virtual int yylex()
执行与yylex()对普通flex扫描器相同的角色:它扫描输入流,使用标记,直到规则的操作返回值。如果你从yyFlexLexer中派生出一个子类S,并且想要在yylex()中访问S的成员函数和变量,那么你需要使用%option yyclass=’S’来通知flex你将使用该子类而不是yyFlexLexer。在这种情况下,而不生成yyFlexLexer::yylex(),flex生成S::yylex()(并且还生成一个虚拟的yyFlexLexer::yylex(),如果被调用则调用yyFlexLexer::LexerError())。
virtual void switch_streams(istream* new_in = 0, ostream* new_out = 0)
virtual void switch_streams(istream& new_in, ostream& new_out)
将yyin重新分配给新的in(如果非空),将yyout重新分配给新的out(如果非空),如果重新分配yyin,则删除先前的输入缓冲区。
int yylex( istream* new_in, ostream* new_out = 0 )
int yylex( istream& new_in, ostream& new_out )
首先通过切换流切换输入流(new in,new out),然后返回yylex()的值
此外,yyFlexLexer定义了以下受保护的虚拟函数,您可以在派生类中重新定义以定制扫描器:
virtual int LexerInput( char* buf, int max_size )
将最大大小的字符读入buf并返回所读的字符数。为了指示输入的结束,返回0个字符。注意interactive扫描器(查看扫描器选项-B和-I标志)定义的宏YY_INTERACTIVE。如果你重新定义LexerInput()且需要根据扫描器是否扫描交互式输入源采取不同的操作,则可以通过#ifdef语句测试是否存在该名称。
virtual void LexerOutput( const char* buf, int size )
从缓冲区buf中写出size个字符,当遇到NUL终止符时,如果扫描器规则可能匹配文本内的NULs则也可能包含内部的NULs。
virtual void LexerError( const char* msg )
报告一个故障错误消息。这个函数默认的版本写消息到流cerr并退出。
注意yyFlexLexer对象包含它的整个扫描状态。因此你可以使用一些对象创建可重入的扫描器,但是也可以查看可重入章节。你可以实例化同一个yyFlexLexer类的多个实例,也可以使用上面讨论的’-P’选项在同一个程序中组合多个C++扫描器类。
最后,注意%array功能在C++扫描器类中是无效的;你必须使用%pointer(默认)。
这里是一个简单的C++扫描器的例子:
// An example of using the flex C++ scanner class.
%{
#include <iostream>
using namespace std;
int mylineno = 0;
%}
%option noyywrap c++
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';
%%
// This include is required if main() is an another source file.
//#include <FlexLexer.h>
int main( int /* argc */, char** /* argv */ )
{
FlexLexer* lexer = new yyFlexLexer;
while(lexer->yylex() != 0)
;
return 0;
}
如果你想创建多个(不同)词法类,你使用’-P’标志(或prefix=选项)重命名每个yyFlexLexer为其它的’xxFlexLexer’。当每次使用词法类时你然后可以包括<FlexLexer.h>在你的其它的源文件中,首先重命名yyFlexLexer如下:
#undef yyFlexLexer
#define yyFlexLexer xxFlexLexer
#include <FlexLexer.h>
#undef yyFlexLexer
#define yyFlexLexer zzFlexLexer
#include <FlexLexer.h>
例如,你对一个扫描器使用%option prefix=’xx’,而对另一个扫描器使用%option prefix=’zz’。