顯示具有 Lex 標籤的文章。 顯示所有文章
顯示具有 Lex 標籤的文章。 顯示所有文章

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

27 9月 2007

[Lex] 簡易語法教學

Lex是個古老的工具
雖然是個老東西,但是還是挺好用的!
Lex的功用主要是對一個文件寫下rule
然後產生一個compiler去paser這種文件

安裝:
  目前Lex / Flex在linux下皆可以安裝執行
  以Ubuntu為例,只要下指令
  % apt-get install flex
  即自動幫你安裝完成
  接著只要輸入指令flex即可執行Lex程式了

執行Lex的順序:
  Lex的input file,必須是*.l 的檔案 ( 副檔名為l ... 小寫的L )
  接著只要輸入指令
  % flex test.l
  然後Lex就會自動產生一個output file:lex.yy.c
  接著只要compile這個lex.yy.c 就可以執行這個token parser了
  % gcc lex.yy.c -ll
  而-ll是為了include lex的library

Lex的Input File架構:
  *.l 主要分三個部分:definition & rules & user code
  這三個部分以「%%」為分界

  definition
  %%
  rules
  %%
  user code


  definition:使用者自己定義的變數,都放在這個地方
  rulesparser對token match的規則
  user code:最後產生的lex.yy.c最底下會有一模一樣的code

Definition:
  在Definition的區間裡,可以宣告一些在rule中的code要使用的變數(寫法跟c一模一樣)
  而這些code必須用%{%} 將跨行的code包起來
  因為在這個區間的code都會被完完整整、一字不漏地output至lex.yy.c檔中
  所以在compile lex.yy.c檔時,才不會產生error!
  Ex:
  %{//要記錄parser的input file的總字數與行數
     int num_char = 0;
     int num_line = 0;
  %}
  %%
  \n { num_line++; }
  . { num_char++; }

  也可以宣告一些「rule的變數」,讓rule的寫法更簡潔
  寫法為:
  name definition
  
  Ex :
  number [0-9]+
  identifier [a-zA-Z_][a-zA-Z_0-9]*
  %%
  {number} printf("%s this token is a number\n", yytext);
  {identifier} printf("%s this token is a identifier\n", yytext);

  上面的意思其實就是...
  {[0-9]+} printf("%s this token is a number\n", yytext);
  {[a-zA-Z_][a-zA-Z_0-9]*} printf("%s this token is a identifier\n", yytext);

Rule:
  要對input file切token的規則,全寫在這裡。
  寫法的規則是:
  pattern action
  
  pattern可以輸入一些正規表示法,或是一些word,而正規表示法在此不再贅述
  想了解的人,自己想辦法吧,筆者累了....Or2...
  action則是當pattern match後,執行相對應的code(跟c一模一樣),因此這些code會原封不動地寫入output file中
  若action的code太多,則可以用"{" "}"跨行將code包起來
  Ex:
  [0-9]+ ECHO;printf("this is a number!\n");
  等同於...
  [0-9]+ {
        ECHO;
        printf("this is a number!\n");
       }
  
  在這邊有一個特別的word可以用在action中
  ECHO 可以印出yytext(match pattern的字串)中的內容至output中

Global Variable:
  這個是lex的預設變數,在寫*.l檔的definition & rule時,可以直接使用這些變數
  yyin 是lex的input來源,型態為FILE * ,初始預設為stdin
  yytext 當rule中match一個pattern時,match的string就會存在yytext中,型態為char *
  yyleng 記錄yytext的長度
  yylineno 記錄目前的yyin讀到第幾行了
  

Example:
  這是一個計算input file的總字數&行數的lex檔  

%{
int num_lines = 0, num_chars = 0;
%}

%%
\n   { ++num_lines; ++num_chars; }
.    { ++num_chars; }

%%
main()
{
yylex();
printf( "# of lines = %d, # of chars = %d\n",
num_lines, num_chars );
}