编译原理实验三:用lex设计词法分析器2
实验目的:学会用lex设计一个词法分析器,并考虑其与后续语法分析器的链接问题。实验内容:修改上次实验2的词法分析器,使其满足下列要求。实验要求:要求每次调用词法分析函数yylex时,只返回一个记号(token);为记号选择适当的属性值,并且每次词法分析函数返回记号前,都将记号的属性值存入全局变量yylval中。(yylval可以自己定义为全局变量);记号属性值的选择:标识符的属性为标识符的名字字符
所有实验的源代码:点此下载
实验目的:
学会用lex设计一个词法分析器,并考虑其与后续语法分析器的链接问题。
实验内容:
修改上次实验2的词法分析器,使其满足下列要求。
实验要求:
- 要求每次调用词法分析函数yylex时,只返回一个记号(token);
- 为记号选择适当的属性值,并且每次词法分析函数返回记号前,都将记号的属性值存入全局变量yylval中。(yylval可以自己定义为全局变量);
- 记号属性值的选择:标识符的属性为标识符的名字字符串(例如,标识符name1的属性为字符串”name1”),整数的属性为整数值,浮点数的属性为浮点数值。其他记号属性值可自己选择。关键字可以省略属性。
- 注意:由于属性值需要存入yylval中,并且记号属性值的类型比较多(可能为字符串、整数、浮点数等),因此yylval必须能同时存放各种类型的值(提示:将yylval设置为union类型)。
- 在cygwin下用flex和gcc工具将实验调试通过,并能通过例子parser0中testcases目录下的test1.p测试例的测试。
重要算法或重要的文法正规式:
这个实验需要在实验二的基础上进行修改,以考虑词法分析器和后续语法分析器的链接问题。语法分析器每次调用词法分析器,词法分析器都会返回一个记号。通过前面的学习,已经知道记号是个二元组,它是由记号名和属性值组成的,记号名是以函数的返回值的形式直接返回给语法分析器,它实际上是一个宏定义的整数,属性值是通过yylval这个全局变量来传递的。对于像关键字、标点符号、算符等,可以不使用它的属性值,而像整数、浮点数、标识符等,它们的属性值的类型是不同的,因此可以考虑将yylval定义为union类型,使其可以传递不同类型的属性值。union的类型定义如下:
union U{
int i;
float f;
char* id;
};
当词法分析器识别出一个整数时,执行动作sscanf(yytext, “%d”, &yylval.i)以将这个整数的值保存到yylval中;当识别出一个浮点数时,执行动作sscanf(yytext, “%f”, &yylval.f)以将这个浮点数的值保存到yylval中;当识别出一个标识符时,执行动作yylval.id = yytext以将这个标识符保存到yylval中。
为了验证上面所做的工作是有效果的,我们改写了输出函数,修改之后的结果如下。
case INT: fprintf(yyout, "(INT, \"%d\")",yylval.i);break;
case FLOAT: fprintf(yyout, "(FLOAT, \"%.2f\")",yylval.f);break;
case ID: fprintf(yyout, "(ID, \"%s\") ", yylval.id);break;
可以看出,将原先的yytext换成了相应的属性值,只要是现在的输出结果和使用yytext的输出结果相同,那么就能证明我们所做的修改是正确的。
有一个地方需要说明一下,在真正和语法分析器链接时,yylval并不需要在词法分析器中定义,它其实是在语法分析器中定义好了的,只需要在词法分析器的代码中包含其头文件即可。
实验结果:
源代码:
/* 把讨厌的注释去掉 */
%{
#include <stdio.h>
#define LT 1
#define LE 2
#define GT 3
#define GE 4
#define EQ 5
#define NE 6
#define WHILE 18
#define DO 19
#define ID 20 //标识符
#define INT 21 //整数
#define FLOAT 25 //小数
#define RELOP 22
#define NEWLINE 23
#define ERRORCHAR 24
#define PROGRAM 26
#define BEGIN0 27
#define END 28
#define VAR 29
#define INTEGER 35
#define REAL 36
#define ASSIGNMENT 31 //赋值
#define IF 32
#define THEN 33
#define ELSE 34
#define PLUS 37
#define MINUS 38
#define TIMES 39
#define DIVIDE 40
#define SEMI 41
#define COLON 42
#define COMMA 43
#define DOT 44
union U{
int i;
float f;
char* id;
};
union U yylval;
%}
delim [ \t \n]
ws {delim}+
letter [A-Za-z]
digit [0-9]
int {digit}+
float \.{int}|{int}\.{int}
id {letter}({letter}|{digit})*
/* 状态(或条件)定义可以定义在这里
* INITIAL是一个默认的状态,不需要定义
*/
%s COMMENT
%s LINECOMMENT
%%
<INITIAL>"/*" { BEGIN COMMENT;}
<COMMENT>"*/" { BEGIN INITIAL;}
<COMMENT>.|\n { ;}
<INITIAL>"//" { BEGIN LINECOMMENT;}
<LINECOMMENT>\n { BEGIN INITIAL;}
<LINECOMMENT>. { ;}
/* ECHO是一个宏,相当于 fprintf(yyout, "%s", yytext)*/
<INITIAL>"IF" { return (IF);}
<INITIAL>"THEN" {return (THEN);}
<INITIAL>"ELSE" {return (ELSE);}
<INITIAL>"WHILE" { return (WHILE);}
<INITIAL>"DO" { return (DO);}
<INITIAL>"PROGRAM" {return (PROGRAM);}
<INITIAL>"BEGIN" {return (BEGIN0);}
<INITIAL>"END" {return (END);}
<INITIAL>"VAR" {return (VAR);}
<INITIAL>"INTEGER" {return (INTEGER);}
<INITIAL>"REAL" {return (REAL);}
<INITIAL>"<" {return (LT);}
<INITIAL>"<=" {return (LE);}
<INITIAL>">" {return (GT);}
<INITIAL>">=" {return (GE);}
<INITIAL>"=" {return (EQ);}
<INITIAL>"<>" {return (NE);}
<INITIAL>"+" {return (PLUS);}
<INITIAL>"-" {return (MINUS);}
<INITIAL>"*" {return (TIMES);}
<INITIAL>"/" {return (DIVIDE);}
<INITIAL>":=" { return (ASSIGNMENT);}
<INITIAL>";" {return (SEMI);}
<INITIAL>":" {return (COLON);}
<INITIAL>"," {return (COMMA);}
<INITIAL>"." {return (DOT);}
<INITIAL>{id} {yylval.id = yytext; return (ID);}
<INITIAL>{int} {sscanf(yytext, "%d", &yylval.i); return (INT);}
<INITIAL>{float} {sscanf(yytext, "%f", &yylval.f); return (FLOAT);}
<INITIAL>{ws} {;}
<INITIAL>. { return ERRORCHAR;}
%%
int yywrap (){
return 1;
}
void writeout(int c){
switch(c){
case ERRORCHAR: fprintf(yyout, "(ERRORCHAR, \"%s\") ", yytext);break;
case RELOP: fprintf(yyout, "(RELOP, \"%s\") ", yytext);break;
case WHILE: fprintf(yyout, "(WHILE, \"%s\") ", yytext);break;
case DO: fprintf(yyout, "(DO, \"%s\") ", yytext);break;
case PROGRAM: fprintf(yyout, "(PROGRAM, \"%s\") ", yytext);break;
case BEGIN0: fprintf(yyout, "(BEGIN, \"%s\") ", yytext);break;
case END: fprintf(yyout, "(END, \"%s\") ", yytext);break;
case VAR: fprintf(yyout, "(VAR, \"%s\") ", yytext);break;
case INTEGER: fprintf(yyout, "(INTEGER, \"%s\") ", yytext);break;
case REAL: fprintf(yyout, "(REAL, \"%s\") ", yytext);break;
case ID: fprintf(yyout, "(ID, \"%s\") ", yylval.id);break;
case NEWLINE: fprintf(yyout, "\n");break;
case ASSIGNMENT:fprintf(yyout, "(ASSIGNMENT, \"%s\")",yytext);break;
case IF:fprintf(yyout, "(IF, \"%s\")",yytext);break;
case THEN:fprintf(yyout, "(THEN, \"%s\")",yytext);break;
case ELSE:fprintf(yyout, "(ELSE, \"%s\")",yytext);break;
case LT:fprintf(yyout, "(LT, \"%s\")",yytext);break;
case LE:fprintf(yyout, "(LE, \"%s\")",yytext);break;
case GT:fprintf(yyout, "(GT, \"%s\")",yytext);break;
case GE:fprintf(yyout, "(GE, \"%s\")",yytext);break;
case EQ:fprintf(yyout, "(EQ, \"%s\")",yytext);break;
case NE:fprintf(yyout, "(NE, \"%s\")",yytext);break;
case PLUS:fprintf(yyout, "(PLUS, \"%s\")",yytext);break;
case MINUS:fprintf(yyout, "(MINUS, \"%s\")",yytext);break;
case TIMES:fprintf(yyout, "(TIMES, \"%s\")",yytext);break;
case DIVIDE:fprintf(yyout, "(DIVIDE, \"%s\")",yytext);break;
case SEMI:fprintf(yyout, "(SEMI, \"%s\")",yytext);break;
case COLON:fprintf(yyout, "(COLON, \"%s\")",yytext);break;
case COMMA:fprintf(yyout, "(COMMA, \"%s\")",yytext);break;
case INT: fprintf(yyout, "(INT, \"%d\")",yylval.i);break;
case FLOAT: fprintf(yyout, "(FLOAT, \"%.2f\")",yylval.f);break;
case DOT: fprintf(yyout, "(DOT, \"%s\")",yytext);break;
default:break;
}
return;
}
int main (int argc, char ** argv){
int c,j=0;
if (argc>=2){
if ((yyin = fopen(argv[1], "r")) == NULL){
printf("Can't open file %s\n", argv[1]);
return 1;
}
if (argc>=3){
yyout=fopen(argv[2], "w");
}
}
while (c = yylex()){
writeout(c);
j++;
if (j%5 == 0) writeout(NEWLINE);
}
if(argc>=2){
fclose(yyin);
if (argc>=3) fclose(yyout);
}
return 0;
}
实验结果截图:
在cygwin下用flex和gcc工具将实验调试通过,并能通过例子parser0中testcases目录下的test1.p测试例的测试。
实验中遇到的问题,解决方法、实验难点:
起初没能看明白实验要求,感觉无论改不改代码,运行的结果都是一样的,因为现在根本没有语法分析器来调用,只能是自己写个main函数模拟这个调用过程。后来老师和我们说,这个实验是为后续词法分析器和语法分析器的链接做铺垫的,如果这个实验做的好,后面的链接工作会相对容易,不过确实是无法从输出结果上来判断自己所做的修改是否正确。后来我们想到,在输出记号时,都是把yytext所指向的缓存输出,那如果改变一下思路,把存放到yylval中的属性值输出,那么就可以判断自己所做的修改是否正确了。
更多推荐


所有评论(0)