聲 明:原創文章,轉載註明出處http://www.cnblogs.com/lucasysfeng/p/4842310.html html
筆者不會過多地陳述理論,而是但願經過實踐還原一個C編譯器的完成過程。正則表達式
先來看一個簡單的源文件main.c:函數
#include <stdio.h> int main() { printf(「Hello World\n」); return 0; }
咱們會用gcc main.c生成可執行文件a.out, 那麼問題來了,gcc是如何識別include int main printf return這些詞彙的呢,又是如何根據這些詞彙作出相應操做的呢?答案是使用lex和yacc.學習
lex 表明 lexical analyzar(詞法分析器),yacc 表明 yet another compiler compiler(編譯器代碼生成器)。lex和yacc在UNIX下分別叫flex和bison. 簡單地理解下lex&yacc, lex詞法分析器,讀取文件中的關鍵詞(後面說到的token標記其實可看作關鍵詞);而後把關鍵詞遞交給yacc,yacc對一些關鍵詞進行匹配,看它們是否符合必定的語法邏輯,若是符合就進行相應動做。flex
咱們使用lex&yacc寫編譯器,因此先來學習下lex&yacc吧。this
跟着筆者將下面的程序編譯運行一遍,相信你會有所收穫。spa
1. 程序代碼。htm
/* 第一段 */ %{ int chars = 0; int words = 0; int lines = 0; %} /* 第二段 */ %% [a-zA-Z]+ { words++; chars += strlen(yytext); } \n { chars++; lines++; } . { chars++; } %% /* 第三段 */ main(int argc, char **argv) { yylex(); printf("%8d%8d%8d\n", lines, words, chars); }
分析:blog
這段lex程序的做用是:根據輸入的字符串,輸出其行數、單詞數和字符的個數。token
(1) %%把文件分爲3段,第一段是c和lex的全局聲明,第二段是規則段,第三段是c代碼。
(2) 第一段的c代碼要用%{和%}括起來,第三段的c代碼不用。
(3) 第二段是規則段,[a-zA-Z]+ \n . 是正則表達式,表示匹配的內容,{}內的是c編寫的動做。
上面程序中yytext是lex變量,匹配模式的文本存儲在這一變量中。yylex()這一函數開始分析,它由lex自動生成。關於lex變量和函數後續介紹,這裏只是經過簡單的lex程序來認識lex.
二、按照下面過程編譯運行。
#flex test.l
#gcc lex.yy.c –lfl
#./a.out
而後輸入一段文字,按ctrl+d結束輸入,則會輸出行數,單詞數和字符的個數。
編譯過程和運行結果等見下圖:
修改第二節程序,將正則表達式放在全局聲明中,使邏輯更清晰。
%{ int chars = 0; int words = 0; int lines = 0; %} mywords [a-zA-Z]+ mylines \n mychars . %% {mywords} { words++; chars += strlen(yytext); } {mylines} { chars++; lines++; } {mychars} { chars++; } %% main(int argc, char **argv) { yylex(); printf("%8d%8d%8d\n", lines, words, chars); }
編譯運行同第二節。
下面給出一個lex程序,這個程序在掃描到 + 或 - 時作一個特殊輸出。當調用yylex()函數時,若掃描到return對應的標記時,yylex返回,且值就爲return後的值;若沒掃描到return對應的標記,yylex繼續執行,不返回。下次調用自動從前一次的掃描位置處開始。
%{ enum yytokentype { ADD = 259, SUB = 260, }; %} myadd "+" mysub "-" myother . %% {myadd} { return ADD; } {mysub} { return SUB; } {myother} { printf("Mystery character\n"); } %% main(int argc, char **argv) { int tok; while (tok = yylex()) { if (tok == ADD || tok == SUB) { printf("meet + or -\n"); } else { printf("this else statement will not be printed, \ because if yylex return,the retrun value must be ADD or SUB."); } } }
編譯同上,運行結果見下圖:
到如今,僅僅介紹了lex,後續會介紹yacc.