编译原理实验四.docx
《编译原理实验四.docx》由会员分享,可在线阅读,更多相关《编译原理实验四.docx(7页珍藏版)》请在冰豆网上搜索。
编译原理实验四
实验四算符优先文法处理算术表达式与赋值语句
一、实验目的
算术表达式和赋值语句的文法可以是(你可以根据需要适当改变):
S→i=E
E→E+E|E-E|E*E|E/E|(E)|i
根据算符优先分析法,将赋值语句进行语法分析,翻译成等价的一组基本操作,每一基本操作用四元式表示。
二、估计实验时间
1.课余准备15小时;2.上机三次6小时;3.完成实验报告5小时。
三、实验过程和指导
(一)准备:
1.阅读课本有关章节,花一周时间确定算术表达式的文法,设计出算符优先关系表;2.考虑好设计方案;3.设计出模块结构、测试数据,初步编制好程序。
(二)上课上机:
上机调试,发现错误,分析错误,再修改完善。
教师根据学生的设计方案与学生进行探讨,以修改方案和代码。
(三)程序要求:
程序输入/输出示例:
如参考C语言的运算符。
输入如下表达式(以分号为结束)和输出结果:
(1)a=10;
输出:
(=,a,10,-)
(2)b=a+20;
输出:
(=,r1,20,-)
(+,r2,a,r1)
(=,b,r2,-)
注:
此例可以进行优化后输出(不作要求):
(+,b,a,20)
(3)c=(1+2)/3+4-(5+6/7);
输出:
(+,r1,1,2)(/,r2,r1,3)(/,r3,6,7)(+,r4,5,r3,)(+,r5,r2,4)(-,r6,r5,r4)(=,c,r6,-)
注:
输出格式有多种,如上面第二个括号可改为(/,r1,r1,3)以节约变量(当然后面相应的变量引用也要改变)。
注意:
1.表达式中允许使用运算符(+-*/)、分割符(括号)、常量(无符号整数)和变量;
2只要能表达正确的计算过程,任何输出方式都可以;
3.如果遇到错误的表达式,应输出错误提示信息(该信息越详细越好);
4.测试用的表达式事先放在文本文件中,一行存放一个表达式,同时以分号分割。
同时将预期的输出结果写在另一个文本文件中,以便和输出进行对照;
5.其中临时变量名可不用r1,r2,也可用别的形式代替,如r[1],r[2]等。
6.为了降低难度,文法可适当降低要求,如只处理含有+-()的表达式,也可只处理+-*/的表达式;
7.对学有余力的同学,可增加一个函数来计算四元式,最后输出被赋值变量的值(如果表达式中含有变量,则设它为0),计算过程用浮点表示,但要注意不要被0除。
程序思路(仅供参考):
1.借用实验二的结果,可将其中的取字符函数几乎原封不动地移植过来,其中的分割和分析单词的方法可借用过来分割现在这个实验的运算符、常量和变量。
2.模块结构:
(1)初始化:
设立算符优先关系表(或优先函数)、初始化变量空间(包括堆栈、结构体、数组、临时变量等);
(2)控制部分:
将一个表达式从文件中读出;
(3)词法分析:
将表达式分割成单词序列;
(4)利用算符优先文法进行表达式处理:
根据算符优先关系表(或优先函数)对表达式单词序列进行堆栈(或其他)操作,得到并保存四元组,如果遇到错误则显示错误信息;
(5)输出四元组。
(四)练习该实验的目的和思路:
程序相当复杂,需要利用到大量的编译原理,也用到了大量编程技巧和数据结构,通过这个练习可大大提高软件开发能力。
程序规模大概为400行。
本实验的结果可作为课程设计的基础。
通过练习,掌握对表达式进行处理的一种方法。
(五)为了能设计好程序,主意以下事情:
1.模块设计:
将程序分成合理的多个模块(函数),每个模块做具体的同一事情。
2.写出(画出)设计方案:
模块关系简图、流程图、全局变量、函数接口等。
3.编程时注意编程风格:
空行的使用、注释的使用、缩进的使用、变量合理命名等。
四、上交
1.程序源代码(主文件名为by3);
2.已经测试通过的测试数据10组(全部存在文件中,以“第一组输入/输出/第二组输入/输出/第三组输入/输出……”的格式存放);
3.实验报告:
(1)功能描述:
该程序具有什么功能
(2)程序结构描述:
函数调用格式、参数含义、返回值描述、函数功能;函数之间的调用关系图、程序总体执行流程图(参考课本第二章)。
(3)实验过程记录:
出错次数、出错严重程度、解决办法摘要。
(4)实验总结:
你在编程过程中花时多少多少时间在纸上设计多少时间上机输入和调试多少时间在思考问题遇到了哪些难题你是怎么克服的你对你的程序的评价你的收获有哪些
五、参考源代码
...
...
h=='#')o][Peek(n0).No];
if(r=='<')h=='E'&&Peek
(1).ch=='#'&&Token[ipToken].ch=='#')
returnTRUE;
else
returnFALSE;
}
h)
{
k=TRUE;h=='E')
{
n++;
}
returnn;
}
词结束(遇到“#”号),无法移进,需要规约,返回:
1
词没有结束,需判断是否可以移进
栈单词<=单词:
移进后返回:
2
栈单词>单词:
不能移进,需要规约,返回:
1
单词没有优先关系:
出错,返回:
-1
intMoveIn()
{
STokens,t;h='#';
Token[TokenNumber].No=O_NUL;
returnTRUE;h='+';
Token[TokenNumber].No=O_PLUS;
GetChar();
break;
case'-':
Token[TokenNumber].ch='-';
Token[TokenNumber].No=O_MINUS;
GetChar();
break;
case'*':
Token[TokenNumber].ch='*';
Token[TokenNumber].No=O_TIMES;
GetChar();
break;
case'/':
Token[TokenNumber].ch='/';
Token[TokenNumber].No=O_SLASH;
GetChar();
break;
case'(':
Token[TokenNumber].ch='(';
Token[TokenNumber].No=O_L_PAREN;
GetChar();
break;
case')':
Token[TokenNumber].ch=')';
Token[TokenNumber].No=O_R_PAREN;
GetChar();
break;
default:
if(ch>='0'&&ch<='9')h='i';
Token[TokenNumber].No=O_IDENT;
}
else
{
return!
MakeErr("表达式中含有非法字符。
");
}
break;
}
TokenNumber++;
}
}
//从表达式缓冲区中取到下面第一个非空字符
//成功:
返回字符;不成功:
返回'\0'
charGetFirstChar()
{
while(GetChar()!
='\0')
{
if(ch>32)returnch;
}
return'\0';
}
//从表达式缓冲区取一个字符,返回该字符的同时将它存于全局变量ch中
//成功:
返回字符;不成功:
返回'\0'
charGetChar()
{
if((ch=Buffer[ipBuffer])!
='\0')
ipBuffer++;
returnch;
}
//生成错误信息
//错误信息存于全局变量ErrMsg中
//返回:
TRUE
boolMakeErr(char*ErrMassage)
{
ErrMsg=ErrMassage;
returnTRUE;
}