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 (*<=*)

用C语言编写一个PL

用C语言编写一个PL

用C语言编写一个PL/0词法分析器,为语法语义分析提供单词,使之能把输入的字符串形式的源程序分割成一个个单词符号传递给语法语义分析,并把分析结果(基本字,运算符,标识符,常数以及界符)输出。

#include <iostream>#include<string>using namespace std;#define MAX 22char ch =' ';string key[15]={"begin","end","if","then","else","while","write","read","do", "call","const","char","until","procedure","repeat"};int Iskey(string c){ //关键字判断int i;for(i=0;i<MAX;i++) {if(key[i].compare(c)==0) return 1;}return 0;}int IsLetter(char c) { //判断是否为字母if(((c<='z')&&(c>='a'))||((c<='Z')&&(c>='A'))) return 1;else return 0;}int IsDigit(char c){ //判断是否为数字if(c>='0'&&c<='9') return 1;else return 0;}void analyse(FILE *fpin){string arr="";while((ch=fgetc(fpin))!=EOF) {arr="";if(ch==' '||ch=='t'||ch=='n'){}else if(IsLetter(ch)){while(IsLetter(ch)||IsDigit(ch)) {if((ch<='Z')&&(ch>='A')) ch=ch+32;arr=arr+ch;ch=fgetc(fpin);}fseek(fpin,-1L,SEEK_CUR);if (Iskey(arr)){cout<<arr<<"t$关键字"<<endl;}else cout<<arr<<"t$普通标识符"<<endl;}else if(IsDigit(ch)){while(IsDigit(ch)||ch=='.'&&IsDigit(fgetc(fpin))){arr=arr+ch;ch=fgetc(fpin);}fseek(fpin,-1L,SEEK_CUR);cout<<arr<<"t$无符号实数"<<endl;}else switch(ch){case'+':case'-' :case'*' :case'=' :case'/' :cout<<ch<<"t$运算符"<<endl;break;case'(' :case')' :case'[' :case']' :case';' :case'.' :case',' :case'{' :case'}' :cout<<ch<<"t$界符"<<endl;break;case':' :{ch=fgetc(fpin);if(ch=='=') cout<<":="<<"t$运算符"<<endl;else {cout<<"="<<"t$运算符"<<endl;;fseek(fpin,-1L,SEEK_CUR);}}break;case'>' :{ch=fgetc(fpin);if(ch=='=') cout<<">="<<"t$运算符"<<endl;if(ch=='>')cout<<">>"<<"t$输入控制符"<<endl;else {cout<<">"<<"t$运算符"<<endl;fseek(fpin,-1L,SEEK_CUR);}}break;case'<' :{ch=fgetc(fpin);if(ch=='=')cout<<"<="<<"t$运算符"<<endl;else if(ch=='<')cout<<"<<"<<"t$输出控制符"<<endl;else if(ch=='>') cout<<"<>"<<"t$运算符"<<endl;else{cout<<"<"<<"t$运算符"<<endl;fseek(fpin,-1L,SEEK_CUR);}}break;default : cout<<ch<<"t$无法识别字符"<<endl;}}}void main(){char in_fn[30];FILE * fpin;cout<<"请输入源文件名(包括路径和后缀名):";for(;;){cin>>in_fn;if((fpin=fopen(in_fn,"r"))!=NULL) break;else cout<<"文件路径错误!请输入源文件名(包括路径和后缀名):";}cout<<"n********************分析如下*********************"<<endl;analyse(fpin);fclose(fpin);}。

编译原理(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编译程序讲解ppt课件

PL0编译程序讲解ppt课件
end;
begin { 读入n } read(n); sum := 0; while n > 0 do begin m := n; fact := 1; call factorial; sum := sum + fact; n := n - 1; end; { 输出n !数值取到栈顶,a为常数值 将变量值取到栈顶,a为偏移量,l为层差 将栈顶内容送入某变量单元中,a为偏移量,l为层差 调用过程,a为过程地址,l为层差 在运行栈中为被调用的过程开辟a个单元的数据区 无条件跳转至a地址 条件跳转,当栈顶布尔值非真则跳转至a地址,否则顺序执行
过程调用结束后,返回调用点并退栈 栈顶元素取反 次栈顶与栈顶相加,退两个栈元素,结果值进栈 次栈顶减去栈顶,退两个栈元素,结果值进栈 次栈顶乘以栈顶,退两个栈元素,结果值进栈 次栈顶除以栈顶,退两个栈元素,结果值进栈 栈顶元素的奇偶判断,结果值在栈顶
( 1) jmp 0 2 转向过程p入口 ( 2) int 0 3 为过程p开辟空间
( 3) lod 1 3
const var
a=10; b,c;
( 4) lit 0 10
procedure
p;
( 5) opr 0 2 ( 6) sto 1 4 ( 7) opr 0 0 退栈并返回调用点 ( 8) int 0 5 ( 9) opr 0 16 (10) sto 0 3
VAR D;(* P的局部变量说明部分 *) PROCEDURE Q; (* P的局部过程说明部分 *) VAR X;
BEGIN READ(X); D:=X; IF X#0 DO CALL P;
END; BEGIN
CALL Q; WRITE(D); END; BEGIN CALL P; END.

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函数注释

// pl0 compiler source code#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include "set.h"#include "pl0.h"//////////////////////////////////////////////////////////////////////// print error message.//出错报告过程void error(n){//打印字符串数组err_msg对应的下标为n的字符串int i;printf(" ");for (i = 1; i <= cc - 1; i++)printf(" ");fprintf(outfile, " ");fprintf(outfile, "^\n");printf("^\n");fprintf(outfile, "Error %3d: %s\n", n, err_msg[n]);printf("Error %3d: %s\n", n, err_msg[n]);err++;} // error//////////////////////////////////////////////////////////////////////void getch(void)//获得字符{if (cc == ll){if (feof(infile))//检查文件是否结束结束返回非零值否则返回0{printf("\nPROGRAM INCOMPLETE\n");exit(1);}ll = cc = 0;//line length ,character countfprintf(outfile, "%5d ", cx);printf("%5d ", cx);while ( (!feof(infile)) // added & modified by alex 01-02-09&& ((ch = getc(infile)) != '\n'))//从文件infile中读取字符并放ch中{fprintf(outfile, "%c", ch);printf("%c", ch);line[++ll] = ch;//将从文件中读取的字符存储在数组line中} // whilefprintf(outfile, "\n");printf("\n");line[++ll] = ' ';}ch = line[++cc];//将当前第一个读取到的字符赋值给ch;字符数+1;} // getch//////////////////////////////////////////////////////////////////////// gets a symbol from input stream.void getsym(void){int i, k;char a[MAXIDLEN + 1];while (ch == ' '|| ch == '\t') //若是空格或是制表符直接跳过获取下一字符// modified by yzhang 02-03-12,add some white spacegetch();//判断ch是保留字或者是标识符if (isalpha(ch))//判断字符ch是否为英文字母若为英文字母返回非零值否则返回零{ // symbol is a reserved word or an identifier. 判断是为标识符和保留字k = 0;//k记录当前数组a中存入的字符个数do{if (k < MAXIDLEN)a[k++] = ch;//将ch放进数组a中getch();//读取下一字符}//若当前字符是字母或者数字循环执行while (isalpha(ch) || isdigit(ch));//isdigit函数判断字符c是否为数字若为数字返回非零a[k] = 0;strcpy(id, a);//id在文件p10.h中声明为char型数组将数组a中的内容给数组idword[0] = id;//word在文件p10.h中声明为存储保留字的字符串数组将该数组的第一个元素赋值为idi = NRW;//令i的值为保留字的数目while (strcmp(id, word[i--]));//将字符串id与Word中保留字对应的字符串进行比较if (++i)//表示id是已存在于word数组中的保留字sym = wsym[i]; // symbol is a reserved wordelse//id不是已定义的保留字是标识符sym = SYM_IDENTIFIER; // symbol is an identifier}//判断ch是否是数字else if (isdigit(ch))//判断字符ch是否为数字若为数字返回非零值否则返回零{ // symbol is a number.k = num = 0;sym = SYM_NUMBER;do{num = num * 10 + ch - '0';//将读进的数字转化为对应的十进制数存储在num 中k++;getch();//读取下一字符}while (isdigit(ch));//当为数字是循环写入if (k > MAXNUMLEN) //该数字中所含数字的个数大于MAXNUMLENerror(25); // The number is too great.该数字过大}//判断ch是否是关系运算符else if (ch == ':')//如果当前字符是':'{getch();//获取下一字符if (ch == '=')//下一字符为'=',表示赋值{sym = SYM_BECOMES; // :=getch();//继续读取字符}else//非法字符{sym = SYM_NULL; // illegal?}}else if (ch == '>')//如果当前字符为'>'{getch();//获取下一字符if (ch == '=')//下一字符为'=' ,表示>={sym = SYM_GEQ; // >=getch();}else//表示>{sym = SYM_GTR; // >}}else if (ch == '<')//如果当期字符为'<'{getch();if (ch == '=')//读取下一字符为'=',表示<={sym = SYM_LEQ; // <=getch();}else if (ch == '>')//读取下一字符为'>',表示<>不相等{sym = SYM_NEQ; // <>getch();}else//表示'<'{sym = SYM_LES; // <}}//判断ch是否是算术运算符else//是其他字符{ // other tokensi = NSYM;//令i的值为NSYMcsym[0] = ch;//令数组csym的0号元素为当前字符chwhile (csym[i--] != ch);//在csym中查找是否存在ch ,判断ch是否是运算符if (++i)//存在,表示ch为运算符{sym = ssym[i];getch();}else//出错ch为未知符号{printf("Fatal Error: Unknown character.\n");fprintf(outfile, "Fatal Error: Unknown character.\n");exit(1);}}} // getsym//////////////////////////////////////////////////////////////////////// generates (assembles) an instruction.//代码生成void gen(int x, int y, int z)//将x,y,z放入code数组{if (cx > CXMAX)//cx的值大于CXMAX(size of code array)当前生成代码的行号大于允许的最大代码行数//报错{fprintf(outfile, "Fatal Error: Program too long.\n");printf("Fatal Error: Program too long.\n");exit(1);}//将x,y,z 对应赋值给数组code中下标为cx的元素//把代码写进当前目标代码数组当前cx所指的位置code[cx].f = x;code[cx].l = y;code[cx++].a = z;//并移动cx指向下一个空位} // gen//////////////////////////////////////////////////////////////////////// tests if error occurs and skips all symbols that do not belongs to s1 or s2.//测试是否出现错误并跳过所有不属于s1和s2的符号//出错恢复过程void test(symset s1, symset s2, int n){//s1:当语法分析进入或退出某一语法单元时当前单词符合应属于的集合//s2:在某一出错状态下,可恢复语法分析正常工作的补充单词集合//n:出错信息编号,当当前符号不属于合法的s1集合时发出的出错信息symset s;if (! inset(sym, s1))//调用inset函数判断sym是否在s1中{//当前符号符号不在s1中showset(s1);//调用showset 函数输出s1和s2中的元素showset(s2);printf("sym=%d, id=%s\n", sym, id);error(n);//报错s = uniteset(s1, s2);//将s1和s2合并并赋值给swhile(! inset(sym, s))getsym();//通过循环找到下一个合法的符号,以恢复语法分析工作destroyset(s);//删除s}} // test//////////////////////////////////////////////////////////////////////int dx; // data allocation index 数据分配指数// enter object(constant, variable or procedre) into table.//登陆名字表过程void enter(int kind){mask* mk;// added by yzhang 02-02-28//id在table中已经存在报错标识符重复定义if ( position(id)> 0 ){error(26); //Redeclared identifier.}// end//id在table中不存在tx++;//table中所含标识符个数+1strcpy(table[tx].name, id);//将id赋值给table中第tx号元素table[tx].kind = kind;switch (kind)//判别id类型{case ID_CONSTANT://常数if (num > MAXADDRESS)//该数中所含数字个数过多{error(25); // The number is too great.num = 0;}table[tx].value = num;break;case ID_VARIABLE://变量mk = (mask*) &table[tx];mk->level = level;mk->address = dx++;break;case ID_PROCEDURE://程序mk = (mask*) &table[tx];mk->level = level;break;} // switch} // enter//////////////////////////////////////////////////////////////////////// locates identifier in symbol table.//查找id在table中的位置,并将位置返回//查询名字表过程int position(char* id){int i;strcpy(table[0].name, id);//将id赋值给table中的0号元素i = tx + 1;while (strcmp(table[--i].name, id) != 0);return i;//返回id在table中对应的下标} // position////////////////////////////////////////////////////////////////////////常量定义分析过程void constdeclaration(){if (sym == SYM_IDENTIFIER)//当前为标识符{getsym();//调用getsym函数,获取下一个tokenif (sym == SYM_EQU || sym == SYM_BECOMES)//当前字符特征表示为=或:={if (sym == SYM_BECOMES)//若为:=报错(常量声明中应为等号不是赋值号) error(1); // Found ':=' when expecting '='.1号错误getsym();//调用getsym(),获取下一个tokenif (sym == SYM_NUMBER)//读取的是数字{enter(ID_CONSTANT);//调用enter函数并传递参数ID_CONTANT 常数,把这个常量登录到符号表getsym();//调用getsym(),获取下一个token}else//读到的不是数字报错{error(2); // There must be a number to follow '='.等号后应接数字}}//常量标识符后接的不是=或:=else{error(3); // There must be an '=' to follow the identifier.}}//常量声明过程中遇到的第一个符号不是标识符报错else //added by yzhang 02-02-28error(4); // There must be an identifier to follow 'const', 'var', or 'procedure'.} // constdeclaration////////////////////////////////////////////////////////////////////////变量定义分析过程void vardeclaration(void){if (sym == SYM_IDENTIFIER)//当前为标识符{enter(ID_V ARIABLE);//将参数ID_V ARIABLE传递给函数enter,登陆到符号表getsym();//继续读取,获取下一个token}else//变量声明后遇到的第一个符号不是标识符报错{error(4); // There must be an identifier to follow 'const', 'var', or 'procedure'.}} // vardeclaration////////////////////////////////////////////////////////////////////////列出code代码过程void listcode(int from, int to){int i;printf("\n");fprintf(outfile, "\n");for (i = from; i < to; i++)//列出从from 到to-1的代码{printf("%5d %s\t%d\t%d\n", i, mnemonic[code[i].f], code[i].l, code[i].a);//同时打印进文件fprintf(outfile, "%5d %s\t%d\t%d\n", i, mnemonic[code[i].f], code[i].l, code[i].a);}printf("\n");fprintf(outfile, "\n");} // listcode////////////////////////////////////////////////////////////////////////因子处理过程void factor(symset fsys){void expression();int i;symset set;test(facbegsys, fsys, 24); // The symbol can not be as the beginning of an expression.// 开始因子处理前,先检查当前token是否在facbegsys 集合中while (inset(sym, facbegsys))//循环处理因子{if (sym == SYM_IDENTIFIER)//遇到的是标识符{if ((i = position(id)) == 0)//查符号表,找到当前标识符在符号表中的位置{error(11); //表示没有找到标识符// Undeclared identifier.}else//如果在符号表中找到了当前标识符的位置,开始生成相应代码{switch (table[i].kind){mask* mk;case ID_CONSTANT://如果这个标识符对应的是常量,值为value,生成lit 指令,把value放到栈顶gen(LIT, 0, table[i].value);break;case ID_VARIABLE:// 如果标识符是变量名,生成lod指令,//把位于距离当前层level的层的偏移地址为address 的变量放到栈顶mk = (mask*) &table[i];gen(LOD, level - mk->level, mk->address);break;case ID_PROCEDURE://如果在因子处理中遇到的标识符是过程名,出错,抛21号错error(21); // Procedure identifier can not be in an expression.break;} // switch}getsym();//获取下一个token}else if (sym == SYM_NUMBER)//遇到的是数字{if (num > MAXADDRESS)//数字过大,抛出25号错误{error(25); // The number is too great.num = 0;//把数字按0处理}gen(LIT, 0, num);//生成lit指令,把这个数值常量放到栈顶getsym();//获取下一个token}else if (sym == SYM_LPAREN)//遇到的是左括号{getsym();//获取下一个tokenset = uniteset(createset(SYM_RPAREN, SYM_NULL), fsys);//合并expression(set);//调用expression表达式分析过程,分析setdestroyset(set);//删除setif (sym == SYM_RPAREN)//遇到的是右括号{getsym();//获取下一个token}else//否则抛出错误,缺少右括号{error(22); // Missing ')'.}}else // added by yzhang 02-02-28test(fsys, createset(SYM_LPAREN, SYM_NULL), 23);//一个因子处理完毕,遇到的token应在fsys集合中,如果不是,抛23号错,//并找到下一个因子的开始,使语法分析可以继续运行下去} // while} // factor////////////////////////////////////////////////////////////////////////项处理过程void term(symset fsys){int mulop;symset set;set = uniteset(fsys, createset(SYM_TIMES, SYM_SLASH, SYM_NULL));factor(set);//调用因子处理过程,每一个项都应该由因子开始,因此调用factor子程序分析因子while (sym == SYM_TIMES || sym == SYM_SLASH)//一个因子后应当遇到乘号或除号{mulop = sym;//保存当前运算符getsym();//获取下一个tokenfactor(set);//运算符后应是一个因子,故调factor子程序分析因子if (mulop == SYM_TIMES)//遇到乘号{gen(OPR, 0, OPR_MUL);//生成乘法指令}else{gen(OPR, 0, OPR_DIV);//生成除法指令}} // whiledestroyset(set);//删除set} // term////////////////////////////////////////////////////////////////////////表达式处理过程void expression(symset fsys){int addop;symset set;set = uniteset(fsys, createset(SYM_PLUS, SYM_MINUS, SYM_NULL));//将fsys与新建的含有'+','-'的链表合并if (sym == SYM_PLUS || sym == SYM_MINUS)//若当前为'+'或'-',//一个表达式可能会由加号或减号开始,表示正负号{addop = sym;//把当前符号保存,并继续向下读取getsym();//获取下一个tokenterm(set);//调用项处理过程termif (addop == SYM_MINUS)//保存下来的是'-'{gen(OPR, 0, OPR_NEG);//生成取反指令}}else//如果不是由正负号开头,就应是一个项开头{term(set);//调用term子程序分析项}while (sym == SYM_PLUS || sym == SYM_MINUS)//项后应该是加减运算{addop = sym;//保存运算符getsym();//获取下一个tokenterm(set);//加减运算符后应跟一个项,调用term 分析项if (addop == SYM_PLUS)//保存的是'+',生成加法指令{gen(OPR, 0, OPR_ADD);}else//否则生成减法指令{gen(OPR, 0, OPR_MIN);}} // whiledestroyset(set);//删除set} // expression////////////////////////////////////////////////////////////////////////条件处理过程//fsys: 如果出错可用来恢复语法分析的符号集合void condition(symset fsys){int relop;//用来临时记录token内容symset set;if (sym == SYM_ODD)//如果是odd运算符{getsym();//获取下一个tokenexpression(fsys);//调用expression 进行处理运算gen(OPR, 0, 6);//生成6号指令:奇偶判断}else//不是odd运算符{set = uniteset(relset, fsys);//relset中存储逻辑运算符expression(set);//对表达式左部进行处理运算destroyset(set);if (! inset(sym, relset))//如果不是逻辑运算符,抛出20号错误{error(20);}else{relop = sym;//记录当前逻辑运算符getsym();//获取下一个tokenexpression(fsys);//对表达式右边进行处理switch (relop)//分析保存下来的逻辑运算符{case SYM_EQU://'=',生成判断相等的指令gen(OPR, 0, OPR_EQU);break;case SYM_NEQ://'-',生成判断不等的指令gen(OPR, 0, OPR_NEQ);break;case SYM_LES://'<',生成小于指令gen(OPR, 0, OPR_LES);break;case SYM_GEQ://'>=',生成大于等于的指令gen(OPR, 0, OPR_GEQ);break;case SYM_GTR://'>',生成大于的指令gen(OPR, 0, OPR_GTR);break;case SYM_LEQ://'<=',生成小于等于的指令gen(OPR, 0, OPR_LEQ);break;} // switch} // else} // else} // condition////////////////////////////////////////////////////////////////////////fsys:如果出错可用来恢复语法分析的符号集合void statement(symset fsys)//语句分析过程{int i, cx1, cx2;symset set1, set;if (sym == SYM_IDENTIFIER)//类型为标识符{ // variable assignmentmask* mk;if (! (i = position(id)))//id不在符号表中报错{error(11); // Undeclared identifier.}else if (table[i].kind != ID_V ARIABLE)//如果在符号表中找到该标识符,但不是变量报错{error(12); // Illegal assignment.i = 0;//i置0作为错误标志}getsym();//获取下一个tokenif (sym == SYM_BECOMES)//当前token为赋值{//读取下一个tokengetsym();}else//否则报错{error(13); // ':=' expected.}expression(fsys);//调用表达式处理过程expressionmk = (mask*) &table[i];if (i)//如果不曾出错,i将不为0,i所指为当前语名左部标识符在符号表中的位置{//产生一行把表达式值写往指定内存的STO目标代码gen(STO, level - mk->level, mk->address);}}else if (sym == SYM_CALL)//如果是CALL{ // procedure callgetsym();//获取下一个tokenif (sym != SYM_IDENTIFIER)//不是标识符,14号错误,call后应连接标识符{error(14); // There must be an identifier to follow the 'call'.}else{if (! (i = position(id)))//未在符号表中查找到该标识符,11号错误{error(11); // Undeclared identifier.}else if (table[i].kind == ID_PROCEDURE)//该标识符是过程名{mask* mk;mk = (mask*) &table[i];gen(CAL, level - mk->level, mk->address);//生成call目标代码,呼叫该过程}else//不是过程名,15号错误{error(15); // A constant or variable can not be called.}getsym();//或取下一个token} // else}else if (sym == SYM_IF)//如果是if{ // if statementgetsym();//获取下一个token(应为一个逻辑表达式)set1 = createset(SYM_THEN, SYM_DO, SYM_NULL);set = uniteset(set1, fsys);//出错恢复集中加入then和do语句condition(set);////对逻辑表达式进行分析计算destroyset(set1);//删除set1,setdestroyset(set);if (sym == SYM_THEN)//遇到then{getsym();//获取下一个token(应为一个语句)}else//没有遇到then,16号错误{error(16); // 'then' expected.}cx1 = cx;//记下当前代码分配指针位置gen(JPC, 0, 0);//生成跳转指令,跳转位置暂时填0,分析完语句后再填写statement(fsys);//分析then后语句code[cx1].a = cx;// 上一行指令(cx1所指的)的跳转位置应为当前cx所指位置}else if (sym == SYM_BEGIN)//如果遇到begin{ // blockgetsym();//获取下一个tokenset1 = createset(SYM_SEMICOLON, SYM_END, SYM_NULL);set = uniteset(set1, fsys);statement(set);//对begin和end之间的语句进行处理while (sym == SYM_SEMICOLON || inset(sym, statbegsys))//如果分析完一句后遇到分号或语句开始符{//循环分析if (sym == SYM_SEMICOLON)//遇到分号{getsym();//获取下一个token}else//否则报错{error(10);}statement(set);//分析语句} // whiledestroyset(set1);destroyset(set);if (sym == SYM_END)//遇到end{getsym();//或取下一个token}else//否则报错{error(17); // ';' or 'end' expected.}}else if (sym == SYM_WHILE)//遇到while{ // while statementcx1 = cx;//记下当前代码分配位置,这是while循环的开始位置getsym();//获取下一个tokenset1 = createset(SYM_DO, SYM_NULL);set = uniteset(set1, fsys);condition(set);//进行分析运算destroyset(set1);destroyset(set);cx2 = cx;//记下当前代码分配位置,这是while的do中的语句的开始位置gen(JPC, 0, 0);//生成条件跳转指令,跳转位置暂时填0if (sym == SYM_DO)//遇到do{getsym();//获取下一个token}else//报错{error(18); // 'do' expected.}statement(fsys);//分析do后的语句gen(JMP, 0, cx1);//循环跳转到cx1位置,即再次进行逻辑判断code[cx2].a = cx;//把刚才填0的跳转位置改成当前位置,完成while语句的处理}else //added by yzhang 02-02-28test(fsys, phi, 19);// 至此一个语句处理完成,一定会遇到fsys集中的符号,如果没有遇到,就抛19号错} // statement////////////////////////////////////////////////////////////////////////程序分析过程//fsys:用于出错恢复的单词集合void block(symset fsys){int cx0; // initial code index 记录本层开始时代码段分配位置mask* mk;//mask是在pl0.h中声明的结构体定义mask类的指针mkint block_dx;int savedTx;symset set1, set;dx = 3;//声明的全局变量数据分配指数//地址指示器给出每层局部量当前已分配到的相对位置。

PL0词法分析器

PL0词法分析器

PL0词法分析器#include<stdio.h>#include<string.h>char symbol[] = {'+','-','*','/','(',')','=',',','.','#',';'};char ssym[][20] = {"plus","minus","tsimes","slash","lparen","rparen","eql","comma","peroid","neg","semicolon"}; char word[][20] = {"begin","call","const","do","end","if","odd","procedure","read","then","var","while","write"}; char wsym[][20] = {"beginsym","callsym","constsym","dosym","endsym","ifsym","oddsym","procsym","readsym"," thensym","varsym","whilesym","writesym"};intnumWord = 13; //保留字的个数为13个intnumSymbol = 11; //单符号个数FILE * fin;voidgetsym();void judgeConst(char* str); //保留字判断intjudgeSignel(char ch); //单符号判断boolchecknumber(char ch); //数字变量错误判断int main(){fin=fopen("in.txt","r");getsym();fclose(fin);return 0;}voidgetsym(){charch;ch = fgetc(fin);while(ch != '#'){if(ch>='a' &&ch<='z'){ //判断保留字与自定义变量charstr[50]="";do{str[strlen(str)]=ch;str[strlen(str)]='\0';ch = fgetc(fin);}while(ch>='a' &&ch<='z' || ch>='0' &&ch<='9');judgeConst(str);}else{if(ch>='0' &&ch<='9'){ //检测是否为数字:以0..9开头doublenum = 0.0;do{num = 10 * num + ch - '0'; //将char变为doublech = fgetc(fin);}while(ch>='0' &&ch<='9');if(!checknumber(ch)){printf("error : a word can't be in a number\n");return;}long div = 1;if (ch == '.'){//实型数的判断ch = fgetc(fin);while(ch>='0' &&ch<='9'){div = div * 10;num = num +static_cast<double>(ch-'0') / div; //转换ch = fgetc(fin); //强制装换成double.}if(!checknumber(ch)){printf("error : a word can't be in a number\n");return;}}printf("类型number , 名称%g\n",num);}else{if (ch == ':'){ //检测赋值符号charstr[50]="";str[strlen(str)]=ch;str[strlen(str)]='\0';ch = fgetc(fin);if (ch == '='){str[strlen(str)]=ch;str[strlen(str)]='\0';printf("类型becomes , 名称%s\n",str);}else{printf("类型notBecomes , 名称%s\n",str);}ch = fgetc(fin);}else{if (ch == '<'){ //检测小于或小于等于符号charstr[50]="";str[strlen(str)]=ch;str[strlen(str)]='\0';ch = fgetc(fin);if (ch == '='){str[strlen(str)]=ch;str[strlen(str)]='\0';printf("类型leq , 名称%s\n",str);}else{printf("类型less , 名称%s\n",str);}ch = fgetc(fin);}else{if (ch == '>'){ //检测大于或大于等于符号charstr[50]="";str[strlen(str)]=ch;str[strlen(str)]='\0';ch = fgetc(fin);if (ch == '='){str[strlen(str)]=ch;str[strlen(str)]='\0';printf("类型geq , 名称%s\n",str);}else{printf("类型gtr , 名称%s\n",str);}ch = fgetc(fin);}else{if(judgeSignel(ch) != -1){inti=judgeSignel(ch);printf("类型%s , 名称%c\n", ssym[i],ch);ch = fgetc(fin);}else ch = fgetc(fin); //忽略空格、换行}}}}}}}voidjudgeConst(char * str){inti = 0;for (i=0; i<numWord; i++){if (stricmp( str, word[i]) == 0){printf("类型%s , 名称%s\n",wsym[i],str);break;}}if (i == numWord){ //一直未找到匹配的保留字符。

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文法

pl0文法PL0文法PL0是一种简单的编程语言,其语法规则可以用BNF(巴克斯范式)表示。

PL0文法是由Niklaus Wirth于1972年提出的,它是一种上下文无关文法(CFG),可以被用来生成PL0程序。

1. PL0的基本结构一个PL0程序由以下几个部分组成:- 常量定义- 变量定义- 过程定义- 主程序体2. BNF表示PL0文法在BNF中,每个非终结符都有一个产生式(production rule),它指定了如何将该非终结符替换为终结符和其他非终结符的序列。

以下是PL0文法的BNF表示:<program> ::= <block> "."<block> ::= [<const-declaration-part>] [<var-declaration-part>] [<procedure-declaration-part>] <statement-part><const-declaration-part> ::= "const" <constant-definition> {"," <constant-definition>} ";"<constant-definition> ::= <identifier> "=" <number><var-declaration-part> ::= "var" <variable-declaration> {","<variable-declaration>} ";"<variable-declaration> ::= <identifier><procedure-declaration-part> ::= {<procedure-definition>}<procedure-definition> ::= "procedure" <identifier> ";"[<parameter-list>] ";" <block><parameter-list> ::= "(" <formal-parameter-section> {";"<formal-parameter-section>} ")"<formal-parameter-section>::= ["var"] <identifier><statement-part>::=<compound-statement><compound-statement>::="begin" <statement> {";"<statement>} "end"<statement> ::= <simple-statement>|<structured-statement> <simple-statement>::=<assignment-statement>|<procedure-call>|<read-statement>|<write-statement><assignment-statement>::=<variable> ":=" <expression><variable>::=<identifier><expression>::=<simple-expression>|<simple-expression><relational-operator> <simple-expression><relational-operator>::="="|"<>"|"<"|">"|"<="|">="<simple-expression>::=[ "+"|"-"] <term> {("+"|"-"|"or") <term>} <term>::=<factor> {("*"|"/"|"and") <factor>}<factor>::=<variable>|<number>| "(" <expression>")"| "not" <factor>3. PL0文法的解释- program:程序是由一个代码块和一个终止符号(.)组成的。

(完整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源代码

源代码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目标代码生成与解释运行

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 叫过程)

PL000编译程序

PL000编译程序

第二章 编译程序的实现
26
2、PL/0语法规则的BNF表示
<写语句>::= WRITE(<表达式>{, <表达式>}) <写语句>举例: WRITE( a+b,b*c);
第二章 编译程序的实现
27
2、PL/0语法规则的BNF表示
<过程调用语句>::=CALL <标识符>; <过程调用语句>举例:
第二章 编译程序的实现
30
2、PL/0语法规则的BNF表示
<因子>::=
<标识符>|<常量>|(<表达式>)
PROCEDURE FACTOR P294~P295
第二章 编译程序的实现
31
2、PL/0语法规则的BNF表示
<项>::=
<因子>|<项> <乘法运算符><因子>
<乘法运算符>::=*|/
第二章 编译程序的实现 38
符号表的内容
标识符名:NAME
类型:KIND ①CONSTANT(常量)
②VARIABLE(变量) ③PROCEDURE(过程) 值或层次:VAL或LEVEL 地址:ADR 大小:SIZE
第二章 编译程序的实现 39
地址:ADR
地址:ADR 简单变量或常量:该量在数据区所占单元 的绝对地址或相对地址;
(PROGRAM PROGSYM),(mysource IDENT) , (CONST CONSTSYM) , (a IDENT) , (= EQL) , (10 NUMBER) , (; SEMICOLON) , (VAR VARSYM) , (b IDENT) , (, COMMA) , (c IDENT) , (; SEMICOLON) , (PROCEDURE PROCSYM) , …… (g IDENT) , (:= BECOMES) , (a IDENT) , (+ PLUS) , (e IDENT) , (; SEMICOLON) This is a assign Statement;(赋值语句) (f IDENT) , (:= BECOMES) , (g IDENT) , (; SEMICOLON) This is a assign Statement; (赋值语句) (WRITE WRITESYM) , (( LPAREN) , (g IDENT) , ( ) RPAREN) , (; SEMICOLON) This is a write Statement;(写语句) ……

理解PL0编译程序原理

理解PL0编译程序原理

课程: 编译原理理解PL/0编译程序原理实验报告系专业班级姓名学号指导教师1.实验目的1. 学习使用教学辅助软件THPL0CAI2. 掌握PL/0源程序的编译和解释过程2.实验平台Windows + THPL0CAI3.实验内容目录:pl0演示1.运行THPL0CAI 程序1) 选择0 - Static Link 方式进入2) 选择Open/Create a source file 打开一个PL/0源程序, 如Test2.pl0 const a=10;var b,c;procedure p;var k;beginc:=b+10;end;beginread(b);while b#0 dobegincall p;write(2*c);read(b)endend.2.按F9键开始单步编译Test2.pl0 程序1) 观察符号表的构造过程Table.dat 窗口2) 观察目标代码的构造过程Code.dat 窗口3. 按F9键开始单步执行编译Test2.pl0 生成的代码1) 观察运行栈的变化过程Stack.dat 窗口2) 观察数据的输入输出Result.dat 窗口4.实验报告给出编译过程中符号表的建立过程,1.选定编译内容test22.按空格键进行编译3.得出的符号表table.dat给出运行过程中运行栈的变化过程,只给出部分说明即可。

1.F9开始运行栈2.输入2,给变量赋值3.输入3,给变量赋值,得出结果4.输入0,结束运行栈5.思考题1) 理解编译和解释的含义,目标代码是按何种方式执行的?PL/0编译程序所产生的目标代码是一个假想栈式计算机的汇编语言,可称为类PCODE指令代码,它不依赖任何具体计算机,其指令集极为简单,指令格式也很单纯,其格式如下:f l a其中f代表功能码,l表示层次差,也就是变量或过程被引用的分程序与说明该变量或过程的分程序之间的层次差。

a的含意对不同的指令有所区别,对存取指令表示位移量,而对其它的指令则分别有不同的含义,见下面对每条指令的解释说明。

编译原理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)。
ch=line[cc];
cc++;
return 0;
}
/*词法分析,获取一个符号
*/
int getsym()
{
int i,j,k;
while( ch==' '||ch==10||ch==9)
{
getchdo;
}
if(ch>='a'&&ch<='z')
statbegsys[ifsym]=true;
statbegsys[whilesym]=true;
/*设置因子开始符号集*/
facbegsys[ident]=true;
facbegsys[number]=true;
facbegsys[lparen]=true;
}
/*
*用数组实现集合的集合运算
tableswitch=(fname[0]=='y'||fname[0]=='Y');
fa1=fopen("fa1.tmp","w");
fprintf(fa1,"Iput pl/0 file ?");
fprintf(fa1,"%s\n", fname);
init(); /*初始化*/
*/
void error(int n)
{
char space[81];
memset(space,32,81); printf("-------%c\n",ch);
space[cc-1]=0;//出错时当前符号已经读完,所以cc-1
printf("****%s!%d\n",space,n);
scanf("%s",fname);
listswitch=(fname[0]=='y'||fname[0]=='Y');
printf("List symbol table ? (Y/N)"); /*是否输出名字表*/
scanf("%s",fname);
strcpy(&(word[10][0]),"then");
strcpy(&(word[11][0]),"var");
strcpy(&(word[12][0]),"while");
strcpy(&(word[13][0]),"write");
/*设置保留字符号*/
wsym[0]=beginsym;
return 0;
}
fclose(fa);
fclose(fa1);
fclose(fas);
if(sym!=period)
{
error(9);
}
if(err==0)
{
fa2=fopen("fa2.tmp", "w");
err++;
}
/*
* 漏掉空格,读取一个字符
*
* 每次读一行,存入line缓冲区,line被getsym取空后再读一行
*
* 被函数getsym调用
*/
int getch()
{
if(cc==ll)
{
if(feof(fin))
{
printf("program incomplete");
strcpy(&(mnemonic[jpc][0]),"jpc");
/*设置符号集*/
for(i=0;i<symnum;i++)
{
declbegsys[i]=false;
statbegsys[i]=false;
facbegsys[i]=false;
}
/*设置声明开始符号集*/
wsym[10]=thensym;
wsym[11]=varsym;
wsym[12]=whilesym;
wsym[13]=writesym;
/*设置指令名称*/
strcpy(&(mnemonic[lit][0]),"lit");
strcpy(&(mnemonic[opr][0]),"opr");
{
line[ll]=0;
break;
}
printf("%c",ch);
fprintf(fa1,"%c",ch);
line[ll]=ch;
ll++;
}
printf("\n");
fprintf(fa1,"\n");
}
scanf("%s",fname); /*输入文件名*/
fin=fopen(fname,"r");
if(fin)
{
printf("List object code ?(Y/N)"); /*是否输出虚拟机代码*/
}
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;
}
/*
*出错处理,打印出错位置和错误编码
*/
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];
ssym['/']=slash;
ssym['(']=lparen;
ssym[')']=rparen;
ssym['=']=eql;
ssym[',']=comma;
ssym['.']=period;
ssym['#']=neq;
ssym[';']=semicolon;
/*设置保留字名字,按照字母顺序,便于折半查找*/
{
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;
interpret();
fclose(fa2);
}
else
{
printf("Errors in pl/0 program");
}
}
fclose(fin);
}
else
{
printf("Can't open file! \n");
strcpy(&(word[0][0]),"begin");
strcpy(&(word[1][0]),"call");
strcpy(&(word[2][0]),"const");
strcpy(&(word[3][0]),"do");
strcpy(&(word[4][0]),"else");
*/
#include<stdio.h>
#include"pl0.h"
#include"string.h"
/*解释执行时使用的栈*/
#define stacksize 500
int main()
{
bool nxtlev[symnum];
printf("Input pl/0 file ?");
declbegsys[constsym]=true;
declbegsys[varsym]=true;
declbegsys[procsym]=true;
/*设置语句开始符号集*/
statbegsys[beginsym]=true;
statbegsys[callsym]=true;
*Redha 32 platform
*使用方法:
*运行后输入PL/0 源程序文件名
*回答是否输出虚拟机代码
*回答是否输出名字表
*fa.tmp 输出虚拟机代码
*fa1.tmp 输出源文件及其各行对应的首地址
*fa2.tmp 输出结果
*fas.tmp 输出名字表
return -1;
}
ll=0;
cc=0;
相关文档
最新文档