📄 source.cpp
字号:
int i=0,j=0;
char relop[10];//用于临时记录token(这里一定是一个二元逻辑运算符)的内容
if(strcmp(sym,"odd")==0){ //如果是odd运算符(一元)
getsym();
expression(); //对odd的表达式进行处理计算
gen(opr,0,6); //生成6号操作指令:奇偶判断运算
}
else { //如果不是odd运算符(那就一定是二元逻辑运算符)
expression();//对表达式左部进行处理计算
strcpy(relop,sym);//记录下当前的逻辑运算符
getsym();
expression();//对表达式右部进行处理计算
if(strcmp(relop,"eql")==0) gen(opr,0,8);//等号:产生8号判等指令
if(strcmp(relop,"neq")==0) gen(opr,0,9);//不等号:产生9号判不等指令
if(strcmp(relop,"lss")==0) gen(opr,0,10);//小于号:产生10号判小指令
if(strcmp(relop,"geq")==0) gen(opr,0,11);//大于等号号:产生11号判不小于指令
if(strcmp(relop,"gtr")==0) gen(opr,0,12);//大于号:产生12号判大于指令
if(strcmp(relop,"leq")==0) gen(opr,0,13);//小于等于号:产生13号判不大于指令
}
}
/*****************语句处理过程statement********************/
void statement(int plev)
{
int i,cx1,cx2,m=0,n=0;
if(strcmp(sym,"ident")==0){
i=position(id);
if(i==0)error(7);
else{
if(table[i].kind!=variable){//如果在符号表中找到该标识符,但该标识符不是变量名
error(12);//抛出12号错误
i=0;//i置0作为错误标志
}
}
getsym();
if (strcmp(sym,"becomes")==0) getsym();
else error(13);// 如果赋值语句的左部标识符号后所接不是赋值号,抛出13号错误
expression();
if (i!=0)
gen(sto,plev-table[i].level,table[i].adr);//产生一行把表达式值写往指定内存的sto目标代码
}
else if (strcmp(sym,"read")==0) {//如果不是赋值语句,而是遇到了read语句
getsym(); //获得下一token,正常情况下应为左括号
if (strcmp(sym,"lparen")==0) {
do{
getsym();
if(strcmp(sym,"ident")==0) i=position(id);
else i=0;
if (i==0) error(21);//read的标识符未定义,抛出21号错误
else{
gen(opr,0,16);//生成16号操作指令:从键盘读入数字
gen(sto,plev-table[i].level,table[i].adr);//生成sto指令,把读入的值存入指定变量所在的空间
}
getsym();
}while(strcmp(sym,"comma")==0);/*不断生成代码直到read语句的
参数表中的变量遍历完为止,这里遇到不是逗号,应为右括号*/
}
if(strcmp(sym,"rparen")!=0) {
error(9);
}
else getsym();//如果read语句正常结束,得到下一个token,一般为分号或end
}
else if(strcmp(sym,"write")==0){
getsym();
if(strcmp(sym,"lparen")==0){
do{ //依次获取括号中的每一个值,进行输出
getsym();
expression();//调用expression过程分析表达式
gen(opr,0,14);//生成14号指令:向屏幕输出
}while(strcmp(sym,"comma")==0);//循环直到遇到的不再是逗号,这时应是右括号
if(strcmp(sym,"rparen")!=0) error(9);//如果不是右括号,抛出9号错误
else getsym();
}
gen(opr,0,15);//生成一个15号操作的目标代码,功能是输出一个换行
}
else if(strcmp(sym,"call")==0){
getsym();
if(strcmp(sym,"ident")!=0) error(11);//如果call后跟的不是标识符,抛出11号错误
else{
i=position(id);
if(i==0) error(7);
else{
if(table[i].kind==procedur)//如果这个标识符为一个过程名
gen(cal,plev-table[i].level,table[i].adr);//生成cal目标代码,呼叫这个过程*/
else error(12);//如果call后跟的不是过程名,抛出12号错误
}
getsym();
}
}
else if(strcmp(sym,"if")==0){
getsym();//获取一token应是一个逻辑表达式
condition();//对逻辑表达式进行分析计算
if(strcmp(sym,"then")==0) getsym();
else error(13);//如果if后没有then,抛出16号错误
cx1=cx;//记下当前代码分配指针位置
gen(jpc,0,0);//生成条件跳转指令,跳转位置暂时填0,分析完语句后再填写
statement(plev);//分析then后的语句
code[cx1].a=cx;//上一行指令(cx1所指的)的跳转位置,应为当前cx所指位置*/
if(strcmp(sym,"semicolon")==0) getsym();
else error(17);//如果后面跟的不是';',抛出17号错误
if (strcmp(sym,"else")==0) {
getsym();
statement(plev);
}
}
else if (strcmp(sym,"begin")==0){
getsym();
statement(plev);//对begin与end之间的语句进行分析处理
while(strcmp(sym,"semicolon")==0){//如果分析完一句后遇到分号或语句开始符循环分析下一句语句
getsym();
statement(plev);
}
if(strcmp(sym,"end")==0) getsym();//如果语句全分析完了,应该遇到end
else error(14);//如果不是end,抛出14号错
}
else{
if(strcmp(sym,"while")==0) {
cx1=cx;//记下当前代码分配位置,这是while循环的开始位置
getsym();//获取下一token,应为一逻辑表达式
condition();//对这个逻辑表达式进行分析计算
cx2=cx;//记下当前代码分配位置,这是while的do中的语句的开始位置
gen(jpc,0,0);//生成条件跳转指令,跳转位置暂时填0
if(strcmp(sym,"do")==0) //逻辑表达式后应为do语句
getsym();
else error(15);//while后缺少do,抛出15号错误
statement(plev);//分析do后的语句块
gen(jmp,0,cx1);//循环跳转到cx1位置,即再次进行逻辑判断
code[cx2].a=cx;//把刚才填0的跳转位置改成当前位置,完成while语句的处理
}
}
}
/******************语法分析过程block****************/
/*
参数:lev:这一次语法分析所在的层次
tx:符号表指针
*/
void block(int plev)
{
int tx0;//记录本层开始时符号表位置
int cx0;//记录本层开始时代码段分配位置
int ddx=3;
dx=3; //数据段内存分配指针,指向下一个被分配空间在数据段中的偏移位置
/*地址指示器给出每层局部量当前已分配到的相对位置。
置初始值为3的原因是:每一层最开始的位置有三个空间
用于存放静态链SL、动态链DL和返回地址RA
*/
lev=plev;
tx0=tx;//初始符号表指针指向当前层的符号在符号表中的开始位置
cx0=cx;
table[tx].adr=cx;//符号表当前位置记下当前层代码的开始位置
gen(jmp,0,1);//产生一行跳转指令,跳转位置暂时未知填0
if(plev>LEVMAX) error(16);//如果当前过程嵌套层数大于最大允许的套层数,发出16号错误
do{ //开始循环处理源程序中所有的声明部分
if(strcmp(sym,"const")==0){
getsym();
constdeclaration();
while(strcmp(sym,"comma")==0){
getsym();
constdeclaration();
}
if(strcmp(sym,"semicolon")==0) getsym();
else error(17);//若后面跟的不是';',抛出17号错误
}
if(strcmp(sym,"var")==0){
getsym();
vardeclaration();
while(strcmp(sym,"comma")==0){
getsym();
vardeclaration();
}
if(strcmp(sym,"semicolon")==0) getsym();
else error(17);
}
ddx=dx;
while(strcmp(sym,"procedure")==0){
getsym();
if(strcmp(sym,"ident")==0){
enter(procedur);//把这个过程登记到名字表中
getsym();
}
else error(18);//如果token不是标识符,抛出18号错误
if(strcmp(sym,"semicolon")==0) getsym();
else error(17);
block(plev+1);//递归调用语法分析过程,当前层次加一,
lev=lev-1;
if(strcmp(sym,"semicolon")==0)//递归返回后当前token应为递归调用时的最后一个end后的分号
getsym();
}
}while((strcmp(sym,"const")&&strcmp(sym,"var")&&strcmp(sym,"procedure"))==0);
code[cx0].a=cx;//把前面生成的跳转语句的跳转位置改成当前位置
table[tx0].adr=cx;//地址为当前代码分配地址
table[tx0].size=ddx;//长度为当前数据代分配位置
cx0=cx;//记下当前代码分配位置
gen(INT,0,ddx);//生成分配空间指令,分配dx个空间
statement(plev);//处理当前遇到的语句或语句块
gen(opr,0,0);//生成从子程序返回操作指令
}
/*****************PL/0编译器产生的类PCODE目标代码解释运行过程interpret*************/
void interpret()
{
int p=0;//p为程序指令指针,指向下一条要运行的代码,从0号代码开始执行程序
int b=1;//b为基址指针,指向每个过程被调用时数据区中,分配给它的局部变量数据段基址
int t=0;/*t为栈顶寄存器,类PCODE是在一种假想的栈式计算上运行的,
这个变量记录这个计算机的当前栈顶位置.程序开始运行时栈顶寄存器置0
*/
struct instruction i;//i变量中存放当前在运行的指令
int s[STACKSIZE];//s为栈式计算机的数据内存区
printf("start PL/0\n"); //PL/0程序开始运行
s[0]=0;
s[1]=0;
s[2]=0;
s[3]=0;//数据内存中为SL,DL,RA三个单元均为0,标识为主程序
do{ //开始依次运行程序目标代码
i=code[p];//获取一行目标代码
p=p+1;//指令指针加一,指向下一条代码
switch(i.f){ //如果i的f,即指令助记符是下面的某种情况,执行不同的功能
case lit: t=t+1;
s[t]=i.a;
break;
case opr:switch(i.a){
case 0:t=b-1;
p=s[t+3];
b=s[t+2];
break;
case 1:s[t]=-s[t];
break;
case 2:t=t-1;
s[t]=s[t]+s[t+1];
break;
case 3:t=t-1;
s[t]=s[t]-s[t+1];
break;
case 4:t=t-1;
s[t]=s[t]*s[t+1];
break;
case 5:t=t-1;
s[t]=s[t]/s[t+1];
break;
case 6:if(s[t]%2==0) s[t]=0;
else s[t]=1;
break;
case 8:t=t-1;
if(s[t]==s[t+1]) s[t]=1;
else s[t]=0;
break;
case 9:t=t-1;
if(s[t]==s[t+1]) s[t]=0;
else s[t]=1;
break;
case 10:t=t-1;
if(s[t]<s[t+1]) s[t]=1;
else s[t]=0;
break;
case 11:t=t-1;
if(s[t]>=s[t+1]) s[t]=1;
else s[t]=0;
break;
case 12:t=t-1;
if(s[t]>s[t+1]) s[t]=1;
else s[t]=0;
break;
case 13:t=t-1;
if(s[t]<=s[t+1]) s[t]=1;
else s[t]=0;
break;
case 14:printf("%d",s[t]);
fprintf(fresult,"%d",s[t]);
t=t-1;
break;
case 15:printf("\n");
fprintf(fresult,"\n");
break;
case 16:t=t+1;
printf("?");
fprintf(fresult,"?");
scanf("%d",&s[t]);
fprintf(fresult,"%d",s[t]);
break;
}
break;
case lod: t=t+1;
s[t]=s[s[b]+i.a];
break;
case sto:s[s[b]+i.a]=s[t];
t=t-1;
break;
case cal:s[t+1]=s[b];
s[t+2]=b;
s[t+3]=p;
b=t+1;
p=i.a;
break;
case INT:t=t+i.a;break;
case jmp:p=i.a;break;
case jpc:if(s[t]==0) p=i.a;
t=t-1;
break;
}
}while(p!=0);/*如果p等于0,意味着在主程序运行时遇到了从子程序返回指令,
也就是整个程序运行的结束*/
fclose(fresult);//关闭用于记录屏幕输入输出的fresult文件
}
/******************************main*****************************/
main()
{
int m=0,n=0;
if((fpro=fopen("fpro.txt","w"))==NULL){
printf("Cannot open file\n");
exit(0);
}
printf("Please input the program file's name(*.txt file):\n");
scanf("%s",fname);
fprintf(fpro,"%s\n",fname);
if((fin=fopen(fname,"r"))==NULL){
printf("Cannot open %s file \n",fname);
exit(0);
}
if((fcode=fopen("fcode.txt","w"))==NULL){
printf("Cannot open fcode.txt file\n");
exit(0);
}
if((fresult=fopen("fresult.txt","w"))==NULL){
printf("Cannot open fa2.txt file\n");
exit(0);
}
err=0;//记录出错次数
cc=1;//词法分析行缓冲区指针
cx=0; //类PCODE代码表指针置0
ll=0;//词法分析行缓冲区长度置0
ch=' ';//词法分析当前字符为空格
getsym();
if(!strcmp(sym,"program"))
{ getsym();
if(strcmp(sym,"ident")==0) getsym();
else error(19);//若programe后跟的不是标识符,抛出19号错误
if(strcmp(sym,"semicolon")==0) getsym();
else error(17);
}
else error(20);//不是以programe开头,不合法,抛出20号错误
fprintf(fresult,"program:result\n",0);
block(0);//开始进行主程序(也就是第一个分程序)的语法分析
if(!err) list();
fclose(fcode);
fclose(fpro);
if(strcmp(sym,"period")!=0)
error(0);/*program is not end maybe need '.'*/
if(!err)
interpret();
else
printf("%d errors in pascal program\n",err);
fclose(fin);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -