我的好兄弟之Flex&Bison 第三章 Flex,细品!(1)

前面的话:在此之前,如果我接到一个解析文本的工作,我会逐行读取并存储我想要的数据再去处理数据。最近,工作中需要去解析verilog代码,相信verilog有许多人都用过,各关键字有相对应的含义和用法,很明显不能通过上述的方法来做,大概浏览了github,给我这个没有学过编译原理的人指出了一条明路:yacc&lex,或者,flex&bison。

本系列文章:我写这个系列的博客主要是记录收获的知识和踩过的坑,初学者的缘故,并不对其他人具有指导意义。当然,也可能你也和我有相同的问题或者感想,我们可以好好讨论一下。

我的目标:以后如果碰到需要解析的工作,一个下午搞定。

前章:我的好兄弟之Flex&Bison 第一章 实现人生第一个Flex!

          我的好兄弟之Flex&Bison 第二章 让Flex和Bison一起干活!


Flex,细品!

目前掌握的只是皮毛,现在细细咀嚼Flex~

1、Flex如何处理二义性

故名思意,二义性就是相同的输入可能被多种不同的模式匹配。那么Flex是如何规避二义性的呢?

很简单,两条规则来解决:

(1)词法分析器匹配输入时匹配尽可能多的字符串;

(2)如果两个模式都可以匹配的话,匹配在程序中更早出现的模式。

举一个简单的例子

"+"       { return ADD; }
"="       { return ASSIGN; }
"+="      { return ASSIGNADD; }
"if"      { return KEYWORDIF; }
"else"    { return KEYWORDELSE; }
[a-zA-Z][a-zA-Z0-9_]*    { return IDENTIFIER; }

对于前三个模式,字符串+=被匹配为一个记号,因为"+="比"+”更长。

对于后三个模式,只要匹配关键字的模式先于匹配表示符的模式,词法分析器就可以正确地匹配关键字。

2、文件I/O操作

在应用的过程中,通常都是读取一个文件。如何来做呢?

词法分析器总是通过一个名为yyin的文件句柄读取输入。因此,如果去读取一个文件,只需要在第一次调用yylex之前重新设定yyin即可。

举例:

%option noyywrap
%{
%}
%%%%
main(int argc, char** argv){
    if(argc > 1){
        if(!yyin = fopen(argv[1],"r"))){
            error(argv[1]);
            return(1);
        }
    }
    yylex();
}

程序会打开文件并把相应的文件句柄赋给yyin;否则,yyin将保持未赋值的状态,yylex会把stdin赋给它。

顺道了解一下%option noyywrap。yywrap是早期lex遗留下来的鸡肋。默认的做法是当有另一个输入文件时,yywrap可以调整yyin的值并返回0来重新词法分析。后续的lex和flex保留了yywrap,但事实上,它总是返回1。所以,我们通过option设置取消yywrap并实现自己的功能。

另一方面,如果有多个文件需要输入应该怎么办呢?

Flex提供了yyrestart(),使用yyrestart()把打开的文件作为词法分析器的输入,然后调用yylex()进行词法分析。

举例:

main(int argc, char** argv){
    int i;
    if(argc < 2){
        yylex();
        return 0;
    }
    for(i = 1; i < argc; i++){
        FILE *f = fopen(argv[i], "r");
        if(!f)
            return(1);
        yyrestart(f);
        yylex();
        fclose(f);
    }
}

 3、Flex的输入输出

输入:

对于输入,Flex提供了灵活的三层输入系统。

  • 设置yyin来读取所需文件
  • 创建并使用YY_BUFFER_STATE输入缓冲区
  • 重定义YY_INPUT

上一节已经了解了yyin,现在需要了解一下YY_BUFFER_STATE。

Flex词法分析器使用名为YY_BUFFER_STATE的数据结构来处理输入数据,该结构定义了一个单一的输入源。它包含一个字符串缓冲区以及一些变量和标记。通常他会有一个指向所读取文件的FILE*。默认的Flex词法分析器的输入行为大致如下:

YY_BUFEER_STATE bp;
extern FILE* yyin;

......//此处为任何在第一次调用词法分析器之前所需要做的事情

if(!yyin) yyin = stdin;//默认输入设备时stdin
bp = yy_create_buffer(yyin,YY_BUFFER_SIZE);//YY_BUFFER_SIZE由Flex定义,大小通常为16K
yy_switch_to_buffer(bp);//告诉它使用我们刚刚创建的缓冲区
yylex();

先是读取yyin,如果没有yyin,那么就把stdin设置给它。然后使用yy_create_buffer来创建缓冲区,通过yy_switch_to_buffer告诉词法分析器从缓冲区读入。

再来了解一下YY_INPUT

Flex将YY_INPUT用于读取输入到当前缓冲区的宏:

#define  YY_INPUT(buf,result,max_size)...

每当词法分析器的输入缓冲区为空时,它就调用YY_INPUT,buf是缓冲区,maxsize是缓冲区大小,result用来放置实际读取的长度,如果位于EOF,则result就等于0。

了解了这三层输入系统之后,一定有一个疑问,哪一种更好呢?目前是YY_BUFFER_STATE更好,为什么嘞?我也不知道。

输出:

这一部分就简单很多了,所有没有被匹配到的输入都会拷贝到yyout中。通常的建议是通过%option nodefault来取消该设置,让词法分析器去报错。

 

好了,今天了解了Flex如何处理二义性、3种输入机制以及输出的常规操作。

加油:)

posted on 2021-05-09 14:03  QzZq  阅读(525)  评论(0)    收藏  举报

导航