09 1月 2008

[Lex] flex如何結合c++的code

我在之前的文章介紹過Lex的基本用法了,flex是用來處理Token Analysis的好工具,原本的.l檔在flex compile過後,會變成lex.yy.c,再用gcc compile後,就可以成為執行檔了。所以一般在寫入.l檔時,都是使用c的library。不過有時候想使用c++的std library時,也是有方法可以達成的!以下介紹兩個方法供參考。

1. 偷吃步 簡易法
 在.l檔直接include c++ std library,用flex產生lex.yy.c後,再用g++去compile即可。

2. c++達人 完全物件法
 flex提供了一個Lex的c++版,.l檔的寫法完全class物件化,因此用了這種方法,並不能與之前的寫法相容喔。
 使用方法有兩種:
 a. 在.l檔裡,檔案開頭加上%option c++
 b. flex指令加上-+
 而flex compile過後的檔案是lex.yy.cc,而非lex.yy.c。而且會幫你自動include FlexLexer.h檔。檔案裡頭宣告了兩個class:FlexLexer和yyFlexLexer,FlexLexer是一個interface,是給yyFlexLexer繼承的,原本用到的lex c function,全部都被包進class yyFlexLexer裡,所以,在使用時,必須先宣告一個yyFlexLexer物件來使用,原本c的yyin不見了,所以要指定input、output的來源必須透過yyFlexLexer的constructor來指定(預設是stdin & stdout),下面會給個範例可以讀檔當input。

 將c++版的class yyFlexLexer裡面的function與原本的c版的lex做簡單的比較:
 const char * YYText()
   回傳目前match的token字串,與c版的yytext相同。
 int YYLeng()
   回傳目前match的token字串長度,與c版的yyleng相同。
 int lineno() const
   回傳目前parser到的是文件中的第幾行(若要使用,記得寫入%option yylineno)

以下有個簡單完整的例子,是用c++版的Flex寫的,可以parser出文件中的number, name, string:

%option noyywrap
%{
using std::cout;
int mylineno = 0;
%}

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 */
{number} cout << "number " << YYText() << '\n';
\n mylineno++;
{name} cout << "name " << YYText() << '\n';
{string} cout << "string " << YYText() << '\n';


%%
#include <fstream>
int main( int argc, char **argv )
{
std::ifstream input;
FlexLexer* lexer;

++argv, --argc;
//--- File input ---//
if ( argc > 0 ){
input.open(argv[0]);
lexer = new yyFlexLexer( &input, &std::cout );
}
//--- Stdin ---//
else{
lexer = new yyFlexLexer;
}

while( lexer->yylex() != 0 );
return 0;
}

compile的指令:
flex -+ xxx.l
g++ lex.yy.cc -o myoutput

參考文章:FreeBSD flex

沒有留言: