我的好兄弟之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种输入机制以及输出的常规操作。
加油:)
浙公网安备 33010602011771号