生成C++的词法生成器
翻译自1 对原文格式有一点小修改
实验性
目前扫描类(scanning class)的形式是实验性的,在主要版本之间可能会有很大的变化。
scanner/lexer 词法生成器/lexer 认定为同一个东西
flex
提供了两种不同的方式去生成C++代码的lexer。第一种方法是简单地使用 C++ 编译器而不是 C 编译器编译flex
生成的lexer。你应该不会遇到任何编译错误(见报告错误。然后你可以在你的规则动作中使用C++代码而不是C代码。注意,你的scanner的默认输入源仍然是yyin
,默认的echo
仍然是对yyout
进行的。这两个仍然是 FILE *
变量,而不是C++流(streams)。
你也可以使用flex
生成一个C++scanner类,使用-+
选项(或者,使用%option c++
),如果flex
可执行文件的名称以'+'结尾,例如flex++
,则自动指定该选项。当使用该选项时,flex默认生成的scanner为lex.yy.cc
文件,而不是lex.yy.c
。生成的scanner包括头文件FlexLexer.h
,它定义了两个C++类的接口。
FlexLexer.h
FlexLexer.h
FlexLexer
: 提供了一个定义一般scanner类接口的抽象基类。yyFlexLexer
,派生于FlexLexer
。定义了额外的成员函数
FlexLexer
提供了以下成员函数。
const char* YYText()
: 返回最近匹配的标记的文本,相当于yytext。int YYLeng()
: 返回最近匹配的标记的长度,相当于yyleng。int lineno() const
: 返回当前的输入行数(见%option yylineno
),如果没有使用%option yylineno
,则返回1。void set_debug( int flag )
: 设置scanner的调试标志,相当于分配给yy_flex_debug
(见scanner选项)。注意,你必须使用%option debug
来构建scanner,以便在其中包含调试信息。int debug() const
: 返回当前调试标志的设置。
还提供了相当于yy_switch_to_buffer()
、yy_create_buffer()
(尽管第一个参数是一个istream&
对象引用,而不是一个FILE*
)、yy_flush_buffer()
、yy_delete_buffer()
和yyrestart()
(同样,第一个参数是一个istream&
对象引用)的成员函数。
yyFlexLexer
定义了以下额外的成员函数。
yyFlexLexer( istream* arg_yyin = 0, ostream* arg_yyout = 0 );
yyFlexLexer( istream& arg_yyin, ostream& arg_yyout );
2
virtual int yylex()
执行与yylex()
对普通flex
scanner相同的作用:它扫描输入流,消耗token,直到规则的动作返回一个值。如果你从yyFlexLexer
派生出一个子类S
,并想在yylex()
中访问S
的成员函数和变量,那么你需要使用%option yyclass="S"
来通知flex,你将使用该子类而不是yyFlexLexer
。在这种情况下,flex
不会生成yyFlexLexer::yylex()
,而是生成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);
2
重新分配yyin
到new_in
(如果非空)和yyout
到new_out
(如果非空),如果yyin
被重新分配,则删除之前的输入缓冲区。
int yylex( istream* new_in, ostream* new_out = 0 );
int yylex( istream& new_in, ostream& new_out );
2
首先通过switch_streams(new_in,new_out)
切换输入流,然后返回yylex()
的值。
:::detail yylex(new_int,new_out)
// 可能的实现
int yylex( istream* new_in, ostream* new_out = 0 ){
switch_streams(new_in,new_out);
return yylex();
}
int yylex( istream& new_in, ostream& new_out ){
switch_streams(new_in,new_out);
return yylex();
}
2
3
4
5
6
7
8
9
:::
此外,yyFlexLexer
定义了以下受保护的虚函数,你可以在派生类中重新定义这些函数,以定制scanner。
protected
virtual int LexerInput( char* buf, int max_size );
向buf中读取最多max_size的字符,并返回读取的字符数。为了表示输入结束,返回0个字符。注意,交互式scanner(见scanner选项中的-B
和-I
标志)定义了宏YY_INTERACTIVE
。如果你重新定义了LexerInput()
,并且需要根据scanner是否可能正在扫描一个交互式输入源而采取不同的行动,你可以通过#ifdef
语句测试这个名字的存在。
virtual void LexerOutput( const char* buf, int size );
从缓冲区buf中写出size的字符,虽然是NUL结尾的,但如果scanner的规则可以匹配其中有NUL的文本,那么它也可能包含内部的NUL。
virtual void LexerError( const char* msg );
报告一个致命的错误信息。这个函数的默认版本将信息写入流cerr
并退出。
TIP
yyFlexLexer对象包含其整个扫描状态。
因此你可以使用这样的对象来创建可重入的scanner,但也请看可重入(Reentrant)。
你可以实例化同一个yyFlexLexer
类的多个实例,你也可以在同一个程序中使用上面讨论的 -P
选项将多个C++ scanner类组合在一起。
最后,请注意,%array
功能对 C++ scanner类是不可用的;你必须使用%pointer
(这是默认的)。
example
下面是一个简单的C++scanner的例子。
// 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;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
如果你想创建多个(不同的)scanner类,你可以使用-P
标志(或者prefix=
选项)将每个yyFlexLexer
重命名为其他'xxFlexLexer'。然后,你可以在你的其他源中包含<FlexLexer.h>
,每个lexer类一次,首先对yyFlexLexer
进行重命名,如下所示。
#undef yyFlexLexer
#define yyFlexLexer xxFlexLexer
#include <FlexLexer.h>
#undef yyFlexLexer
#define yyFlexLexer zzFlexLexer
#include <FlexLexer.h>
2
3
4
5
6
7
例如,如果你对你的一个scanner使用%option prefix="xx"
,对另一个使用%option prefix="zz"
。