通达学院
题
专
学
班
指
指
日II 词法分析程序的构造 业 生 姓 名 级 学 号 导 教 师 导 单 位 计算机学院计算机科学与技术系 期 专业课程设计目:
词法分析程序的构造
一、 课题内容和要求
通过状态转换图构造C或者PASCAL语言子集的词法分析程序。
原理解析:选取语言,例如选取了C语言,选取其中一个子集,例如包含了部分关
键字main、float、if、for等等,特殊符号( 、
的标识符变量以及部分常量等,采用《编译原理》词法分析中有穷自动
机的思想构建出该语言子集的状态转换图,并编码实现。
基本要求:(1)将选取的语言子集编写一个简单程序,放在一个文本文件中;
(2)要将一个个单词区分清楚并归类(例如for属于关键字)。
二、需求和思路分析
本课题是用C++语言设计,选取的是C语言子集。编写对简单语言进行词法分
析的词法分析程序。
1、识别子集中的关键字、标识符、常数、运算符和分界符等。
2、对子集中的字符类型进行归类
三、概要设计
(1)状态转换图:
(2)核心代码:
1)定义:
char cbuffer;
char*keyword[14]={"if","else","for","while","do","float","return","break","continue","int","void
","main","const","printf"}; //关键字
char *border[8]={ "," , ";" , "{" , "}" , "(" , ")" ,":=","."}; //分隔符
char *arithmetic[6]={"+" , "-" , "*" , "/" , "++" , "--"}; //运算符
char *relation[7]={"" , ">=" , "==" ,"!="};
char *lableconst[80];
2)函数调用: search(char searchchar[],int wordtype)//查找类型
alphaprocess(char buffer) //字符处理过程
digitprocess(char buffer) //数字处理过程
otherprocess(char buffer) //分隔符、运算符、逻辑运算符等
main()//主函数
3) 状态类型:
状态转换图的形式:
■每个状态对应一个带标号的case语句 ■转向边对应goto语句
switch (wordtype)
{
case 1:
{ for (i=0;i
{
if (strcmp(keyword[i],searchchar)==0)
return(i+1);
}
return(0);}
case 2:
{
for (i=0;i
{
if (strcmp(border[i],searchchar)==0)
return(i+1);
}
return(0);
//关系运算符 //标识符
}
case 3:
{
for (i=0;i
{
if (strcmp(arithmetic[i],searchchar)==0)
return(i+1);
}
return(0);
}
case 4:
{
for (i=0;i
{
if (strcmp(relation[i],searchchar)==0)
return(i+1);
}
return(0);
}
case 5:
{
for (t=40;t
{
if (strcmp(searchchar,lableconst[t])==0)//判断该常数是否已出现过
return(t+1);
}
lableconst[t-1]=(char *)malloc(sizeof(searchchar)); //为新的元素分配内存
空间
strcpy(lableconst[t-1],searchchar); //为数组赋值lableconst
指针数组名
constnum++; //常数个数自加
return(t);
}
case 6:
{
for (i=0;i
{
if (strcmp(searchchar,lableconst[i])==0) //判断标识符是否已出现过
return(i+1);
}
lableconst[i-1]=(char *)malloc(sizeof(searchchar));
strcpy(lableconst[i-1],searchchar);
lableconstnum++; //标识符个数自加
return(i);
}
5) 单字符判断
if (otypetp=search(othertp,3)) //判断该运算符是否是由连续的两个字符组成
的
{
cout
fp.get(buffer);
goto out;
}
else //单字符逻辑运算符
{
othertp[1]='\0';
cout
"
goto out;
}
四、详细设计
实验环境:visual C++6.0 win7系统
源程序代码:
#include
#include
#include
#include
#include
using namespace std;
ifstream fp("09002801.txt",ios::in);
char cbuffer;
char *keyword[14]={"if","else","for","while","do","float","return","break","continue","int","void"
,"main","const","printf"}; //关键字
char *border[8]={ "," , ";" , "{" , "}" , "(" , ")" ,":=","."}; //分隔符
char *arithmetic[6]={"+" , "-" , "*" , "/" , "++" , "--"}; //运算符
char *relation[7]={"" , ">=" , "==" ,"!="}; //关系运算符 char *lableconst[80]; //标识符
int constnum=40;
int lableconstnum=0; //统计常数和标识符数量
int row=1;
int search(char searchchar[],int wordtype)
{
int i=0,t=0;
switch (wordtype)
{
case 1:
{ for (i=0;i
{
if (strcmp(keyword[i],searchchar)==0)
return(i+1);
}
return(0);}
case 2:
{
for (i=0;i
{
if (strcmp(border[i],searchchar)==0)
return(i+1);
}
return(0);
}
case 3:
{
for (i=0;i
{
if (strcmp(arithmetic[i],searchchar)==0)
return(i+1);
}
return(0);
}
case 4:
{
for (i=0;i
{
if (strcmp(relation[i],searchchar)==0)
return(i+1);
}
return(0);
}
case 5:
{
for (t=40;t
{
if (strcmp(searchchar,lableconst[t])==0)//判断该常数是否已出现过
return(t+1);
}
lableconst[t-1]=(char *)malloc(sizeof(searchchar)); //为新的元素分配内存空间
strcpy(lableconst[t-1],searchchar); //为数组赋值lableconst指针数组名 constnum++; //常数个数自加
return(t);
}
case 6:
{
for (i=0;i
{
if (strcmp(searchchar,lableconst[i])==0) //判断标识符是否已出现过
return(i+1);
}
lableconst[i-1]=(char *)malloc(sizeof(searchchar));
strcpy(lableconst[i-1],searchchar);
lableconstnum++; //标识符个数自加
return(i);
}
default:cout
}
}
char alphaprocess(char buffer) //字符处理过程
{
int atype;
int i=-1;
char alphatp[20];
while ((isalpha(buffer))||(isdigit(buffer)))
//这两个函数分别是判字符和判数字函数位于ctype.h中 {
alphatp[++i]=buffer;
fp.get(buffer);
}
alphatp[i+1]='\0';//在末尾添加字符串结束标志
if (atype=search(alphatp,1))
cout
else
{
atype=search(alphatp,6); //标识符
cout
}
return(buffer);
}
char digitprocess(char buffer) //数字处理过程
{
int i=-1;
char digittp[20];
int dtype;
while ((isdigit(buffer)))
{
digittp[++i]=buffer;
fp.get(buffer);
}
digittp[i+1]='\0';
dtype=search(digittp,5);
cout
return(buffer);
}
char otherprocess(char buffer) //分隔符、运算符、逻辑运算符等
{
int i=-1;
char othertp[20];
int otype,otypetp;
othertp[0]=buffer;
othertp[1]='\0';
if (otype=search(othertp,3))
{
fp.get(buffer);
othertp[1]=buffer;
othertp[2]='\0';
if (otypetp=search(othertp,3)) //判断该运算符是否是由连续的两个字符组成的 {
cout
goto out;
}
else //单字符逻辑运算符
{
othertp[1]='\0';
cout
}
}
if (otype=search(othertp,4)) //关系运算符
{
fp.get(buffer);
othertp[1]=buffer;
othertp[2]='\0';
if (otypetp=search(othertp,4)) //判断该关系运算符是否是由连续的两个字符组成的 {
cout
goto out;
}
else //单字符逻辑运算符
{
othertp[1]='\0';
cout
}
}
if (buffer=='!') //"=="的判断
{
fp.get(buffer);
if (buffer=='=')
//cout
fp.get(buffer);
goto out;
else
{
if (otype=search(othertp,2)) //分界符
{
cout
fp.get(buffer);
goto out;
}
}
if ((buffer!='\n')&&(buffer!=' '))
cout
fp.get(buffer);
out:
return(buffer);
}
void main()
{
printf("=========================词法分析器==========================\n"); int i;
for (i=0;i
{
lableconst[i]=" ";//用于保存标识符
}
if (!fp)
cout
else
{
fp.get (cbuffer);
while (!fp.eof())
{
if(cbuffer=='\n')
{
row++;
fp.get(cbuffer);
}
else if (isalpha(cbuffer))
{
cbuffer=alphaprocess(cbuffer);
}
else if (isdigit(cbuffer))
cbuffer=digitprocess(cbuffer);
}
else
cbuffer=otherprocess(cbuffer);
}
cout
i=0;
while(i
{
cout
}
cout
cout
getchar();
}
}
(3)程序流程图:
五、测试数据及其结果分析
若源程序中没有09002801.txt文档,则会出现如下提示:
若文档中包含09002801.txt文档,文档代码为:
运行后结果如下:
六、调试过程中的问题
在代码调试过程中,由于代码编写时将主函数main和文件打开的顺序颠倒,导致未写入源文档也会提示词法分析结束,实际上并未进行词法分析过程,应该提示文件打开错,返回检查。在重新修改之后能够达到理想要求编译运行。
七、专业课程设计总结
通过这次实验,我对编译原理关于词法分析有了进一步的了解,能够把《编译原理》中有穷自动机的理论知识应用于实验中。
通过这次语义分析的实验, 我对高级语言的学习有了更深的认识,了解得更透彻我了解了高级语言转化为目标代码或汇编指令的过程。对今后的学习将起很大的作用,对以后的编程有很大的帮助。本次实验通过查找书本和网上搜索了解了大致地实验思路,虽然只是完成了一个简单的程序,但在组织程序结构中学到了很多,加深了对编译原理词法编译器的理解,掌握了编译程序的实现方法和技术,巩固了前面所学的相关理论知识。虽然完成了本次课程设计,但代码还有一些优化的地方,因为可能出现的符号均是事先例举出来,并不能包含所有可能的符号,有一定的局限性,今后会多多锻炼语句的优化方面。
参考资料:
[1] 王汝传. 编译技术原理及其实现方法. 成都: 成都科技大学出版社, 1998.
[2] 吕映芝 等编著. 编译原理. 北京: 清华大学出版社, 1998.
[3] 陈火旺 等编著. 程序设计语言编译原理. 北京: 国防工业出版社, 2000.
通达学院
题
专
学
班
指
指
日II 词法分析程序的构造 业 生 姓 名 级 学 号 导 教 师 导 单 位 计算机学院计算机科学与技术系 期 专业课程设计目:
词法分析程序的构造
一、 课题内容和要求
通过状态转换图构造C或者PASCAL语言子集的词法分析程序。
原理解析:选取语言,例如选取了C语言,选取其中一个子集,例如包含了部分关
键字main、float、if、for等等,特殊符号( 、
的标识符变量以及部分常量等,采用《编译原理》词法分析中有穷自动
机的思想构建出该语言子集的状态转换图,并编码实现。
基本要求:(1)将选取的语言子集编写一个简单程序,放在一个文本文件中;
(2)要将一个个单词区分清楚并归类(例如for属于关键字)。
二、需求和思路分析
本课题是用C++语言设计,选取的是C语言子集。编写对简单语言进行词法分
析的词法分析程序。
1、识别子集中的关键字、标识符、常数、运算符和分界符等。
2、对子集中的字符类型进行归类
三、概要设计
(1)状态转换图:
(2)核心代码:
1)定义:
char cbuffer;
char*keyword[14]={"if","else","for","while","do","float","return","break","continue","int","void
","main","const","printf"}; //关键字
char *border[8]={ "," , ";" , "{" , "}" , "(" , ")" ,":=","."}; //分隔符
char *arithmetic[6]={"+" , "-" , "*" , "/" , "++" , "--"}; //运算符
char *relation[7]={"" , ">=" , "==" ,"!="};
char *lableconst[80];
2)函数调用: search(char searchchar[],int wordtype)//查找类型
alphaprocess(char buffer) //字符处理过程
digitprocess(char buffer) //数字处理过程
otherprocess(char buffer) //分隔符、运算符、逻辑运算符等
main()//主函数
3) 状态类型:
状态转换图的形式:
■每个状态对应一个带标号的case语句 ■转向边对应goto语句
switch (wordtype)
{
case 1:
{ for (i=0;i
{
if (strcmp(keyword[i],searchchar)==0)
return(i+1);
}
return(0);}
case 2:
{
for (i=0;i
{
if (strcmp(border[i],searchchar)==0)
return(i+1);
}
return(0);
//关系运算符 //标识符
}
case 3:
{
for (i=0;i
{
if (strcmp(arithmetic[i],searchchar)==0)
return(i+1);
}
return(0);
}
case 4:
{
for (i=0;i
{
if (strcmp(relation[i],searchchar)==0)
return(i+1);
}
return(0);
}
case 5:
{
for (t=40;t
{
if (strcmp(searchchar,lableconst[t])==0)//判断该常数是否已出现过
return(t+1);
}
lableconst[t-1]=(char *)malloc(sizeof(searchchar)); //为新的元素分配内存
空间
strcpy(lableconst[t-1],searchchar); //为数组赋值lableconst
指针数组名
constnum++; //常数个数自加
return(t);
}
case 6:
{
for (i=0;i
{
if (strcmp(searchchar,lableconst[i])==0) //判断标识符是否已出现过
return(i+1);
}
lableconst[i-1]=(char *)malloc(sizeof(searchchar));
strcpy(lableconst[i-1],searchchar);
lableconstnum++; //标识符个数自加
return(i);
}
5) 单字符判断
if (otypetp=search(othertp,3)) //判断该运算符是否是由连续的两个字符组成
的
{
cout
fp.get(buffer);
goto out;
}
else //单字符逻辑运算符
{
othertp[1]='\0';
cout
"
goto out;
}
四、详细设计
实验环境:visual C++6.0 win7系统
源程序代码:
#include
#include
#include
#include
#include
using namespace std;
ifstream fp("09002801.txt",ios::in);
char cbuffer;
char *keyword[14]={"if","else","for","while","do","float","return","break","continue","int","void"
,"main","const","printf"}; //关键字
char *border[8]={ "," , ";" , "{" , "}" , "(" , ")" ,":=","."}; //分隔符
char *arithmetic[6]={"+" , "-" , "*" , "/" , "++" , "--"}; //运算符
char *relation[7]={"" , ">=" , "==" ,"!="}; //关系运算符 char *lableconst[80]; //标识符
int constnum=40;
int lableconstnum=0; //统计常数和标识符数量
int row=1;
int search(char searchchar[],int wordtype)
{
int i=0,t=0;
switch (wordtype)
{
case 1:
{ for (i=0;i
{
if (strcmp(keyword[i],searchchar)==0)
return(i+1);
}
return(0);}
case 2:
{
for (i=0;i
{
if (strcmp(border[i],searchchar)==0)
return(i+1);
}
return(0);
}
case 3:
{
for (i=0;i
{
if (strcmp(arithmetic[i],searchchar)==0)
return(i+1);
}
return(0);
}
case 4:
{
for (i=0;i
{
if (strcmp(relation[i],searchchar)==0)
return(i+1);
}
return(0);
}
case 5:
{
for (t=40;t
{
if (strcmp(searchchar,lableconst[t])==0)//判断该常数是否已出现过
return(t+1);
}
lableconst[t-1]=(char *)malloc(sizeof(searchchar)); //为新的元素分配内存空间
strcpy(lableconst[t-1],searchchar); //为数组赋值lableconst指针数组名 constnum++; //常数个数自加
return(t);
}
case 6:
{
for (i=0;i
{
if (strcmp(searchchar,lableconst[i])==0) //判断标识符是否已出现过
return(i+1);
}
lableconst[i-1]=(char *)malloc(sizeof(searchchar));
strcpy(lableconst[i-1],searchchar);
lableconstnum++; //标识符个数自加
return(i);
}
default:cout
}
}
char alphaprocess(char buffer) //字符处理过程
{
int atype;
int i=-1;
char alphatp[20];
while ((isalpha(buffer))||(isdigit(buffer)))
//这两个函数分别是判字符和判数字函数位于ctype.h中 {
alphatp[++i]=buffer;
fp.get(buffer);
}
alphatp[i+1]='\0';//在末尾添加字符串结束标志
if (atype=search(alphatp,1))
cout
else
{
atype=search(alphatp,6); //标识符
cout
}
return(buffer);
}
char digitprocess(char buffer) //数字处理过程
{
int i=-1;
char digittp[20];
int dtype;
while ((isdigit(buffer)))
{
digittp[++i]=buffer;
fp.get(buffer);
}
digittp[i+1]='\0';
dtype=search(digittp,5);
cout
return(buffer);
}
char otherprocess(char buffer) //分隔符、运算符、逻辑运算符等
{
int i=-1;
char othertp[20];
int otype,otypetp;
othertp[0]=buffer;
othertp[1]='\0';
if (otype=search(othertp,3))
{
fp.get(buffer);
othertp[1]=buffer;
othertp[2]='\0';
if (otypetp=search(othertp,3)) //判断该运算符是否是由连续的两个字符组成的 {
cout
goto out;
}
else //单字符逻辑运算符
{
othertp[1]='\0';
cout
}
}
if (otype=search(othertp,4)) //关系运算符
{
fp.get(buffer);
othertp[1]=buffer;
othertp[2]='\0';
if (otypetp=search(othertp,4)) //判断该关系运算符是否是由连续的两个字符组成的 {
cout
goto out;
}
else //单字符逻辑运算符
{
othertp[1]='\0';
cout
}
}
if (buffer=='!') //"=="的判断
{
fp.get(buffer);
if (buffer=='=')
//cout
fp.get(buffer);
goto out;
else
{
if (otype=search(othertp,2)) //分界符
{
cout
fp.get(buffer);
goto out;
}
}
if ((buffer!='\n')&&(buffer!=' '))
cout
fp.get(buffer);
out:
return(buffer);
}
void main()
{
printf("=========================词法分析器==========================\n"); int i;
for (i=0;i
{
lableconst[i]=" ";//用于保存标识符
}
if (!fp)
cout
else
{
fp.get (cbuffer);
while (!fp.eof())
{
if(cbuffer=='\n')
{
row++;
fp.get(cbuffer);
}
else if (isalpha(cbuffer))
{
cbuffer=alphaprocess(cbuffer);
}
else if (isdigit(cbuffer))
cbuffer=digitprocess(cbuffer);
}
else
cbuffer=otherprocess(cbuffer);
}
cout
i=0;
while(i
{
cout
}
cout
cout
getchar();
}
}
(3)程序流程图:
五、测试数据及其结果分析
若源程序中没有09002801.txt文档,则会出现如下提示:
若文档中包含09002801.txt文档,文档代码为:
运行后结果如下:
六、调试过程中的问题
在代码调试过程中,由于代码编写时将主函数main和文件打开的顺序颠倒,导致未写入源文档也会提示词法分析结束,实际上并未进行词法分析过程,应该提示文件打开错,返回检查。在重新修改之后能够达到理想要求编译运行。
七、专业课程设计总结
通过这次实验,我对编译原理关于词法分析有了进一步的了解,能够把《编译原理》中有穷自动机的理论知识应用于实验中。
通过这次语义分析的实验, 我对高级语言的学习有了更深的认识,了解得更透彻我了解了高级语言转化为目标代码或汇编指令的过程。对今后的学习将起很大的作用,对以后的编程有很大的帮助。本次实验通过查找书本和网上搜索了解了大致地实验思路,虽然只是完成了一个简单的程序,但在组织程序结构中学到了很多,加深了对编译原理词法编译器的理解,掌握了编译程序的实现方法和技术,巩固了前面所学的相关理论知识。虽然完成了本次课程设计,但代码还有一些优化的地方,因为可能出现的符号均是事先例举出来,并不能包含所有可能的符号,有一定的局限性,今后会多多锻炼语句的优化方面。
参考资料:
[1] 王汝传. 编译技术原理及其实现方法. 成都: 成都科技大学出版社, 1998.
[2] 吕映芝 等编著. 编译原理. 北京: 清华大学出版社, 1998.
[3] 陈火旺 等编著. 程序设计语言编译原理. 北京: 国防工业出版社, 2000.