PL0 编译程序讲解

合集下载

PL0编译程序

PL0编译程序

PL/0语言编译系统春秋五霸目录1 PL/0语言及编译系统2 驱动代码3 编译程序4 解释程序①词法分析②语法分析③语义分析及目标代码生成1 PL/0语言编译系统PL/语言编译系统PL/0语言编译系统是世界著名计算机科学家N.Wirth编写的。

对PL/0编译程序进行实例分析,有助于对一般编译过程和编译程序结构的理解。

PL/0语言功能简单、结构清晰、可读性强,又具备了一般高级语言的必须部分,因而PL/0语言的编译程序能充分体现一个高级语言编译程序实现的基本技术和步骤,是一个非常合适的编译程序教学模型。

PL/0源程序PL/0编译系统语法语义分析程序词法分析程序目标代码代码生成程序表格管理程序出错处理程序PL/0源程序PL/0编译系统构成类P-code程序PL/0编译程序输出数据输入数据类P-code虚拟机类P-code解释程序PL/0语言文法的EBNF表示●〈程序〉∷=〈分程序〉.●〈分程序〉∷=[〈常量说明部分〉][〈变量说明部分〉][〈过程说明部分〉]〈语句〉●〈常量说明部分〉∷=CONST〈常量定义部分〉{,〈常量定义〉};●〈常量定义〉 ::= 〈标识符〉=〈无符号整数〉;●〈无符号整数〉∷=〈数字〉{〈数字〉}●〈变量说明部分〉∷=V AR〈标识符〉{,〈标识符〉};●〈标识符〉∷=〈字母〉{〈字母〉|〈数字〉}PL/0语言文法的EBNF表示●<过程说明部分>::=<过程首部><分程序>{;<过程说明部分>}●<过程首部>::=PROCEDURE<标识符>;●<语句>::=<赋值语句>|<条件语句>|<当型循环语句>|<过程调用语句>|<读语句>|<写语句>|<复合语句>|<空>●<赋值语句>::= <标识符>:=<表达式>●<复合语句>::= BEGIN<语句>{;<语句>}END●<条件>::= <表达式><关系运算符><表达式>PL/0语言文法的EBNF表示●<表达式>::=[+|-]<项>{<加法运算符><项>}●<项>::= <因子>{<乘法运算符><因子>}●<因子>::= <标识符>|<无符号整数>|‘(’<表达式>‘)’PL/0语言文法的EBNF表示●<加法运算符>::= +|-●<乘法运算符>::= *|/●<关系运算符>::= =|#|<|<=|>|>=●<条件语句>::= IF<条件>THEN<语句>●<过程调用语句>::= CALL<标识符>●<当型循环语句>::= WHILE<条件>DO<语句>●<读语句>::= READ’(’<标识符>{,<标识符>}‘)’PL/0语言文法的EBNF表示●<写语句>::= WRITE’(’<表达式>{,<表达式>}‘)’●<字母>::= a|b|…|X|Y|Z●<数字>::= 0|1|…|8|9指令过程调用相关指令类P-code 虚 拟机指令系 统…存取指令int 0 a cal l ajmp 0 alit 0 alod l a sto l a指令一元运算和比较指令类P-code 虚 拟机指令系 统…二元运算指令opr 0 5opr 0 1opr 0 6opr 0 2opr 0 3 opr 0 4指令转移指令类P-code 虚 拟机指令系 统输入输出指令二元运算指令opr 0 13 jmp 0 ajpc 0 aopr 0 8 opr 0 10opr 0 12opr 0 14opr 0 16opr 0 15opr 0 9opr 0 112 驱动代码词法分析主要函数:main()函数功能:驱动整个编译系统的运行变量说明:fa.tmp 输出虚拟机代码fa1.tmp 输出源文件及其各行对应的首地址fa2.tmp 输出结果fas.tmp 输出名字表main函数int main(){……/*打开源程序文件*/If 打开源程序文件成功{…… /*初始化输出文件信息*/init(); /*初始化各类名字和符号信息*/err=0; /*初始化错误数*/…… /*初始化其他信息*/if(-1!=getsym()) /*成功读取第一个单词*/{……main函数if(-1==block(0,0,nxtlev)) /*调用编译程序*/{…… /*编译过程未成功结束关闭所有已打开的文件,返回*/}…… /*编译过程结束关闭所有已打开的输出文件*/if(sym!=period) /*当前符号不是程序结束符’.’*/{error(9); /*提示9号出错信息:缺少程序结束符’.’*/}if(err==0) /*未发现程序中的错误*/{……interpret(); /*调用解释程序,执行所产生的类P-code代码*/……main函数else{printf("Errors in pl/0 program");}}…… /*关闭已打开的文件*/}else{printf(“Can‘t open file! \n”);/*打开源程序文件不成功*/}printf("\n");return 0; /*返回*/}集合运算函数int inset(int e,bool* s){/*返回e在数组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){/*s1[i]为true,s2[i]为false时,sr[i]才为true*/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);fprintf(fa1,"-------%c\n",ch);space[cc-1]=0;//出错时当前符号已经读完,所以cc-1printf("****%s!%d\n",space,n);fprintf(fa1,"****%s!%d\n",space,n);err++;}PL/O语言的出错信息表:出错编号出错原因1:常数说明中的“=”写成“∶=”。

PL0编译程序

PL0编译程序

PL/0编译程序的运行机制:语法分析开始后,首先调用分程序处理过程(block)处理分程序。

过程入口参数置为:0层、符号表位置0、出错恢复单词集合为句号、声明符或语句开始符。

进入block过程后,首先把局部数据段分配指针设为3,准备分配3个单元供运行期存放静态链SL、动态链DL和返回地址RA。

然后用tx0记录下当前符号表位置并产生一条jmp指令,准备跳转到主程序的开始位置,由于当前还没有知到主程序究竟在何处开始,所以jmp的目标暂时填为0,稍后再改。

同时在符号表的当前位置记录下这个jmp指令在代码段中的位置。

在判断了嵌套层数没有超过规定的层数后,开始分析源程序。

首先判断是否遇到了常量声明,如果遇到则开始常量定义,把常量存入符号表。

接下去用同样的方法分析变量声明,变量定义过程中会用dx变量记录下局部数据段分配的空间个数。

然后如果遇到procedure保留字则进行过程声明和定义,声明的方法是把过程的名字和所在的层次记入符号表,过程定义的方法就是通过递归调用block过程,因为每个过程都是一个分程序。

由于这是分程序中的分程序,因此调用block时需把当前的层次号lev加一传递给block过程。

分程序声明部分完成后,即将进入语句的处理,这时的代码分配指针cx的值正好指向语句的开始位置,这个位置正是前面的jmp指令需要跳转到的位置。

于是通过前面记录下来的地址值,把这个jmp指令的跳转位置改成当前cx的位置。

并在符号表中记录下当前的代码段分配地址和局部数据段要分配的大小(dx 的值)。

生成一条int指令,分配dx个空间,作为这个分程序段的第一条指令。

下面就调用语句处理过程statement分析语句。

分析完成后,生成操作数为0的opr指令,用于从分程序返回(对于0层的主程序来说,就是程序运行完成,退出)。

逻辑表达式的处理:首先判断是否为一元逻辑表达式:判奇偶。

如果是,则通过调用表达式处理过程分析计算表达式的值,然后生成判奇指令。

编译原理(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编译概述讲解

类P-code虚拟机
指令 “OPR 0 0”

T’
《编译原理》
过程调用结束后,返回调用点并退栈 重置基址寄存器和栈顶寄存器
. . . RA’ DL’ . . . T . . . RA B DL SL . . . B = DL’ 返返返 返返返 返返 P 返 RA’ T = B’
B’
SL’ . . . RA DL ቤተ መጻሕፍቲ ባይዱL . . .
PL/0 语言的 EBNF 表示
例:PL/0 语言的EBNF表示
<程序> ::= <分程序>.
<分程序> ::= [<常量说明部分>] [<变量说明部分>] [<过程说明部分>] <语句>
《编译原理》
<常量说明部分> ::= CONST <常量定义> { ,<常量定义> } ; <常量定义> ::= <标识符> = <无符号整数> <无符号整数> ::= <数字> {<数字>} <变量说明部分> ::= VAR <标识符 > { , <标识符 > } ; <标识符> ::= <字母> {<字母>|<数字>} <过程说明部分> ::= <过程首部><分程序>{; <过程说明部分> }; <过程首部> ::= PROCEDURE <标识符> ;
PL/0 程序示例
计算最大公约数
var m, n, r, q; { 计算m和n的最大公约数 } procedure gcd; begin while r#0 do begin q := m / n; r := m - q * n; m := n; n := r; end end;

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。

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

第2章 PL0编译程序的实现

第2章 PL0编译程序的实现

2
PL/0语言的语法描述图
程序 分程序
分程序
.
常量说明部分
变量说明部分 过程说明部分 语句
3
PL/0语言的语法描述图
分程序
常量说明部分 变量说明部分 过程说明部分 语句
4
5
例子

因子

a 10 a b*10 a a+10 a+b*10 -a+2*(x+10)-10*(b-10)





<分程序>::=[<常量说明部分>][<变量说明部分>][<过程说明部分>]<语句>
9
由语法描述图转化到BNF范式
常量定义
<常量说明部分>::=CONST<常量定义>{,<常量定义>}; <常量定义>::=<标识符>=<无符号整数> <无符号整数>::=<数字>{<数字>}
10
回家作业

1 编写一个PL0语言的程序,要求:
a) 声明2个常数,c1为2,c2为10 b) 声明3个变量,num1、count和sum c) 第一条语句是赋值语句,将c1赋给num1 d) 第二条语句是条件语句,如果num1>0,则执行第三条语句, 否则将num1减1 e) 第三条语句是赋值语句,将0赋给sum e) 第四条语句是循环语句,循环变量count从1到c2,每次将 count值加到sum中
回家作业
2 编写一个PL0语言的程序,要求: a) 包含一个过程procedure 过程名 字为GetSum, 功能是计算累加和 const c1=0,c2=10; var count,sum; procedure GetSum; begin sum:=sum+count end begin sum:=0; count=c1; while count<=c2 do begin count:=count+1; call GetSum; end; end.

PL0 编译程序讲解

PL0 编译程序讲解

write(2*c);
变量c
(16) lit 0 2 (17) lod 0 4 (18) opr 0 4 (19) opr 0 14
read(b);
变量b
end
end.
RA 0
(20) opr 0 15 换行
DL 0
(21) opr 0 16 (22) sto 0 3 (23) jmp 0 11 (24) opr 0 0
调用getsym取单词
调用block过程
当前单词 是否为源程序结束符
'.'? Y
源程序中 是否有错误?
N 调用解释过程interpret
解释执行目标程序
PL/0编译程序 语法、语义分
析的核心
N 出错
Y 打印错误
结束
3.2 PL/0编译程序词法分析的实现
词法分析函数getsym()所识别的单词: 保留字或关键字:如:BEGIN、 END、 IF、 THEN等 运算符: 如:+、-、*、/、:=、#、>=、<=等 标识符: 用户定义的变量名、常数名、过程名 常数: 如:10、25、100等整数 界符: 如:‘,’、‘.’ 、‘;’ 、‘(’ 、‘)’等
PL/0程序示例
CONST A=10; (* 常量说明部分 *) VAR B,C; (* 变量说明部分 *) PROCEDURE P; (* 过程说明部分 *)
VAR D;(* P的局部变量说明部分 *) PROCEDURE Q; (* P的局部过程说明部分 *) VAR X;
BEGIN READ(X); D:=X; IF X#0 DO CALL P;
PL/0编译程序
PL/0源程序
PL/0

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;

第二章PL0编译程序PPT课件

第二章PL0编译程序PPT课件

PL/0的语法描述图
内的文字表示非终结符

内的文字或符号表示终结符
程序
分程序
.
PL/0的语法描述图
分程序
const
标识符
var
标识符
number
procedure 语句
标识符
分程序
PL/0的语法描述图
语句
标识符
:=
表达式
begin
语句
end
语句
read
(
标识符
)
,
PL/0语言文法的EBNF表示
目标代码类pcode是一种假想栈式计算机的汇编语言。 指令格式:
f la
f 功能码 l 层次差 (标识符引用层减去定义层) a 根据不同的指令有所区别
类pcode指令功能表
LIT 0 a LOD l a STO l a CAL l a INT 0 a JMP 0 a JPC 0 a
OPR 0 0 OPR 0 1 OPR 0 2 OPR 0 3 OPR 0 4 OPR 0 5 OPR 0 6 OPR 0 7 OPR 0 8 OPR 0 9 OPR 0 10 OPR 0 11 OPR 0 12 OPR 0 13 OPR 0 14 OPR 0 15 OPR 0 16
PROCEDURE P; (* 过程说明部分 *)
VAR D;
PROCEDURE Q; VAR X; BEGIN READ(X);
D:=X;
Q的过程体
WHILE X#0
DO CALL P; END;
BEGIN WRITE(D); CALL Q;
END; BEGIN
p的过程体
CALL P; END.
主程序体

实验一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编译程序的实现
【学习目标】 本章目的:以PL/0语言编译程序为实例,学习编
译程序实现的基本步骤和相关技术,对编译程序的 构造和实现得到一些感性认识和建立起整体概念, 为后面的原理学习打下基础。
◇ 了解并掌握用语法图和扩充的巴科斯-瑙尔 范式(EBNF)对计算机语言的形式描述。
【难 重 点】 重点: ◇ 弄清源语言(PL/0)目标语言(类 pcode)实现
2. V集n合是N一on个-te非rm空in有als穷,集且合V,t称Vn作= 非。终结符号 3. S Vn ,称作开始符号Start Symbol 。 4. P是一个非空有穷集合,称为产生式集合
Production Rules ,每条产生式形为A,其22 中AV ,(V V )*。
例2.1 简单的算术表达式
17
Bakus-Naur范式(BNF)
18
Examples of BNF
19
Syntax Graph
20
2.2 语法定义
❖ 语法描述程序设计语言的层次结构,如C语言 的条件语句为
if_stmt if (expr) stmt else stmt if_stmt if (expr) stmt
string
string + string
string - string 2
9
5
string
string - string
9 string + string
5
2
对某个文法,如一个句子有两棵以上的分析树,称为
二义(歧义)文法。
29
2.2.3 Associativity of Operators
❖ 可以用括号来表明计算顺序,如 9-5+2 = (9-5)+2

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编译程序实例剖析

PL/0 语言的EBNF表示 语言的 表示
语言的EBNF表示片断 例:PL/0 语言பைடு நூலகம் 表示片断
<程序 ::= <分程序 程序> 分程序>. 程序 分程序 <分程序 ::= [<常量说明部分 [<变量说明部分 分程序> 常量说明部分>] 变量说明部分 变量说明部分>] 分程序 常量说明部分 [<过程说明部分 <语句 过程说明部分>] 语句 语句> 过程说明部分
指令格式
f : 操作码
f
l
a
l : 层次差 (标识符引用层减去定义层) 标识符引用层减去定义层) a : 不同的指令含义不同
类P-code虚拟机 虚拟机
指令 "INT 0 A"
《编译原理》
在栈顶开辟 A 个存储单元,服务于被调用的过程 个存储单元, A 等于该过程的局部变量数加 3 等于该过程的局部变量数加 3 个特殊的联系单元 特殊的联系单元
PL/0 语言的语义规则
类型, 类型,上下文约束与作用域规则
数据类型只有整数类型 数据类型只有整数类型
数据结构只支持简单变量和常数 数据结构只支持简单变量和 简单变量 所支持的数字为最长 9 位的十进制数 标识符的有效长度为 标识符的有效长度为10 标识符引用前先要声明 过程无参数 过程可嵌套,最多嵌套 3 层 过程可嵌套, 过程可递归调用
第二讲
PL/0 编译程序
《编译原理》
PL/0编译程序 编译程序
PL/0 编译程序总体结构 PL/0 语言简介 类 P-code 虚拟机 PL/0 编译程序的词法分析 PL/0 编译程序的语法分析 PL/0 编译程序的语义分析 PL/0 编译程序的错误处理 PL/0 编译程序的目标代码生成

第2章 PL0编译程序

第2章 PL0编译程序

n. <表达式>::=[十|一]<项> {<加法运算符><项>} o. <项>::=<因子>{<乘法运算符><因子>} p.<因子>::=<标识符>|<无符号整数>|<表达式> q. <加法运算符>::=十|一
2007年9月12日星期二5时12分32秒
共147页, Page19
q. <加法运算符>::=十|一 r.<乘法运算符>::=*|/ s.<关系运算符>::==|#|<|<=|>|>= t.<条件语句}::=IF<条件>THEN<语句> u.<过程调用语句>::=call<标识符>
共147页, Page28
启动 置初值 PLO编译程序总体流程 Call getsym取单词
Call block
当前单词是否 为源程序结束符‘.’?
N
出错 打印错误
Y
源程序是否有错误‘.’?
Y
N Call Interpret 解译执行目标程序
结束
2007年9月12日星期二5时12分32秒 共147页, Page29
2007年9月12日星期二5时12分32秒 共147页, Page22
PL/0编译程序的结构
PL/0源程序 表 格 管 理 程 序 词法分析程序 语法分析程序 代码程序 出 错 处 理 程 序
共147页, Page23
目标程序
2007年9月12日星期二5时12分32秒
PL/0编译程序的子程序
PI/0编程序是用PASCAL语言书写,整个编译程序 (包括主程序) 是由18个嵌套及并列的过程或函数 组成.下面分别简要给出这些函数功能及它们的层 次结构。

编译原理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程序设计语言是一个较简单的语言,它以赋值语句为基础,构造概念有顺序、条件和重复(循环)三种。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
OPR 0 8 OPR 0 k
OPR 0 14 OPR 0 15
OPR 0 16
将常数值取到栈顶,a为常数值 将变量值取到栈顶,a为偏移量,l为层差 将栈顶内容送入某变量单元中,a为偏移量,l为层差 调用过程,a为过程地址,l为层差 在运行栈中为被调用的过程开辟a个单元的数据区 无条件跳转至a地址 条件跳转,当栈顶布尔值非真则跳转至a地址,否则顺序执行
PL/0编译程序
PL/0源程序
PL/0
PL/0编译程序


类 p-code代码
程 序



类 p-code解释程序
框 架
输入数据
输出数据
出错处理程序 表格管理程序
PL/0编译程序的结构
PL/0源程序 词法分析程 序 语法语义分析程序
代码生成程序
目标程序
编 译 系 统 总 体 流 程 图
启动
置初值
构成EBNF的元素:非终结符,终结符,开始符,规则
EBNF的元符号:
< > 用左右尖括号括起来的内容为非终结符 ∷=或→ 读做‘定义为’,→的左部由右部定义
|
读做‘或’ 表示右部候选内容
{ } 表示花括号内的内容可重复任意次或限定次数
[ ] 表示方括号内的内容为任选项 ( ) 表示圆括号内的内容优先
子程序的调用、执行和返回
T
过程被调用时,子程序的每次调用都需在数据栈
顶为其分配独立的数据区
B区 B
子程序返回时,需做两件事情:一是代码返回 (需记住RA),二是数据区的同步恢复(DL)
C区
子程序运行时,要存取外层数据区中的存储单元
B区
当前B数据区须记住: ① 返回地址RA ② 动态链DL—记录调用者数据区基地址
➢两种存储 程序存储CODE[] 被解释执行
数据存储s[] 栈S,运算在栈顶进行
➢指令寄存器----I,放当前正在解释的指令
➢地址寄存器
栈顶地址寄存器 T 程序地址寄存器 P - 指向下一条执行的目标 指令 基址寄存器 B
二、运行时数据的存储与访问----栈式存储
若有执行调用序列:A->B->C->B ---先调用,后结束
语法图
程序
分程序
.
分程序
const
ident
=
,
;
var
ident
,
;
;
procedure
ident
语句
number
; 分程序
语句
ident begin
:= 表达式
语句
end
语句
;
read
(
ident
)
,
PL/0语言的EBNF表示
BNF(BACKUS-NAUR FORM)与EBNF的介绍
BNF是根据美国的John W.Backus与丹麦的Peter Naur来命名的,它是从 语法上描述程序设计语言的元语言。采用BNF就可说明哪些符号序列是对 于某给定语言在语法上有效的程序。
A区
假设A、C同层, 且A中嵌套B
③ 静态链SL—记录定义该过程的直接外层过程数据区的基地址, 以便访问外层数据
运行时数据栈S的变化
T
var m1,m2,m3; Procedure A; B
var a1; procedure B;
var b1,b2; procedure C;
… C过程call B;
PL/0程序示例
CONST A=10; (* 常量说明部分 *) VAR B,C; (* 变量说明部分 *) PROCEDURE P; (* 过程说明部分 *)
VAR D;(* P的局部变量说明部分 *) PROCEDURE Q; (* P的局部过程说明部分 *) VAR X;
BEGIN READ(X); D:=X; IF X#0 DO CALL P;
用状态转换图实现词法分析程序的设计方法
( 9) opr 0 16
(10) sto 0 3
c:=b+a; end; begin read(b);
RA 16
(11) lod 0 3
while b#0 do
DL 0
(12) lit 0 0
(13) opr 0 9 (14) jpc 0 24 条件不满足转24
begin
call
p;
SL 0
(15) cal 0 2
在编译程序中,单词的表示方式:(sym, id/num)
词法分析过程:getsym()框图(P19图2.5)
enum symbol {nul,ident,number,plus,…,varsym,procsym};
当识别出标识符时先查保留字表 保留字及内部表示对应表: char word[norw][al]; enum symble wsym[norw]; 字符对应的单词表: enum symble ssym[256]; ssym[‘+’]=plus; ssym[‘-’]=minus; … 词法分析通过三个全程量 symbol sym; char id[]; int num; 将识别出的单词信息传递给语法分析程序。
PL/0功能简单、结构清晰、可读性强,而又具备了一 般高级语言的必备部分,因而其编译程序能充分体 现一个高级语言编译程序的基本技术和步骤。
PL/0语言:PASCAL语言的子集,用于教学 PL/0程序示例 PL/0的语法描述图 PL/0语言的EBNF表示
PL/0语言是PASCAL语言的子集
过程可嵌套定义,内层可引用包围它的外层定义的标识符,可递归调用 数据类型,只有整型 数据结构 ,只有简变和常数 标识符的有效长度是10 语句种类:
write(2*c);
变量c
(16) lit 0 2 (17) lod 0 4 (18) opr 0 4 (19) opr 0 14
read(b);
变量b
end
end.
RA 0
(20) opr 0 15 换行
DL 0
(21) opr 0 16 (22) sto 0 3 (23) jmp 0 11 (24) opr 0 0
END; BEGIN
CALL Q; WRITE(D); END; BEGIN CALL P; END.
递归计算 sum = 1! + 2 ! + ... + n!
var n, m, fact, sum;
{ 递规计算 fact = m! } procedure factorial; begin
if m > 0 then begin fact := fact * m; m := m - 1; call factorial; end;
第二章 PL/0编译程序的实现
本章以PL/0编译程序为实例, 使大家对编译程序的 实现建立起整体概念,对编译程序的构造得到一 些感性认识和初步了解。
§1 PL/0语言 §2 PL/0处理机—假想栈式机 §3 PL/0编译程序 §4 符号表的一般形式讨论 §5 栈式存储管理的再讨论
§1 PL/0语言
SL:106
RA: r2
DL:110 SL:110
b2=25
b1=20
RA: r3
DL:106
SL:106 a1=15
RA: r4
DL:100
SL:100
m3=118
m2=472 m1=335
RA:0
DL:0
100
SL:0
三、PL/0机的指令系统
指令格式:
fla
f: 功能码
l: 层次差 (标识符引用层减去定义层) a: 根据不同的指令有所区别
( 0) jmp 0 8 转向主程序入口
( 1) jmp 0 2 转向过程p入口 ( 2) int 0 3 为过程辟空间
( 3) lod 1 3
const var
a=10; b,c;
( 4) lit 0 10
procedure
p;
( 5) opr 0 2
begin
( 6) sto 1 4 ( 7) opr 0 0 退栈并返回调用点 ( 8) int 0 5
所有运算对栈顶的两个或一个元素进行,并用运算结果代替 原来的运算对象。
LIT 0 a
LOD l a STO l a
CAL l a
INT 0 a JMP 0 a
指 JPC 0 a OPR 0 0
令 OPR 0 1
OPR 0 2
功 OPR 0 3 能 OPR 0 4
OPR 0 5
表 OPR 0 6 OPR 0 7
PL/0语言文法的EBNF表示
〈程序〉->〈分程序〉.
〈分程序〉->[<常量说明部分>][<变量说明部分>][<过程说明部分>]<语句>
〈常量说明部分〉->CONST〈常量定义部分〉{,〈常量定义〉}; 〈无符号整数〉->〈数字〉{〈数字〉} 〈变量说明部分〉->VAR〈标识符〉{,〈标识符〉}; 〈标识符〉->〈字母〉{〈字母〉|〈数字〉}
SL:静态链
SL 0 0
演示执行过程 DL:动态链
运行栈
RA:返回地址
§3 PL/0编译程序的实现
PL/0编译程序的总体设计 PL/0编译程序词法分析的设计与实现 PL/0编译程序语法分析的设计与实现 PL/0编译程序语义分析的设计与实现 PL/0编译程序语法错误处理的实现 PL/0编译程序代码生成的实现 pcode代码解释器的设计与实现
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 !} write(sum);
相关文档
最新文档