http://flex.sourceforge.net/manual/ html
說明:發現這個manual太羅嗦了,要是按照它的思路下去,個人筆記又成爲了翻譯。果斷跳過,仍是從例子開始來學習比較好,先弄出例子,而後看須要什麼功能,再翻這個文檔,再理解就是了。linux
1. hello,worldgit
先用一個最簡單的例子看看flex究竟是什麼玩意。正則表達式
根據前面的內容,寫一個flex的輸入文件以下:ide
/* filename: input.lex */ /* Definitions Section */ %% /* Rules Section */ %% /* User Code Section */
很顯然,所有都是註釋。而後運行一下「flex input.lex"會提示錯誤。緣由是註釋的問題,rules部分的第一行要求爲正則表達式,直接寫註釋有問題。參考前面的內容知道,爲了簡化,註釋開頭最好都是另起一行,同時最好/*前面有一個以上空格或tab。既然如此,之後就follow這個規則,防止出錯(第一行我就不follow了,看起來太醜了)。函數
/* filename: input.lex */ /* Definitions Section */ %% /* Rules Section */ %% /* User Code Section */ /* main */
而後運行flex input.lex,沒有錯誤,獲得一個輸出爲:lex.yy.c的C語言文件。打開生成的文件看看,內容還挺多,可是能夠找到上面幾個註釋都被生成在了輸出的文件中。而後用gcc編譯lex.yy.c,發現有錯誤,沒有定義main函數。因此,修改輸入的lex文件以下:
/* filename: input.lex */ /* Definitions Section */ %% /* Rules Section */ %% /* User Code Section */ /* main */ int main() { printf("Hello, world\n"); return 0; }
一樣生成lex.yy.c而後編譯,仍是出錯,提示yywrap沒有定義。關於yywrap函數,Google一下就知道緣由了。具體就不解釋了。總之,就目前而言,將其實現一下就能夠了。有幾種方法能夠避免這個問題,好比使用gcc lex.yy.c -lfl編譯(即連接到flex的庫,其中有yywrap的默認實現)還能夠本身實現如下,目前返回1便可。後面再來理解這個函數的做用。最終的hello,world的輸入文件以下:
/* filename: input.lex */ /* Definitions Section */ %% /* Rules Section */ %% /* User Code Section */ int yywrap() { return 1;} /* main */ int main() { printf("Hello, world\n"); return 0; }
運行以下:工具
#ls input.lex #flex input.lex #ls input.lex lex.yy.c #gcc lex.yy.c #./a.out Hello, world #
經過hello,world大概就更容易瞭解flex是什麼了,其輸入有一些什麼,輸出又是什麼,涉及到哪些工具等。
2. 理解definition section&user code section學習
/* filename: input.lex */ /* Definitions Section */ /* usage of %top */ %top{ /* This code goes at the "top" of the generated file. */ #include <stdint.h> #include <inttypes.h> } /* usage of %{ %} */ %{ // This part will also be copied to the output int a = 1; %} /* usage of definitions section, "name definition" */ digit [0-9] number {digit}+ letter [a-zA-Z] identifier {letter}+ newline n whitespace [ t]+ %% /* Rules Section */ %% /* User Code Section */ int yywrap() { return 1;} /* main */ int main() { printf("Hello, world, %d\n", a); return 0; }
使用flex和gcc運行上面的input.lex文件,分析來理解definition section&user code section。PS:main函數自己就屬於user code section裏面了。這裏的definition section中digit等定義在生成的文件中是沒有對應內容的,這些內容須要結合其它內容一塊兒使用纔有意義,這裏主要了解%top和%{%}的做用和效果。
3. 理解rules sectionflex
到這裏,已經大概知道flex是怎麼回事了,其實就是根據咱們的輸入.lex文件定義的一些內容,生成一個掃描器,用這個掃描器進行詞法分析。spa
那麼第一個問題是:掃描器如何啓動?即如何開始掃描?如何指定要掃描哪個文件?答案是,flex的輸出lex.yy.c中其實就實現了一些函數,還有一些變量也是能夠被使用的,好比extern FILE *yyin, *yyout;就是用於指定掃描器輸入輸出文件的,因此咱們只須要在咱們的user code section裏面對這些變量賦值就能夠了。(其實也不必定非要在user code section了,在definition section裏面用%top或%{%}也是能夠的啦,應該很容易理解了,固然rules section裏面也可使用%{%}的內容了)
下一個問題是:rules section是幹嗎的?rules section就是指定掃描器工做的過程了,裏面定義一些pattern,當掃描器掃描的時候知足的時候就執行一些action,這樣就能完成整個掃描了。
其實,definition sections部分的定義就是爲了給rules section的pattern用的,定義一些正則表達式,而後用在這裏,使得其看起來簡潔。前面說過,用{name}就能夠引用definition sections裏面的定義了。
下面的問題就是lex中pattern即模式是如何定義的,以及如何匹配的,還有actions能夠爲一些什麼內容。這也是lex的核心內容。
4. 模式(正則表達式)
可參考:
https://www.ibm.com/developerworks/cn/linux/sdk/lex/#2
http://flex.sourceforge.net/manual/Patterns.html#Patterns
其正則表達式規則應該是GNU的正則表達式規則,對於lex,經常使用的一些正則表達式其實就那麼一些,查看一下手冊就知道怎麼寫了。
5. 匹配規則
http://flex.sourceforge.net/manual/Matching.html#Matching
當生成的掃描器運行的時候,它會分析輸入並查找字符串來進行模式的匹配。若是它找到超過一個匹配,它就會選擇匹配最長的內容。若是它找到兩個或多個一樣長度的匹配,那麼rules section中列舉在前面的被選擇。
一旦匹配了,知足該匹配的文字(即token)就會保存在lex的全局變量yytext中(char*指針類型),它的長度保存在yyleng中。而後,其對應的action會被執行,而後爲下一個匹配繼續掃描剩下的輸入。若是沒有找到匹配,那麼default的rule被執行:即輸入的下一個字符來考慮進行匹配並複製到標準輸出中。因此,最簡單的做爲flex的輸入的是:
%%
這樣會生成一個掃描器,會將全部的輸入複製到輸出中。
6. 匹配的action
每個rule中匹配的模式都有一個對應的action。action能夠爲任意的C語句。若是action爲空,那麼匹配的token就直接被刪除。
若是一個action只有一個"|"符號,那麼表示「與下一個rule的action相同"。
a |
ab |
abc {return 0;}
即表示對於token爲a或ab或abc,都執行return 0.
須要說明的是,action裏面能夠是任意的C代碼,包括return語句,那麼return語句是返回給誰呢?是返回一個值到yylex()函數。每一次yylex()被調用的時候,它從上次掃描離開的地方繼續處理,知道達到文件結束或者執行到return。另外,action也能夠修改yytext和yyleng等lex的變量,可是有一些相關的規則。
有一些特殊的指令,能夠被用於action中:
ECHO:複製yytext到掃描器的輸出
BEGIN:後面緊接一個start condition(開始條件)的名字,將掃描器放置到對應的開始條件中(後面繼續理解開始條件)。
REJECT:告訴掃描器繼續執行能匹配該模式的」第二好「的規則。簡單理解,拒絕嘛。就是忽略這個匹配,繼續考慮是否有其餘的rules能匹配的,執行其它的操做。好比:
%%
special special();REJECT;
spec dosth();
這樣,首先,當輸入有special時候,根據前面的匹配,因爲special是最長的匹配,因此匹配到這一個,這時候會執行special(),而後遇到了REJECT,就會讓掃描器對這個"special"的匹配拒絕,就會從新匹配一個「第二好"的,那麼就能匹配到"specia"知足匹配,就會執行dosth()的action。固然,這裏兩行交換順序並不會影響這個流程。若是沒有REJECT,只會匹配到special的,不會匹配spec。
7. Lex的函數、宏、變量、數據類型、鉤子、選項等等
http://flex.sourceforge.net/manual/Indices.html#Indices
這裏能夠查到這些信息,經常使用的一些函數和變量要了解。
可參考此文(https://www.ibm.com/developerworks/cn/linux/sdk/lex/#8)瞭解幾個最經常使用的變量和函數。
經常使用的正則表達式模式: http://flex.sourceforge.net/manual/Common-Patterns.html#Common-Patterns