所有实验的源代码:点此下载

实验目的:

学会用lex设计一个词法分析器,并考虑其与后续语法分析器的链接问题。

实验内容:

修改上次实验2的词法分析器,使其满足下列要求。

实验要求:

  1. 要求每次调用词法分析函数yylex时,只返回一个记号(token);
  2. 为记号选择适当的属性值,并且每次词法分析函数返回记号前,都将记号的属性值存入全局变量yylval中。(yylval可以自己定义为全局变量);
  3. 记号属性值的选择:标识符的属性为标识符的名字字符串(例如,标识符name1的属性为字符串”name1”),整数的属性为整数值,浮点数的属性为浮点数值。其他记号属性值可自己选择。关键字可以省略属性。
  4. 注意:由于属性值需要存入yylval中,并且记号属性值的类型比较多(可能为字符串、整数、浮点数等),因此yylval必须能同时存放各种类型的值(提示:将yylval设置为union类型)。
  5. 在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中的属性值输出,那么就可以判断自己所做的修改是否正确了。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐