flex工具学习二
参考书:flex与bsion
目标:读入文本并打印文本信息及行号,遇到文本中包含#include "file" 则读入嵌套文本并打印
创建text.l写入以下代码:
1 %option noyywrap 2 %x IFILE 3 4 %{ 5 struct bufstack { 6 struct bufstack *prev; 7 YY_BUFFER_STATE bs; 8 int lineno; 9 char *filename; 10 FILE *f; 11 } *curbs = 0; 12 char *curfilename; 13 int newfile(char *fn); 14 int popfile(void); 15 %} 16 17 %% 18 [ \t]*#include[ \t]*[\"|<] { 19 BEGIN IFILE; 20 } 21 22 <IFILE>[^ \t\n\">]+ { 23 { 24 int c; 25 while((c = input()) && c != '\n'); 26 } 27 yylineno++; 28 if(!newfile(yytext)) yyterminate(); 29 BEGIN INITIAL; 30 } 31 32 <IFILE>.|\n { 33 fprintf(stderr, "%4d bad include line\n", yylineno); 34 } 35 36 <<EOF>> { if(!popfile()) yyterminate(); } 37 38 ^. { fprintf(yyout, "%4d %s", yylineno, yytext); } 39 40 \n { ECHO; yylineno++; } 41 %% 42 43 main(int argc, char **argv) 44 { 45 if(argc<2) { 46 fprintf(stderr, "need filename\n"); 47 return 1; 48 } 49 if(newfile(argv[1])) yylex(); 50 } 51 52 int newfile(char *fn) 53 { 54 FILE *f = fopen(fn, "r"); 55 struct bufstack *bs = malloc(sizeof(struct bufstack)); 56 if(!f){ 57 perror(fn); 58 return 0; 59 } 60 if(!bs){ 61 perror("malloc"); 62 exit(1); 63 } 64 if(curbs) curbs->lineno = yylineno; 65 bs->prev = curbs; 66 bs->bs = yy_create_buffer(f, YY_BUF_SIZE); 67 bs->f = f; 68 bs->filename = fn; 69 yy_switch_to_buffer(bs->bs); 70 curbs = bs; 71 yylineno = 1; 72 curfilename = fn; 73 return 1; 74 } 75 76 int popfile(void) 77 { 78 struct bufstack *bs = curbs; 79 struct bufstack *prevbs; 80 if(!bs) return 0; 81 fclose(bs->f); 82 yy_delete_buffer(bs->bs); 83 prevbs = bs->prev; 84 free(bs); 85 if(!prevbs) return 0; 86 yy_switch_to_buffer(prevbs->bs); 87 curbs = prevbs; 88 yylineno = curbs->lineno; 89 curfilename = curbs->filename; 90 return 1; 91 }
编译代码:
flex text.l
gcc -o test lex.yy.c -lfl
创建test.txt、test2.txt,里面内容分别为:
#include "test2.txt"
local i = 1;
local x = 2
测试:
./test test.txt
输出:
1 local x = 2 2 local i = 1;
源码分析:
第1行的%option 为flex选项,noyywrap表示不调用yywrap()函数。yywrap()函数可以用于扫描多个源文件。
其它选项有:
%option yylineno // 自动使用yylineno记录符号所在行号
%option case-insensitive // 匹配时自动忽略大小写,比如[a-z]也可以匹配A-Z字符,但不改变yytext里字符的大小写。
%option debug // 扫描器在debug模式下运行
第2行的%IFILE定义一个状态,用户可以定义多个状态。flex本身有一个默认状态INITIAL,规则区的正则表达式前加入<IFILE>表示该正则表达式只在IFILE状态时启用后面的处理例程。没有加状态的正则表达式则是在默认状态下匹配。
源代码第36行的<<EOF>>是指扫描任意源文件结束时调用后面例程:将当前文本出栈
bufstack是一个栈结构,当扫描器匹配到#include时,则进入IFILE状态,接下来读取紧跟的文件名,然后调用newfile记录当件文本当前扫描位置并入栈,读取新的文本,然后进入默认状态。在默认状态下匹配\n ^.等规则。ECHO为回车。