用LEX(FLEX)生成PL语言的词法分析器

本文介绍了如何使用LEX/FLEX工具生成词法分析器,详细讲解了LEX/FLEX源文件的三个主要段落:定义段、规则段和用户子程序段,并通过实例说明了规则的编写方法,包括正则表达式的使用和规则的动作部分。

lex/flex的语法结构

lex/flex是通过处理其源文件来生词法和语法分析器的,源文件的扩展名为.l,其语法被分为三个部分:

/* 定义段 */
%{
%}
%%
/* 规则段 */
%%
/* 用户子程序段 */

三个段用 %% 进行分离。
1、定义段,这一部分一般是一些声明及选项设置等。C 语言的注释、头文件包含等一般就放在 %{ %} 之间,这一部分的内容会被直接复制到输出文件的开头部分;
2、规则段为一系列匹配模式和动作,模式一般使用正则表达式书写,动作部分为C代码:模式1 {动作1(C代码)},在输入和模式 1 匹配的时候,执行动作部分的代码;
一定要记住

注:上图一定要记住,因为写正规式的时候是要使用这些字符来表示对应的含义的。

3、用户子程序段,这里为 C 代码,会被原样复制到输出文件中,一般这里定义一些辅助函数等,如动作代码中使用到的辅助函数。

如何写一条规则

下面通过一个简单的例子来学习如何写一条规则:

/* 这里是定义段 */
%{
/* 这里的部分会被直接拷贝到生成的 .c 文件的开始部分,在这里可以包含需要使用的头文件,如 stdio.h
*/
#include <stdio.h>
%}
/* 下面是规则段 */
%%
/* 这里定义了四条规则,前面的部分是模式,处于一行的开始位置,后面的部分是动作。第一个模式是匹配连续的一个到多个字符,匹配到之后就将其打印出来。注意到 yytext,在输入匹配到该模式的时候,匹配的部分就存储在这个 yytext 里面,这里把它作为字符串直接输出就可以了;第二条规则的模式部分,就是匹配连续的一个或者多个数字,匹配到了之后,也是以字符串的形式输出;第三条规则的模式部分,就是匹配一个换行符了,并且匹配到之后就打印一个新行的信息;第四条规则的模式部分,是一个点。正则表达式里面这个也就是匹配任何除了 \n 之外的字符。因此,下面的规则就是,匹配到字符串,则将该字符串输出,匹配到连续数字,将其输出;匹配到换行符,打印一条信息;匹配到任何其他的字符,则直接忽略*/
[a-zA-Z]+       {printf("get string:%s\n", yytext);}
[0-9]+          {printf("get number:%s\n", yytext);}
\n              {printf("get new line\n"); }
.               { }
/* 下面是用户子程序段 */
%%
int yywrap() { return 1; }
int main(int argc, char** argv) {
    if (argc > 1) {
        if (!(yyin = fopen(argv[1], "r"))) {
            perror(argv[1]);
            return 1;
        }
    }
    while (yylex());
    return 0;
}

这里的lex/flex的规则就这么多,做过实验的就知道我是把实验中的相关知识照搬过来了,这个实验其实是为了锻炼大家写正规式。
第一关里只需弄清楚规则段怎么写就OK了:模式1 {动作1(C代码)}
模式1其实就是正规式,例如本关需要实现对以小写字母ab结尾的字符串(只包含大小写字母)的识别,如Helloab和Goab。那么这个正规式的最后两个字母就是ab,那么前面就是任意字符,可不可以为空呢?当然可以了所以用乘号,任意字符是[a-zA-Z],所以总结一下正规式就是[a-zA-Z]*ab。
{动作1(C代码)}根据题中要求是如Helloab: Hit!的形式,所以我就直接写了{printf("%s: Hit!\n",yytext);}。
OK,所以第一关的答案就出来了,这一条规则就是:

[a-zA-Z]*ab	{printf("%s: Hit!\n",yytext);}

是不是还能接受,其实只要懂了怎么写正规式,还是挺简单的。
第二关就有点难度喽。
做两点补充吧!
1、对于正规式,是可以取别名的,要放在定义段,比如:

INTCON			[\-]?[0-9][0-9]*

2、在规则段引用的时候需要注意,应该这么写:

{INTCON}			{printf("%s: INTCON\n", yytext);}

好了,根据提示就可以开始写了,要是自己写的话,注意顺序哦,别忘了还有一个类别ERROR。
这里我就直接贴代码了,要是自己有能力的话,还是自己手动做做吧!

 /* begin */
OFSYM			of
ARRAYSYM		array
MODSYM			mod
ANDSYM			and
ORSYM			or
NOTSYM			not
PROGRAMSYM		program
BEGINSYM		begin
ENDSYM			end
IFSYM			if
THENSYM			then
ELSESYM			else
WHILESYM		while
DOSYM			do
CALLSYM			call
CONSTSYM		const
TYPESYM			type
VARSYM			var
PROCSYM			procedure
OTHER			[~!@#$%^&_]?|\\
INTCON			[\-]?[0-9][0-9]*
IDENT			[A-Za-z][A-Za-z0-9]*
CHARCON			['][^']*[']
PLUS			\+
MINUS			\-
TIMES			\*
DIVSYM			\/
BECOME			:=
EQL				\=
NEQ				<>
LEQ				<=
LSS				\<
GEQ				>=
GTR				\>
LBRACK			\[
RBRACK			\]
LPAREN			\(
RPAREN			\)
COMMA			\,
SEMICOLON		\;
PERIOD			\.
COLON			\:


 /* end */

%%
 /* begin */
{OFSYM}				{printf("%s: OFSYM\n", yytext);}
{ARRAYSYM}			{printf("%s: ARRAYSYM\n", yytext);}
{PROGRAMSYM}		{printf("%s: PROGRAMSYM\n", yytext);}
{MODSYM}			{printf("%s: MODSYM\n", yytext);}
{ANDSYM}			{printf("%s: ANDSYM\n", yytext);}
{ORSYM}				{printf("%s: ORSYM\n", yytext);}
{NOTSYM}			{printf("%s: NOTSYM\n", yytext);}
{BEGINSYM}			{printf("%s: BEGINSYM\n", yytext);}
{ENDSYM}			{printf("%s: ENDSYM\n", yytext);}
{IFSYM}				{printf("%s: IFSYM\n", yytext);}
{THENSYM}			{printf("%s: THENSYM\n", yytext);}
{ELSESYM}			{printf("%s: ELSESYM\n", yytext);}
{WHILESYM}			{printf("%s: WHILESYM\n", yytext);}
{DOSYM}				{printf("%s: DOSYM\n", yytext);}
{CALLSYM}			{printf("%s: CALLSYM\n", yytext);}
{CONSTSYM}			{printf("%s: CONSTSYM\n", yytext);}
{TYPESYM}			{printf("%s: TYPESYM\n", yytext);}
{VARSYM}			{printf("%s: VARSYM\n", yytext);}
{PROCSYM}			{printf("%s: PROCSYM\n", yytext);}
{OTHER}				{printf("%s: ERROR\n", yytext);}
{INTCON}			{printf("%s: INTCON\n", yytext);}
{IDENT}				{printf("%s: IDENT\n", yytext);}
{CHARCON}			{printf("%s: CHARCON\n", yytext);}
{PLUS}				{printf("%s: PLUS\n", yytext);}
{MINUS}				{printf("%s: MINUS\n", yytext);}
{TIMES}				{printf("%s: TIMES\n", yytext);}
{DIVSYM}			{printf("%s: DIVSYM\n", yytext);}
{BECOME}			{printf("%s: BECOME\n", yytext);}
{EQL}				{printf("%s: EQL\n", yytext);}
{NEQ}				{printf("%s: NEQ\n", yytext);}
{LEQ}				{printf("%s: LEQ\n", yytext);}
{LSS}				{printf("%s: LSS\n", yytext);}
{GEQ}				{printf("%s: GEQ\n", yytext);}
{GTR}				{printf("%s: GTR\n", yytext);}
{LBRACK}			{printf("%s: LBRACK\n", yytext);}
{RBRACK}			{printf("%s: RBRACK\n", yytext);}
{LPAREN}			{printf("%s: LPAREN\n", yytext);}
{RPAREN}			{printf("%s: RPAREN\n", yytext);}
{COMMA}				{printf("%s: COMMA\n", yytext);}
{SEMICOLON}			{printf("%s: SEMICOLON\n", yytext);}
{PERIOD}			{printf("%s: PERIOD\n", yytext);}
{COLON}				{printf("%s: COLON\n", yytext);}

 /* end */

好了,至此第一个实验就做完了,是不是觉得自己学到了,那就点个赞呗,也不枉您白来一趟。

实验二 词法分析器 一、实验目的 掌握词法分析器的构造原理,掌握手工编程或LEX编程方法之一。 二、实验内容 编写一个LEX源程序,使之生成一个词法分析器,能够输入的源程序转换为单词序列输出。 三、实验环境 Flex+VC6.0 四、实验注意 1.Id正则表达式:{letter}({letter}|{digit})* 2.Num正则表达式:{digit}+(\.{digit}+)?(E[+-]?{digit}+)? 3.注释:(\/\*(.)*\*\/) 4.关键字再加上其他字符就又能编程id,所以在词法分析时,id的判断应该放在关键字前面,这样才不会误判 5.由于本程序知识简单的打印数字,因此没有考虑数字的转换 6.">="比">"多一个字符,它应该放在前面判断,其他类似的也应该如此安排 五、实验代码 ******************************************************************************* 实验文件:lex.l、lex.yy.c 实验结果:lex.exe 运行方式:打开lex.exe,弹出input.txt,在其中输入所要测试的程序,保存并关闭,即可在output.txt中看到所得结果 ******************************************************************************* %{ void Install(char *type); %} %option noyywrap delim [ \t] newline [\n] digit [0-9] num {digit}+(\.{digit}+)?(E[+-]?{digit}+)? letter [A-Za-z] id {letter}({letter}|{digit})* key ("if"|"while"|"do"|"break"|"true") basic ("int"|"float"|"bool"|"char") op (">="|""|"<"|"="|"!="|"+"|"-"|"*"|"/") comment (\/\*(.)*\*\/) %% delim {;} newline {printf("\n");} {num} {Install("Num");} {key} {Install("Key");} {basic} {Install("Basic");} {op} {Install("Op");} ";" {Install("Comma");} {id} {Install("ID");} {comment} {Install("Comment");} "(" | "[" | "{" {Install("lbracket");} ")" | "]" | "}" {Install("rbracket");} %% void Install(char *s) { fprintf(yyout, "%s:%s ", s, yytext); } int main() { printf("please input the test program in input.txt\n"); system("input.txt"); yyin = fopen("input.txt", "r"); yyout = fopen("output.txt", "w" ); yylex(); fclose(yyout); fclose(yyin); printf("analysis result in output.txt\n"); system("output.txt"); return 0; } 六、实验小结 本次的实验由于使用了flex,所以代码较短,麻烦的事flex的正则式表达,由于该使用规则只有简单介绍,而网上找的教程难免有比重就轻之嫌,所以得到上述表达式着实费力,且有的没有成功,例如bracket的(\ ((.)*\ ))或者("("(.)*")")使用时都没有成功,所以便单独写出,有点不伦不类。至于其他的,都较为简单,完。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JYL_CG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值