背景知識
在學編譯原理的時候,同時在做南京大學的編譯原理課程實驗,這里是鏈接,整個實驗的效果是實現一個完整的 C--
語法的編譯器。C--
語法是他們老師指定的一種類 C 語言。
Flex 和 Bison 是兩個在編譯前期最常實驗的工具,分別是用來做 lexical analyse 和 semantic analyse 的,這兩個工具的使用基本不需要很深的編譯知識,只需要掌握正則表達式的書寫(lexical analyse階段使用)和上下文無關文法(semantic analyse 階段使用),就可以完成這兩個階段的分析了。
Flex大體介紹
Flex 主要是用在詞法分析階段,不需要我們去手寫分析器,只需要制定好相應的正則表達式規則,他可以自動對輸入文件進行詞法分析。
Flex 主要在 Linux 系統下工作,安裝方式也很簡單。
sudo apt-get install flex
安裝好 flex 之后,我們創建一個 .l
后綴的文件,這個文件里面主要由三部分組成,定義了詞法分析的規則,整個文件的結構如下。
definitions
%%
rules
%%
user subroutines
在編寫好文件后,可以使用命令 flex file.l
進行編譯,編譯之后,我們會得到一個名為 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 參數進行代替。
這樣之后,對某個文件進行詞法分析就可以直接運行 ./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。