课程名称___编译原理_________ _ 题目名称___PL/0编译器的扩充 __ 学生学院___计算机学院______ ___ 专业班级_ 计算机科学与技术13(9) 学 号 学生姓名 指导教师___林志毅________________
2016 年 1 月 2 日
一、 已完成的内容:
(1) 扩充赋值运算:*= 和 /=
(2) 扩充语句(Pascal的FOR语句)
FOR <变量>:=<表达式>STEP<表达式> UNTIL<表达式>Do<语句> (3) 增加类型:① 字符类型; ② 实数类型。 (4) 增加注释; 多行注释由/*和*/包含,单行注释为//
二、 实验环境与工具
(1)计算机及操作系统:PC机,Windows7 (2)程序设计语言:C++
(3)使用软件Borland C++ Builder 6.0 (4)教学型编译程序:PL/0
三、 具体实现:
1. 扩充赋值运算:*= 和 /=
(1) 语法树
变GetSym()方法(写出修改的代码)表达式 *=(2) 修改 else if(CH=='*') { GetCh();
if(CH=='=') { SYM=TIMESBECOMES; GetCh(); } else SYM=TIMES; } else if(CH=='/') { GetCh();
if(CH=='=') { SYM=SLASHBECOMES; GetCh(); }
(3) 修改STATEMENT()方法(写出修改的代码)
case IDENT:
i=POSITION(ID,TX); if (i==0) Error(11);
else if (TABLE[i].KIND==VARIABLE || TABLE[i].KIND==FLOATTYPE)
{ /*ASSIGNMENT TO NON-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); } GetSym(); } else Error(13);
EXPRESSION(FSYS,LEV,TX);
if(RELOP==TIMESBECOMES) { GEN(OPR,0,4);
} else if(RELOP==SLASHBECOMES) { GEN(OPR,0,5);
} else if(RELOP==PLUSBECOMES) { GEN(OPR,0,2);
} else if(RELOP==MINUSBECOMES) { GEN(OPR,0,3); }
GEN(STO,LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR);
}
(4) 运行测试(测试的PL0源码扩充单词的测试并贴运行结果截图) PL0源码: PROGRAM EX01; VAR A,B,C,D; BEGIN A:=16; A/=2;
WRITE(A); //结果为8
(或其他类似符号)则先把符号左边的变量值放入栈中
B:=4; B*=2;
WRITE(B); //结果为8 C:=6;
C+=2;
WRITE(C); //结果为8 D:=10; D-=2;
WRITE(D); //结果为8 END.
(5) 运行结果:
(6) 出现的问题及解决
开始时在实现/=和*=操作时,*=的实现很顺利,而/=却一直没有得到理想的结果,通过与同学的讨论得知代码中除号指令的解析中,其实为除余操作,于是将%改为/,但是结果还是错误,经过调试发现是两个相除的数在栈中的位置相反了,正确的状态应该是除数位于次栈顶,而被除数位于栈顶。
解决:在调用EXPRESSION函数解析/=右边表达式前,先将其左边变量的值放入栈中。
2. 扩充语句(Pascal的FOR语句)
FOR <变量>:=<表达式>STEP<表达式> UNTIL<表达式>Do<语句> (1) 语法图
(2) 修改GetSym()方法(写出修改的代码)
此处无修改,FOR、STEP、UNTIL及DO等关键字已放置关键字数组中,通过该数组便可识别。
(3) 修改STATEMENT()方法(写出修改的代码)
case FORSYM:
GetSym();
STATEMENT(SymSetUnion(SymSetNew(STEPSYM),FSYS),LEV,TX); //处理for后面的赋值语句
CX3=CX; GEN(JMP,0,0); //用于第一次执行for语句时跳过step语句
CX1=CX; //记录step后语句的代码位置 if(SYM==STEPSYM) { GetSym();
STATEMENT(SymSetUnion(SymSetNew(UNTILSYM),FSYS),LEV,TX); //处理step后面的表达式
} else Error(8); if(SYM==UNTILSYM) {
CODE[CX3].A=CX; //回填第一次进入for循
环时的直接跳转地址
GetSym(); } else Error(8);
CONDITION(SymSetAdd(DOSYM, FSYS),LEV,TX); //处理条件语句
if(SYM==DOSYM) { GetSym(); } else Error(8);
CX2=CX; GEN(JPC,0,0); //条件跳转,栈顶若为非真,则跳转到参数三位置,该位置待回填
STATEMENT(FSYS,LEV,TX); //处理do后面内容
GEN(JMP,0,CX1); //执行完for语句内的语句则重新跳回step处
CODE[CX2].A=CX; //回填条件跳转 break;
(4) 运行测试
PL0源码: PROGRAM EX01; VAR A,SUM; BEGIN
SUM:=0;
FOR A:=1 STEP A+=1 UNTIL A<=100 DO //求1~100的和 SUM+=A;
WRITE(SUM); //结果为5050 END.
(5) 运行结果:
(6) 出现的问题及解决
实现FOR循环的前提是理解表达式处理函数及语句处理函数,关键是处理好跳转关系,例如第一次进入FOR循环时,生成FOR后面的表达式后应该有一个直接跳转跳过STEP语句进入UNTIL的条件判断;而调用CONDITION处理UNTIL后的条件语句后,便有一个条件跳转跳到STEP后的表达式代码位置。由于有了之前IF...ELSE...的参考及大致理解相关函数,因此实现时没出现太大问题。
3. 增加类型:① 字符类型; ② 实数类型。
(1) 在GetSym中增加字符型及实数型数据的识别
else if(CH=='\\'') { GetCh(); NUM=CH; GetCh();
if(CH=='\\'') { SYM=CHARACTER; GetCh(); } else Error(8); }
else if (CH>='0' && CH<='9') { /*NUMBER*/
K=0; NUM=0; SYM=NUMBER; do {
NUM=10*NUM+(CH-'0');
K++; GetCh();
}while(CH>='0' && CH<='9'); if(CH=='.') { //实现小数点后的识别 i=0; M=0; GetCh();
while(CH>='0' && CH<='9') {//以下获取小数点后的值 J=0;
M=(CH-'0')*0.1; while(JNUM=NUM+M; i++; GetCh(); }
if (i>LMAX) Error(30); //LMAX为小数点后数字最大度 fprintf(FOUT,\"%f\ }
(2) 修改Block中的方法运行测试
if (SYM==CHARSYM) { GetSym(); do {
CharDeclaration(LEV,TX,DX); while }
if (SYM==FLOATSYM) { GetSym(); do {
(SYM==COMMA)
{
GetSym();
CharDeclaration(LEV,TX,DX); }
if (SYM==SEMICOLON) GetSym(); else Error(5);
}while(SYM==IDENT);
FloatDeclaration(LEV,TX,DX); while
(SYM==COMMA)
{
GetSym();
DoubleDeclaration(LEV,TX,DX); }
if (SYM==SEMICOLON) GetSym(); else Error(5);
}while(SYM==IDENT); }
(3) 修改STATEMENT中的方法 case IDENT: GetSym(); {
RELOP=SYM;
if(SYM!=BECOMES) { //若为除等于(或其他类似)。。则先把符号左边的变量值放入栈中
GEN(LOD,LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); }
GetSym(); } else Error(13); EXPRESSION(FSYS,LEV,TX);
if(RELOP==TIMESBECOMES) { GEN(OPR,0,4);
} else if(RELOP==SLASHBECOMES) { GEN(OPR,0,5);
} else if(RELOP==PLUSBECOMES) { GEN(OPR,0,2);
} else if(RELOP==MINUSBECOMES) { GEN(OPR,0,3); }
GEN(STO,LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); } else {
i=POSITION(ID,TX); if (i==0) Error(11);
else if (TABLE[i].KIND==CHARTYPE || TABLE[i].KIND==VARIABLE ||
if (SYM==BECOMES || SYM==TIMESBECOMES || SYM==SLASHBECOMES
TABLE[i].KIND==FLOATTYPE) { /*ASSIGNMENT TO NON-VARIABLE*/
|| SYM==PLUSBECOMES|| SYM==MINUSBECOMES)
Error(12); i=0; }
break;
//根据不同类型进行相应输出
GetSym();
if (SYM==LPAREN) { do {
GetSym();
EXPRESSION(SymSetUnion(SymSetNew(RPAREN,COMMA),FSYS),LEV,TX);
case WRITESYM:
i=POSITION(ID,TX); switch (TABLE[i].KIND) {
case CHARTYPE:GEN(OPR,0,17); break; case FLOATTYPE:GEN(OPR,0,18); break; default:{GEN(OPR,0,14);}
}
}while(SYM==COMMA);
if (SYM!=RPAREN) Error(SBNUM); else GetSym(); }
GEN(OPR,0,15); break; /*WRITESYM*/
while (SymIn(SYM,FACBEGSYS)) { if (SYM==IDENT) { i=POSITION(ID,TX); if (i==0) Error(11); else
switch (TABLE[i].KIND) {
case CONSTANT: GEN(LIT,0,TABLE[i].VAL); break; case
case PROCEDUR: Error(21); break;
VARIABLE:
(4) 修改FACTOR中的方法(包含字符的识别处理)
GEN(LOD,LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); break;
case FLOATTYPE: GEN(LOD,LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); break;
case CHARTYPE: GEN(LOD,LEV-TABLE[i].vp.LEVEL,TABLE[i].vp.ADR); break;
}
GetSym();
} else if (SYM==NUMBER) {
if (NUM>AMAX) { Error(31); NUM=0; }
GEN(LIT,0,NUM); GetSym(); //将表达式中的数字置于栈顶
} else if (SYM==LPAREN) { //左 括 号 GetSym();
EXPRESSION(SymSetAdd(RPAREN,FSYS),LEV,TX);
if (SYM==RPAREN) GetSym(); else Error(22);
} else if (SYM==CHARACTER) { GEN(LIT,0,NUM); //将字符放入栈顶 GetSym(); }
(5) 修改ENTER中的方法
case CHARTYPE:
TABLE[TX].vp.LEVEL=LEV; TABLE[TX].vp.ADR=DX; DX++; break;
TABLE[TX].vp.LEVEL=LEV; TABLE[TX].vp.ADR=DX; DX++; break;
case FLOATTYPE:
(6) 在Interpret指令OPR中增加字符输出和实数输出
case 17:
Form1->printcs(S[T]); fprintf(FOUT,\"%c\\n\T--; //增加字符输出
break;
case 18: Form1->printrs(\"\
fprintf(FOUT,\"%d\\n\T--; //增加实数型输出
break;
(7) 其它修改
1) 增加相应的关键字char和float;
2) 由于增加实型,因此将模拟栈的数组类型改为double型;另目标指令
结构体INSTRUCTION中的A,及全局变量NUM也改为double型
(8) 运行测试:
PL0源码: PROGRAM EX01;
CHAR A,B; FLOAT C,D,SUM; BEGIN A:='A'; B:='B'+2;
WRITE(A); WRITE(B); WRITE(B+2); C:=123.321; D:=321.123; SUM:=C+D+0.0004; WRITE(SUM); END.
(9) 运行结果: (10) 出现的问题及解决
字符型的实现主要问题是在赋值、字符表达式的处理及输出三个方面。赋值刚开始没有思路,之后借鉴VAR型的赋值,才清楚需要使用到栈这个媒介进入赋值:先把字符值放入栈中,在将栈顶值传给字符型变量。而字符表达式的处理在Factor里进行识别处理。关于输出则需要在WRITESYM的识别处理中进行类型判断采取不同输出方式,并需新建一条指令专门用于字符输出
实数型的实现主要问题为:1、数据获取:小数点后的值的获取需要用到循环帮助实现;2、变量更改:在没有引入实数型前,用来模拟栈的数组类型为int型,而引入后,则需要将其更改为实数型,在程序中与栈值有关的那些变量类型
也同样改为实数型。
4. 增加注释; 多行注释由/*和*/包含,单行注释为//
(1) 修改GetSym()方法(写出修改的代码)
else if(CH=='/') { GetCh();
if(CH=='=') { SYM=SLASHBECOMES; GetCh(); } else if(CH=='*') { GetCh(); i=CH;
while(i!='*' || CH!='/') { i=CH; GetCh();
// Form1->printcs(CH); }
if(i!='*'&&CH!='/') Error(19); else {
GetCh();//不能忽略,为下一次调用GetSym提供第一个字符,用于switch语句
GetSym(); }
} else if(CH=='/') { //单行注释功能 i=CX;//记录当前行
while(CC!=LL) GetCh(); //CC为当前字符在行中位置,LL为当前行的字数
GetSym(); //滤去注释后继续获取后面的字符串 } else SYM=SLASH; }
(2) 运行测试
PL0源码: PROGRAM EX01; VAR A,SUM; BEGIN
/*下面代码求解1到100的之间 每个整数的相加之和*/ SUM:=0;
FOR A:=1 STEP A+=1 UNTIL A<=100 DO //求1~100的和 SUM+=A; WRITE(SUM); END.
(3) 运行结果 (4) 出现问题及解决
刚开始就认为注释功能的实现比较容易,但是在实现过程还是出现不少问题。我的思路是在getSym方法中使用循环滤去注释的内容,过程不断调用getCh读取下一个字符。在循环处理注释结束后需要调用自身GetSym,因为上次调用的GetSym方法都用于处理注释了。然而/**/这种情况却一直有报19号错误,经过查找发现该情况的循环执行完后,当前CH为符号’/’,而不是’/’后的字符,因此需要在调用GetSym前先调用getCh以便获取下一个新字符用于GetSym中的switch判断。//不存在此情况,其循环结束CH为注释结束的下一字符。
四、 心得
经过了一周多的奋战,此次课程设计的实现过程让我收获颇多,同时也让我意识到自己在编码过程中的不足。在没做课程设计之前,我对代码编译的实现过程和对PL0代码的体系结构都很陌生,而当一步一步从实现/=等、for循环、注释及增加类型等功能后,
我对代码编译过程有了一个整体而深入的认识。在实现的过程中,虽然出现很多问题,但是通过自己的思考和请教同学都能得到解决。在实现的过程中,我发现自己在考虑功能的实现方面不够全面仔细,例如字符型的增加,我只考虑到了字母这一种字符型,而没有考虑其他符号也为字符型,因此先前字符的识别我是通过判别当前SYM是否为ident型且其字符串长度是否为1这两个条件进行识别出来的。当完成过后才发现该方法存在缺陷,才在GetSym里面另起分支进行识别处理。这也提醒我以后考虑问题应当尽量周全。
源码下载地址:
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- huatuowenda.com 版权所有 湘ICP备2023022495号-1
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务