PL0源代码

合集下载

PL0编译程序源程序

PL0编译程序源程序

procedure constdeclaration; (*常量定义的处理*) begin if sym = ident then begin getsym; if sym in [eql,becomes] then begin if sym = becomes then error(1); getsym; if sym = number then begin enter(constant); getsym; end else error(2) end else error(3) end else error(4); end; (*constdeclaration 结束*)
until kk = k; id:= a; i:= 1; (*二分法查保留字表*) j:= norw; repeat k:= (i+j) div 2; if id <= word[k] then j:= k-1; if id >= word[k] then i:= k+1 until i>j; if i-1 > j then sym:= wsym[k] else sym:= ident; end else if ch in ['0'..'9'] then begin (*number*) k:= 0; num:= 0; sym:= number; repeat num:= 10 * num+(ord(ch)-ord('0')); k:= k+1; getch until not (ch in ['0'..'9']); if k > nmax then error(30); end else if ch = ':' then begin getch; if ch = '=' then begin sym:= becomes; getch; end else sym:= nul; end else (*<=*)

编译原理(PL0编译程序源代码)

编译原理(PL0编译程序源代码)

/*PL/0编译程序(C语言版)*编译和运行环境:*Visual C++6.0*WinXP/7*使用方法:*运行后输入PL/0源程序文件名*回答是否将虚拟机代码写入文件*回答是否将符号表写入文件*执行成功会产生四个文件(词法分析结果.txt符号表.txt虚拟代码.txt源程序和地址.txt)*/#include <stdio.h>#include"pl0.h"#include"string"#define stacksize 500//解释执行时使用的栈int main(){bool nxtlev[symnum];printf("请输入源程序文件名:");scanf("%s",fname);fin=fopen(fname,"r");//以只读方式打开pl0源程序文件cifa=fopen("词法分析结果.txt","w");fa1=fopen("源程序和地址.txt","w");//输出源文件及各行对应的首地址fprintf(fa1,"输入pl0源程序文件名:");fprintf(fa1,"%s\n",fname);if(fin){printf("是否将虚拟机代码写入文件?(Y/N)");//是否输出虚拟机代码scanf("%s",fname);listswitch=(fname[0]=='y'||fname[0]=='Y');printf("是否将符号表写入文件?(Y/N)");//是否输出符号表scanf("%s",fname);tableswitch=(fname[0]=='y'||fname[0]=='Y');init();//初始化err=0;cc=cx=ll=0;ch=' ';if(-1!=getsym()){fa=fopen("虚拟代码.txt","w");fas=fopen("符号表.txt","w");addset(nxtlev,declbegsys,statbegsys,symnum);nxtlev[period]=true;if(-1==block(0,0,nxtlev)){//调用编译程序fclose(fa);fclose(fa1);fclose(fas);fclose(fin);return 0;}if(sym!=period){error(9);//结尾丢失了句号}if(err!=0){printf("pl0源程序出现错误,退出编译!!!请从第一个错误处开始修改.\n\n");fprintf(cifa,"源程序出现错误,请检查!!!");fprintf(fa1,"源程序出现错误,请检查!!!");fprintf(fa,"源程序出现错误,请检查!!!");fprintf(fas,"源程序出现错误,请检查!!!");}fclose(fa);fclose(fa1);fclose(fas);}fclose(fin);}else{printf("Can't open file!\n");}fclose(cifa);//printf("\n");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]),"end");strcpy(&(word[5][0]),"if");strcpy(&(word[6][0]),"odd");strcpy(&(word[7][0]),"procedure");strcpy(&(word[8][0]),"read");strcpy(&(word[9][0]),"then");strcpy(&(word[10][0]),"var");strcpy(&(word[11][0]),"while");strcpy(&(word[12][0]),"write");wsym[0]=beginsym;//设置保留字类别一字即一类wsym[1]=callsym;wsym[2]=constsym;wsym[3]=dosym;wsym[4]=endsym;wsym[5]=ifsym;wsym[6]=oddsym;wsym[7]=procsym;wsym[8]=readsym;wsym[9]=thensym;wsym[10]=varsym;wsym[11]=whilesym;wsym[12]=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;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;}void error(int n){//出错处理,打印出错位置和错误编码char space[81];memset(space, 32,81);space[cc-1]=0;printf("error(%d)",n);fprintf(fa1,"error(%d)",n);switch(n){case 1:printf("\t\t常量说明中的“=”写成“:=”\n");fprintf(fa1,"\t\t常量说明中的“=”写成“:=”\n");break;case 2:printf("\t\t常量说明中的=后应该是数字\n");fprintf(fa1,"\t\t常量说明中的=后应该是数字\n");break;case 3:printf("\t\t常量说明符中的表示符应该是=\n" );fprintf(fa1,"\t\t常量说明符中的表示符应该是=\n");break;case 4:printf("\t\tconst,var,procedure后应为标识符\n" );fprintf(fa1,"\t\tconst,var,procedure后应为标识符\n");break;case 5:printf("\t\t漏掉了“,”或“;”\n" );fprintf(fa1,"\t\t漏掉了“,”或“;”\n" );break;case 6:printf("\t\t过程说明后的符号不正确\n" );fprintf(fa1,"\t\t过程说明后的符号不正确\n");break;case 7:printf("\t\t应是语句开始符\n" );fprintf(fa1,"\t\t应是语句开始符\n" );break;case 8:printf("\t\t程序体语句部分的后跟符不正确\n" );fprintf(fa1,"\t\t程序体语句部分的后跟符不正确\n" );break;case 9:printf("\t\t程序结尾丢了句号“.”\n\n" );fprintf(fa1,"\t\t程序结尾丢了句号“.”\n");break;case 10:printf("\t\t语句之间漏了“;”\n" );fprintf(fa1,"\t\t语句之间漏了“;”\n");break;case 11:printf("\t\t标识符拼写错误或未说明\n" );fprintf(fa1,"\t\t标识符拼写错误或未说明\n");break;case 12:printf("\t\t赋值语句中,赋值号左部标识符属性应是变量\n" );fprintf(fa1,"\t\t赋值语句中,赋值号左部标识符属性应是变量\n");break;case 13:printf("\t\t赋值语句左部标识符后应是复制号“:=”\n" );fprintf(fa1,"\t\t赋值语句左部标识符后应是复制号“:=”\n");break;case 14:printf("\t\tcall后应为标识符\n" );fprintf(fa1,"\t\tcall后应为标识符\n");break;case 15:printf("\t\tcall后标识符属性应为过程\n" );fprintf(fa1,"\t\tcall后标识符属性应为过程\n");break;case 16:printf("\t\t条件语句中丢了then\n" );fprintf(fa1,"\t\t条件语句中丢了then\n");break;case 17:printf("\t\t丢了“end”或“;”\n" );fprintf(fa1,"\t\t丢了“end”或“;”\n");break;case 18:printf("\t\twhile型循环语句中丢了“do”\n" );fprintf(fa1,"\t\twhile型循环语句中丢了“do”\n");break;case 19:printf("\t\t语句后的符号不正确\n" );fprintf(fa1,"\t\t语句后的符号不正确\n" );break;case 20:printf("\t\t应为关系运算符\n" );fprintf(fa1,"\t\t应为关系运算符\n");break;case 21:printf("\t\t表达式标示符属性不能是过程\n" );fprintf(fa1,"\t\t表达式标示符属性不能是过程\n");break;case 22:printf("\t\t表达式漏掉了右括号\n" );fprintf(fa1,"\t\t表达式漏掉了右括号\n");break;case 23:printf("\t\t因子后的非法符号\n" );fprintf(fa1,"\t\t因子后的非法符号\n");break;case 24:printf("\t\t表达式的开始符不能是此符号\n" );fprintf(fa1,"\t\t表达式的开始符不能是此符号\n");break;case 25:printf("\t\t标识符越界\n" );fprintf(fa1,"\t\t标识符越界\n");break;case 26:printf("\t\t非法字符\n" );fprintf(fa1,"\t\t非法字符\n");break;case 31:printf("\t\t数越界\n");fprintf(fa1,"\t\t数越界\n");break;case 32:printf("\t\tread语句括号中的标识符不是变量\n" );fprintf(fa1,"\t\tread语句括号中的标识符不是变量\n");break;case 33:printf("\t\twrite()或read()中应为完整表达式\n" );fprintf(fa1,"\t\twrite()或read()中应为完整表达式\n");break;default:printf("\t\t出现未知错误\n" );fprintf(fa1,"\t\t出现未知错误\n");}err++;}//漏掉空格,读取一个字符,每次读一行,存入line缓冲区,line被getsym取空后再读一//行,被函数getsym调用int getch(){if(cc==ll){if(feof(fin)){printf("program incomplete");return-1;}ll=0;cc=0;printf("\n%d ",cx);fprintf(fa1,"\n%d ",cx);ch=' ';while(ch!=10){if(EOF==fscanf(fin,"%c",&ch)){line[ll]=0;break;}printf("%c",ch);fprintf(fa1,"%c",ch);line[ll]=ch;ll++;}fprintf(cifa,"\n");}ch=line[cc];cc++;return 0;}int getsym(){//词法分析int i,j,k,l;while(ch==' '||ch==10||ch==9)//忽略空格换行 TABgetchdo;if(ch>='a'&&ch<='z'){//以字母开头的为保留字或者标识符k=0,l=1;do{if(k<al){//al为标识符或保留字最大长度a[k]=ch;k++;}if(k==al&&l==1){error(25);l=0;}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];fprintf(cifa,"%s\t\t%ssym\n",id,id);//printf("%s\t\t%ssym\n",id,id);}else{//否则为标识符或数字sym=ident;fprintf(cifa,"%s\t\tident\n",id);//printf("%s\t\tident\n",id);}}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){//数字的长度限制fprintf(cifa,"0\t\tnumber\n");num=0;error(31);}elsefprintf(cifa,"%d\t\tnumber\n",num);//printf("%d\t\tnumber\n",num);}else{if(ch==':'){//检测赋值符号,:只能和=匹配,否则不能识别getchdo;if(ch=='='){sym=becomes;fprintf(cifa,":=\t\tbecomes\n");getchdo;}else{sym=nul;}}else{if(ch=='<'){getchdo;if(ch=='='){sym=leq;//小于等于fprintf(cifa,"<=\t\tleq\n");getchdo;}else{sym=lss;//小于fprintf(cifa,"<\t\tlss\n");}}else{if(ch=='>'){getchdo;if(ch=='='){sym=geq;//大于等于fprintf(cifa,">=\t\tgeq\n");getchdo;}else{sym=gtr;//大于fprintf(cifa,">\t\tgtr\n");}}else{sym=ssym[ch];//不满足上述条件时按单字//符处理switch(ch){case'+':fprintf(cifa,"%c\t\tplus\n",ch);break;case '-':fprintf(cifa,"%c\t\tminus\n",ch );break;case '*':fprintf(cifa,"%c\t\ttimes\n",ch);break;case '/':fprintf(cifa,"%c\t\tslash\n",ch);break;case '(':fprintf(cifa,"%c\t\tlparen\n",ch);break;case ')':fprintf(cifa,"%c\t\trparen\n",ch);break;case '=':fprintf(cifa,"%c\t\teql\n",ch);break;case ',':fprintf(cifa,"%c\t\tcomma\n",ch);break;case '#':fprintf(cifa,"%c\t\tneq\n",ch);break;case ';':fprintf(cifa,"%c\t\tsemicolon\n",ch);break;case '.':break;default :error(26);}if(sym!=period){//判断是否结束getchdo;}else{printf("\n");fprintf(cifa,".\t\tperiod\n");}}}}}}return 0;}//生成目标代码//目标代码的功能码,层差和位移量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;}//测试字符串int test(bool* s1,bool* s2,int n){if(!inset(sym,s1)){//测试sym是否属于s1,不属于则报错nerror(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;//保留初始txint cx0;//保留初始cxbool nxtlev[symnum];dx=3;//相对地址从3开始,前3个单元即0、1、2单元分别为 SL:静态链;//DL:动态链;RA:返回地址tx0=tx;//记录本层的初始位置table[tx].adr=cx;gendo(jmp,0,0);if(lev>levmax){//层数超过3error(32);}do{if(sym==constsym){//收到常量声明printf("该语句为常量定义语句\n");getsymdo;do{constdeclarationdo(&tx,lev,&dx);//常量声明处理,dx 会改//变所以使用指针while(sym==comma){//处理一次多常量定义getsymdo;constdeclarationdo(&tx,lev,&dx);}if(sym==semicolon){//常量声明处理结束getsymdo;}else{error(5);//漏掉了逗号或者分号(一般是分号)}}while(sym==ident);}if(sym==varsym){//收到变量声明printf("该语句为变量声明语句\n");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){//收到过程声明printf("该语句为过程声明语句\n");getsymdo;if(sym==ident){enter(procedur,&tx,lev,&dx);//记录过程名getsymdo;}else{error(4);//过程声明后应为标识符}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;cx0=cx;gendo(inte,0,dx);//生成分配存代码if(tableswitch){//输出符号表if(tx0+1<tx){fprintf(fas,"TABLE:\n");//printf("NULL\n");}for(i=tx0+1;i<=tx;i++){switch(table[i].kind){case constant:fprintf(fas,"%d const %s ",i,table[i].name);fprintf(fas,"val=%d\n",table[i].val);break;case variable:fprintf(fas,"%d var %s ",i,table[i].name);fprintf(fas,"lev=%daddr=%d\n",table[i].level,table[i].adr);break;case procedur:fprintf(fas,"%d proc %s ",i,table[i].name);fprintf(fas,"lev=%daddr=%dsize=%d\n", table[i].level,table[i].adr,table[i].size);break;}}}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);//分程序没有补救集合testdo(fsys,nxtlev,8);//检测后跟符号集的正确性listcode(cx0);//输出代码return 0;}//k:const var procedure//ptx:符号表尾的指针//pdx:dx为当前应分配变量的相对地址//lev:符号名字所在的层次//往符号表中添加void enter(enum object k,int* ptx,int lev,int* pdx){(* ptx)++;strcpy(table[(*ptx)].name,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;}}//寻找符号在符号表中的地址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);}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++){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;printf("该语句为赋值语句。

编译原理(PL0编译程序源代码)

编译原理(PL0编译程序源代码)

/*PL/0编译程序(C语言版)*编译和运行环境:*Visual C++*WinXP/7*利用方式:*运行后输入PL/0源程序文件名*回答是不是将虚拟机代码写入文件*回答是不是将符号表写入文件*执行成功会产生四个文件(词法分析结果.txt符号表.txt虚拟代码.txt源程序和地址.txt)*/#include <>#include""#include"string"#define stacksize 500xt","w");fa1=fopen("源程序和地址.txt","w");xt","w");fas=fopen("符号表.txt","w");addset(nxtlev,declbegsys,statbegsys,symnum);nxtlev[period]=true;if(-1==block(0,0,nxtlev)){n\n");fprintf(cifa,"源程序显现错误,请检查!!!");fprintf(fa1,"源程序显现错误,请检查!!!");fprintf(fa,"源程序显现错误,请检查!!!");fprintf(fas,"源程序显现错误,请检查!!!");}fclose(fa);fclose(fa1);fclose(fas);}fclose(fin);}else{printf("Can't open file!\n");}fclose(cifa);]=period;ssym['#']=neq;ssym[';']=semicolon;strcpy(&(word[0][0]),"begin");\n\n" );fprintf(fa1,"\t\t程序结尾丢了句号“.”\n");break;case 10:printf("\t\t语句之间漏了“;”\n" );fprintf(fa1,"\t\t语句之间漏了“;”\n");break;case 11:printf("\t\t标识符拼写错误或未说明\n" );fprintf(fa1,"\t\t标识符拼写错误或未说明\n");break;case 12:printf("\t\t赋值语句中,赋值号左部标识符属性应是变量\n" );fprintf(fa1,"\t\t赋值语句中,赋值号左部标识符属性应是变量\n");break;case 13:printf("\t\t赋值语句左部标识符后应是复制号“:=”\n" );fprintf(fa1,"\t\t赋值语句左部标识符后应是复制号“:=”\n");break;case 14:printf("\t\tcall后应为标识符\n" );fprintf(fa1,"\t\tcall后应为标识符\n");break;case 15:printf("\t\tcall后标识符属性应为进程\n" );fprintf(fa1,"\t\tcall后标识符属性应为进程\n");break;case 16:printf("\t\t条件语句中丢了then\n" );fprintf(fa1,"\t\t条件语句中丢了then\n");break;case 17:printf("\t\t丢了“end”或“;”\n" );fprintf(fa1,"\t\t丢了“end”或“;”\n");break;case 18:printf("\t\twhile型循环语句中丢了“do”\n" ); fprintf(fa1,"\t\twhile型循环语句中丢了“do”\n");break;case 19:printf("\t\t语句后的符号不正确\n" );fprintf(fa1,"\t\t语句后的符号不正确\n" );break;case 20:printf("\t\t应为关系运算符\n" );fprintf(fa1,"\t\t应为关系运算符\n");break;case 21:printf("\t\t表达式内标示符属性不能是进程\n" ); fprintf(fa1,"\t\t表达式内标示符属性不能是进程\n");break;case 22:printf("\t\t表达式漏掉了右括号\n" );fprintf(fa1,"\t\t表达式漏掉了右括号\n");break;case 23:printf("\t\t因子后的非法符号\n" );fprintf(fa1,"\t\t因子后的非法符号\n");break;case 24:printf("\t\t表达式的开始符不能是此符号\n" );fprintf(fa1,"\t\t表达式的开始符不能是此符号\n");break;case 25:printf("\t\t标识符越界\n" );fprintf(fa1,"\t\t标识符越界\n");break;case 26:printf("\t\t非法字符\n" );fprintf(fa1,"\t\t非法字符\n");break;case 31:printf("\t\t数越界\n");fprintf(fa1,"\t\t数越界\n");break;case 32:printf("\t\tread语句括号中的标识符不是变量\n" );fprintf(fa1,"\t\tread语句括号中的标识符不是变量\n");break;case 33:printf("\t\twrite()或read()中应为完整表达式\n" );fprintf(fa1,"\t\twrite()或read()中应为完整表达式\n");break;default:printf("\t\t显现未知错误\n" );fprintf(fa1,"\t\t显现未知错误\n");}err++;}:break;default :error(26);}if(sym!=period){t\tperiod\n");}}}}}}return 0;}=x;code[cx].l=y;code[cx].a=z;cx++;return 0;}dr=cx;gendo(jmp,0,0);if(lev>levmax){dr].a=cx;dr=cx;ize=dx;cx0=cx;gendo(inte,0,dx);ind){case constant:fprintf(fas,"%d const %s ",i,table[i].name);fprintf(fas,"val=%d\n",table[i].val);break;case variable:fprintf(fas,"%d var %s ",i,table[i].name);fprintf(fas,"lev=%daddr=%d\n",table[i].level,table[i].adr);break;case procedur:fprintf(fas,"%d proc %s ",i,table[i].name);fprintf(fas,"lev=%daddr=%dsize=%d\n", table[i].level,table[i].adr,table[i].size);break;}}}memcpy(nxtlev,fsys,sizeof(bool)*symnum);ame,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;}}ame,idt);i=tx;ame,idt)!=0){i--;}return i;}],code[i].l,code[i],a);}}}ind!=variable){error(12);i=0;}else{getsymdo;if(sym==becomes){getsymdo;printf("该语句为赋值语句。

编译原理(PL0编译程序源代码)

编译原理(PL0编译程序源代码)

下载可编辑/*PL/0编译程序(C语言版)*编译和运行环境:*Visual C++6.0*WinXP/7*使用方法:*运行后输入 PL/0 源程序文件名*回答是否将虚拟机代码写入文件*回答是否将符号表写入文件* 执行成功会产生四个文件(词法分析结果.txt符号表.txt虚拟代码.txt源程序和地址.txt)*/#include <stdio.h>#include"pl0.h"#include"string"#define stacksize 500//解释执行时使用的栈int main(){bool nxtlev[symnum];printf("请输入源程序文件名:");scanf("%s",fname);fin=fopen(fname,"r");//以只读方式打开pl0 源程序文件cifa=fopen(" 词法分析结果.txt","w");fa1=fopen(" 源程序和地址 .txt","w");//输出源文件及各行对应的首地址fprintf(fa1,"输入 pl0 源程序文件名:");fprintf(fa1,"%s\n",fname);if(fin){printf("是否将虚拟机代码写入文件?(Y/N)");//是否输出虚拟机代码scanf("%s",fname);listswitch=(fname[0]=='y'||fname[0]=='Y');printf("是否将符号表写入文件?(Y/N)");//是否输出符号表scanf("%s",fname);tableswitch=(fname[0]=='y'||fname[0]=='Y');init();//初始化err=0;cc=cx=ll=0;ch=' ';if(-1!=getsym()){fa=fopen("虚拟代码.txt","w");fas=fopen(" 符号表 .txt","w");addset(nxtlev,declbegsys,statbegsys,symnum);nxtlev[period]=true;if(-1==block(0,0,nxtlev)){//调用编译程序fclose(fa);fclose(fa1);fclose(fas);fclose(fin);return 0;}if(sym!=period){error(9);//结尾丢失了句号}if(err!=0){printf("pl0源程序出现错误,退出编译!!!请从第一个错误处开始修改 .\n\n");fprintf(cifa,"源程序出现错误,请检查!!!");fprintf(fa1,"源程序出现错误,请检查!!!");fprintf(fa,"源程序出现错误,请检查!!!");fprintf(fas,"源程序出现错误,请检查!!!");}fclose(fa);fclose(fa1);fclose(fas);}fclose(fin);}else{printf("Can't open file!\n");}fclose(cifa);//printf("\n");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]),"end");strcpy(&(word[5][0]),"if");strcpy(&(word[6][0]),"odd");strcpy(&(word[7][0]),"procedure");strcpy(&(word[8][0]),"read");strcpy(&(word[9][0]),"then");strcpy(&(word[10][0]),"var");strcpy(&(word[11][0]),"while");strcpy(&(word[12][0]),"write");wsym[0]=beginsym;//设置保留字类别一字即一类wsym[1]=callsym;wsym[2]=constsym;wsym[3]=dosym;wsym[4]=endsym;wsym[6]=oddsym;wsym[7]=procsym;wsym[8]=readsym;wsym[9]=thensym;wsym[10]=varsym;wsym[11]=whilesym;wsym[12]=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;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;}void error(int n){//出错处理,打印出错位置和错误编码char space[81];memset(space, 32,81);space[cc-1]=0;printf("error(%d)",n);fprintf(fa1,"error(%d)",n);case 1:printf("\t\t常量说明中的“=”写成“ := ” \n");fprintf(fa1,"\t\t常量说明中的“=”写成“ := ”\n");break;case 2:printf("\t\t常量说明中的=后应该是数字\n");fprintf(fa1,"\t\t常量说明中的 =后应该是数字\n");break;case 3:printf("\t\t常量说明符中的表示符应该是=\n" );fprintf(fa1,"\t\t常量说明符中的表示符应该是=\n");break;case 4:printf("\t\tconst,var,procedure后应为标识符 \n" );fprintf(fa1,"\t\tconst,var,procedure后应为标识符\n");break;case 5:printf("\t\t漏掉了“ , ”或“ ; ” \n" );fprintf(fa1,"\t\t漏掉了“ , ”或“ ; ”\n" );break;case 6:printf("\t\t过程说明后的符号不正确\n" );fprintf(fa1,"\t\t过程说明后的符号不正确\n");break;case 7:printf("\t\t应是语句开始符\n" );fprintf(fa1,"\t\t应是语句开始符\n" );break;case 8:printf("\t\t程序体语句部分的后跟符不正确\n" );fprintf(fa1,"\t\t程序体语句部分的后跟符不正确\n" );break;case 9:printf("\t\t程序结尾丢了句号“. ”\n\n" );fprintf(fa1,"\t\t程序结尾丢了句号“. ” \n");break;case 10:printf("\t\t语句之间漏了“; ” \n" );fprintf(fa1,"\t\t语句之间漏了“; ” \n");break;case 11:printf("\t\t标识符拼写错误或未说明\n" );fprintf(fa1,"\t\t标识符拼写错误或未说明\n");break;case 12:printf("\t\t赋值语句中,赋值号左部标识符属性应是变量\n" );fprintf(fa1,"\t\t赋值语句中,赋值号左部标识符属性应是变量\n");break;case 13:printf("\t\t赋值语句左部标识符后应是复制号“:= ” \n" );fprintf(fa1,"\t\t赋值语句左部标识符后应是复制号“:= ” \n");break;case 14:printf("\t\tcall后应为标识符 \n" );fprintf(fa1,"\t\tcall后应为标识符 \n");break;case 15:printf("\t\tcall后标识符属性应为过程\n" );fprintf(fa1,"\t\tcall后标识符属性应为过程\n");break;case 16:printf("\t\t条件语句中丢了then\n" );fprintf(fa1,"\t\t条件语句中丢了then\n");break;case 17:printf("\t\t丢了“ end”或“ ; ” \n" );fprintf(fa1,"\t\t丢了“ end”或“ ; ”\n");break;case 18:printf("\t\twhile型循环语句中丢了“do” \n" );fprintf(fa1,"\t\twhile型循环语句中丢了“do” \n");break;case 19:printf("\t\t语句后的符号不正确\n" );fprintf(fa1,"\t\t语句后的符号不正确\n" );break;case 20:printf("\t\t应为关系运算符\n" );fprintf(fa1,"\t\t应为关系运算符\n");break;case 21:printf("\t\t表达式标示符属性不能是过程\n" );fprintf(fa1,"\t\t表达式标示符属性不能是过程\n");break;case 22:printf("\t\t表达式漏掉了右括号\n" );fprintf(fa1,"\t\t表达式漏掉了右括号\n");break;case 23:printf("\t\t因子后的非法符号\n" );fprintf(fa1,"\t\t因子后的非法符号\n");break;case 24:printf("\t\t表达式的开始符不能是此符号\n" );fprintf(fa1,"\t\t表达式的开始符不能是此符号\n");break;case 25:printf("\t\t标识符越界 \n" );fprintf(fa1,"\t\t标识符越界 \n");break;case 26:printf("\t\t非法字符 \n" );fprintf(fa1,"\t\t非法字符 \n");break;case 31:printf("\t\t数越界 \n");fprintf(fa1,"\t\t数越界 \n");break;case 32:printf("\t\tread语句括号中的标识符不是变量\n" );fprintf(fa1,"\t\tread语句括号中的标识符不是变量\n");break;case 33:printf("\t\twrite()或 read() 中应为完整表达式\n" );fprintf(fa1,"\t\twrite()或 read() 中应为完整表达式\n");break;default:printf("\t\t出现未知错误 \n" );fprintf(fa1,"\t\t出现未知错误 \n");}err++;}// 漏掉空格,读取一个字符,每次读一行,存入line缓冲区,line被getsym取空后再读一// 行,被函数getsym 调用int getch(){if(cc==ll){if(feof(fin)){printf("program incomplete");return-1;}ll=0;cc=0;printf("\n%d ",cx);fprintf(fa1,"\n%d ",cx);ch=' ';while(ch!=10){if(EOF==fscanf(fin,"%c",&ch)){line[ll]=0;break;}printf("%c",ch);fprintf(fa1,"%c",ch);line[ll]=ch;ll++;}fprintf(cifa,"\n");}ch=line[cc];cc++;return 0;}int getsym(){//词法分析int i,j,k,l;while(ch==' '||ch==10||ch==9)//忽略空格换行TABgetchdo;if(ch>='a'&&ch<='z'){//以字母开头的为保留字或者标识符k=0,l=1;do{if(k<al){//al为标识符或保留字最大长度a[k]=ch;k++;}if(k==al&&l==1){error(25);l=0;}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];fprintf(cifa,"%s\t\t%ssym\n",id,id);//printf("%s\t\t%ssym\n",id,id);}else{//否则为标识符或数字sym=ident;fprintf(cifa,"%s\t\tident\n",id);//printf("%s\t\tident\n",id);}}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){//数字的长度限制fprintf(cifa,"0\t\tnumber\n");num=0;error(31);}elsefprintf(cifa,"%d\t\tnumber\n",num);//printf("%d\t\tnumber\n",num);}else{if(ch==':'){//检测赋值符号,:只能和=匹配,否则不能识别getchdo;if(ch=='='){sym=becomes;fprintf(cifa,":=\t\tbecomes\n");getchdo;}else{sym=nul;}}else{if(ch=='<'){getchdo;if(ch=='='){sym=leq;//小于等于fprintf(cifa,"<=\t\tleq\n");getchdo;}else{sym=lss;//小于fprintf(cifa,"<\t\tlss\n");}}else{if(ch=='>'){getchdo;if(ch=='='){sym=geq;// 大于等于fprintf(cifa,">=\t\tgeq\n");getchdo;}else{sym=gtr;//大于fprintf(cifa,">\t\tgtr\n");}}else{sym=ssym[ch];//不满足上述条件时按单字// 符处理switch(ch){case'+':fprintf(cifa,"%c\t\tplus\n",ch);break;case '-':fprintf(cifa,"%c\t\tminus\n",ch);break;case '*':fprintf(cifa,"%c\t\ttimes\n",ch);break;case '/':fprintf(cifa,"%c\t\tslash\n",ch);break;case '(':fprintf(cifa,"%c\t\tlparen\n",ch);break;case ')':fprintf(cifa,"%c\t\trparen\n",ch);break;case '=':fprintf(cifa,"%c\t\teql\n",ch);break;case ',':fprintf(cifa,"%c\t\tcomma\n",ch);break;case '#':fprintf(cifa,"%c\t\tneq\n",ch);break;case ';':fprintf(cifa,"%c\t\tsemicolon\n",ch);break;case '.':break;default :error(26);}if(sym!=period){//判断是否结束getchdo;}else{printf("\n");fprintf(cifa,".\t\tperiod\n");}}}}}}return 0;}//生成目标代码 // 目标代码的功能码,层差和位移量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;}//测试字符串int test(bool* s1,bool* s2,int n){if(!inset(sym,s1)){//测试sym是否属于s1,不属于则报错n 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;//保留初始txint cx0;//保留初始cxbool nxtlev[symnum];dx=3;//相对地址从 3 开始,前 3 个单元即0、 1、 2 单元分别为SL: 静态链;//DL :动态链; RA:返回地址tx0=tx;//记录本层的初始位置table[tx].adr=cx;gendo(jmp,0,0);if(lev>levmax){//层数超过3error(32);}do{if(sym==constsym){//收到常量声明printf("该语句为常量定义语句\n");getsymdo;do{constdeclarationdo(&tx,lev,&dx);//常量声明处理,dx 会改 // 变所以使用指针while(sym==comma){//处理一次多常量定义getsymdo;constdeclarationdo(&tx,lev,&dx);}if(sym==semicolon){//常量声明处理结束getsymdo;}else{error(5);//漏掉了逗号或者分号(一般是分号)}}while(sym==ident);}if(sym==varsym){//收到变量声明printf("该语句为变量声明语句\n");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){//收到过程声明printf("该语句为过程声明语句\n");getsymdo;if(sym==ident){enter(procedur,&tx,lev,&dx);//记录过程名getsymdo;}else{error(4);//过程声明后应为标识符}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;cx0=cx;gendo(inte,0,dx);//生成分配存代码if(tableswitch){//输出符号表if(tx0+1<tx){fprintf(fas,"TABLE:\n");//printf("NULL\n");}for(i=tx0+1;i<=tx;i++){switch(table[i].kind){case constant:fprintf(fas,"%d const %s ",i,table[i].name);fprintf(fas,"val=%d\n",table[i].val);break;case variable:fprintf(fas,"%d var %s ",i,table[i].name);fprintf(fas,"lev=%daddr=%d\n",table[i].level,table[i].adr);break;case procedur:fprintf(fas,"%d proc %s ",i,table[i].name);fprintf(fas,"lev=%daddr=%dsize=%d\n",table[i].level,table[i].adr,table[i].size);break;}}}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);//分程序没有补救集合testdo(fsys,nxtlev,8);//检测后跟符号集的正确性listcode(cx0);//输出代码return 0;}//k:const var procedure//ptx:符号表尾的指针//pdx:dx为当前应分配变量的相对地址//lev: 符号名字所在的层次// 往符号表中添加void enter(enum object k,int* ptx,int lev,int* pdx){(* ptx)++;strcpy(table[(*ptx)].name,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;}}//寻找符号在符号表中的地址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);}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++){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;printf("该语句为赋值语句。

pl0编译原理

pl0编译原理

pl0编译原理编译原理是计算机科学中的一门重要课程,它研究的是如何将高级语言转化为机器语言的过程。

在编译原理中,pl0是一种简单的编程语言,它的设计目标是为了教学和研究目的而产生的。

本文将介绍pl0编译原理的基本概念和主要过程。

一、pl0编译原理的基本概念1.1 什么是pl0编程语言pl0是一种结构化的过程性编程语言,它的语法规则简单明了,易于学习和理解。

pl0支持基本的数据类型和控制结构,包括整型、实型、布尔型等。

1.2 pl0编译器的作用pl0编译器的主要作用是将pl0源代码转化为目标代码,使计算机能够理解和执行这些代码。

编译器的工作包括词法分析、语法分析、语义分析、中间代码生成和目标代码生成等。

1.3 pl0编译过程的主要阶段pl0编译过程主要包括词法分析、语法分析、语义分析和代码生成等阶段。

在词法分析阶段,编译器将源代码分解成一个个的词法单元;在语法分析阶段,编译器将词法单元按照语法规则组织成一个抽象语法树;在语义分析阶段,编译器对抽象语法树进行语义检查和类型推导;最后,在代码生成阶段,编译器将抽象语法树转化为目标代码。

二、pl0编译原理的主要过程2.1 词法分析词法分析是编译过程的第一步,它将源代码分解成一个个的词法单元。

在pl0编译器中,常见的词法单元包括关键字、标识符、常量、运算符和界符等。

编译器通过正则表达式和有限自动机等技术来实现词法分析。

2.2 语法分析语法分析是编译过程的第二步,它将词法单元按照语法规则组织成一个抽象语法树。

在pl0编译器中,常见的语法规则包括表达式、语句、函数和过程等。

编译器通过上下文无关文法和递归下降等技术来实现语法分析。

2.3 语义分析语义分析是编译过程的第三步,它对抽象语法树进行语义检查和类型推导。

在pl0编译器中,常见的语义检查包括变量声明检查、类型匹配检查和作用域检查等。

编译器通过符号表和类型推导等技术来实现语义分析。

2.4 代码生成代码生成是编译过程的最后一步,它将抽象语法树转化为目标代码。

PL0编译器源程序分析

PL0编译器源程序分析

PL/0编译器源程序分析PL/0语言是Pascal语言的一个子集,我们这里分析的PL/0的编译程序包括了对PL/0语言源程序进行分析处理、编译生成类PCODE代码,并在虚拟机上解释运行生成的类PCODE代码的功能。

PL/0语言编译程序采用以语法分析为核心、一遍扫描的编译方法。

词法分析和代码生成作为独立的子程序供语法分析程序调用。

语法分析的同时,提供了出错报告和出错恢复的功能。

在源程序没有错误编译通过的情况下,调用类PCODE解释程序解释执行生成的类PCODE代码。

词法分析子程序分析:词法分析子程序名为getsym,功能是从源程序中读出一个单词符号(token),把它的信息放入全局变量sym、id和num中,语法分析器需要单词时,直接从这三个变量中获得。

(注意!语法分析器每次用完这三个变量的值就立即调用getsym子程序获取新的单词供下一次使用。

而不是在需要新单词时才调用getsym过程。

)getsym过程通过反复调用getch子过程从源程序过获取字符,并把它们拼成单词。

getch过程中使用了行缓冲区技术以提高程序运行效率。

词法分析器的分析过程:调用getsym时,它通过getch过程从源程序中获得一个字符。

如果这个字符是字母,则继续获取字符或数字,最终可以拼成一个单词,查保留字表,如果查到为保留字,则把sym变量赋成相应的保留字类型值;如果没有查到,则这个单词应是一个用户自定义的标识符(可能是变量名、常量名或是过程的名字),把sym置为ident,把这个单词存入id变量。

查保留字表时使用了二分法查找以提高效率。

如果getch获得的字符是数字,则继续用getch获取数字,并把它们拼成一个整数,然后把sym置为number,并把拼成的数值放入num变量。

如果识别出其它合法的符号(比如:赋值号、大于号、小于等于号等),则把sym则成相应的类型。

如果遇到不合法的字符,把sym置成nul。

语法分析子程序分析:语法分析子程序采用了自顶向下的递归子程序法,语法分析同时也根据程序的语意生成相应的代码,并提供了出错处理的机制。

PL0编译程序的实现(精)

PL0编译程序的实现(精)

( 0) ( 1) ( 2) ( 3) ( 4) ( 5) ( 6) ( 7) ( 8) ( 9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24)
jmp jmp int lod lit opr sto opr int opr sto lod lit opr jpc cal lit lod opr opr opr opr sto jmp opr
PL/0编译程序功能的框架 PL/0源程序 PL/0编译程序
类 pcode代码
类 pcode解释程序 输入 输出
第2 章
PL/0编译程序的实现
步骤1. 源语言PL/0与目标代码类pcode 之间的映射 步骤2. PL/0编译程序的总体设计 步骤3. PL/0编译程序词法分析的设计与实现 步骤4. PL/0编译程序语法语义分析的设计与实现 步骤5. PL/0编译程序代码生成的实现 *步骤6. PL/0编译程序错误处理的实现 步骤7. 类pcode代码解释器的设计与实现
步骤1 PL/0程序到类pcode代码的映射
目标代码类pcode是一种假想栈式计算机的汇编 语言。 指令格式 f l a
f
l a
功能码
层次差 (标识符的引用层减去它的定义层) 根据不同的指令有所区别
假想栈式计算机的运行栈 T是栈顶指针 B是基地址:分配给一个过程数据空间的开始位置 T
B
2 1 0
步骤3
PL/0编译程序词法分析的 设计与实现
词法分析如何把单词传递给语法分析 单词定义(见教材288页) type symbol=(nul,ident,number,plus,…, varsym,procsym); ( 定义纯量/枚举类型,类似C的enum) sym:symbol; id:alfa; (type alfa=packed array[1..al] of char) al=10; num:integer;

COMPILER-PL0语言的示例-语法描述

COMPILER-PL0语言的示例-语法描述

补充: PL/0语言及其编译程序的实现✧PL/0语言的编译程序把PL/0语言程序翻译成为一种称为类pcode的假想的栈式计算机汇编语言程序。

这种汇编语言与机器无关,若要在某一机器上实现PL/0语言程序,只需用机器上配置的任何语言对类pcode语言程序进行解释执行。

✧世界著名计算机科学家N.Wirth编写了"PL/0语言的编译程序"。

✧后面附了PL/0编译程序源代码。

⏹何为PL/0语言-PL/0语言是PASCAL语言的子集. 具备了一般高级语言的必备部分.(如: read,write,if-then,do,while,call,begin-end,赋值语句)☐PL/0语言中的数据类型只有整型,没有浮点数,所以圆周率只能近似为3。

数字最多为14位。

标识符的有效长度是10 ☐PL/0语言允许过程嵌套定义和递归调用的。

过程最多可嵌套三层。

☐过程可以引用自己定义的局部标识符,也可以引用包围在它的外过程(包括主程序)定义的标识符。

以下将用扩充的巴科斯-瑙尔范式(BACKUS-NAUR FORM)和语法图(EBNF)两种形式给出PL/0语言的语法描述。

------------------------------------------------------------------------------------------------------------------------------------------------------- (a) PL/0语言文法的EBNF表示EBNF表示的符号说明1)〈〉:用左右尖括号括起来的中文字表示语法构造成分,或称语法单位,为非终结符。

2)∷= :该符号的左部由右部定义,可读作'定义为'。

3)| :表示'或',为左部可由多个右部定义。

<标识符| 常量> 是不允许的. 因为非终结符作为一个独立的单位,不可分割4){ } :花括号表示其内的语法成分可以重复。

(完整word版)PL0源程序-编译原理实验代码

(完整word版)PL0源程序-编译原理实验代码

附件1 小组成员:程序清单Main.c#include<stdio.h>#include<stdlib.h>#include<string.h>void error(int n);void getsym();//void enter(enum object k,int *ptx,int lev,int *pdx);int position(char*idt,int tx);int constdeclaration(int *ptx,int lev,int *pdx);int vardeclaration(int *ptx,int lev,int *pdx);int factor(int*ptx,int lev);int term(int *ptx,int lev);int expression(int *ptx,int lev);int statement(int *ptx,int lev);int block();enum object{constant,variable,procedure};struct tab{char name[14];enum object kind;int val;int level;int adr;int size;}table[100];enum symbol{nul,ident,number,plus,minus,times,slash,oddsym,eql,neq,lss,leq,gtr,geq,lparen,r paren,comma,semicolon,period,becomes,beginsym,endsym,ifsym,thensym,whilesym,writesym,readsym,dosym,ca llsym,constsym,varsym,procsym,progsym,};enum symbol sym=nul;enum symbol mulop;enum symbol wsym[14];enum symbol ssym[256];char ch=' ';char *str;charword[14][10]={"begin","call","const","do","end","if","odd","procedure","program ","read","then","var","while","write"};//设置保留字名字int num;int lev=0;int tx=0;int k;int *mm;//=(int *)malloc(sizeof(int));char *id,sign[14];char a[14];FILE *fp1,*fp2,*fp3;void error(int n){switch(n){case 1:printf("常数说明中的\"=\"写成了\":=\"。

编译原理PL0报告(附源码教程)

编译原理PL0报告(附源码教程)

编译原理课程设计学院计算机学院专业计算机科学与技术班级学号姓名指导教师20 年月日一、课程设计要求基本内容(成绩范围:“中”、“及格”或“不及格”)(1)扩充赋值运算:*= 和/=扩充语句(Pascal的FOR语句):①FOR <变量>:=<表达式> TO <表达式> DO <语句>②FOR <变量>:=<表达式> DOWNTO <表达式> DO <语句>其中,语句①的循环变量的步长为2,语句②的循环变量的步长为-2。

(3)增加运算:++ 和--。

选做内容(成绩评定范围扩大到:“优”和“良”)(1)增加类型:①字符类型;②实数类型。

(2)扩充函数:①有返回值和返回语句;②有参数函数。

(3)增加一维数组类型(可增加指令)。

(4)其他典型语言设施。

二、概述目标:实现PL0某些特定语句实现语言:C语言实现工具平台:VS201运行平台:WIN7三、结构设计说明与功能块描述PL/0编译程序的结构图PL/0编译程序的总体流程图四、主要成分描述1、符号表编译程序里用了一个枚举类型enum symbol,然后定义了enum symbol sym来存放当前的符号,前面讲过,主程序定义了一个以字符为元素的一维数组word,称保留字表,这个保留字表也存放在符号表里,为了识别当前的符号是属于哪些保留字;还有标识符,拼数,拼符合词等的符号名都存放在符号表里,当sym存放当前的符号时,我们可以判断它是属于哪类的符号,然后加以处理。

在运行的过程中,主程序中又定义了一个名字表,也就是符号表,来专门存放变量、常量和过程名的各个属性,里面的属性包括name,kind,val/level,adr,size,我们来举一个PL/0语言过程说明部分的片段:Const a=35,b=49;Var c,d,e;Procedure p;Var g;当遇到标识符的引用时就调用position函数,根据当前sym的符号类型来查table表,看是否有过正确的定义,若已有,则从表中取相应的有关信息,供代码的生成用。

pl0源代码

pl0源代码

源代码pl0c.h/* 关键字个数 */#define norw 13/* 名字表容量 */#define txmax 100/* 所有的add1用于定义数组 */#define txmaxadd1 101/* number的最大位数 */#define nmax 14/* 符号的最大长度 */#define al 10/* 地址上界 */#define amax 2047/* 最大允许过程嵌套声明层数 */#define levmax 3/* 最多的虚拟机代码数 */#define cxmax 200#define cxmaxadd1 201/* 当函数中会发生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 -1typedef enum {false,true} bool;/* 符号 */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,calls ym,constsym,varsym,procsym};#define symnum 32/* 名字表中的类型 */enum object {constant,variable,procedur};/* 虚拟机代码 */enum fct {lit,opr,lod,sto,cal,inte,jmp,jpc};#define fctnum 8/* 虚拟机代码结构 */struct instruction{enum fct f; /* 虚拟机代码指令 */int l; /* 引用层与声明层的层次差 */int a; /* 根据f的不同而不同 */};FILE* fas; /* 输出名字表 */FILE* fa; /* 输出虚拟机代码 */FILE* fa1; /* 输出源文件及其各行对应的首地址 */FILE* fa2; /* 输出结果 */bool listswitch; /* 显示虚拟机代码与否 */bool tableswitch; /* 显示名字表与否 */char ch; /* 获取字符的缓冲区,getch 使用 */enum symbol sym; /* 当前的符号 */char id[al]; /* 当前ident */int num; /* 当前number */int cc,ll,kk; /* getch使用的计数器,cc表示当前字符(ch)的位置 */ int cx; /* 虚拟机代码指针 */char line[81]; /* 读取行缓冲区 */char a[al]; /* 临时符号 */struct instruction code[cxmaxadd1]; /* 存放虚拟机代码的数组 */ 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 or procedure */int val; /* 数值,仅const使用 */int level; /* 所处层,仅const不使用 */int adr; /* 地址,仅const不使用 */int size; /* 需要分配的数据区空间,仅procedure使用 */ };struct tablestruct table[txmaxadd1]; /* 名字表 */FILE* fin;FILE* fout;char fname[al];int err; /* 错误计数器 */void 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 postion(char* idt,int tx);void enter(enum object k,int* ptx,int lev,int* pdx);int base(int l,int* s,int b);pl0c.c/*Windows 下c语言PL/0编译程序在Visual C++ 6.0和Visual 上运行通过使用方法:运行后输入PL/0源程序文件名回答是否输出虚拟机代码回答是否输出名字表fa.tmp输出虚拟机代码fa1.tmp输出源文件及其各行对应的首地址fa2.tmp输出结果fas.tmp输出名字表*/#include <stdio.h>#include "pl0c.h"#include "string.h"/* 解释执行时使用的栈 */#define stacksize 500int main(){bool nxtlev[symnum];init(); /* 初始化 */fas=fopen("fas.tmp","w");fa1=fopen("fa1.tmp","w");printf("Input file? ");fprintf(fa1,"Input file? ");scanf("%s",fname); /* 输入文件名 */fin=fopen(fname,"r");if(fin){fprintf(fa1,"%s\n",fname);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');err=0;cc=cx=ll=0;ch=' ';kk=al-1;if(-1!=getsym()){fa=fopen("fa.tmp","w");fa2=fopen("fa2.tmp","w");addset(nxtlev,declbegsys,statbegsys,symnum);nxtlev[period]=true;if(-1==block(0,0,nxtlev)) /* 调用编译程序 */{fclose(fa);fclose(fa1);fclose(fin);printf("\n");return 0;}fclose(fa);fclose(fa1);if(sym!=period)error(9);if(err==0)interpret(); /* 调用解释执行程序 */else{printf("Errors in pl/0 program");}}fclose(fin);}else{printf("Can't open file!\n");fprintf(fa1,"Can't open file!\n");fclose(fa1);}fclose(fas);printf("\n");return 0;}/* 在适当的位置显示错误 */void error(int n){char space[81];memset(space,32,81);space[cc-1]=0; /* 出错时当前符号已经读完,所以cc-1 */printf("****%s!%d\n",space,n);fprintf(fa1,"****%s!%d\n",space,n);err++;}/* 词法分析,获取一个符号 */int getsym(){int i,j,k;while(ch==' '||ch==10||ch==9) /* 忽略空格、换行和TAB */{getchdo;}if(ch>='a'&&ch<='z'){ /* 名字或保留字以a..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'){ /* 检测是否为数字:以0..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{sym=ssym[ch]; /* 当符号不满足上述条件时,全部按照单字符符号处理 */getchdo;}}}}}return 0;}/* 生成虚拟机代码 */int gen(enum fct x, /* f */int y, /* l */int z /* a */){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负责这项监测,并且负责当监测不通过时的补救措施,程序在需要检测时指定当前需要的符号集合和补救用的集合(如之前未完成部分的后跟符号),以及检测不通过时的错误号 */int test(bool* s1, /* 我们需要的符号 */bool* s2, /* 如果不是我们需要的,则需要一个补救用的集合 */int n) /* 错误号 */{if(!inset(sym,s1)){error(n);/* 当检测不通过时,不停获取符号,直到它属于需要的集合或补救的集合 */while((!inset(sym,s1))&&(!inset(sym,s2))){getsymdo;}}return 0;}/* 编译程序主体 */int block(int lev, /* 当前分程序所在层 */int tx, /* 名字表当前尾指针 */bool* fsys /* 当前模块后跟符号集合 */){int i;int dx; /* 名字分配到的相对地址 */int tx0; /* 保留初始tx */int cx0; /* 保留初始cx */bool nxtlev[symnum]; /* 在下级函数的参数中,符号集合均为值参,但由于使用数租实现,传递进来的是指针,为防止下级函数改变上级函数的集合,开辟新的空间传递给下级函数,之后所有的nxtlev都是这样 */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;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=%dsize=%d\n",table[i].level,table[i].adr,table[i].size);fprintf(fas," %d proc %s ",i,table[i].name);fprintf(fas,"lev=%d addr=%dsize=%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); /*分程序没有补救集合 */testdo(fsys,nxtlev,8); /* 检测后跟符号正确性 */listcode(cx0); /* 输出代码 */return 0;}/* 解释程序 */void interpret(){int p,b,t; /* 指令指针,指令基址,栈顶指针 */struct instruction i; /* 存放当前指令 */int s[stacksize]; /* 栈 */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:printf("%d",s[t-1]);fprintf(fa2,"%d",s[t-1]);t--;break;case 15:printf("\n");fprintf(fa2,"\n");break;case 16:printf("?");fprintf(fa2,"?");scanf("%d",&(s[t]));fprintf(fa2,"%d\n",s[t]);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);fclose(fa2);}/* 初始化 */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]),"end");strcpy(&(word[5][0]),"if");strcpy(&(word[6][0]),"odd");strcpy(&(word[7][0]),"procedure"); strcpy(&(word[8][0]),"read"); strcpy(&(word[9][0]),"then"); strcpy(&(word[10][0]),"var"); strcpy(&(word[11][0]),"while"); strcpy(&(word[12][0]),"write");/* 设置保留字符号 */wsym[0]=beginsym;wsym[1]=callsym;wsym[2]=constsym;wsym[3]=dosym;wsym[4]=endsym;wsym[5]=ifsym;wsym[6]=oddsym;wsym[7]=procsym;wsym[8]=readsym;wsym[9]=thensym;wsym[10]=varsym;wsym[11]=whilesym;wsym[12]=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;/* 设置因子开始符号集 */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;}/* 供getsym取一个字符,每次读一行,存入line缓冲区,line被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);printf("%c",ch);fprintf(fa1,"%c",ch);line[ll]=ch;ll++;}printf("\n");fprintf(fa1,"\n");}ch=line[cc];cc++;return 0;}/* 生成一项名字表 */void enter(enum object k, /* 名字种类const,var or procedure */int* ptx, /* 名字表尾指针的指针,为了可以改变名字表尾指针的值,以后所有的ptx 都是这样 */int lev, /* 名字所在的层次,,以后所有的lev都是这样 */int* pdx /* dx为当前应分配的变量的相对地址,分配后要增加1,所以使用指针,以后所有的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 */int postion(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); /* var后应是标识 */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) /* 参数意义见block和enter函数 */{int i,cx1,cx2;bool nxtlev[symnum]; /* 意义见block函数 */if(sym==ident) /* 准备按照赋值语句处理 */{i=postion(id,*ptx);if(i==0)error(11); /* 变量未找到 */else{if(table[i].kind!=variable){error(12); /* 赋值语句格式错误 */i=0;}}getsymdo;if(sym==becomes){getsymdo;}else error(13); /* 检测赋值符号 */memcpy(nxtlev,fsys,sizeof(bool)*symnum);expressiondo(nxtlev,ptx,lev); /* 处理赋值符号右侧表达式 */if(i!=0){gendo(sto,lev-table[i].level,table[i].adr); /* expression将执行一系列指令,但最终结果将会保存在栈顶,执行sto命令完成赋值 */}}else{if(sym==readsym) /* 准备按照read语句处理 */{getsymdo;if(sym!=lparen)error(34); /* 格式错误,应是左括号 */else{do{getsymdo;if(sym==ident)i=postion(id,*ptx); /* 查找要读的变量 */else i=0;if(i==0)error(35); /* read()中应是声明过的变量名 */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;memcpy(nxtlev,fsys,sizeof(bool)*symnum);nxtlev[rparen]=true;nxtlev[comma]=true; /* write的后跟符号为) or , */expressiondo(nxtlev,ptx,lev); /* 调用表达式处理,此处与read不同,read为给变量赋值 */.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=postion(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后的语句 */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类似 */}memset(nxtlev,0,sizeof(bool)*symnum); /* 语句结束无补救集合*/testdo(fsys,nxtlev,19); /* 检测语句结束的正确性 */}}}}}}return 0;}/* 表达式处理 */int expression(bool* fsys,int* ptx,int lev) /* 参数意义见block和enter函数 */{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 condition(bool* fsys,int* ptx,int lev) /* 参数意义见block和enter函数 */ {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;}/* 项处理 */int term(bool* fsys,int* ptx,int lev) /* 参数意义见block和enter函数 */ {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) /* 参数意义见block和enter函数 */{int i;bool nxtlev[symnum];testdo(facbegsys,fsys,24); /* 检测因子的开始符号 */while(inset(sym,facbegsys)) /* 循环直到不是因子开始符号 */{if(sym==ident) /* 因子为常量或变量 */{i=postion(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); /* 缺少右括号 */}test(fsys,facbegsys,23); /* 因子后有非法符号 */ }}}return 0;}/* 通过过程基址求上l层过程的基址 */int base(int l,int* s,int b){int b1;b1=b;while(l>0){b1=s[b1];l--;}return b1;}。

实验一PL0编译程序

实验一PL0编译程序

实验一PL0编译程序编译原理上机实验一1.软件准备(1)、pl/0编译程序:pl0 .exe(2)、pl/0编译程序源代码程序(PASCAL程序):pl0.pas(或pl0pl0.pas)(3)、pl/0程序实例(文本文件):text.pl0, text1.pl0, test2.pl0, … text9.pl0共10个例子2.实验要求:(1)阅读pl/0源程序实例(text.pl0,test1.pl0,………text9.pl0共10个例子)理解每个PL0程序的功能;熟悉并掌握pl/0语言相关规则。

(2)用pl/0编译程序,对提供的10个例子逐一进行编译并运行。

熟悉pl/0编译程序使用操作方法。

通过理解每个实例程序的功能,编程思想,进一步理解PL/0语言的语法及其语义;提高阅读程序的能力,应用程序设计语言的编程能力;增强程序语言语法、语义意识。

(3)用pl/0语言编写以下程序,进一步熟悉PL/0语言:a、由三角形的三条边长计算三角形面积。

b、编写一个PL0过程,计算以a为直径的圆的面积。

用主程序确定圆的直径,输出计算结果。

c、编写递归程序,计算n!。

d、计算1000之内的所有素数,并输出结果。

求出1000之内所有素数之和。

(要求编写3个以上的pl/0过程实现)3.文件(软件)使用说明(1)、打开文件目录“PL0编译…”,运行PL/0编译程序(pl0.exe),按提示要求输入PL0源程序文件名(如test1.pl0),┉(2)、打开Fa2.txt文件,可看到当前经过编译并运行后的pl/0程序的目标代码及其执行结果。

(3)、打开out1.txt或out2.txt、┉或out9.txt文件,可看到各个pl/0程序实例的编译、运行、操作过程结果。

4.提交实验报告及要求(1)、简述test4.pl0 …test7.pl0各程序语法含义,执行结果。

(2)、提交实验要求编写的4个PL/0源程序及其执行结果。

(3)、简写本次实验的收获与体会。

PL0语言编译器分析实验报告

PL0语言编译器分析实验报告

PL0语言编译器分析实验报告1. 引言1.1 PL0语言简介PL0语言是一种早期的程序设计语言,由瑞士计算机科学家尼克劳斯·沃斯(Niklaus Wirth)于1970年左右设计。

它是为了教学目的而设计的,具有简单、清晰、易于理解的特点。

PL0语言是一种过程式编程语言,不支持面向对象编程。

它的语法与Pascal语言相似,是许多编程语言教材中的入门语言。

1.2 编译器概述编译器是一种将高级编程语言源代码转换为低级机器语言或中间代码的程序。

它主要包括词法分析、语法分析、语义分析、中间代码生成、代码优化和目标代码生成等阶段。

编译器的主要目的是提高程序的可移植性、执行效率和开发效率。

1.3 实验目的与意义本次实验通过对PL0语言编译器的分析,旨在让学生深入了解编译器的工作原理和实现方法,提高编程实践能力。

实验的意义在于:1.加深对编译原理的理解,掌握编译器各阶段的基本任务和关键技术;2.培养学生独立分析问题、解决问题的能力;3.提高学生的编程技巧,为后续学习更高级的编程语言打下基础;4.通过实验,使学生了解编译器在软件开发中的重要作用,为今后从事软件开发工作奠定基础。

2 PL0语言编译器原理2.1 编译器的工作流程编译器的工作流程主要包括词法分析、语法分析、语义分析、中间代码生成、代码优化和目标代码生成等阶段。

1.词法分析:将源程序中的字符序列转换为记号(Token)序列。

2.语法分析:根据语法规则,将记号序列转换为抽象语法树(AST)。

3.语义分析:检查源程序是否有语义错误,如类型检查、作用域检查等。

4.中间代码生成:将AST转换为中间代码,方便后续优化和目标代码生成。

5.代码优化:对中间代码进行优化,提高目标代码的执行效率。

6.目标代码生成:将优化后的中间代码转换为特定平台的目标代码。

2.2 PL0语言的词法、语法和语义1.词法:PL0语言的词法规则包括标识符、常数、运算符和界限符等。

2.语法:PL0语言的语法规则定义了各种语句和表达式的结构,如条件语句、循环语句、赋值语句等。

pl0词法分析器(C语言版)

pl0词法分析器(C语言版)

#include<stdio.h>#include<string.h>#include<stdlib.h>void init();void getsym();enum symbol {nul,ident,number,plus,minus,times,slash,oddsym,eql,neq, beginsym,endsym,ifsym,thensym,whilesym,writesym,readsym,dosym,callsym, constsym,varsym,procsym,programsym,};#define norw 14#define al 10#define nmax 10char word [norw][al];char ch;enum symbol sym;enum symbol wsym[norw];enum symbol ssym[256];FILE *fin,*fout;void main(){printf("please input outfile's name:\n");//词法分析之后的结果要输出到的文件}char outname[30],inname[30]; scanf("%s",outname);if((fout=fopen(outname,"w"))==NULL){printf("cannot open output file!");exit(0);}printf("please input infile's name:\n");//需要词法分析的源程序scanf("%s",inname);if((fin=fopen(inname,"r"))==NULL){printf("cannot open file!\n");exit(0);}ch=fgetc(fin);while(ch!=EOF){}getsym();//读取源文件void getsym(){long m;char id[al+1],a[al+1];int i,k;init();if(ch==' '||ch==10||ch==9){}else {if(ch>='a'&&ch<='z'||ch>='A'&&ch<='Z'){k=0;ch=fgetc(fin);do {if(k<al){a[k]=ch;k++;}ch=fgetc(fin);}while((ch>='a'&&ch<='z'||ch>='0'&&ch<='9')||(ch>='A'&&ch<='Z'||ch>='0'&&ch<='9') );a[k]=0;strcpy(id,a);for(i=0;i<norw;i++){if(strcmp(id,word[i])==0){sym=wsym[i]; printf("%s %ssym\n",id,id);fprintf(fout,"%s %ssym\n",id,id);break;}else {if(i==(norw-1)){sym=ident;printf("%s ident\n",id);fprintf(fout,"%s ident\n",id);}}}}else {if(ch>='0'&&ch<='9'){k=0;m=0;sym=number;do}{m=10*m+ch-'0';k++;ch=getc(fin);}while(ch>='0'&&ch<='9');if(k>=nmax){printf("error!数值太大!\n");//exit(0);}printf("%d number\n",m);fprintf(fout,"%d number\n",m); else {if(ch==':'){ch=getc(fin);printf(":fprintf(fout,":ch=fgetc(fin);}else {}sym=nul; fprintf(fout,":nul\n");printf(":nul\n");else {if(ch=='<'){ch=fgetc(fin); if(ch=='='){sym=leq;printf("<=lep\n");fprintf(fout,"<=ch=fgetc(fin);}else {sym=lss; printf("<fprintf(fout,"<}}else {if(ch=='>'){ch=fgetc(fin); if(ch=='='){sym=geq;printf(">=fprintf(fout,">=ch=fgetc(fin);}else {sym=gtr;printf(">fprintf(fout,">}}else {sym=ssym[ch];if(ch=='+'){printf("+plus\n");ch=fgetc(fin);}else lep\n");lss\n");lss\n");geq\n");geq\n");gtr\n");gtr\n");plus\n");fprintf(fout,"+if(ch=='-'){printf("-minus\n");fprintf(fout,"-else if(ch=='*'){printf("*times\n");fprintf(fout,"*elseif(ch=='/'){printf("/slash\n");fprintf(fout,"/minus\n");ch=fgetc(fin);} times\n");ch=fgetc(fin);}slash\n");ch=fgetc(fin);}if(ch=='('){printf("(rparen\n");ch=fgetc(fin);}eql\n");ch=fgetc(fin);}neq\n");ch=fgetc(fin);}period\n");ch=fgetc(fin);}semicoln\n");ch=fgetc(fin);}nul\n",ch);ch=fgetc(fin);}}}}}}}}//对关键字等实现初始化void init(){int i;for(i=0;i<=255;i++){elselparen\n");fprintf(fout,"(elseif(ch==')'){printf(")elseif(ch=='='){printf("=elseif(ch==','){printf(",elseif(ch=='#'){printf("#elseif(ch=='.'){printf(".elseif(ch==';'){printf(";else{printf("%c lparen\n");ch=fgetc(fin);}rparen\n");fprintf(fout,")neq\n");fprintf(fout,"#period\n");fprintf(fout,".semicoln\n");fprintf(fout,"; nul\n",ch);fprintf(fout,"%c }ssym[i]=nul;}ssym['+']=plus;ssym['-']=minus;ssym['*']=times;ssym['/']=slash;ssym['(']=lparen;ssym[')']=rparen;ssym['=']=eql;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]),"end");strcpy(&(word[5][0]),"if");strcpy(&(word[6][0]),"odd");strcpy(&(word[7][0]),"procedure");strcpy(&(word[8][0]),"read");strcpy(&(word[9][0]),"program"); strcpy(&(word[10][0]),"var"); strcpy(&(word[11][0]),"whlie"); strcpy(&(word[12][0]),"write"); strcpy(&(word[13][0]),"then"); wsym[0]=beginsym;wsym[1]=callsym;wsym[2]=constsym;wsym[3]=dosym;wsym[4]=endsym;wsym[5]=ifsym;wsym[6]=oddsym;wsym[7]=procsym;wsym[8]=readsym;wsym[9]=programsym;wsym[10]=varsym;wsym[11]=whilesym;wsym[12]=writesym;wsym[13]=thensym;。

PL0目标代码生成与解释运行

PL0目标代码生成与解释运行
在过程返回时,解释程序通过返回地址恢复指令指针的值到调用 前的地址。
在例中程序执行到C过程后, 在C过程中又调用B过程时 数据区栈中的状况,这时过 程B的静态链是指向过程A的 基地址,而不是指向过程C 的基地址。
例:若有程序结构为: ... procedure A;
... procedure B;
int, 0, dx—生成分配空间指令,分配dx个空间
参考:OPR 的l域为0,a=1 栈顶值取反结果在栈顶。 OPR 的l域为0,a=2~5 将栈顶和次栈顶内容做算术运算,结果存放在次栈顶。 STO 将栈顶的内容送人某变量单元中。a 域是说明(定义)变量的过程,给变量 分配的存储空间,相对该过程在运行栈基地址的偏移量。 INT 为被调用的过程(或主程序)在运行栈中开辟数据区。a 域为开辟的单元个数。
引用该变量
说明该变量
或减 或
=层次差
过程的分程序
过程的分程序
层差用法的举例:gen(sto,lev-level,adr); lev:当前处理的过程层次。 level:被引用变量或过程所在层次。
目标指令有八条:
lit 0, a load constant a(负载常数) LIT 将常量值取到运行栈顶。 LIT a域为常数值。
jpc 0, a jump conditional to a(有条件 JPC 转入)
JPC 条件转移指令,当栈顶的布尔值为非真 时,转向a域的地址,否则顺序执行。
lod l, a load variable l, a(负载变量) LOD 放到栈顶。 LOD a 域是说明(定义)变量的过程,给变量分配的存 储空间,相对该过程在运行栈基地址的偏移量 。l为调用层与说明层的层差值。
cal l, a call procedure at level l a(呼 CAL 叫过程)

第2章 PL0编译程序的实现(4学时)

第2章 PL0编译程序的实现(4学时)
13 13
表格管理举例
CONST A=35,B=49; VAR C,D,E; PROCEDURE P; VAR G
名字
TX0 NAME:A
类型
层次/值
地址
存储空间
NAME:B NAME:C NAME:D NAME:E NAME:P TX1 NAME:G ……
KIND:CONSTANT KIND:CONSTANT KIND:VARIABLE KIND:VARIABLE KIND:VARIABLE KIND:PROCEDUR KIND:VARIABLE ……
何为PL/0语言?
PL/0语言: PASCAL语言的子集,功 能简单,结构清晰,可 读性强,具备了一般高 级语言的必备部分。
PL/0程序示例: CONST A=10; VAR B,C; PROCEDURE P; VAR D; PROCEDURE Q; VAR X; BEGIN READ(X); D:=X; WHILE X#0 DO CALL P; END; BEGIN WRITE(D); CALL Q; END; BEGIN CALL P; END.
存储空间
SIZE:4
ADR:DX SIZE:4 SIZE:5 ADR:DX ADR:DX ADR:DX+1 SIZE:5 ADR:DX ADR:DX+1
15 15
2.5 PL/0编译程序的目标代码结构和代码生成
• 目标代码pcode是一种假想栈式计算机的汇编语言。
• 指令格式 功能码
f l
a
层次差 通常为“位移量” 根据不同的指令 有所不同
18
Const a=10; var b,c; procedure p; begin c:=b+a; end; begin read(b); call p; write(c); end.

编译原理第2版第二章PL0编译程序

编译原理第2版第二章PL0编译程序
… ssym[‘;’]:=semicolon;
词法分析如何把单词传递给语法分析 type symbol=(nul,ident,number,plus,…,varsym,procsym);
3个全程量 SYM:symbol; ID:alfa; NUM:integer;
通过三个全程量 SYM 、ID和NUM 将识别出的单词信息传递给语 法分析程序。 SYM:存放单词的类别 如:有程序段落为: begin initial := 60;end 对应单词翻译后变为: begin beginsym, initial ident, ‘:= ‘ becomes, 60 number, ‘;’ semicolon, end endsym 。 ID: 存放用户所定义的标识符的值 如: initial (在 SYM中放ident,在ID中放initial) NUM:存放用户定义的数 如:60 (在SYM中放number,在NUM中放60)
OPR 0 12 OPR 0 13
次栈顶是否大于栈顶,退两个栈元素,结果值进栈 次栈顶是否小于等于栈顶,退两个栈元素,结果值进栈
OPR 0 14 OPR 0 15 OPR 0 16
栈顶值输出至屏幕 屏幕输出换行 从命令行读入一个输入置于栈顶
const a=10; var b,c; procedure p;
出错处理程序 表格管理程序
PL/0编译程序的结构
PL/0源程序 词法分析程 序 语法语义分析程序
代码生成程序
目标程序
PL/0编译程序的总体设计
以语法、语义分析程序为核心 词法分析程序和代码生成程序都作为一个过程, 当语法分析需要读单词时就调用词法分析程序, 而当语法、语义分析正确,需要生成相应的目标 代码时,则调用代码生成程序。

编译原理PL0教程

编译原理PL0教程

序言 (1)第一部分PL/0语言及其编译器 (2)1.PL/0语言介绍 (2)1.1 PL/0语言的语法图 (3)2.PL/0语言编译器 (6)2.1词法分析 (7)2.2 语法分析 (7)2.3 语义分析 (9)2.4 代码生成 (9)2.5 代码执行 (11)2.6 错误诊断处理 (13)2.7 符号表管理 (15)2.8其他 (16)第二部分上机实践要求 (17)第三部分PL/0语言编译器源程序 (19)1.一个例子 (19)1.1 PL/0语言源程序 (19)1.2 生成的代码(片段) (21)2.PL/0语言编译器源程序 (21)编译原理实践教程序言《编译原理和技术》的课程实践至少有两种可能的安排。

其一,为配合编译课程教学,而安排多次小型实践,分别支持编译程序的各个阶段。

其二,针对某一规模适中的语言来设计和实现一个相对完整、独立编译器。

《编译原理实践教程》作为《编译原理和技术》课程的延伸,其目的是让大家动手设计和实现某一规模适中的语言的编译器,该编译器不仅涉及编译程序的各个阶段,而且也强调了编译的总体设计、各个阶段的接口安排等等。

通过上机实践,来设计这个相对完整的编译器,一方面可以使学生增加对编译程序的整体认识和了解——巩固《编译原理和技术》课程所学知识,另一方面,通过上机练习,学生也可以学到很多程序调试技巧和设计大型程序一般的原则,如模块接口的协调,数据结构的合理选择等等。

为了使学生能尽早动手实践,我们建议把实践分成三部分,首先阅读本教程第一部分,在这部分就PL/0语言的语法及其编译程序的各个阶段作了简单介绍,以便对PL/0编译程序有个初步的印象。

其次要认真阅读理解第三部分所给出的PL/0编译器源程序,使上一阶段的初步印象得以加深、具体化。

最后按照第二部分的实验要求扩充PL/0语言的功能并加以实现。

第一部分 PL/0语言及其编译器1. PL/0语言介绍PL/0程序设计语言是一个较简单的语言,它以赋值语句为基础,构造概念有顺序、条件和重复(循环)三种。

编译原理实践-PL0的词法分析程序构造

编译原理实践-PL0的词法分析程序构造
编译原理实践-PL0的词法 分析程序构造
通过本次演讲,将带你领略编译原理的奇妙之处,并深入介绍PL0语言的词 法分析及程序构造,让你轻松掌握。
编译原理简介
编译原理是计算机科学的重要基础,涉及源代码的解析、优化和转换过程。它是开发高效程序和语言的 关键。
PL0语言简介
PL0是一种简单且易于学习的编程语言,适合编译原理教学。它具有C语言的 语法特点,是学习编译原理的理想选择。
词法分析的概念和作用
词法分析是编译过程中的第一步,将源代码分解为各种标记(Token)。它有助于语法分析和语义分析的 进行。
PL0的词法规则
PL0的词法规则定义了它的各类标记,如关键字、标识符、运算符和常量。了解规则对于正确理解代码 至关重要。
词法分析程序的设计思路
设词法分析程序需要考虑标识符的判别、关键字的识别等问题。采用有限自动机是一种常用的设计方 法。
PL0的词法分析程序实现
使用C语言编写PL0的词法分析程序,结合有限自动机算法,有效地识别源代 码中的各种标记。
实验结果与总结
通过实验,我们验证了词法分析程序的正确性和性能。深入总结实验结果,对编译原理的学习和应用有 了更深入的理解。
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

/*PL/0 编译系统C版本头文件 */# define norw 13 ET and Visual C++.NET 2003*WinNT, Win 200, WinXP and Win2003*2 gcc version (Red Hat Linux Fedora core 1*Intel 32 platform*使用方法:*运行后输入PL/0 源程序文件名*回答是否输出虚拟机代码*回答是否输出名字表* 输出虚拟机代码* 输出源文件及其各行对应的首地址* 输出结果* 输出名字表*/#include<>#include""#include""/*解释执行时使用的栈*/#define stacksize 500int main(){bool nxtlev[symnum];printf("Input pl/0 file ");scanf("%s",fname); /*输入文件名*/ fin=fopen(fname,"r"); ]=period;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]),"end");strcpy(&(word[5][0]),"if");strcpy(&(word[6][0]),"odd");strcpy(&(word[7][0]),"procedure");strcpy(&(word[8][0]),"read");strcpy(&(word[9][0]),"then");strcpy(&(word[10][0]),"var");strcpy(&(word[11][0]),"while");strcpy(&(word[12][0]),"write");/*设置保留字符号*/wsym[0]=beginsym;wsym[1]=callsym;wsym[2]=constsym;wsym[3]=dosym;wsym[4]=endsym;wsym[5]=ifsym;wsym[6]=oddsym;wsym[7]=procsym;wsym[8]=readsym;wsym[9]=thensym;wsym[11]=whilesym;wsym[12]=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;/*设置因子开始符号集*/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;=x;code[cx].l=y;code[cx].a=z;cx++;}/**测试当前符号是否合法**在某一部分(如一条语句,一个表达式)将要结束时时我们希望下一个符号属于某集合*(该部分的后跟符号) 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;}}}/**编译程序主体**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=%daddr=%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);],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){}else{error(13);}memcpy(nxtlev,fsys,sizeof(bool)* symnum);expressiondo(nxtlev,ptx,lev);if(i!=0){gendo(sto,lev-table[i].level,table[i].adr);}}}}else{if(sym==readsym){getsymdo;if(sym!=lparen){error(34);}else{do{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))/* 出错补救,直到收到上层函数的后跟符号*/{}}else{getsymdo;}}else{if(sym==writesym) /* 准备按照write语句处理,与read类似*/{getsymdo;if(sym==lparen){do{getsymdo;memcpy(nxtlev,fsys,sizeof(bool)*symnum);nxtlev[rparen]=true;nxtlev[comma]=true; /* write的后跟符号为)or,*/expressiondo(nxtlev,ptx,lev);/* 调用表达式处理,此处与read不同,read为给变量赋值*/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后的语句*/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; /*用于保存乘除法符号*/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;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]; /*栈*/printf("start pl0\n");t=0;b=0;p=0;s[0]=s[1]=s[2]=0;do{i=code[p]; /*读当前指令*/p++;switch{case lit: /*将a的值取到栈顶*/s[t]=;t++;break;case opr: /*数字、逻辑运算*/switch{case 0:t=b;p=s[t+2];b=s[t+1];break;case 1:s[t-1]=-s[t-1];break;case 2: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: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:printf("%d",s[t-1]);fprintf(fa2,"%d",s[t-1]);t--;break;case 15:printf("\n");fprintf(fa2,"\n");break;case 16:printf("");fprintf(fa2,"");scanf("%d",&(s[t]));fprintf(fa2,"%d\n",s[t]);t++;break;}break;case lod: /*取相对当前过程的数据基地址为a的内存的值到栈顶*/ s[t]=s[base,s,b)+];t++;break;case sto: /*栈顶的值存到相对当前过程的数据基地址为a的内存*/ t--;s[base,s,b)+]=s[t];break;case cal: /*调用子程序*/s[t]=base,s,b); /*将父过程基地址入栈*/s[t+1]=b; /*将本过程基地址入栈,此两项用于base函数*/s[t+2]=p; /*将当前指令指针入栈*/b=t; /*改变基地址指针值为新过程的基地址*/p=; /*跳转*/break;case inte: /*分配内存*/t+=;break;case jmp: /*直接跳转*/p=;break;case jpc: /*条件跳转*/t--;if(s[t]==0){p=;}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;}。

相关文档
最新文档