這篇“l(fā)inux的中怎么安裝和使用flex”文章的知識(shí)點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價(jià)值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“l(fā)inux的中怎么安裝和使用flex”文章吧。
創(chuàng)新互聯(lián)建站主營張家港網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營網(wǎng)站建設(shè)方案,重慶APP開發(fā),張家港h5成都微信小程序搭建,張家港網(wǎng)站營銷推廣歡迎張家港等地區(qū)企業(yè)咨詢
在linux中,flex是一個(gè)詞法分析工具,能夠識(shí)別文本中的詞法模式;Flex讀入給定的輸入文件,如果沒有給定文件名的話,則從標(biāo)準(zhǔn)輸入讀取,從而獲得一個(gè)關(guān)于需要生成的掃描器的描述。
本教程操作環(huán)境:linux5.9.8系統(tǒng)、Dell G3電腦。
flex:詞法分析器
flex是一個(gè)詞法分析器。用來將一個(gè).l文件生成一個(gè).c程序文件。即生成一個(gè)詞法分析器。然后讀取輸入,和正則表達(dá)式匹配,再執(zhí)行相應(yīng)的動(dòng)作,實(shí)現(xiàn)了程序的功能。我們可以發(fā)現(xiàn)flex實(shí)現(xiàn)在程序外部就可以接受輸入的功能。
Flex是一個(gè)生成掃描器的工具,能夠識(shí)別文本中的詞法模式。Flex 讀入給定的輸入文件,如果沒有給定文件名的話,則從標(biāo)準(zhǔn)輸入讀取,從而獲得一個(gè)關(guān)于需要生成的掃描器的描述。此描述叫做規(guī)則,由正則表達(dá)式和 C代碼對(duì)組成。Flex 的輸出是一個(gè) C 代碼文件——lex.yy.c——其中定義了yylex() 函數(shù)。編譯輸出文件可以生成一個(gè)可執(zhí)行文件。當(dāng)運(yùn)行可執(zhí)行文件的時(shí)候,它分析輸入文件,為每一個(gè)正則表達(dá)式尋找匹配。當(dāng)發(fā)現(xiàn)一個(gè)匹配時(shí),它執(zhí)行與此正則表達(dá)式相關(guān)的C代碼。Flex 不是GNU工程,但是GNU為Flex 寫了手冊(cè)。
用法
安裝 flex
sudo apt-get install flex //或者下載相應(yīng)版本的安裝文件安裝
然后新建一個(gè)文本文件,輸入以下內(nèi)容:
%% [0-9]+ printf("?"); # return 0; . ECHO; %% int main(int argc, char* argv[]) { yylex(); return 0; } int yywrap() { return 1; }
將此文件另存為 hide-digits.l 。注意此文件中的 %% 必須在本行的最前面(即 %% 前面不能有任何空格)。
之后,在終端輸入:
flex hide-digits.l
此時(shí)目錄下多了一個(gè) “l(fā)ex.yy.c” 文件,把這個(gè) C 文件編譯并運(yùn)行一遍:
gcc -o hide-digits lex.yy.c ./hide-digits
然后在終端不停的敲入任意鍵并回車,可以發(fā)現(xiàn),敲入的內(nèi)容中,除數(shù)字外的字符都被原樣的輸出了,而每串?dāng)?shù)字字符都被替換成 ? 了。最后敲入 # 后程序退出了。如下:
eruiewdkfj eruiewdkfj 1245 ? fdsaf4578 fdsaf? ... #
當(dāng)在命令行中運(yùn)行 flex 時(shí),第二個(gè)命令行參數(shù)(此處是 hide-digits.l )是提供給 flex 的分詞模式文件, 此模式文件中主要是用戶用正則表達(dá)式寫的分詞匹配模式,用flex 會(huì)將這些正則表達(dá)式翻譯成 C 代碼格式的函數(shù) yylex ,并輸出到 lex.yy.c 文件中,該函數(shù)可以看成一個(gè)有限狀態(tài)自動(dòng)機(jī)。
當(dāng)在命令行中運(yùn)行 flex 時(shí),第二個(gè)命令行參數(shù)(此處是 hide-digits.l )是提供給 flex 的分詞模式文件, 此模式文件中主要是用戶用正則表達(dá)式寫的分詞匹配模式,用flex 會(huì)將這些正則表達(dá)式翻譯成 C 代碼格式的函數(shù) yylex ,并輸出到 lex.yy.c 文件中,該函數(shù)可以看成一個(gè)有限狀態(tài)自動(dòng)機(jī)。
下面再來詳細(xì)解釋一下 hide-digits.l 文件中的代碼,首先第一段是:
%% [0-9]+ printf("?"); # return 0; . ECHO; %%
flex 模式文件中,用%% 和 %%做分割, 上面分割的內(nèi)容被稱為 規(guī)則(rules),本文件中每一行都是一條規(guī)則,每條規(guī)則由 匹配模式(pattern) 和 事件(action) 組成, 模式在前面,用正則表達(dá)式表示,事件在后面,即 C 代碼。每當(dāng)一個(gè)模式被匹配到時(shí),后面的 C 代碼被執(zhí)行。
flex 會(huì)將本段內(nèi)容翻譯成一個(gè)名為 yylex 的函數(shù),該函數(shù)的作用就是掃描輸入文件(默認(rèn)情況下為標(biāo)準(zhǔn)輸入),當(dāng)掃描到一個(gè)完整的、最長的、可以和某條規(guī)則的正則表達(dá)式所匹配的字符串時(shí),該函數(shù)會(huì)執(zhí)行此規(guī)則后面的 C 代碼。如果這些 C 代碼中沒有 return 語句,則執(zhí)行完這些 C 代碼后, yylex 函數(shù)會(huì)繼續(xù)運(yùn)行,開始下一輪的掃描和匹配。
當(dāng)有多條規(guī)則的模式被匹配到時(shí), yylex 會(huì)選擇匹配長度最長的那條規(guī)則,如果有匹配長度相等的規(guī)則,則選擇排在最前面的規(guī)則。
int main(int argc, char *argv[]) { yylex(); return 0; } int yywrap() { return 1; }
第二段中的 main 函數(shù)是程序的入口, flex 會(huì)將這些代碼原樣的復(fù)制到 lex.yy.c 文件的最后面。最后一行的 yywrap 函數(shù), flex 要求有這么一個(gè)函數(shù)。
示例
word-spliter.l
%{ #define T_WORD 1 int numChars = 0, numWords = 0, numLines = 0; %} WORD([^ \t\n\r\a]+) %% \n{ numLines++; numChars++; } {WORD}{ numWords++; numChars += yyleng; return T_WORD; } <>{ return 0; } .{ numChars++; } %% int main() { int token_type; while (token_type = yylex()) { printf("WORD:\t%s\n", yytext); } printf("\nChars\tWords\tLines\n"); printf("%d\t%d\t%d\n", numChars, numWords, numLines); return 0; } int yywrap() { return 1; }
本例中使用到了 flex 提供的兩個(gè)全局變量 yytext 和 yyleng,分別用來表示剛剛匹配到的字符串以及它的長度
編譯執(zhí)行
flex word-spliter.l gcc -o word-spliter lex.yy.c ./word-spliter < word-spliter.l 輸出: WORD: %{ WORD: #define ... WORD: } Chars Words Lines 470 70 27
可見此程序其實(shí)就是一個(gè)原始的分詞器,它將輸入文件分割成一個(gè)個(gè)的 WORD 再輸出到終端,同時(shí)統(tǒng)計(jì)輸入文件中的字符數(shù)、單詞數(shù)和行數(shù)。此處的 WORD 指一串連續(xù)的非空格字符。
擴(kuò)展
(1) 列出所需的所有類型的 token;
(2) 為每種類型的 token 分配一個(gè)唯一的編號(hào),同時(shí)寫出此 token 的正則表達(dá)式;
(3) 寫出每種 token 的 rule (相應(yīng)的 pattern 和 action )。
第 1 類為單字符運(yùn)算符,一共 15 種:
+ * - / % = , ; ! < > ( ) { }
第 2 類為雙字符運(yùn)算符和關(guān)鍵字,一共 16 種:
<=, >=, ==, !=, &&, || void, int, while, if, else, return, break, continue, print, readint
第 3 類為整數(shù)常量、字符串常量和標(biāo)識(shí)符(變量名和函數(shù)名),一共 3 種。
拓展后
%{ #include "token.h" int cur_line_num = 1; void init_scanner(); void lex_error(char* msg, int line); %} /* Definitions, note: \042 is '"' */ INTEGER ([0-9]+) UNTERM_STRING (\042[^\042\n]*) STRING (\042[^\042\n]*\042) IDENTIFIER ([_a-zA-Z][_a-zA-Z0-9]*) OPERATOR ([+*-/%=,;!<>(){}]) SINGLE_COMMENT1 ("http://"[^\n]*) SINGLE_COMMENT2 ("#"[^\n]*) %% [\n] { cur_line_num++; } [ \t\r\a]+ { /* ignore all spaces */ } {SINGLE_COMMENT1} { /* skip for single line comment */ } {SINGLE_COMMENT2} { /* skip for single line commnet */ } {OPERATOR} { return yytext[0]; } "<=" { return T_Le; } ">=" { return T_Ge; } "==" { return T_Eq; } "!=" { return T_Ne; } "&&" { return T_And; } "||" { return T_Or; } "void" { return T_Void; } "int" { return T_Int; } "while" { return T_While; } "if" { return T_If; } "else" { return T_Else; } "return" { return T_Return; } "break" { return T_Break; } "continue" { return T_Continue; } "print" { return T_Print; } "readint" { return T_ReadInt; } {INTEGER} { return T_IntConstant; } {STRING} { return T_StringConstant; } {IDENTIFIER} { return T_Identifier; } <> { return 0; } {UNTERM_STRING} { lex_error("Unterminated string constant", cur_line_num); } . { lex_error("Unrecognized character", cur_line_num); } %% int main(int argc, char* argv[]) { int token; init_scanner(); while (token = yylex()) { print_token(token); puts(yytext); } return 0; } void init_scanner() { printf("%-20s%s\n", "TOKEN-TYPE", "TOKEN-VALUE"); printf("-------------------------------------------------\n"); } void lex_error(char* msg, int line) { printf("\nError at line %-3d: %s\n\n", line, msg); } int yywrap(void) { return 1; }
上面這個(gè)文件中,需要注意的是,正則表達(dá)式中,用雙引號(hào)括起來的字符串就是原始字符串,里面的特殊字符是不需要轉(zhuǎn)義的,而雙引號(hào)本身必須轉(zhuǎn)義(必須用 \” 或 \042 ),這是 flex 中不同于常規(guī)的正則表達(dá)式的一個(gè)特性。
除單字符運(yùn)算符外的 token 的編號(hào)則在下面這個(gè) token.h 文件,該文件中同時(shí)提供了一個(gè) print_token 函數(shù),可以根據(jù) token 的編號(hào)打印其名稱。
#ifndef TOKEN_H #define TOKEN_H typedef enum { T_Le = 256, T_Ge, T_Eq, T_Ne, T_And, T_Or, T_IntConstant, T_StringConstant, T_Identifier, T_Void, T_Int, T_While, T_If, T_Else, T_Return, T_Break, T_Continue, T_Print, T_ReadInt } TokenType; static void print_token(int token) { static char* token_strs[] = { "T_Le", "T_Ge", "T_Eq", "T_Ne", "T_And", "T_Or", "T_IntConstant", "T_StringConstant", "T_Identifier", "T_Void", "T_Int", "T_While", "T_If", "T_Else", "T_Return", "T_Break", "T_Continue", "T_Print", "T_ReadInt" }; if (token < 256) { printf("%-20c", token); } else { printf("%-20s", token_strs[token-256]); } } #endif
makefile
out: scanner scanner: lex.yy.c token.h gcc -o $@ $< lex.yy.c: scanner.l flex $<
以上就是關(guān)于“l(fā)inux的中怎么安裝和使用flex”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對(duì)大家有幫助,若想了解更多相關(guān)的知識(shí)內(nèi)容,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。