PL/0的研究与改进

1.扩充else语句

if-then-else语句的EBNF范式描述
<条件语句>:=if<条件>then<语句>[else <语句>]
语法描述图为

需要修改的代码:
1.头文件修改
2.设置保留字名字和符号
3.statement中修改
(文章最后会放入整体代码)

2. 扩充for-to|downto语句

for语句的EBNF范式描述为
<for循环语句>:=for<赋值语句>to|downtodo<语句>
语法描述图为

需要修改的代码:
1.头文件修改:增加关键字个数,扩充符号集
2.设置保留字名字和符号、增加语句开始符号集
3.statement中修改

3.+=、-=、*=、/=、注释语句

EBNF范式描述
<赋值语句>::=<标识符>[+=|-=| * =| /=|:=]<标识符>
<行注释> ::= // <语句>
<块注释> ::=/ * 语句{<语句>} * /
语法描述图


代码的修改:
①注释只需要修改.c文件中的getsym函数
②其余运算符需修改:
1.头文件修改:增加关键字个数,扩充符号集
2.修改getsym(),读出+=、-=、*=、/=运算符
3.statement中修改

4.++、–的扩充

EBNF描述为:
<自增语句>::=++<标识符>|<标识符>++
<自减语句>::=–<标识符>|<标识符>–
语法描述图为:

代码的修改:
1.头文件修改:增加关键字个数,扩充符号集
2.修改语句开始符号集
3.修改getsym(),读出++、–运算符
4.对statement中修改

代码

pl0.c

//else  ok;for  ok;to  and  downto ok;
//识别出*=  /=  后++  后--   能识别出/* */注释 ; //Array4.pl0    识别if-then-else
//Array16.pl0 识别for循环
//Array11  *=  /=   注释
//Array8 后++  后--  前++  前--
//Array18 +=   -=
//Array12  识别单字符 /*
*使用方法:
*运行后输入PL/0 源程序文件名
*回答是否输出虚拟机代码
*回答是否输出名字表
*fa.tmp 输出虚拟机代码
*fa1.tmp  输出源文件及其各行对应的首地址
*fa2.tmp  输出结果
*fas.tmp  输出名字表
*/
#include<stdio.h>
#include"pl0(11).h"
#include"string.h"
/*解释执行时使用的栈*/
#define stacksize 500
int main()
{bool nxtlev[symnum];printf("Input pl/0 file ?");scanf("%s",fname);                                     /*输入文件名*/fin=fopen(fname,"r");if(fin){printf("List object code ?(Y/N)");                /*是否输出虚拟机代码*/scanf("%s",fname);listswitch=(fname[0]=='y'||fname[0]=='Y');printf("List symbol table ? (Y/N)");             /*是否输出名字表*/scanf("%s",fname);tableswitch=(fname[0]=='y'||fname[0]=='Y');fa1=fopen("fa1.tmp","w");fprintf(fa1,"Iput pl/0 file ?");fprintf(fa1,"%s\n", fname);init();                                          /*初始化*/err=0;cc=cx=ll=0;ch=' ';if(-1!=getsym()){fa=fopen("fa.tmp","w");fas=fopen("fas.tmp","w");addset(nxtlev,declbegsys,statbegsys,symnum);nxtlev[period]=true;    if(-1==block(0,0,nxtlev))           /*调用编译程序*/{fclose(fa);fclose(fa1);fclose(fas);fclose(fin);printf("\n");return 0;}fclose(fa);fclose(fa1);fclose(fas);if(sym!=period){error(9);}if(err==0){fa2=fopen("fa2.tmp", "w");interpret();fclose(fa2);}else{printf("Errors in pl/0 program");}}fclose(fin);}else{printf("Can't open file! \n");}printf("\n");getchar();getchar();return 0;
}
/*
*初始化
*/
void init()
{int i;for(i=0;i<=255;i++){ssym[i]=nul;}ssym['+']=plus;ssym['-']=minus;ssym['*']=times;ssym['/']=slash;ssym['(']=lparen;ssym[')']=rparen;ssym['=']=eql;ssym[',']=comma;ssym['.']=period;ssym['#']=neq;  //去掉原先单字符‘#’的设置部分ssym[';']=semicolon;/*设置保留字名字,按照字母顺序,便于折半查找*/strcpy(&(word[0][0]),"begin");strcpy(&(word[1][0]),"call");strcpy(&(word[2][0]),"const");strcpy(&(word[3][0]),"do");strcpy(&(word[4][0]),"downto");/*增加保留字downto*/strcpy(&(word[5][0]),"else");/*增加保留字else*/strcpy(&(word[6][0]),"end");strcpy(&(word[7][0]),"for");/**增加保留字for*/strcpy(&(word[8][0]),"if");strcpy(&(word[9][0]),"odd");strcpy(&(word[10][0]),"procedure");strcpy(&(word[11][0]),"read");strcpy(&(word[12][0]),"then");strcpy(&(word[13][0]),"to");/*增加保留字to*/strcpy(&(word[14][0]),"var");strcpy(&(word[15][0]),"while");strcpy(&(word[16][0]),"write");/*设置保留字符号*/wsym[0]=beginsym;wsym[1]=callsym;wsym[2]=constsym;wsym[3]=dosym;wsym[4]=downtosym;/*增加保留字符号downtosym*/wsym[5]=elsesym;/*增加保留字符号elsesym*/wsym[6]=endsym;wsym[7]=forsym;/*增加保留字符号forsym*/wsym[8]=ifsym;wsym[9]=oddsym;wsym[10]=procsym;wsym[11]=readsym;wsym[12]=thensym;wsym[13]=tosym;/*增加保留字符号tosym*/ wsym[14]=varsym;wsym[15]=whilesym;wsym[16]=writesym;/*设置指令名称*/strcpy(&(mnemonic[lit][0]),"lit");strcpy(&(mnemonic[opr][0]),"opr");strcpy(&(mnemonic[lod][0]),"lod");strcpy(&(mnemonic[sto][0]),"sto");strcpy(&(mnemonic[cal][0]),"cal");strcpy(&(mnemonic[inte][0]),"int");strcpy(&(mnemonic[jmp][0]),"jmp");strcpy(&(mnemonic[jpc][0]),"jpc");/*设置符号集*/for(i=0;i<symnum;i++){declbegsys[i]=false;statbegsys[i]=false;facbegsys[i]=false;}/*设置声明开始符号集*/declbegsys[constsym]=true;declbegsys[varsym]=true;declbegsys[procsym]=true;/*设置语句开始符号集*/statbegsys[beginsym]=true;statbegsys[callsym]=true;statbegsys[ifsym]=true;statbegsys[whilesym]=true;statbegsys[forsym]=true;statbegsys[plusplus]=true;statbegsys[minusminus]=true;/*设置因子开始符号集*/facbegsys[ident]=true;facbegsys[number]=true;facbegsys[lparen]=true;}/**用数组实现集合的集合运算*/
int inset(int e,bool* s)
{return s[e];
}
int addset(bool* sr,bool* s1,bool* s2,int n)
{int i;for(i=0;i<n;i++){sr[i]=s1[i]||s2[i];  }return 0;
}
int subset(bool* sr,bool* s1,bool* s2,int n)
{int i;for(i=0;i<n;i++){sr[i]=s1[i]&&(!s2[i]);}return 0;
}
int mulset(bool* sr,bool* s1,bool* s2,int n)
{int i;for(i=0;i<n;i++){sr[i]=s1[i]&&s2[i];  } return 0;
}
/**出错处理,打印出错位置和错误编码*/
void error(int n){char space[81];memset(space,32,81); printf("-------%c\n",ch);space[cc-1]=0;//出错时当前符号已经读完,所以cc-1printf("****%s!%d\n",space,n);err++;
}
/**  漏掉空格,读取一个字符**  每次读一行,存入line缓冲区,line被getsym取空后再读一行**  被函数getsym调用*/
int getch()
{if(cc==ll){if(feof(fin)){printf("program incomplete");return -1;}ll=0;cc=0;printf("%d ",cx );fprintf(fa1,"%d ",cx);ch=' ';while(ch!=10){//fscanf(fin,"%c",&ch)if(EOF==fscanf(fin,"%c",&ch)){line[ll]=0;break;}printf("%c",ch);fprintf(fa1,"%c",ch);line[ll]=ch;ll++;}printf("\n");fprintf(fa1,"\n");}ch=line[cc];cc++;return 0;
}/*词法分析,获取一个符号*/int getsym()
{int i,j,k;while( ch==' '||ch==10||ch==9){getchdo;}if(ch>='a'&&ch<='z'){ k=0;do{if(k<al){ a[k]=ch;k++;}getchdo; }while(ch>='a'&&ch<='z'||ch>='0'&&ch<='9');a[k]=0;strcpy(id,a);i=0;j=norw-1;do{k=(i+j)/2;if(strcmp(id,word[k])<=0){j=k-1;}if(strcmp(id,word[k])>=0){i=k+1;}}while(i<=j);if(i-1>j){sym=wsym[k];}else{sym=ident;}}else{if(ch>='0'&&ch<='9'){k=0;num=0;sym=number;do{num=10*num+ch-'0';k++;getchdo;}while(ch>='0'&&ch<='9'); /*获取数字的值*/k--;if(k>nmax){error(30);}}else{if(ch==':')             /*检测赋值符号*/{getchdo;if(ch=='='){sym=becomes;getchdo;}else{sym=nul;            /*不能识别的符号*/}}else{if(ch=='<')         /*检测小于或小于等于符号*/{getchdo;if(ch=='='){sym=leq;getchdo;}else{ sym=lss;}}else{if(ch=='>')          /*检测大于或大于等于符号*/{getchdo;if(ch=='='){sym=geq;getchdo;}else{sym=gtr;}}else if(ch=='*'){getchdo;if(ch=='='){sym=timeseql;//构成*=号getchdo;}else{sym=times;}}else if(ch=='/'){getchdo;if(ch=='='){sym=slasheql;//构成/=号getchdo;}else if(ch == '*'){getch();while(1){while(ch != '*')getch();getch();if(ch == '/')break;}getch();getsym();}else if(ch=='/')//增加注释功能{cc=ll;ch=' ';getsym();}else{sym=slash;}}else if(ch=='+'){getchdo;if(ch=='+'){sym=plusplus;//构成++号getchdo;}else if(ch=='='){sym=pluseql;getchdo;}else{sym=plus;}}else if(ch=='-'){getchdo;if(ch=='-'){sym=minusminus;//构成--号getchdo;}else if(ch=='='){sym=minuseql;getchdo;}else{sym=minus;}}else{sym=ssym[ch];/* 当符号不满足上述条件时,全部按照单字符号处理*///getchdo;//richardif(sym!=period){getchdo;}//end richard}}}}}return 0;
}
/*
*生成虚拟机代码
*
*x:instruction.f;
*y:instruction.l;
*z:instruction.a;
*/
int gen(enum fct x,int y,int z)
{if(cx>=cxmax){printf("Program too long"); /*程序过长*/return -1;}code[cx].f=x;code[cx].l=y;code[cx].a=z;cx++;return 0;
}
/*
*测试当前符号是否合法
*
*在某一部分(如一条语句,一个表达式)将要结束时时我们希望下一个符号属于某集合
*(该部分的后跟符号) test 负责这项检测,并且负责当检测不通过时的补救措施
*程序在需要检测时指定当前需要的符号集合和补救用的集合(如之前未完成部分的后跟
*符号),以及不通过时的错误号
*
*S1:我们需要的符号
*s2:如果不是我们需要的,则需要一个补救用的集合
*n:错误号
*/int test(bool* s1,bool* s2,int n)
{if(! inset(sym,s1)){error(n);/*当检测不通过时,不停获取符号,直到它属于需要的集合或补救的集合*/while((! inset(sym,s1))&&(! inset(sym,s2))){getsymdo; }}return 0;
}
/**编译程序主体**lev:当前分程序所在层*tx:名字表当前尾指针*fsys:当前模块后跟符号集合*/
int block(int lev,int tx,bool* fsys)
{int i;int dx;                         /*名字分配到的相对地址*/int tx0;                        /*保留初始tx*/int cx0;                        /*保留初始cx*/bool nxtlev[symnum];            /*在下级函数的参数中,符号集合均为值参,但由于使用数组实现,传递进来的是指针,为防止下级函数改变上级函数的集合,开辟新的空间传递给下级函数*/dx=3;tx0=tx;                         /*记录本层名字的初始位置*/table[tx].adr=cx;gendo(jmp,0,0);if(lev > levmax){error(32);                                                                                                                                         }do{if(sym==constsym)         /*收到常量声明符号,开始处理常量声明*/{getsymdo;do{constdeclarationdo(&tx,lev,&dx);   /*dx的值会被constdeclaration改变,使用指针*/while(sym==comma){getsymdo;constdeclarationdo(&tx,lev,&dx);}if(sym==semicolon){getsymdo;}else{error(5); /*漏掉了逗号或者分号*/}}while(sym==ident);}if(sym==varsym)/*收到变量声名符号,开始处理变量声名*/{getsymdo;do{vardeclarationdo(&tx,lev,&dx);while(sym==comma){getsymdo;vardeclarationdo(&tx,lev,&dx);}if(sym==semicolon){getsymdo;}else{error(5);}}while(sym==ident);}while(sym==procsym)/*收到过程声名符号,开始处理过程声名*/{getsymdo;if(sym==ident){enter(procedur,&tx,lev,&dx);/*记录过程名字*/getsymdo;}else{error(4);/*procedure后应为标识符*/}if(sym==semicolon){getsymdo;}else{error(5);/*漏掉了分号*/}memcpy(nxtlev,fsys,sizeof(bool)*symnum);nxtlev[semicolon]=true;if(-1==block(lev+1,tx,nxtlev)){return -1;/*递归调用*/}if(sym==semicolon){getsymdo;memcpy(nxtlev,statbegsys,sizeof(bool)*symnum);nxtlev[ident]=true;nxtlev[procsym]=true;testdo(nxtlev,fsys,6);}else{error(5);                       /*漏掉了分号*/}}memcpy(nxtlev,statbegsys,sizeof(bool)*symnum);nxtlev[ident]=true;nxtlev[period]=true;testdo(nxtlev,declbegsys,7);}while(inset(sym,declbegsys));                /*直到没有声明符号*/code[table[tx0].adr].a=cx;                    /*开始生成当前过程代码*/table[tx0].adr=cx;                            /*当前过程代码地址*/table[tx0].size=dx;                           /*声明部分中每增加一条声明都会给dx增加1,声明部分已经结束,dx就是当前过程数据的size*/cx0=cx;gendo(inte,0,dx);                             /*生成分配内存代码*/if(tableswitch)                               /*输出名字表*/{printf("TABLE:\n");if(tx0+1>tx){printf("NULL\n");}for(i=tx0+1;i<=tx;i++){switch(table[i].kind){case constant:printf("%d const %s",i,table[i].name);printf("val=%d\n",table[i].val);fprintf(fas,"%d const %s",i,table[i].name);fprintf(fas,"val=%d\n",table[i].val);break;case variable:printf("%d var%s",i,table[i].name);printf("lev=%d addr=%d\n",table[i].level,table[i].adr);fprintf(fas,"%d var %s",i,table[i].name);fprintf(fas,"lev=%d addr=%d\n",table[i].level,table[i].adr);break;case procedur:printf("%d proc%s",i,table[i].name);printf("lev=%d addr=%d size=%d\n",table[i].level,table[i].adr,table[i].size);fprintf(fas,"%d proc%s",i,table[i].name);fprintf(fas,"lev=%d adr=%d size=%d \n",table[i].level,table[i].adr,table[i].size);break;}}printf("\n");}/*语句后跟符号为分号或end*/memcpy(nxtlev,fsys,sizeof(bool)*symnum);/*每个后跟符号集和都包含上层后跟符号集和,以便补救*/nxtlev[semicolon]=true;nxtlev[endsym]=true;statementdo(nxtlev,&tx,lev);gendo(opr,0,0); /*每个过程出口都要使用的释放数据段命令*/memset(nxtlev,0,sizeof(bool)*symnum); /*分程序没有补救集合*/test(fsys,nxtlev,8);                  /*检测后跟符号正确性*/listcode(cx0);                        /*输出代码*/return 0;
}
/*
*在名字表中加入一项
*
*k:名字种类const,var or procedure
*ptx:名字表尾指针的指针,为了可以改变名字表尾指针的数值
*lev:名字所在的层次,以后所有的lev都是这样
*pdx:为当前应分配的变量的相对地址,分配后要增加1
*/
void enter (enum object k,int *ptx,int lev, int *pdx)
{(*ptx)++;strcpy(table[(*ptx)].name,id);       /*全局变量id中已存有当前名字的名字*/table[(*ptx)].kind=k;switch(k){case constant:                      /*常量名字*/if (num>amax){error(31);num=0;}table[(*ptx)].val=num;break;case variable:                     /*变量名字*/table[(*ptx)].level=lev;table[(*ptx)].adr=(*pdx);(*pdx)++;break;                          /*过程名字*/case procedur:table[(*ptx)].level=lev;break;}}
/**查找名字的位置*找到则返回在名字表中的位置,否则返回0**idt: 要查找的名字*tx::当前名字表尾指针*/
int position(char *  idt,int  tx)
{int i;strcpy(table[0].name,idt);i=tx;while(strcmp(table[i].name,idt)!=0){i--;}return i;
}
/**常量声明处理*/
int constdeclaration(int *  ptx,int lev,int *  pdx)
{if(sym==ident){getsymdo;if(sym==eql ||sym==becomes){if(sym==becomes){error(1);                     /*把=写出成了:=*/}getsymdo;if(sym==number){enter(constant,ptx,lev,pdx);getsymdo;}else{error(2);                  /*常量说明=后应是数字*/}         }else{error(3);                       /*常量说明标识后应是=*/}}else{error(4);                        /*const后应是标识*/}return 0;
}
/***/
int vardeclaration(int * ptx,int lev,int * pdx)
{if(sym==ident){enter(variable,ptx,lev,pdx);//填写名字表getsymdo;}else{error(4);}return 0;
}/**输入目标代码清单*/
void listcode(int cx0)
{int i;if (listswitch){for(i=cx0;i<cx;i++){printf("%d %s %d %d\n",i,mnemonic[code[i].f],code[i].l,code[i].a);fprintf(fa,"%d %s %d %d\n",i,mnemonic[code[i].f],code[i].l,code[i].a);}}
}
/*
*语句处理
*/
int statement(bool* fsys,int * ptx,int lev)
{int i,cx1,cx2;bool nxtlev[symnum];if(sym==ident){i=position(id,*ptx);//在符号表中查到该标识符所在位置if(i==0){error(11);}else{if((table[i].kind!=variable)){error(12);i=0;}else{getsymdo;if(sym==becomes){getsymdo;memcpy(nxtlev,fsys,sizeof(bool)* symnum);expressiondo(nxtlev,ptx,lev);if(i!=0)//如果不曾出错,i将不为0,i所指为当前语句//左部标识符在符号表中的位置 {gendo(sto,lev-table[i].level,table[i].adr);}}else if(sym==timeseql) // *=运算 {getsymdo;memcpy(nxtlev,fsys,sizeof(bool)* symnum);gendo(lod,lev-table[i].level,table[i].adr);expressiondo(nxtlev,ptx,lev); if(i!=0){gendo(opr,0,4);gendo(sto,lev-table[i].level,table[i].adr);}}else if(sym==slasheql) // /=运算 {getsymdo;memcpy(nxtlev,fsys,sizeof(bool)* symnum);gendo(lod,lev-table[i].level,table[i].adr);expressiondo(nxtlev,ptx,lev);if(i!=0){gendo(opr,0,5);gendo(sto,lev-table[i].level,table[i].adr);}}else if(sym==plusplus) // 后++运算 {getsymdo;gendo(lod,lev-table[i].level,table[i].adr);//找到变量地址,将其值入栈gendo(lit,0,1);//将常数1取到栈顶if(i!=0){gendo(opr,0,2);     //执行加操作gendo(sto,lev-table[i].level,table[i].adr);}}else if(sym==minusminus) // 后--运算 {getsymdo;gendo(lod,lev-table[i].level,table[i].adr);//找到变量地址,将其值入栈gendo(lit,0,1);//将常数1取到栈顶if(i!=0){gendo(opr,0,3);     //执行减操作gendo(sto,lev-table[i].level,table[i].adr);}}else if(sym==pluseql) // +=运算 {getsymdo;memcpy(nxtlev,fsys,sizeof(bool)* symnum);gendo(lod,lev-table[i].level,table[i].adr);expressiondo(nxtlev,ptx,lev); if(i!=0){gendo(opr,0,2);     //执行加操作gendo(sto,lev-table[i].level,table[i].adr);}}else if(sym==minuseql) // -=运算 {getsymdo;memcpy(nxtlev,fsys,sizeof(bool)* symnum);gendo(lod,lev-table[i].level,table[i].adr);expressiondo(nxtlev,ptx,lev); if(i!=0){gendo(opr,0,3);     //执行减操作gendo(sto,lev-table[i].level,table[i].adr);}}else{error(13);}}}}else if(sym==plusplus) // 前++运算 {getsymdo;if(sym==ident){i=position(id,*ptx);if(i==0) error(11);else{if(table[i].kind!=variable) {error(12);i=0;}else    //++后跟的是变量{getsymdo;gendo(lod,lev-table[i].level,table[i].adr);//找到变量地址,将其值入栈gendo(lit,0,1);//将常数1取到栈顶if(i!=0){gendo(opr,0,2);     //执行加操作gendo(sto,lev-table[i].level,table[i].adr);}}}}else{error(19);}}else if(sym==minusminus) // 前--运算 {getsymdo;if(sym==ident){i=position(id,*ptx);if(i==0) error(11);else{if(table[i].kind!=variable) {error(12);i=0;}else  //--后跟的是变量{getsymdo;gendo(lod,lev-table[i].level,table[i].adr);//找到变量地址,将其值入栈gendo(lit,0,1);//将常数1取到栈顶if(i!=0){gendo(opr,0,3);     //执行减操作gendo(sto,lev-table[i].level,table[i].adr);}}}}else{error(19);}}else if(sym==forsym)      //检测到for语句{getsymdo;if(sym==ident){i=position(id,*ptx);if(i==0) error(11);else{if(table[i].kind!=variable) //赋值语句中,赋值号左部标识符属性应是变量{error(12);i=0;}else{getsymdo;if(sym!=becomes) error(13);             //赋值语句左部标识符后应是赋值号:=else getsymdo;memcpy(nxtlev,fsys,sizeof(bool)*symnum); nxtlev[tosym]=true;                     //后跟符to和downtonxtlev[downtosym]=true;expressiondo(nxtlev,ptx,lev);           //处理赋值语句右部的表达式E1gendo(sto,lev-table[i].level,table[i].adr);     //保存初值switch(sym){case tosym:           //步长为的向上增加getsymdo;cx1=cx;       //保存循环开始点//将循环判断变量取出放到栈顶gendo(lod,lev-table[i].level,table[i].adr);                                        memcpy(nxtlev,fsys,sizeof(bool)*symnum);    //处理表达式E2nxtlev[dosym]=true;                         //后跟符doexpressiondo(nxtlev,ptx,lev);/*判断循环变量条件,比如for i:=E1 to E2 do S中,判断i是否小于E2,如小于等于,继续循环,大于的话,跳出循环*/     gendo(opr,0,13);             //生成比较指令,i是否小于等于E2的值cx2=cx;                      //保存循环结束点//生成条件跳转指令,跳出循环,跳出的地址未知gendo(jpc,0,0);                     if(sym==dosym)               //处理循环体S{getsymdo;statement(fsys,ptx,lev);  //循环体处理//增加循环变量步长为1//将循环变量取出放在栈顶gendo(lod,lev-table[i].level,table[i].adr);                                       gendo(lit,0,1);                            //将步长取到栈顶gendo(opr,0,2);                            //循环变量加步长//将栈顶的值存入循环变量gendo(sto,lev-table[i].level,table[i].adr); gendo(jmp,0,cx1);                 //无条件跳转到循环开始点/*回填循环结束点的地址,cx为else后语句执行完的位置,它正是前面未定的跳转地址*/code[cx2].a=cx;                     }else{error(29);    //for语句中少了do}break;case downtosym:            //步长为的向下减少getsymdo;cx1=cx;            //保存循环开始点//将循环判断变量取出放到栈顶                         gendo(lod,lev-table[i].level,table[i].adr);                                        memcpy(nxtlev,fsys,sizeof(bool)*symnum);      //处理表达式E2nxtlev[dosym]=true;                           //后跟符doexpressiondo(nxtlev,ptx,lev);/*判断循环变量条件,比如for i:=E1 downto E2 do S中,判断i是否大于等于E2,如大于等于,继续循环, 小于的话,跳出循环*/gendo(opr,0,11);   //生成比较指令,i是否大于等于E2的值cx2=cx;            //保存循环结束点//生成条件跳转指令,跳出循环,跳出的地址未知             gendo(jpc,0,0);                 if(sym==dosym)     //处理循环体S{getsymdo;statement(fsys,ptx,lev);   //循环体处理//增加循环变量步长为//将循环变量取出放在栈顶gendo(lod,lev-table[i].level,table[i].adr);                                       gendo(lit,0,1);                 //将步长取到栈顶gendo(opr,0,3);                 //循环变量加步长//将栈顶的值存入循环变量gendo(sto,lev-table[i].level,table[i].adr);                                       gendo(jmp,0,cx1);     //无条件跳转到循环开始点/*回填循环结束点的地址,cx为else后语句执行完的位置,它正是前面未定的跳转地址*/code[cx2].a=cx;                     }else {error(29);//for语句中少了do}break;}}}}else {error(19);         //for语句后跟赋值语句,赋值语句左部是变量,缺少变量}}else{if(sym==readsym){getsymdo;if(sym!=lparen){error(34);}else{do{getsymdo;if(sym==ident){i=position(id, *ptx);}else{i=0;}if(i==0){error(35);}else{gendo(opr,0,16);gendo(sto,lev-table[i].level,table[i].adr); /* 储存到变量*/}getsymdo;}while (sym==comma);    /*一条read语句可读多个变量 */}if(sym!=rparen){error(33);          /* 格式错误,应是右括号*/while(!inset(sym,fsys))/* 出错补救,直到收到上层函数的后跟符号*/{getsymdo;}}else{getsymdo;}}else{if(sym==writesym)           /* 准备按照write语句处理,与read类似*/{getsymdo;if(sym==lparen){do{getsymdo;if(sym==ident){i=position(id,*ptx);if(i==0){error(11);          /*过程未找到*/}}memcpy(nxtlev,fsys,sizeof(bool)*symnum);nxtlev[rparen]=true;nxtlev[comma]=true;     /* write的后跟符号为)or,*/expressiondo(nxtlev,ptx,lev);/* 调用表达式处理,此处与read不同,read为给变量赋值*/else gendo(opr,0,14);/* 生成输出指令,输出栈顶的值*/}while(sym==comma);if(sym!=rparen){error(33);/* write()应为完整表达式*/}else{getsymdo;}}gendo(opr,0,15);        /* 输出换行*/}else{if(sym==callsym)        /* 准备按照call语句处理*/{getsymdo;if(sym!=ident){error(14);           /*call后应为标识符*/}else{i=position(id,*ptx);if(i==0){error(11);          /*过程未找到*/}else{if(table[i].kind==procedur){gendo(cal,lev-table[i].level,table[i].adr);  /*生成call指令*/}else{error(15);      /*call后标识符应为过程*/}}getsymdo;}}else{if(sym==ifsym)     /*准备按照if语句处理*/{getsymdo;memcpy(nxtlev,fsys,sizeof(bool)*symnum);nxtlev[thensym]=true;nxtlev[dosym]=true;    /*后跟符号为then或do*/conditiondo(nxtlev,ptx,lev);   /*调用条件处理(逻辑运算)函数*/if(sym==thensym){getsymdo;}else{error(16);}          /*缺少then*/cx1=cx;                /*保存当前指令地址*/gendo(jpc,0,0);        /*生成条件跳转指令,跳转地址暂写0*/statementdo(fsys,ptx,lev);   /*处理then后的语句*/if(sym==elsesym)/*than语句后面发现else*/{getsymdo;cx2=cx;code[cx1].a=cx+1;/*cx为当前的指令地址,cx+1即为then语句执行后的else语句的位置,回填地址*/gendo(jmp,0,0);statementdo(fsys,ptx,lev);code[cx2].a=cx;     /*经statement处理后,cx为else后语句执行完的位置,它正是前面未定的跳转地址,回填地址*/}else {code[cx1].a=cx;}      /*经statement处理后,cx为then后语句执行完的位置,它正是前面未定的跳转地址*/  }else{if(sym==beginsym)   /*准备按照复合语句处理*/{getsymdo;memcpy(nxtlev,fsys,sizeof(bool)*symnum);nxtlev[semicolon]=true;nxtlev[endsym]=true;/*后跟符号为分号或end*//*循环调用语句处理函数,直到下一个符号不是语句开始符号或收到end*/statementdo(nxtlev,ptx,lev);while(inset(sym,statbegsys)||sym==semicolon){if(sym==semicolon){getsymdo;}else{error(10);/*缺少分号*/}statementdo(nxtlev,ptx,lev);}if(sym==endsym){getsymdo;}else{error(17); /*缺少end或分号*/}}else{if(sym==whilesym)/*准备按照while语句处理*/{cx1=cx;        /*保存判断条件超作的位置*/getsymdo;memcpy(nxtlev,fsys,sizeof(bool)*symnum);nxtlev[dosym]=true;/*后跟符号为do*/conditiondo(nxtlev,ptx,lev);  /*调用条件处理*/cx2=cx;       /*保存循环体的结束的下一个位置*/gendo(jpc,0,0);/*生成条件跳转,但跳出循环的地址未知*/if(sym==dosym){getsymdo;}else{error(18);      /*缺少do*/}statementdo(fsys,ptx,lev); /*循环体*/gendo(jmp,0,cx1);/*回头重新判断条件*/code[cx2].a=cx;   /*反填跳出循环的地址,与if类似*/}else{memset(nxtlev,0,sizeof(bool)*symnum);/*语句结束无补救集合*/testdo(fsys,nxtlev,19);/*检测语句结束的正确性*/}}}}}}}return 0;
}
/*
*表达式处理
*/
int expression(bool*fsys,int*ptx,int lev)
{enum symbol addop;                    /*用于保存正负号*/bool nxtlev[symnum];if(sym==plus||sym==minus)             /*开头的正负号,此时当前表达式被看作一个正的或负的项*/{addop=sym;                    /*保存开头的正负号*/getsymdo;memcpy(nxtlev,fsys,sizeof(bool)*symnum);nxtlev[plus]=true;nxtlev[minus]=true;termdo(nxtlev,ptx,lev);                /*处理项*/if(addop==minus){gendo(opr,0,1);                   /*如果开头为负号生成取负指令*/}}else                             /*此时表达式被看作项的加减*/{memcpy(nxtlev,fsys,sizeof(bool)*symnum);nxtlev[plus]=true;nxtlev[minus]=true;termdo(nxtlev,ptx,lev);            /*处理项*/}while(sym==plus||sym==minus){addop=sym;getsymdo;memcpy(nxtlev,fsys,sizeof(bool)*symnum);nxtlev[plus]=true;nxtlev[minus]=true;termdo(nxtlev,ptx,lev);              /*处理项*/if(addop==plus){gendo(opr,0,2);                    /*生成加法指令*/}else{gendo(opr,0,3);                /*生成减法指令*/}}return 0;
}
/*
*项处理
*/
int term(bool*fsys,int *ptx,int lev)
{enum symbol mulop;               /*用于保存乘除法符号*/bool nxtlev[symnum];memcpy(nxtlev,fsys,sizeof(bool)*symnum) ;nxtlev[times]=true;nxtlev[slash]=true;factordo(nxtlev,ptx,lev);       /*处理因子*/while(sym==times||sym==slash){mulop=sym;getsymdo;factordo(nxtlev,ptx,lev);if(mulop==times){gendo(opr,0,4);          /*生成乘法指令*/}else{gendo(opr,0,5);           /*生成除法指令*/}}return 0;
}
/*
*因子处理
*/
int factor(bool*fsys,int *ptx,int lev){int i;bool nxtlev[symnum];testdo(facbegsys,fsys,24);           /*检测因子的开始符好号*/while(inset(sym,facbegsys))          /*循环直到不是因子开始符号*/{if(sym==ident)                   /*因子为常量或者变量*/{i=position(id,*ptx);        /*查找名字*/if(i==0){error(11);               /*标识符未声明*/}else{switch(table[i].kind){case constant:                                      /*名字为常量*/gendo(lit,0,table[i].val);                       /*直接把常量的值入栈*/break;case variable:                                      /*名字为变量*/gendo(lod,lev-table[i].level,table[i].adr);      /*找到变量地址并将其值入栈*/break;case procedur:                                      /*名字为过程*/error(21);                                       /*不能为过程*/break;}}getsymdo;}else{if(sym==number)                                             /*因子为数*/{if(num>amax){error(31);num=0;}gendo(lit,0,num);getsymdo;}else{if(sym==lparen)                                           /*因子为表达式*/{getsymdo;memcpy(nxtlev,fsys,sizeof(bool)*symnum);nxtlev[rparen]=true;expressiondo(nxtlev,ptx,lev);if(sym==rparen){getsymdo;}else{error(22);                                       /*缺少右括号*/}}testdo(fsys,facbegsys,23);                        /*银子后有非法符号*/}}}return 0;
}
/*条件处理*/
int condition(bool* fsys,int* ptx,int lev)
{enum symbol relop;bool nxtlev[symnum];if(sym==oddsym)                        /*准备按照odd运算处理*/{getsymdo;expressiondo(fsys,ptx,lev);gendo(opr,0,6);                              /*生成odd指令*/}else{memcpy(nxtlev,fsys,sizeof(bool)*symnum);nxtlev[eql]=true;nxtlev[neq]=true;nxtlev[lss]=true;nxtlev[leq]=true;nxtlev[gtr]=true;nxtlev[geq]=true;expressiondo(nxtlev,ptx,lev);if(sym!=eql&&sym!=neq&&sym!=lss&&sym!=leq&&sym!=gtr&&sym!=geq){error(20);}else{relop=sym;getsymdo;expressiondo(fsys,ptx,lev);switch(relop){case eql:gendo(opr,0,8);break;case neq:gendo(opr,0,9);break;case lss:gendo(opr,0,10);break;case geq:gendo(opr,0,11);break;case gtr:gendo(opr,0,12);break;case leq:gendo(opr,0,13);break;}}}return 0;
}                                                  /*解释程序*/void interpret()
{int p,b,t;             /*指令指针,指令基址,栈顶指针*/struct instruction i;  /*存放当前指令*/int s[stacksize];      /*栈*///double s[stacksize];  //改成doubule型栈printf("start pl0\n");t=0;b=0;p=0;s[0]=s[1]=s[2]=0;do{i=code[p];         /*读当前指令*/p++;switch(i.f){case lit:        /*将a的值取到栈顶*/s[t]=i.a;t++;break;case opr:        /*数字、逻辑运算*/switch(i.a){case 0:t=b;p=s[t+2];b=s[t+1];break;case 1:s[t-1]=-s[t-1];break;case 2:t--;s[t-1]=s[t-1]+s[t];break;case 3:t--;s[t-1]=s[t-1]-s[t];break;case 4:t--;s[t-1]=s[t-1]*s[t];break;case 5:t--;s[t-1]=s[t-1]/s[t];break;case 6:s[t-1]=s[t-1]%2;break;case 8:t--;s[t-1]=(s[t-1]==s[t]);break;case 9:t--;s[t-1]=(s[t-1]!=s[t]);break;case 10:t--;s[t-1]=(s[t-1]<s[t]);break;case 11:t--;s[t-1]=(s[t-1]>=s[t]);break;case 12:t--;s[t-1]=(s[t-1]>s[t]);break;case 13:t--;s[t-1]=(s[t-1]<=s[t]);break;case 14://14号操作为输出栈顶值操作printf("%d",s[t-1]);//未修改成实型字符型型前fprintf(fa2,"%d",s[t-1]);t--;//栈顶下移break;case 15://15号操作为输出换行操作printf("\n");fprintf(fa2,"\n");break;case 16:printf("?");fprintf(fa2,"?");scanf("%d",&(s[t]));fprintf(fa2,"%d\n",s[t]);t++;break;case 17://17号操作为输出栈顶值操作printf("输出字符:");printf("%c\n",s[t-1]);//输出栈顶值fprintf(fa2,"%c\n",s[t-1]);//同时打印到文件t--;//栈顶下移break;}break;case lod:       /*取相对当前过程的数据基地址为a的内存的值到栈顶*/s[t]=s[base(i.l,s,b)+i.a];t++;break;case sto:       /*栈顶的值存到相对当前过程的数据基地址为a的内存*/t--;s[base(i.l,s,b)+i.a]=s[t];break;case cal:              /*调用子程序*/s[t]=base(i.l,s,b); /*将父过程基地址入栈*/s[t+1]=b;           /*将本过程基地址入栈,此两项用于base函数*/s[t+2]=p;           /*将当前指令指针入栈*/b=t;                /*改变基地址指针值为新过程的基地址*/p=i.a;              /*跳转*/break;case inte:             /*分配内存*/t+=i.a;break;case jmp:             /*直接跳转*/p=i.a;break;case jpc:              /*条件跳转*/t--;if(s[t]==0){p=i.a;}break;}}while (p!=0);
}
/*通过过程基址求上1层过程的基址*/
int base(int l,int * s,int b)
{int b1;b1=b;while(l>0){b1=s[b1];l--;}return b1;
}

pl0.h

  /*PL/0 编译系统C版本头文件 pl0.h*/
typedef enum
{false,true
} bool;# define norw 18                 /*关键字个数*/ //关键字个数数由初始值13增加至18
# define txmax 100               /*名字表容量*/
# define nmax  14                /*number的最大位数*/
# define al 10                   /*符号的最大长度*/
# define amax 2047               /*地址上界*/
# define levmax 3                /*最大允许过程嵌套声明层数[0,lexmax]*/
# define cxmax 200               /*最多的虚拟机代码数*/enum symbol{nul,    ident,     number,     plus,     minus,times,   slash,   oddsym,   eql,     neq,lss,     leq,      gtr,     geq,     lparen,rparen,  comma,   semicolon,period,  becomes,beginsym, endsym, ifsym,    thensym,  whilesym,writesym, readsym,  dosym,  callsym,  constsym,varsym,  procsym, elsesym, forsym, tosym, downtosym, timeseql, slasheql, plusplus, minusminus,pluseql,minuseql
};
#define symnum 43    //符号数初始值由32增加至42
/*-------------*/
enum object{constant,variable,procedur,};enum fct{lit, opr,  lod,  sto,  cal,  inte,  jmp,  jpc,
};
#define fctnum 8struct instruction
{enum fct f;int l;int a;};FILE* fas;  /* 输出名字表 */
FILE* fa;   /* 输出虚拟机代码 */
FILE* fa1;  /* 输出源文件及其各行对应的首地址 */
FILE* fa2;  /* 输出结果 */bool tableswitch;
bool listswitch;
char ch;
enum symbol sym;
char id[al+1];
int  num;
int cc,ll;
int cx;
char line[81];
char a[al+1];
struct instruction code[cxmax];
char word[norw][al];
enum symbol wsym[norw];
enum symbol ssym[256];
char mnemonic[fctnum][5];
bool declbegsys[symnum];
bool statbegsys[symnum];
bool facbegsys[symnum];struct tablestruct
{char name[al];                            /*名字*/enum object kind;                         /*类型:const,var,array or procedure*/int val;                                  /*数值,仅const使用*/int level;                                /*所处层,仅const不使用*/int adr;                                  /*地址,仅const不使用*/int size;                                 /*需要分配的数据区空间,仅procedure使用*/
};
struct tablestruct table[txmax];             /*名字表*/
FILE * fin;
FILE* fout;
char fname[al];
int err;                                       /*错误计数器*/
/*当函数中会发生fatal error时,返回-1告知调用它的函数,最终退出程序*/
#define getsymdo                              if(-1==getsym())return -1
#define getchdo                               if(-1==getch())return -1
#define testdo(a,b,c)                         if(-1==test(a,b,c))return -1
#define gendo(a,b,c)                          if(-1==gen(a,b,c))return -1
#define expressiondo(a,b,c)                   if(-1==expression(a,b,c))return -1
#define factordo(a,b,c)                       if(-1==factor(a,b,c))return -1
#define termdo(a,b,c)                         if(-1==term(a,b,c))return -1
#define conditiondo(a,b,c)                    if(-1==condition(a,b,c))return -1
#define statementdo(a,b,c)                    if(-1==statement(a,b,c))return -1
#define constdeclarationdo(a,b,c)             if(-1==constdeclaration(a,b,c))return -1
#define vardeclarationdo(a,b,c)               if(-1==vardeclaration(a,b,c))return -1void error(int n);
int getsym();
int getch();
void init();
int gen(enum fct x,int y,int z);
int test(bool*s1,bool*s2,int n);
int inset(int e,bool*s);
int addset(bool*sr,bool*s1,bool*s2,int n);
int subset(bool*sr,bool*s1,bool*s2,int n);
int mulset(bool*sr,bool*s1,bool*s2,int n);
int block(int lev,int tx,bool* fsys);
void interpret();
int factor(bool* fsys,int* ptx,int lev);
int term(bool*fsys,int*ptx,int lev);
int condition(bool*fsys,int*ptx,int lev);
int expression(bool*fsys,int*ptx,int lev);
int statement(bool*fsys,int*ptx,int lev);
void listcode(int cx0);
int vardeclaration(int* ptx,int lev, int* pdx);
int constdeclaration(int* ptx,int lev, int* pdx);int position(char* idt,int tx);
void enter(enum object k,int* ptx,int lev,int* pdx);
int base(int l,int* s,int b);

测试用例

else测试

1.pl0

var a,b,c;
begin
b:= 20;
c := 5;
read(a);
write(a);
if a < c
then
c := 2 * c
else
c := 2 * c + 1;
write©;
end.

for测试

2.pl0

var c,b, d, e;
procedure p;
begin
c:=c+1;
write( c );
end;

begin
read( c );
read(d);
read(e);
for b := d to e do
begin
call p;
end;
end.

*=、/=、注释

3.pl0

var b, c,d,e;
begin
read(b);
read( c );
read(d);
read(e);
c/=b; //zhushi
d*=e; /d=de*/
write( c );
write(d);
end.

++、–

4.pl0

var b, c,d,e;
begin
read(b);//1
read©;//2
read(d);//1
read(e);//2
b++;
c–;
++d;
–e;
write(b);//2
write©;//1
write(d);//2
write(e);//1
end.

+=、-=

5.pl0

var b, c,d,e;
begin
read(b);//1
read©;//2
read(d);//3
read(e);//2
b+=c;
d-=e;
write(b);//3
write(d);//1
end.

编译原理三级项目PL/0的研究与改进相关推荐

  1. 编译原理实验一PL/0词法分析器c++

    实验目的 通过实现PL/0语言(一种示例小语言)的词法分析器,理解词法分析过程,掌握程序各部分之间的接口安排. 分析与设计 程序的输入直接使用经过预处理之后的程序,读取文件到数组buffer.(预处理 ...

  2. 编译原理:奇数个0和奇数个1的正规式求解

    编译原理第三版词法分析:P64-8-3 奇数个0或1,一看好像很难下手.我们从简单的做起,一步一步分析. 首先构造奇数个0,一个0时直接就是正规式:0 然后是3,5,7--个,我们需要每次增加两个0, ...

  3. 编译原理-实验四-LR(0)语法分析程序的设计

    一.实验目的 了解LR(0)语法分析算法的基本思想,掌握LR(0)语法分析程序的构造方法. 二.实验内容 根据LR(0)语法分析算法的基本思想,设计一个对给定文法进行LR(0)语法分析的程序,并用C. ...

  4. 编译原理 实验四 LR(0)分析法(LR0分析表的自动生成)

    写在前面 由于代码较长,csdn对文章总长度有字数限制,想只看完整代码的请移步另一篇博客. https://blog.csdn.net/qq_46640863/article/details/1257 ...

  5. 基于c语言的编译原理课程设计,编译原理课程设计心得体会

    与<编译原理课程设计心得体会>相关的范文 本文由leishensc贡献 doc文档可能在WAP端浏览体验不佳.建议您优先选择TXT,或下载源文件到本机查看. 2008-2009 学年第二学 ...

  6. PL/0编译程序的研究与改进

    图片太多了,报告完整下载见链接: 2 PL/0总体结构 PL/0编译程序采用单遍扫描方式的编译过程,由词法分析程序.语法语义分析程序以及代码生成程序3个独立的过程组成.PL/0编译程序以语法语义分析程 ...

  7. 太原理工 编译原理 c语言,太原理工大学编译原理实验

    <太原理工大学编译原理实验>由会员分享,可在线阅读,更多相关<太原理工大学编译原理实验(19页珍藏版)>请在人人文库网上搜索. 1.本科实验报告课程名称: 编译原理 实验项目: ...

  8. PL/0语言编译器扩展 编译原理课程实践(1)

    转眼大学生活就要结束,编译原理课程学的东西很多都忘记了.当时我们编译原理课程实践是PL/0语言编译器扩展,在原有PL/0语言文法进行扩展.我写这次博文一是为了回忆以前学的知识,加深记忆:二是和大家分享 ...

  9. 编译原理LR(0)项目集规范族的构造详解

    学编译原理的时候,感觉什么LL(1).LR(0).SLR(1).LALR(1)思想满天飞. 而且做题的时候,一不留意,一道题就写了三页纸了. 就拿今天这个玩意儿来讲,我真的是考试前花了最多的时间,搞懂 ...

最新文章

  1. 在SpringBoot项目中使用redis简单用法(一)
  2. python开源人脸识别库识别率达99_Python的开源人脸识别库:离线识别率高达99.38%...
  3. mybatis plus generator配置
  4. 解构给默认值_5个 JS 解构有趣的用途
  5. python牛客网编程题_【面经】小米软件开发一面(python)面经 2020 2020
  6. 人类最强运载火箭诞生!马斯克吹的牛再次兑现!
  7. SVN 回滚(撤回)提交的代码
  8. 数据分析没价值?——深思对业务的洞察有多少?
  9. mysql6.7主从_CentOS 6.7下MySQL主从部署
  10. 送给程序员们的经典电子书大礼包
  11. python实现将将输入的可约分数化简为不可约分数
  12. GRIB格式转换心得(转自博客:http://windforestwing.blog.163.com/blog/static/19545412007103084743804/)...
  13. TortoiseSVN打分支、合并分支、切换分支
  14. Extjs6 自定义事件
  15. hive 常用函数操作
  16. 按键消抖(并联0.1uf电容)
  17. centos6.3安装bluefish
  18. 阿里 VP 贾扬清确认离职!尚未创立公司,方向或是 AI 架构?
  19. RN 在Android Studio运行闪退后报错
  20. 手机连接投影机的步骤_投影仪如何投屏手机?手机连接投影仪的步骤

热门文章

  1. 模拟一个具有基本功能的操作系统(JavaGUI)
  2. 新中新身份证读卡器开发问题
  3. 矩阵的压缩存储(随机稀疏矩阵的建立和输出)
  4. Python 带你高效剪辑创作短视频
  5. 虚拟机IC617 REHAT导入工艺库操作
  6. iphone 后台循环播放音乐
  7. C语言职工管理系统(带有报告)
  8. 极验系列文章一:极验三代 极验验证码整体流程分析
  9. Java 读取EXCEL表格中的数据,将数据转为SQL语句
  10. Python 爬虫实例+爬取豆瓣小组 + wordcloud 制作词云图