一:第一个简单的lex例程.
%%
.|\n ECHO;
%%
int yywrap() {return 1;}
void main(){yylex();}
文件命名为
a.l; 运行
lex a.l ; cc yy.tab.c; 生成可执行程序.
yywrap是当解析到文件结尾时调用.该函数返回1. 表示解析结束.否则没有.此函数可以不写,包含lex库可以得到此函数.
cc yy.tab.c -ll
main里面主要调用yylex,这是解析的入口.
二:lex文件语法:文件分为三部分:
定义段此段中需要原封不动拷贝到C程序中的代码段需要用"%{" 开始 "%}"结束包围起来,定义段还可以定义规则,并在规则段使用.如:word [^ \t\n]+,在规则段中,只要使用{word} 后跟行为就可以了.
%%
规则段 此处定义规则.一个规则有两部分组成: 1. 规则; 2 行为(action);中间用空格分离,行为";" 或者 "{}"则表示忽略. "|" 是一种特殊的action; 表示此处的action 与下一个rule相同.
%%
用户程序区:
此处跟上面不一样的是,此部分全部拷贝到c代码中,而不需要%{%};
规则部分会被lex解析成一个函数yylex();行为部分会被拷贝到yylex函数中,所以行为的return会导致yylex函数的返回,否则yylex是不会返回的.而且yylex能记住当前读到哪儿了.再次调用时继续读.
lex是进行词法解释.所以对于解析出来的只有三个相关的属性.1,该词的类型.用return返回.2.该词的值:保存在yylval中.3.该词的文本内容:保存于yytext中.
yylval是一个可以自己定义类型的变量.可以根据程序的需要进行定义.
三: lex的一些自带变量:
- yytext: char 型数组,记录了当前匹配到的单词
- yyin : FILE* 是当前打开的需要分析的文件.
- yyleng: int 记录了当前已经扫描的单词的长度.生成的代码中有yytext[yyleng] = 0;
- %s 这是一个特殊的关键字,开启lex的一个新状态.
- yylval: yylval是一个自定义类型的变量,保存了解析到的单词的值.详细参见上一条.
四:yyac文件语法: 与lex的相同分为三个部分.
第二部分是规则段.
规则由名字(single name) , 冒号(:), 一系列的标识符(symble)和行为代码段(action code),以分号(;)结束.和lex相同只有当行为为空时分号才是必须的.
规则的冒号右边可以使用 "|", 表示左边的规则名字相同 (这与lex
不同,lex表示右手边相同,yacc表示左手边相同). 规则一旦满足,yyparse()程序就会执行行为部分的代码.
yyparse()
函数的返回: yyparse()函数的主要任务是匹配总规则(initial
rule,就是规则部分的第一个规则),一旦匹配且匹配到文件结束,函数就返回,这与yylex不同.如果把总规则理解为可见的规则加文件结束符,则就可
以理解为一旦匹配到文件结束,yyparse就返回.否则继续等待. 当yyparse()返回后重新进入,则会重置所有状态.
五:yylex可以使用的一些关键字:
- token 定义一个token类型.被解析为c后是一个#define *** 1; 在lex中解析到这个token的实际代表的单词时,可以返回该宏,共y文件识别用.
- start 定义了开始标志符.如果没有此关键字,则默认第一条规则左边的标识符为开始标识符.
六:lex的默认处理:
当lex进行词法分析时遇到了无法匹配的内容是默认输出:相当于每个规则部分最后一句都隐含了".|\n ECHO" 时刻记住此规则的存在.否则有些错误就无法理解了.
验证方法:
%{
//#undef output
//#define output(c) putc(c,yyout);putc('\n',yyout);
%}
%%
hello printf("HELLO\n");
hi printf("HI\n");
%%
void main()
{
yylex();
}
此代码只识别hello和hi.那么对于其他的没有输入的怎么办呢?程序默认输出.
所以想程序输入hellolshi时输出为:
HELLO
lsHI
默认输出了ls,且ls的输出是一个字母一个字母输出的.具体程序中是使用了output输出的.
如果我们去掉代码中的注释,就会输出
HELLO
l
s
HI
所以当程序不是对所有的组合都感兴趣是一定要忽略掉其他的组合.
注意:
由于对.的默认处理如果有问题,会能看出来,但是如果对于\n的默认处理就不那么容易看出来了. 而且如果规则中无意包含了对\n的匹配.则\n的默认处理不存在.代码则会出现意向不到问题.如:
%%
[ \t]+ ;
[^ \t]+ printf("%s\n",yytext);
%%
void main()
{
yylex();
}
此段代码看似是以空白为分割符,将每个单词输出的.
但是实际运行时如果输入 hello hi haha
则输出仅为
hello
hi
而haha需要等到下次才会输出.这是因为第二个规则默认包含了\n,所以haha\n在等待下一个最长匹配.
七:lex yacc的编译注意事项:
编译lex的程序需要l库.编译yacc的程序需要y库.两个库中都默认带有调用yylex或者yyparse的main函数.如果同时需要lex和yacc的库,则需要将y放在前面.否则就调用了lex的库.
阅读(984) | 评论(0) | 转发(0) |