linux的中怎么安装和使用flex

76次阅读
没有评论

共计 5344 个字符,预计需要花费 14 分钟才能阅读完成。

这篇“linux 的中怎么安装和使用 flex”文章的知识点大部分人都不太理解,所以丸趣 TV 小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“linux 的中怎么安装和使用 flex”文章吧。

在 linux 中,flex 是一个词法分析工具,能够识别文本中的词法模式;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
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([^ \t\n\r\a]+)
\n{ numLines++; numChars++; }
{WORD}{ numWords++; numChars += yyleng; return T_WORD; }
 EOF { 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 提供的两个全局变量 yytext 和 yyleng,分别用来表示刚刚匹配到的字符串以及它的长度

编译执行

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

可见此程序其实就是一个原始的分词器,它将输入文件分割成一个个的 WORD 再输出到终端,同时统计输入文件中的字符数、单词数和行数。此处的 WORD 指一串连续的非空格字符。

扩展

(1)列出所需的所有类型的 token;

(2)为每种类型的 token 分配一个唯一的编号,同时写出此 token 的正则表达式;

(3)写出每种 token 的 rule(相应的 pattern 和 action)。

第 1 类为单字符运算符,一共 15 种:

+ * - / % = , ; !     ( ) { }

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

=,  =, ==, !=,  , ||
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: \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 (// [^\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; }
 EOF  { 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;}

上面这个文件中,需要注意的是,正则表达式中,用双引号括起来的字符串就是原始字符串,里面的特殊字符是不需要转义的,而双引号本身必须转义(必须用 \”或 \042),这是 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   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 $

以上就是关于“linux 的中怎么安装和使用 flex”这篇文章的内容,相信大家都有了一定的了解,希望丸趣 TV 小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注丸趣 TV 行业资讯频道。

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-08-03发表,共计5344字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)