实验报告一编写词法分析程序.docx
《实验报告一编写词法分析程序.docx》由会员分享,可在线阅读,更多相关《实验报告一编写词法分析程序.docx(25页珍藏版)》请在冰豆网上搜索。
实验报告一编写词法分析程序
编译原理实验报告
实验名称:
编写词法分析程序_______
实验类型:
设计型实验
指导教师:
专业班级:
姓名:
学号:
实验地点:
实验成绩:
日期:
2017年4月15日
实验一编写语法分析程序
一、实验目的
1)通过设计、调试词法分析程序,掌握词法分析程序的设计工具,即有穷自动机,进一步理解自动机理论;
2)掌握正则文法和正则表达式转换成有穷自动机的方法及有穷自动机的实现方法;
3)会确定词法分析程序的输出形式及标识符与关键字的区分方法;
4)加深对课堂教学的理解,提高词法分析方法的实践能力,掌握使用实验环境的技能技巧以及程序的调试方法。
二、实验设计
1、写出TEST语言每条词法规则对应的正则文法或者正则表达式
1)标识符:
字母打头,后接任意字母或数字。
正则表达式:
(a|b|……|z|A|B……|Z)(0|1|……|9|a|b|……|z|A|B……|Z)*
2)保留字:
标符的子集,包括:
if,else,for,while,do,int,write,read。
正则表达式:
if|else|for|while|do|int|write|read
3)无符号整数:
由数字组成,但最高位不能为0,允许一位的0。
正则表达式:
((1……|9)(0|1|……|9)*)|0
4)分界符:
(、)、;、{、}
正则表达式:
(|)|;|{|}
5)运算符:
+、-、*、/、=、<、>、>=、<=、!
=、==
正则表达式:
+|-|*|/|=|<|>|>=|<=|!
=|==
6)注释符:
/**/
正则表达式:
/*(没有连续的*/的任意字符串|ℇ)*/
2、对每个文法或者正则表达式分别构造NFA
1)标识符:
(a|b|……|z|A|B……|Z)(0|1|……|9|a|b|……|z|A|B……|Z)*
2)无符号整数:
((1|2|……|9)(0|1|……|9)*)|0
3)分界符:
(|)|;|{|}
4)运算符:
+|-|*|/|=|<|>|>=|<=|!
=|==
5)注释符:
/*(没有连续的*/的任意字符串|ℇ)*/
3、将NFA合并,确定化,化简得到最终的DFA。
NFA:
DFA:
三、实验过程
1、完成整个实验的先后步骤
a)根据TEST语言的词法规则,分别写出每条规则的正则文法或者正则表达式;
b)将每一个正则文法或者正则表达式转换为NFA;
c)将多个NFA合并后进行确定化并化简;
d)根据化简后的DFA画出流程图;
e)参阅教材PP.69-71的TEST语言语法规则,确定单词分类、单词输出方案;
f)编写词法分析程序;
g)对下面的TEST语言源程序进行词法分析,将合法单词存入lex.txt,并报告词法错误及其位置。
注:
不能修改源程序
{
/*Thisatestprogram.*/
intabc;
int123;
intA$@;
inti;
intn;
intb,c;
int2a;
inta2;
readn;
n=012345;
for(i=1;i<=n;i=i+1)
{
abc=abc+i;
}
if(i!
=n)n=n+i;
if(!
n)b=b+c;
/*Theloopended
writeabc;
}
2、实验调试记录(问题表现,分析原因,解决方案,解决结果)
a)问题表现:
1.不能处理除号
2.不能处理不完整的注释符
3.对于”0123”这类字符串的处理不正确,我之前处理为直接报错说一位以上的数字首位不能为0
b)分析原因:
问题1,2的原因都是在“/”符号处理时出现的问题导致的,程序中出现bug使得一遇到‘/’就会进入死循环。
问题3,不应该直接报错说一位以上的数字首位不能为0,遇到0应该直接输出0这个单词,再接着读数字。
c)解决方案:
d)对于问题1,2,重新梳理逻辑,一步一步对照流程图和DFA来调试修改代码。
对于问题3,遇到0应该直接输出0这个单词,再接着读数字。
e)解决结果:
成功解决了程序遇到‘/’进入死循环问题和“0123”这类字符串的处理。
三、实验结果
列出实验结果并进行分析(含分步测试结果)。
lex.txt文件(存放编译的合法内容)内容:
1{{
2/*Thisatestprogram.*//*Thisatestprogram.*/
3intint
3IDabc
3;;
4intint
4NUM123
4;;
5intint
5IDA
5;;
6intint
6IDi
6;;
7intint
7IDn
7;;
8intint
8IDb
8IDc
8;;
9intint
9NUM2
9IDa
9;;
10intint
10IDa2
10;;
11readread
11IDn
11;;
12IDn
12==
12NUM0
12NUM12345
12;;
13forfor
13((
13IDi
13==
13NUM1
13;;
13IDi
13<=<=
13IDn
13;;
13IDi
13==
13IDi
13++
13NUM1
13))
14{{
15IDabc
15==
15IDabc
15++
15IDi
15;;
16}}
17ifif
17((
17IDi
17!
=!
=
17IDn
17))
17IDn
17==
17IDn
17++
17IDi
17;;
18ifif
18((
18IDn
18))
18IDb
18==
18IDb
18++
18IDc
18;;
四、讨论与分析
1.你的编写词法分析程序满足最长匹配原则吗?
如果满足请给出你的实现方案。
如果不满足请给出改进方案。
答:
不满足,我的处理先后顺序是:
标识符或保留字、数字、分界符、运算符(除开/)、除或者注释,我应该吧注释放在前面,因为一般来说注释都比其它类型符号长些。
改进措施便是将注释这一条词法规则最早处理。
2.给出你的单词分类方案,并说明理由。
答:
根据TEST语言可将单词分为六类:
a)标识符:
字母打头,后接任意字母或数字。
b)保留字:
标识符的子集,包括:
if,else,for,while,do,int,write,read。
c)无符号整数:
由数字组成,但最高位不能为0,允许一位的0。
d)分界符:
(、)、;、{、}
e)运算符:
+、-、*、/、=、<、>、>=、<=、!
=、==
f)注释符:
/**/
3.构建词法分析程序一般过程是怎样的?
答:
构建词法分析程序的一般过程:
1、根据词法规则写出正则文法或者正则文法。
2、为每一个正则表达式构造一个NFA,然后将多个NFA合并为一个NFA
3、将NFA转化成DFA,并且化简最小化DFA
4、确定单词的输出形式
5、根据化简后的DFA和单词输出程序构造词法分析程序
(主要部分:
通过实验对课程知识点的理解;回答实验指导书的实验思考提出的问题等)
五、附录:
关键代码(给出适当注释,可读性高)
#include
#include
#include
#include
#include
usingnamespacestd;
constintKWN=8;//关键字的个数
constintMAXSIZE=400;//标识符最长个数
charkword[KWN][10]={//关键字
"if",
"else",
"for",
"while",
"do",
"int",
"read",
"write"};
intline=1;//行号
interrors=0;//记录错误个数
ofstreamfout;//输出文件流
ifstreamfin;//输入文件流
ofstreamlexout;//存放合法单词的文件流
chartype[6][30]={
"ID",
"保留字",
"NUM",
"分界符",
"运算符",
"注释符"};
intmain()
{
intTEST();//函数声明
TEST();
if(errors==0)
{
cout<<"编译成功。
"<}
else
{
cout<<"编译失败。
共发现"<"<}
return0;
}
////判断是否为字母
intis_Char(charch)
{
if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))
{
return1;
}
return0;
}
////判断是否为无符号整数
intis_Uint(charch)
{
if('0'<=ch&&ch<='9')
{
return1;
}
return0;
}
////判断是否为分界符
intis_Deli(charch)
{
if(ch=='('||ch==')'||ch==';'||ch=='{'||ch=='}')
{
return1;
}
return0;
}
////判断是否为操作符
intis_Oper(charch)
{
charOperater[10]="+-*!
=><";//没有考虑/号
for(inti=0;i<8;i++)
{
if(ch==Operater[i])
{
return1;
}
}
return0;
}
////输入控制
intin(char&ch)
{
fin.get(ch);
if('\n'==ch)
{
line++;
}
if(fin.eof())
{
ch=EOF;
}
return1;
}
////输出控制
voidout(char*type,char*buf)
{
if(strcmp(type,"ID")==0||strcmp(type,"NUM")==0)
lexout<else
lexout<//cout<"<}
////编译程序主要的函数
intTEST()
{
intevent=0;//用于判断输入是否为文件末
//charfilename[300];//存储文件的路径
//////打开文件的操作
//打开编译程序存放合法单词的文件
lexout.open("lex.txt");
//打开用户的文件
//cout<<"请输入要编译的文件的路径:
"<reinput_in:
//cin.get(filename,300,'\n');
//charfilename[300]={"D:
\Software\MicrosoftVisualC++6.0\MicrosoftVisualStudio\MyProjects\编译原理实验一\in.txt"};
fin.open("in.txt");
if(fin==NULL)
{
cout<<"文件打开失败,请重新输入文件路径:
"<gotoreinput_in;
}
//cout<<"请输入词法分析结果文件存储路径:
"<reinput_out:
cin.clear();//清理输出缓冲
cin.sync();//清空流
//cin.get(filename,300,'\n');
//charfilename[300]={"D:
\Software\MicrosoftVisualC++6.0\MicrosoftVisualStudio\MyProjects\编译原理实验一\out.txt"};
fout.open("out.txt");
if(fout==NULL)
{
cout<<"文件打开失败,请重新输入文件路径:
"<gotoreinput_out;
}
//////开始判断
charbuf[300];
charch;
cin.clear();//清理输出缓冲
cin.sync();//清空流
in(ch);
while(!
fin.eof())
{
while(ch==''||ch=='\n'||ch=='\t'||ch=='\r')
{
in(ch);
}
//判断是否为标识符或保留字
if(is_Char(ch))
{
intt=0;
while(is_Char(ch))
{
buf[t++]=ch;
in(ch);
}
buf[t]='\0';
//判断保留字
intj=0;
for(;j{
if(strcmp(kword[j],buf)==0)
{
out(type[1],buf);
break;
}
}
//ID标识符
if(j>=KWN)
{
while(is_Char(ch)||is_Uint(ch))
{
buf[t++]=ch;
in(ch);
}
buf[t]='\0';
out(type[0],buf);
}
}
//判断是否为数字
elseif(is_Uint(ch))
{
intt=0;
while(is_Uint(ch))
{
buf[t++]=ch;
in(ch);
}
buf[t]='\0';
if(t==1)
{
out(type[2],buf);
}
elseif(buf[0]=='0')
{
inti=-1;
while(i{
out(type[2],&"0");
}
out(type[2],buf+i);
}
else
{
out(type[2],buf);
}
}
//判断是否为分界符
elseif(is_Deli(ch))
{
buf[0]=ch;
buf[1]='\0';
out(type[3],buf);
in(ch);
}
//判断是否为运算符(除开/)
elseif(is_Oper(ch))
{
if(ch=='+'||ch=='-'||ch=='*')
{
buf[0]=ch;
buf[1]='\0';
out(type[4],buf);
in(ch);
}
elseif(ch=='!
')
{
buf[0]=ch;
in(ch);
if(ch=='=')
{
buf[1]=ch;
buf[2]='\0';
out(type[4],buf);
in(ch);
}
else
{
cout<<"error"<<++errors<<"line"<'!
'不合法的符号!
"<}
}
elseif(ch=='>'||ch=='<'||ch=='=')
{
buf[0]=ch;
in(ch);
if(ch=='=')
{
buf[1]=ch;
buf[2]='\0';
out(type[4],buf);
in(ch);
}
else
{
buf[1]='\0';
out(type[4],buf);
}
}
}
elseif(ch=='/')//判断是除还是注释
{
intt=0;
buf[t++]=ch;
charch0;
in(ch0);
while
(1)
{
if(ch0==EOF)
{
cout<<"error"<<++errors<<"line"<匹配错误,缺少*/"<break;
}
ch=ch0;
buf[t++]=ch;
in(ch0);
if(ch=='*'&&ch0=='/')
{
buf[t++]=ch0;
buf[t]='\0';
out(type[5],buf);
break;
}
}
in(ch);
}
else
{
cout<<"error"<<++errors<<"line"<'"<in(ch);
}
}
fin.close();
fout.close();
lexout.close();
returnerrors;
}
/*
D:
\Software\MicrosoftVisualC++6.0\MicrosoftVisualStudio\MyProjects\编译原理实验一\in.txt
D:
\Software\MicrosoftVisualC++6.0\MicrosoftVisualStudio\MyProjects\编译原理实验一\out.txt
/*Thisatestprogram.
/*Theloopended
writeabc;
*//*
charch1=getc(fin);
while(true)
{
if(ch1==EOF)
{
printf("Line%d\t%s\t没有匹配!
\n",line,"错误:
");
break;
}
ch=ch1;
ch1=getc(fin);
if(ch=='*'&&ch1=='/')
break;
}
ch=getc(fin);
/*Theloopended
writeabc;
*/
六、实验者自评(主要从实验态度、方法、效果上给一个客观公正的自我评价)
我自认为实验态度很好,只是实验的方法不太好,实验的效果一般。
我是看着DFA图来敲的代码,而不是看流程图来写的代码,这导致我敲到后面出现好几个难以发现的bug,这两个bug花了我相当多的时间,后来为了解决这个问题,我画出流程图再来一点一点对照我的代码,最终终于发现了bug,过程相当艰辛,光只是敲代码画了足足两天时间。
实验效果一般,实验所花的总时长太长,效率不高。