2011年11月6日 星期日

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 );
}
 
 
執行方式:
flex [filename].l
gcc lex.yy.c -ll
cat [filename] | ./a.out //把此文件與pipeline到執行檔產出
如:
file => 
---------------------------- 
1
23
4
5
6
7123adsf
sdf
--------------------------------
下:
flex file.l 
gcc lex.yy.c -ll 
cat file | ./a.out 
 
print => 
24 7 7     //24個字元 個字數 77行
 
解釋:
在這份定義文件內,我們看到在定義區塊裡面有一行C語言的變數宣告,宣告了三個整數變數,nchar、nword及nline,分別用來代表接下來我們要計算的字元數、字數以及行數。

接著在樣式區塊裡面定義了三個樣式,根據我們在上一節裡面學到的對於常規表示式的知識,我們可以知道這三條樣式所代表的意義。 \n   換行字元
 [^ \t\n]+  1個以上的非空白字元
 .   換行字元之外的所有字元我們現在要注意的是在樣式常規表示代的右邊以大括號括起來的程式碼,這就是前頭我們有提到過的動作程式碼。這是在字彙剖析器在分析字串樣式匹配時,若樣式符合且樣式有指定動作程式碼,也就是在樣式右側的程式碼,則這段程式碼就會被執行。

以\n
這條樣式來說明,如果樣式匹配,則程式碼{ nline++; nchar++; 
}會被執行。注意到這裡的大括號,假如動作程式碼只有一行的話,則大括號是可有可無的,像是第三條樣式。但我們一律還是都寫上大括號以免有所遺漏,同時也
要注意的是不管動作程式碼有幾條指令,全部都要寫在同一行裡面。

最後是程式碼區塊。這區塊裡有個main進入點,第一行指令呼叫了
yylex這個函式。在前面的介紹中,我們知道yylex是由lex自動幫我們產生的函式,這個函式的工作就是幫我們作字彙剖析,一直到結束後才會返回。
返回之後,我們再把計算的字元數、字數以及行數列印到畫面上。 
 
from:
http://falldog7.blogspot.com/2007/09/lex.html 
http://good-ed.blogspot.com/2010/04/lexyacc.html

沒有留言:

張貼留言