【Flex学习笔记】1:生成简易的词法分析程序

简述

Flex是重写Lex诞生的快速词法分析生成器,在编译前端(词法分析->语法分析->语义分析)中处在最靠前的位置,它可以用来生成特定的词法分析程序。

安装Flex:

apt-get install flex

没有专用于Flex的IDE,可以在VSCode安装Lex Flex Yacc Bison插件,可以让Flex语法高亮。

Flex使用示例

Flex程序通常写成.l文件,其中由两个%%分成上中下三部分,第一部分是声明和选项设置(编译后被原样写入生成的词法分析程序最顶端),第二部分是正则表达式模式和相应Action,第三部分主程序中是一些与Action相关的例程(也会被照抄)。

第一部分如果不需要就不用写。第三部分如果不写,Flex会提供一个最小的调用词法分析器的主程序。

核心就在第二部分,因为一个正则表达式可以对应一个有限自动机,Flex就是将正则表达式翻译成DFA,然后在DFA转移时执行相应的Action完成处理。

如课本上的统计行数、单词数、字符数的Flex程序:

%{
    int chars = 0; //用于记录字符数
    int words = 0; //用于记录单词数
    int lines = 0; //用于记录行数
%}

%% //分割第一部分和第二部分

[^ \t\n\r\f\v]+ { words++; chars+=strlen(yytext); } //匹配到单词(没有空白符的连续串)时,单词数+1,字符数+=单词长度
\n              { chars++; lines++; } //匹配到换行符时,字符数+1,行数+1
.               { chars++; } //匹配到其它任何字符,只增加字符数

%% //分割第二部分和第三部分

//主函数
int main(int argc, char **argv)
{
    yylex(); //调用Flex提供的词法分析例程yylex()
    printf("%8d%8d%8d\n", lines, words, chars); //输出统计结果
}

使用flex命令对其翻译:

flex WordCount.l

在同一目录下生成了近1800的C语言程序lex.yy.c,这就是Flex生成的程序,只不过这个例子不是词法分析目的。将这个程序用gcc(或者Unix的cc)编译:

gcc lex.yy.c -lfl

其中-lfl参数用于链接flex的库函数。编译后在目录下生成了a.out,直接执行就可以使用这个程序了,输入可换行的文本并最终Ctrl+D(这是Unix/Linux下的换行符)结束输入:

lzh@DESKTOP-HCSIG2E:/mnt/e/Compiler/flex$ ./a.out
I am sb lzh.
I like cute cat, and i wanna eat foods.
That's all, bye!
       3      16      70

这表示,输入的文本有3行,16个单词("That’s"整个视为一个单词),70个字符。

Flex生成词法分析程序

这里按照课本上的案例,做一个整数的四则运算器。

Flex在这里生成词法分析程序,词法分析程序会识别输入的单词,然后将分析结果输出。

%%

"+"     { printf("PLUS\n"); }
"-"     { printf("MINUS\n"); }
"*"     { printf("TIMES\n"); }
"/"     { printf("DIVIDE\n"); }
"|"     { printf("ABS\n"); }
[0-9]+  { printf("NUMBER %s\n", yytext); } //数字
\n      { printf("NEWLINE\n"); }
[ \t]   { } //忽略空白符
.       { printf("Mystery charactor %s\n", yytext); } //其它字符是不合法的,提示错误

%%

翻译,编译,运行:

lzh@DESKTOP-HCSIG2E:/mnt/e/Compiler/flex$ ./a.out
2019+11-30/2
NUMBER 2019
PLUS
NUMBER 11
MINUS
NUMBER 30
DIVIDE
NUMBER 2
NEWLINE
77 + 8 8 | abc
NUMBER 77
PLUS
NUMBER 8
NUMBER 8
ABS
Mystery charactor a
Mystery charactor b
Mystery charactor c
NEWLINE

改进的词法分析程序

一般为模式匹配的Action中设置返回值(而不是像前面那样直接print),这样yylex()每次识别到相应的模式,如果有返回值,就立即返回,然后继续调用yylex()识别下一个模式;如果是没有返回值的模式,就会继续向后识别。

另外,还可以设置记号编号记号值。记号编号用于记录词的类别,而记号的值则用于记录此类别的某个具体值。例如记号编号可以是NUMBER,然后记号值取10,就表示了整型常量10。

Bison创建的语法分析器自动从258开始指派记号编号,这是为了防止和文字字符冲突。Flex里为了和Bison统一,也不妨从258开始编号。

记号的值为了能给不同的类型使用,通常使用union类型。不过这个例子里因为只有数字需要有记号值,所以直接就用int类型。

%{
    //记号编号
    enum yytokentype{
        NUMBER = 258,
        ADD = 259,
        SUB = 260,
        MUL = 261,
        DIV = 262,
        ABS = 263,
        EOL = 264
    };
    //存储记号值
    int yylval;
%}

%%

"+"     { return ADD; }
"-"     { return SUB; }
"*"     { return MUL; }
"/"     { return DIV; }
"|"     { return ABS; }
[0-9]+  { yylval = atoi(yytext); return NUMBER; } //匹配到数字时,将其转为int写入记号值的变量中
\n      { return EOL; }
[ \t]   { } //忽略空白符
.       { printf("Mystery charactor %c\n", *yytext); } //其它字符是不合法的,提示错误

%%

int main(int argc, char **argv) {
    int tok;
    while(tok=yylex()) { //每次从记号流中匹配出一个记号编号
        printf("%d", tok);
        if(tok==NUMBER) //如果是数字,还需要输出记号值
            printf(" = %d\n", yylval);
        else
            printf("\n");
    }
    return 0;
}

翻译,编译,运行:

lzh@DESKTOP-HCSIG2E:/mnt/e/Compiler/flex$ ./a.out
2019+11-30/2
258 = 2019
259
258 = 11
260
258 = 30
262
258 = 2
264
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值