【整理】Flex使用方法

背景知識

Flex 和 Bison 是兩個在編譯前期最常實驗的工具,分別是用來作 lexical analyse 和 semantic analyse 的,這兩個工具的使用基本不須要很深的編譯知識,只須要掌握正則表達式的書寫(lexical analyse階段使用)和上下文無關文法(semantic analyse 階段使用),就能夠完成這兩個階段的分析了。html

Flex大致介紹

Flex 主要是用在詞法分析階段,不須要咱們去手寫分析器,只須要制定好相應的正則表達式規則,他能夠自動對輸入文件進行詞法分析。git

Flex 主要在 Linux 系統下工做,安裝方式也很簡單。web

sudo apt-get install flex正則表達式

或者框架

sudo yum install flexsvg

安裝好 flex 以後,咱們建立一個 .l 後綴的文件,這個文件裏面主要由三部分組成,定義了詞法分析的規則,整個文件的結構以下。函數

definitions
%%
rules
%%
user subroutines

在編寫好文件後,能夠如下使用命令 進行編譯工具

flex file.lflex

編譯以後,咱們會獲得一個名爲 file.yy.c 的文件,這個文件代碼中,咱們只須要使用裏面的 yylex() 函數,這個函數能夠讀入文件中的一個詞法單元,而後進行規則匹配,即詞法分析。指針

咱們能夠在外部定義一個本身的 main() 進行調用,可也以在第三部分 {user subroutines} 中書寫 main() 函數進行調用。便於文件的組織,這裏咱們使用外部文件的方式定義一個新的主函數。

主要的代碼框架以下

extern File* yyin;
int main(int argc, char ** args) {
    if (argc > 1) {
        if( ! (yyin = fopen(argv[1], "r"))) {
            perror(argv[1]);
            return 1;
            }
        }
        while(yylex()!= 0);
        return 0;
}

這個 yyin 能夠理解成輸入文件的文件指針,用來讀取文件,在 file.yy.c 中定義。

而後咱們進行總體的編譯

gcc main.c file.yy.c -lfl -o scanner

-lfl 參數是指定一個庫函數,對於 MacOS 用戶,可使用 -ll 參數進行代替。

這裏有個坑,有可能提示錯誤:/usr/bin/ld: cannot find -lfl
解決方法:須要再安裝如下包:

sudo yum install flex-devel

這樣以後,對某個文件進行詞法分析就能夠直接運行 ./scanner test.cmm 了。

Flex 規則部分

咱們須要注意的是對 Flex 中的規則的編寫,整個 FLex 文件分別由三個部分,第一個部分一般定義一些以後經常使用的正則表達式,能夠簡化書寫,定義格式爲:

name definition

defintion 是一個具體的正則表達式,而 name 是其別名,好比,若是想定義一個識別任意數字的正則表達式,能夠這樣定義

digit [0-9]

這個 digit 就是這個正則表達式的別名,和這個正則表達式的效果同樣,會和任意一個數字進行匹配。

第二部分是規則部分,即針對每個特定的語法單元,咱們對其有什麼樣的操做。定義格式爲

pattern {action}

這個pattern 和咱們上面的同樣,都是正則表達式,而對應的 action 則指定了若是遇到了這個 pattern 以後,咱們的應對方法。這個 pattern 咱們能夠從新定義,也能夠直接使用在第一部分定義好的對應的 name,若是使用 name,則格式爲 {name}。針對一些沒有匹配任何規則的詞法元素,咱們可使用 . 這個 pattern 指定對應的動做。

第三部分是用戶自定義的代碼部分,而這部分定義的方法,函數,都應該在第一部分中進行聲明,聲明格式爲

%{

%}

這樣,聲明後的變量,函數和自定義的代碼片斷都會在 file.yy.c 中生成,方便咱們調用。

完成了這三個部分後,咱們就能夠生成一個簡單的語法分析器了。下面給出一個使用 flex 進行單詞統計的完整文件。

%{
    int chars = 0;
    int words = 0;
    int lines = 0;
%}
letter [a-zA-Z]
%%
{letter}+ {words ++; chars += yyleng; }
\n {chars++; lines ++;}
. {chars++;}
%%
int main(int argc, char** argv) {
    if (argc > 1) {
        if (!(yyin = fopen(argv[1], "r"))) {
            perror(argv[1]);
            return 1;
        }
    }
    yylex();
    printf("lines are %d words are %d chars are %d\n", lines, words, chars);
    return 0;
}

這裏的 yyleng 是 flex 內置提供的變量,記錄當前單詞的長度。

這樣,整個詞法分析的過程就結束了,咱們能夠輸入對應的詞法流,在語法分析階段進行下一步的分析。而語法分析所用的工具,就是 bison。

其餘

正則表達式:

Flex 有幾個正則表達式和傳統的正則表達式規則仍是有點區別:
[a-z]{}[jv] 表示 a-z 裏面再排除 j 和 v
/ 尾部上下文, 0/1 表示匹配 01 中的 0 , / 後面的只用於尾部模式匹配, 匹配出來的是斜線前面的內容, 但斜線後面的內容並不會消耗掉, 會繼續給餘下的規則匹配

語法:

%option noyywrap 是用於關閉 yywrap 這個雞肋的函數, yywrap 主要用於調整 yyin 的值來讀取新文件的內容
yyrestart(f) 放在 yylex 以前, 告訴 flex 讀取文件 f 的內容

示例

單詞統計程序

wc.l 源代碼:

%{
    int chars = 0;
    int words = 0;
    int lines = 0;
%}

%%
[^ \t\n\r\f\v]+ { words++; chars += strlen(yytext); }
\n              { chars++; lines++; }
\t              { return 0;} 
.               { chars++; }
%%

int main(int argc, char **argv) {
    yylex();
    printf("%8d%8d%8d\n", lines, words, chars);
}

在這裏插入圖片描述
源碼備註:

%{ … %} 是直接拷貝到 C 文件開頭
%% … %% 是模式匹配的代碼區域, 左邊是正則表達式, 右邊是匹配的 C 代碼
yytext 表明匹配正則表達式的字符串
flex 的匹配默認是從最長匹配開始, 若是有多個匹配的正則表達式, 從最先的那個開始匹配, 因此上面的模式匹配, 首先是按照單詞 -> 行尾符 -> 剩餘字符串的順序進行匹配的, 不會產生重複統計的問題
yylex 是調用 flex 的詞法分析函數 yylex 進行計算
Linux 系統上用 -lfl 選項編譯, Mac 的編譯選項是 -ll

英美式英語轉換

convert.l 源代碼:

%%
"colour"        { printf("color"); }
"flavour"       { printf("flavor"); }
"clever"        { printf("smart"); }
"conservative"  { printf("libreal"); }
.               { printf("%s", yytext); }
%%

int main(int argc, char **argv) {
    yylex();
}

在這裏插入圖片描述

源碼備註:

匹配英式單詞後, 轉換稱模式後的美式英語
最後的點表示不轉換單詞

參考資料: 1.https://www.cnblogs.com/wAther/p/10662978.html 2.https://www.jianshu.com/p/bad193f67a09