linux flex是什么

linux中,flex是一个词法分析工具,能够识别文本中的词法模式;Flex读入给定的输入文件,如果没有给定文件名的话,则从标准输入读取,从而获得一个关于需要生成的扫描器的描述。

linux flex是什么

本教程操作环境:linux5.9.8系统、Dell G3电脑。

flex:词法分析器

flex是一个词法分析器。用来将一个.l文件生成一个.c程序文件。即生成一个词法分析器。然后读取输入,和正则表达式匹配,再执行相应的动作,实现了程序的功能。我们可以发现flex实现在程序外部就可以接受输入的功能。

Flex是一个生成扫描器的工具,能够识别文本中的词法模式。Flex 读入给定的输入文件,如果没有给定文件名的话,则从标准输入读取,从而获得一个关于需要生成的扫描器的描述。此描述叫做规则,由正则表达式和 C代码对组成。Flex 的输出是一个 C 代码文件——lex.yy.c——其中定义了yylex() 函数。编译输出文件可以生成一个可执行文件。当运行可执行文件的时候,它分析输入文件,为每一个正则表达式寻找匹配。当发现一个匹配时,它执行与此正则表达式相关的C代码。Flex 不是gnu工程,但是GNU为Flex 写了手册。

用法

  • 安装 flex

sudo apt-get install flex //或者下载相应版本的安装文件安装
  • 然后新建一个文本文件,输入以下内容:

%% [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
  • 此时目录下多了一个 “lex.yy.c” 文件,把这个 C 文件编译并运行一遍:

 gcc -o hide-digits lex.yy.c ./hide-digits

然后在终端不停的敲入任意键并回车,可以发现,敲入的内容中,除数字外的字符都被原样的输出了,而每串数字字符都被替换成 ? 了。最后敲入 # 后程序退出了。如下:

eruiewdkfj eruiewdkfj 1245 ? fdsaf4578 fdsaf? ... #
  • 当在命令行中运行 flex 时,第二个命令行参数(此处是 hide-digits.l )是提供给 flex 的分词模式文件, 此模式文件中主要是用户用正则表达式写的分词匹配模式,用flex 会将这些正则表达式翻译成 C 代码格式的函数 yylex ,并输出到 lex.yy.c 文件中,该函数可以看成一个有限状态自动机。

  • 当在命令行中运行 flex 时,第二个命令行参数(此处是 hide-digits.l )是提供给 flex 的分词模式文件, 此模式文件中主要是用户用正则表达式写的分词匹配模式,用flex 会将这些正则表达式翻译成 C 代码格式的函数 yylex ,并输出到 lex.yy.c 文件中,该函数可以看成一个有限状态自动机。

  • 下面再来详细解释一下 hide-digits.l 文件中的代码,首先第一段是:

%% [0-9]+  printf("?"); #       return 0; .       ECHO; %%
  • flex 模式文件中,用%% 和 %%做分割, 上面分割的内容被称为 规则(rules),本文件中每一行都是一条规则,每条规则由 匹配模式(pattern) 和 事件(action) 组成, 模式在前面,用正则表达式表示,事件在后面,即 C 代码。每当一个模式被匹配到时,后面的 C 代码被执行。

  • flex 会将本段内容翻译成一个名为 yylex 的函数,该函数的作用就是扫描输入文件(默认情况下为标准输入),当扫描到一个完整的、最长的、可以和某条规则的正则表达式所匹配的字符串时,该函数会执行此规则后面的 C 代码。如果这些 C 代码中没有 return 语句,则执行完这些 C 代码后, yylex 函数会继续运行,开始下一轮的扫描和匹配。

  • 当有多条规则的模式被匹配到时, yylex 会选择匹配长度最长的那条规则,如果有匹配长度相等的规则,则选择排在最前面的规则。

int main(int argc, char *argv[]) {     yylex();     return 0; } int yywrap() { return 1; }
  • 第二段中的 main 函数是程序的入口, flex 会将这些代码原样的复制到 lex.yy.c 文件的最后面。最后一行的 yywrap 函数, flex 要求有这么一个函数。

示例

word-spliter.l

%{ #define T_WORD 1 int numChars = 0, numWords = 0, numLines = 0; %} WORD([^ tnra]+) %% 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%sn", yytext); } printf("nCharstWordstLinesn"); printf("%dt%dt%dn", numChars, numWords, numLines); return 0; } int yywrap() { return 1; }

本例中使用到了 flex 提供的两个全局变量 yytext 和 yyleng,分别用来表示刚刚匹配到的字符串以及它的长度

编译执行

flex word-spliter.l gcc -o word-spliter lex.yy.c ./word-spliter <p>可见此程序其实就是一个原始的分词器,它将输入文件分割成一个个的 WORD 再输出到终端,同时统计输入文件中的字符数、单词数和行数。此处的 WORD 指一串连续的非空格字符。</p><p><strong>扩展</strong></p><p>(1) 列出所需的所有类型的 token;</p><p>(2) 为每种类型的 token 分配一个唯一的编号,同时写出此 token 的正则表达式;</p><p>(3) 写出每种 token 的 rule (相应的 pattern 和 action )。</p><p>第 1 类为单字符运算符,一共 15 种:</p><pre class="brush:js;toolbar:false">+ * - / % = , ; !  ( ) { }

第 2 类为双字符运算符和关键字,一共 16 种:

=, ==, !=, &amp;&amp;, || void, int, while, if, else, return, break, continue, print, readint

第 3 类为整数常量字符串常量标识符(变量名和函数名),一共 3 种。

拓展后

%{ #include "token.h" int cur_line_num = 1; void init_scanner(); void lex_error(char* msg, int line); %} /* Definitions, note: 42 is '"' */ INTEGER             ([0-9]+) UNTERM_STRING       (42[^42n]*) STRING              (42[^42n]*42) IDENTIFIER          ([_a-zA-Z][_a-zA-Z0-9]*) OPERATOR            ([+*-/%=,;!(){}]) SINGLE_COMMENT1     ("//"[^n]*) SINGLE_COMMENT2     ("#"[^n]*) %% [n]                { cur_line_num++;                       } [ tra]+          { /* ignore all spaces */               } {SINGLE_COMMENT1}   { /* skip for single line comment */    } {SINGLE_COMMENT2}   { /* skip for single line commnet */    } {OPERATOR}          { return yytext[0];         }    "="                { return T_Ge;              } "=="                { return T_Eq;              } "!="                { return T_Ne;              } "&amp;&amp;"                { 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;      } &gt;             { 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%sn", "TOKEN-TYPE", "TOKEN-VALUE");     printf("-------------------------------------------------n"); } void lex_error(char* msg, int line) {     printf("nError at line %-3d: %snn", line, msg); } int yywrap(void) {     return 1; }

上面这个文件中,需要注意的是,正则表达式中,用双引号括起来的字符串就是原始字符串,里面的特殊字符是不需要转义的,而双引号本身必须转义(必须用 ” 或 42 ),这是 flex 中不同于常规的正则表达式的一个特性。

除单字符运算符外的 token 的编号则在下面这个 token.h 文件,该文件中同时提供了一个 print_token 函数,可以根据 token 的编号打印其名称。

#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 <p>makefile</p><pre class="brush:js;toolbar:false">out: scanner scanner: lex.yy.c token.h gcc -o $@ $<p><img src="https://img.php.cn/upload/image/240/562/265/1646296027455685.png" title="1646296027455685.png" alt="1.png"></p><p>相关推荐:《<a href="http://www.php.cn/course/list/33.html" target="_blank">Linux视频教程</a>》</p>

© 版权声明
THE END
喜欢就支持一下吧
点赞5 分享