广工编译原理课程设计报告Word格式.docx
《广工编译原理课程设计报告Word格式.docx》由会员分享,可在线阅读,更多相关《广工编译原理课程设计报告Word格式.docx(15页珍藏版)》请在冰豆网上搜索。
(4)教学型编译程序:
PL/0
三、具体实现:
1.扩充赋值运算:
(1)语法树
(2)修改GetSym()方法(写出修改的代码)
elseif(CH=='
*'
){
GetCh();
if(CH=='
='
){SYM=TIMESBECOMES;
}
elseSYM=TIMES;
}elseif(CH=='
/'
){SYM=SLASHBECOMES;
(3)修改STATEMENT()方法(写出修改的代码)
caseIDENT:
i=POSITION(ID,TX);
if(i==0)Error(11);
elseif(TABLE[i].KIND==VARIABLE||TABLE[i].KIND==FLOATTYPE){/*ASSIGNMENTTONON-VARIABLE*/
GetSym();
if(SYM==BECOMES||SYM==TIMESBECOMES||SYM==SLASHBECOMES||SYM==PLUSBECOMES||SYM==MINUSBECOMES){
RELOP=SYM;
if(SYM!
=BECOMES){//若为除等于(或其他类似符号)则先把符号左边的变量值放入栈中GEN(LOD,LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR);
}elseError(13);
EXPRESSION(FSYS,LEV,TX);
if(RELOP==TIMESBECOMES){
GEN(OPR,0,4);
}elseif(RELOP==SLASHBECOMES){
GEN(OPR,0,5);
}elseif(RELOP==PLUSBECOMES){
GEN(OPR,0,2);
}elseif(RELOP==MINUSBECOMES){
GEN(OPR,0,3);
GEN(STO,LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR);
(4)运行测试(测试的PL0源码扩充单词的测试并贴运行结果截图)
PL0源码:
PROGRAMEX01;
VARA,B,C,D;
BEGIN
A:
=16;
A/=2;
WRITE(A);
//结果为8
B:
=4;
B*=2;
WRITE(B);
C:
=6;
C+=2;
WRITE(C);
D:
=10;
D-=2;
WRITE(D);
END.
(5)运行结果:
(6)出现的问题及解决
开始时在实现/=和*=操作时,*=的实现很顺利,而/=却一直没有得到理想的结果,通过与同学的讨论得知代码中除号指令的解析中,其实为除余操作,于是将%改为/,但是结果还是错误,经过调试发现是两个相除的数在栈中的位置相反了,正确的状态应该是除数位于次栈顶,而被除数位于栈顶。
解决:
在调用EXPRESSION函数解析/=右边表达式前,先将其左边变量的值放入栈中。
2.扩充语句(Pascal的FOR语句)
(1)
语法图
此处无修改,FOR、STEP、UNTIL及DO等关键字已放置关键字数组中,通过该数组便可识别。
caseFORSYM:
STATEMENT(SymSetUnion(SymSetNew(STEPSYM),FSYS),LEV,TX);
//处理for后面的赋值语句
CX3=CX;
GEN(JMP,0,0);
//用于第一次执行for语句时跳过step语句
CX1=CX;
//记录step后语句的代码位置
if(SYM==STEPSYM){
STATEMENT(SymSetUnion(SymSetNew(UNTILSYM),FSYS),LEV,TX);
//处理step后面的表达式
}elseError(8);
if(SYM==UNTILSYM){
CODE[CX3].A=CX;
//回填第一次进入for循环时的直接跳转地址
CONDITION(SymSetAdd(DOSYM,FSYS),LEV,TX);
//处理条件语句
if(SYM==DOSYM){
CX2=CX;
GEN(JPC,0,0);
//条件跳转,栈顶若为非真,则跳转到参数三位置,该位置待回填
STATEMENT(FSYS,LEV,TX);
//处理do后面内容
GEN(JMP,0,CX1);
//执行完for语句内的语句则重新跳回step处
CODE[CX2].A=CX;
//回填条件跳转
break;
(4)运行测试
VARA,SUM;
SUM:
=0;
FORA:
=1STEPA+=1UNTILA<
=100DO//求1~100的和
SUM+=A;
WRITE(SUM);
//结果为5050
实现FOR循环的前提是理解表达式处理函数及语句处理函数,关键是处理好跳转关系,例如第一次进入FOR循环时,生成FOR后面的表达式后应该有一个直接跳转跳过STEP语句进入UNTIL的条件判断;
而调用CONDITION处理UNTIL后的条件语句后,便有一个条件跳转跳到STEP后的表达式代码位置。
由于有了之前IF...ELSE...的参考及大致理解相关函数,因此实现时没出现太大问题。
3.增加类型:
(1)在GetSym中增加字符型及实数型数据的识别
elseif(CH=='
\'
'
NUM=CH;
){SYM=CHARACTER;
elseError(8);
elseif(CH>
0'
&
&
CH<
9'
){/*NUMBER*/
K=0;
NUM=0;
SYM=NUMBER;
do{
NUM=10*NUM+(CH-'
);
K++;
}while(CH>
.'
){//实现小数点后的识别
i=0;
M=0;
while(CH>
){//以下获取小数点后的值
J=0;
M=(CH-'
)*0.1;
while(J<
i){
M*=0.1;
J++;
NUM=NUM+M;
i++;
if(i>
LMAX)Error(30);
//LMAX为小数点后数字最大度
fprintf(FOUT,"
%f"
NUM);
(2)修改Block中的方法运行测试
if(SYM==CHARSYM){
CharDeclaration(LEV,TX,DX);
while(SYM==COMMA){GetSym();
if(SYM==SEMICOLON)GetSym();
elseError(5);
}while(SYM==IDENT);
}
if(SYM==FLOATSYM){
FloatDeclaration(LEV,TX,DX);
DoubleDeclaration(LEV,TX,DX);
(3)修改STATEMENT中的方法
elseif(TABLE[i].KIND==CHARTYPE||TABLE[i].KIND==VARIABLE||TABLE[i].KIND==FLOATTYPE){/*ASSIGNMENTTONON-VARIABLE*/
if(SYM==BECOMES||SYM==TIMESBECOMES||SYM==SLASHBECOMES
||SYM==PLUSBECOMES||SYM==MINUSBECOMES){
=BECOMES){//若为除等于(或其他类似)。
。
则先把符号左边的变量值放入栈中
GEN(LOD,LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR);
EXPRESSION(FSYS,LEV,TX);
}else{
Error(12);
break;
caseWRITESYM:
//根据不同类型进行相应输出
GetSym();
if(SYM==LPAREN){
do{
GetSym();
EXPRESSION(SymSetUnion(SymSetNew(RPAREN,COMMA),FSYS),LEV,TX);
switch(TABLE[i].KIND){
caseCHARTYPE:
GEN(OPR,0,17);
caseFLOATTYPE:
GEN(OPR,0,18);
default:
{GEN(OPR,0,14);
}
}while(SYM==COMMA);
if(SYM!
=RPAREN)Error(SBNUM);
elseGetSym();
GEN(OPR,0,15);
/*WRITESYM*/
(4)修改FACTOR中的方法(包含字符的识别处理)
while(SymIn(SYM,FACBEGSYS)){
if(SYM==IDENT){
i=POSITION(ID,TX);
if(i==0)Error(11);
else
switch(TABLE[i].KIND){
caseCONSTANT:
GEN(LIT,0,TABLE[i].VAL);
caseVARIABLE:
casePROCEDUR:
Error(21);
}elseif(SYM==NUMBER){
if(NUM>
AMAX){Error(31);
GEN(LIT,0,NUM);
//将表达式中的数字置于栈顶
}elseif(SYM==LPAREN){//左括号
EXPRESSION(SymSetAdd(RPAREN,FSYS),LEV,TX);
if(SYM==RPAREN)GetSym();
elseError(22);
}elseif(SYM==CHARACTER){
GEN(LIT,0,NUM);
//将字符放入栈顶
(5)修改ENTER中的方法
caseCHARTYPE:
TABLE[TX].vp.LEVEL=LEV;
TABLE[TX].vp.ADR=DX;
DX++;
(6)在Interpret指令OPR中增加字符输出和实数输出
case17:
Form1->
printcs(S[T]);
fprintf(FOUT,"
%c\n"
S[T]);
T--;
//增加字符输出
case18:
Form1->
printrs("
"
%d\n"
//增加实数型输出
(7)其它修改
1)增加相应的关键字char和float;
2)由于增加实型,因此将模拟栈的数组类型改为double型;
另目标指令结构体INSTRUCTION中的A,及全局变量NUM也改为double型
(8)运行测试:
CHARA,B;
FLOATC,D,SUM;
A'
;
B'
+2;
WRITE(B+2);
=123.321;
=321.123;
=C+D+0.0004;
(9)运行结果:
(10)出现的问题及解决
字符型的实现主要问题是在赋值、字符表达式的处理及输出三个方面。
赋值刚开始没有思路,之后借鉴VAR型的赋值,才清楚需要使用到栈这个媒介进入赋值:
先把字符值放入栈中,在将栈顶值传给字符型变量。
而字符表达式的处理在Factor里进行识别处理。
关于输出则需要在WRITESYM的识别处理中进行类型判断采取不同输出方式,并需新建一条指令专门用于字符输出
实数型的实现主要问题为:
1、数据获取:
小数点后的值的获取需要用到循环帮助实现;
2、变量更改:
在没有引入实数型前,用来模拟栈的数组类型为int型,而引入后,则需要将其更改为实数型,在程序中与栈值有关的那些变量类型也同样改为实数型。
4.增加注释;
(1)修改GetSym()方法(写出修改的代码)
elseif(CH=='
i=CH;
while(i!
||CH!
//Form1->
printcs(CH);
if(i!
CH!
)Error(19);
else{
//不能忽略,为下一次调用GetSym提供第一个字符,用于switch语句
){//单行注释功能
i=CX;
//记录当前行
while(CC!
=LL)GetCh();
//CC为当前字符在行中位置,LL为当前行的字数
//滤去注释后继续获取后面的字符串
}elseSYM=SLASH;
(2)运行测试
/*下面代码求解1到100的之间
每个整数的相加之和*/
(3)运行结果
(4)出现问题及解决
刚开始就认为注释功能的实现比较容易,但是在实现过程还是出现不少问题。
我的思路是在getSym方法中使用循环滤去注释的内容,过程不断调用getCh读取下一个字符。
在循环处理注释结束后需要调用自身GetSym,因为上次调用的GetSym方法都用于处理注释了。
然而/**/这种情况却一直有报19号错误,经过查找发现该情况的循环执行完后,当前CH为符号’/’,而不是’/’后的字符,因此需要在调用GetSym前先调用getCh以便获取下一个新字符用于GetSym中的switch判断。
//不存在此情况,其循环结束CH为注释结束的下一字符。
四、心得
经过了一周多的奋战,此次课程设计的实现过程让我收获颇多,同时也让我意识到自己在编码过程中的不足。
在没做课程设计之前,我对代码编译的实现过程和对PL0代码的体系结构都很陌生,而当一步一步从实现/=等、for循环、注释及增加类型等功能后,我对代码编译过程有了一个整体而深入的认识。
在实现的过程中,虽然出现很多问题,但是通过自己的思考和请教同学都能得到解决。
在实现的过程中,我发现自己在考虑功能的实现方面不够全面仔细,例如字符型的增加,我只考虑到了字母这一种字符型,而没有考虑其他符号也为字符型,因此先前字符的识别我是通过判别当前SYM是否为ident型且其字符串长度是否为1这两个条件进行识别出来的。
当完成过后才发现该方法存在缺陷,才在GetSym里面另起分支进行识别处理。
这也提醒我以后考虑问题应当尽量周全。
源码下载地址: