《pl0源代码.doc》由会员分享,可在线阅读,更多相关《pl0源代码.doc(22页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、如有侵权,请联系网站删除,仅供学习与交流pl0源代码【精品文档】第 22 页源代码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
2、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 fa
3、ctordo(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 vardeclaration
4、do(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,callsym,constsym,varsym,pro
5、csym;#define symnum 32/* 名字表中的类型 */enum object constant,variable,procedur;/* 虚拟机代码 */enum fct lit,opr,lod,sto,cal,inte,jmp,jpc;#define fctnum 8/* 虚拟机代码结构 */struct instructionenum fct f; /* 虚拟机代码指令 */int l; /* 引用层与声明层的层次差 */int a; /* 根据f的不同而不同 */FILE* fas;/* 输出名字表 */FILE* fa; /* 输出虚拟机代码 */FILE* fa1;
6、/* 输出源文件及其各行对应的首地址 */FILE* fa2; /* 输出结果 */bool listswitch; /* 显示虚拟机代码与否 */bool tableswitch; /* 显示名字表与否 */char ch; /* 获取字符的缓冲区,getch 使用 */enum symbol sym; /* 当前的符号 */char idal; /* 当前ident */int num; /* 当前number */int cc,ll,kk; /* getch使用的计数器,cc表示当前字符(ch)的位置 */int cx; /* 虚拟机代码指针 */char line81; /* 读取行缓
7、冲区 */char aal; /* 临时符号 */struct instruction codecxmaxadd1; /* 存放虚拟机代码的数组 */char wordnorwal; /* 保留字 */enum symbol wsymnorw; /* 保留字对应的符号值 */enum symbol ssym256; /* 单字符的符号值 */char mnemonicfctnum5; /* 虚拟机代码指令名称 */bool declbegsyssymnum; /* 表示声明开始的符号集合 */bool statbegsyssymnum; /* 表示语句开始的符号集合 */bool facbeg
8、syssymnum; /* 表示因子开始的符号集合 */* 名字表结构 */struct tablestructchar nameal;/* 名字 */enum object kind;/* 类型:const,var or procedure */int val; /* 数值,仅const使用 */int level; /* 所处层,仅const不使用 */int adr; /* 地址,仅const不使用 */int size; /* 需要分配的数据区空间,仅procedure使用 */struct tablestruct tabletxmaxadd1; /* 名字表 */FILE* fin;
9、FILE* fout;char fnameal;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
10、,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 listcod
11、e(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.cWindows 下c语言PL/0编译程序在Visual C+ 6.0和Visual C.NET上运行通过使用方法:运行后输入PL/0源程序文件名回答是否输出
12、虚拟机代码回答是否输出名字表fa.tmp输出虚拟机代码fa1.tmp输出源文件及其各行对应的首地址fa2.tmp输出结果fas.tmp输出名字表#include #include pl0c.h#include string.h/* 解释执行时使用的栈 */#define stacksize 500 int main()bool nxtlevsymnum;init();/* 初始化 */fas=fopen(fas.tmp,w);fa1=fopen(fa1.tmp,w);printf(Input file? );fprintf(fa1,Input file? );scanf(%s,fname);/
13、* 输入文件名 */fin=fopen(fname,r);if(fin)fprintf(fa1,%sn,fname);printf(List object code?(Y/N);/* 是否输出虚拟机代码 */scanf(%s,fname);listswitch=(fname0=y|fname0=Y);printf(List symbol table?(Y/N);/* 是否输出名字表 */scanf(%s,fname);tableswitch=(fname0=y|fname0=Y);err=0;cc=cx=ll=0;ch= ;kk=al-1;if(-1!=getsym()fa=fopen(fa.
14、tmp,w);fa2=fopen(fa2.tmp,w);addset(nxtlev,declbegsys,statbegsys,symnum);nxtlevperiod=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();/* 调用解释执行程序 */elseprintf(Errors in pl/0 program);fclo
15、se(fin);elseprintf(Cant open file!n);fprintf(fa1,Cant open file!n);fclose(fa1);fclose(fas);printf(n);return 0;/* 在适当的位置显示错误 */void error(int n)char space81;memset(space,32,81);spacecc-1=0; /* 出错时当前符号已经读完,所以cc-1 */ printf(*%s!%dn,space,n);fprintf(fa1,*%s!%dn,space,n);err+;/* 词法分析,获取一个符号 */int getsym(
16、)int i,j,k;while(ch= |ch=10|ch=9)/* 忽略空格、换行和TAB */getchdo;if(ch=a&ch=z)/* 名字或保留字以a.z开头 */k=0;doif(k=a&ch=0&ch=9);ak=0;strcpy(id,a);i=0;j=norw-1;do /* 搜索当前符号是否为保留字 */k=(i+j)/2;if(strcmp(id,wordk)=0)i=k+1;while(ij)sym=wsymk; else sym=ident; /* 搜索失败则,是名字或数字 */elseif(ch=0&ch=0&chnmax)error(30);elseif(ch
17、=:)/* 检测赋值符号 */getchdo;if(ch=)sym=becomes;getchdo;elsesym=nul;/* 不能识别的符号 */elseif(ch=)/* 检测大于或大于等于符号 */getchdo;if(ch=)sym=geq;getchdo;elsesym=gtr;elsesym=ssymch;/* 当符号不满足上述条件时,全部按照单字符符号处理 */getchdo;return 0;/* 生成虚拟机代码 */int gen(enum fct x, /* f */int y, /* l */int z /* a */if(cxcxmax)printf(Program
18、too long);/* 程序过长 */return -1;codecx.f=x;codecx.l=y;codecx.a=z;cx+;return 0;/* 在某一部分(如一条语句,一个表达式)将要结束时时我们希望下一个符号属于某集合(该部分的后跟符号),test负责这项监测,并且负责当监测不通过时的补救措施,程序在需要检测时指定当前需要的符号集合和补救用的集合(如之前未完成部分的后跟符号),以及检测不通过时的错误号 */int test(bool* s1, /* 我们需要的符号 */ bool* s2, /* 如果不是我们需要的,则需要一个补救用的集合 */ int n) /* 错误号 */
19、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 nxtlevsymn
20、um; /* 在下级函数的参数中,符号集合均为值参,但由于使用数租实现, 传递进来的是指针,为防止下级函数改变上级函数的集合,开辟新的空间 传递给下级函数,之后所有的nxtlev都是这样 */dx=3; tx0=tx;/* 记录本层名字的初始位置 */tabletx.adr=cx;gendo(jmp,0,0);if(levlevmax)error(32);doif(sym=constsym)/* 收到常量声明符号,开始处理常量声明 */getsymdo;doconstdeclarationdo(&tx,lev,&dx);/* dx的值会被constdeclaration改变,使用指针 */wh
21、ile(sym=comma) getsymdo;constdeclarationdo(&tx,lev,&dx);if(sym=semicolon)getsymdo;else error(5);while(sym=ident);if(sym=varsym)/* 收到变量声明符号,开始处理变量声明 */getsymdo;dovardeclarationdo(&tx,lev,&dx);while(sym=comma) getsymdo;vardeclarationdo(&tx,lev,&dx);if(sym=semicolon)getsymdo;else error(5);while(sym=ide
22、nt);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);nxtlevsemicolon=true;if(-1=block(lev+1,tx,nxtlev)return -
23、1;/* 递归调用 */if(sym=semicolon)getsymdo;memcpy(nxtlev,statbegsys,sizeof(bool)*symnum);nxtlevident=true;nxtlevprocsym=true;testdo(nxtlev,fsys,6);else error(5);/* 漏掉了分号 */memcpy(nxtlev,statbegsys,sizeof(bool)*symnum);nxtlevident=true;testdo(nxtlev,declbegsys,7);while(inset(sym,declbegsys);/* 直到没有声明符号 */
24、codetabletx0.adr.a=cx;/* 开始生成当前过程代码 */tabletx0.adr=cx;/* 当前过程代码地址 */tabletx0.size=dx;/* 声明部分中每增加一条声明都会给dx增加1,声明部分已经结束,dx就是当前过程数据的size */cx0=cx;gendo(inte,0,dx);/* 生成分配内存代码 */if(tableswitch)/* 输出名字表 */printf(TABLE:n);if(tx0+1tx)printf( NULLn);for(i=tx0+1;i=tx;i+)switch(tablei.kind)case constant:print
25、f( %d const %s ,i,tablei.name);printf(val=%dn,tablei.val);fprintf(fas, %d const %s ,i,tablei.name);fprintf(fas,val=%dn,tablei.val);break;case variable:printf( %d var %s ,i,tablei.name);printf(lev=%d addr=%dn,tablei.level,tablei.adr);fprintf(fas, %d var %s ,i,tablei.name);fprintf(fas,lev=%d addr=%dn,
26、tablei.level,tablei.adr);break;case procedur:printf( %d proc %s ,i,tablei.name);printf(lev=%d addr=%d size=%dn,tablei.level,tablei.adr,tablei.size);fprintf(fas, %d proc %s ,i,tablei.name);fprintf(fas,lev=%d addr=%d size=%dn,tablei.level,tablei.adr,tablei.size);break;printf(n);/* 语句后跟符号为分号或end */memc
27、py(nxtlev,fsys,sizeof(bool)*symnum);/* 每个后跟符号集和都包含上层后跟符号集和,以便补救 */nxtlevsemicolon=true;nxtlevendsym=true;statementdo(nxtlev,&tx,lev);gendo(opr,0,0);/* 每个过程出口都要使用的释放数据段指令 */memset(nxtlev,0,sizeof(bool)*symnum);/*分程序没有补救集合 */testdo(fsys,nxtlev,8);/* 检测后跟符号正确性 */listcode(cx0);/* 输出代码 */return 0;/* 解释程序
28、 */void interpret()int p,b,t;/* 指令指针,指令基址,栈顶指针 */struct instruction i;/* 存放当前指令 */int sstacksize;/* 栈 */printf(start pl0n);t=0;b=0;p=0;s0=s1=s2=0;doi=codep;/* 读当前指令 */p+;switch(i.f)case lit:/* 将a的值取到栈顶 */st=i.a;t+;break;case opr:/* 数学、逻辑运算 */switch(i.a)case 0:t=b;p=st+2;b=st+1;break;case 1:st-1=-st-
29、1;break;case 2:t-;st-1=st-1+st;break;case 3:t-;st-1=st-1-st;break;case 4:t-;st-1=st-1*st;break;case 5:t-;st-1=st-1/st;break;case 6:st-1=st-1%2;break;case 8:t-;st-1=st-1=st;break;case 9:t-;st-1=st-1!=st;break;case 10:t-;st-1=st-1=st;break;case 12:t-;st-1=st-1st;break;case 13:t-;st-1=st-1=st;break;cas
30、e 14:printf(%d,st-1);fprintf(fa2,%d,st-1);t-;break;case 15:printf(n);fprintf(fa2,n);break;case 16:printf(?);fprintf(fa2,?);scanf(%d,&(st);fprintf(fa2,%dn,st);t+;break;break;case lod:/* 取相对当前过程的数据基地址为a的内存的值到栈顶 */st=sbase(i.l,s,b)+i.a;t+;break;case sto:/* 栈顶的值存到相对当前过程的数据基地址为a的内存 */t-;sbase(i.l,s,b)+i.
31、a=st;break;case cal:/* 调用子过程 */st=base(i.l,s,b);/* 将父过程基地址入栈 */st+1=b;/* 将本过程基地址入栈,此两项用于base函数 */st+2=p;/* 将当前指令指针入栈 */b=t;/* 改变基地址指针值为新过程的基地址 */p=i.a;/* 跳转 */break;case inte:/* 分配内存 */t+=i.a;break;case jmp:/* 直接跳转 */p=i.a;break;case jpc:/* 条件跳转 */t-;if(st=0)p=i.a;break;while(p!=0);fclose(fa2);/* 初始
32、化 */void init()int i;/* 设置单字符符号 */for(i=0;i=255;i+)ssymi=nul;ssym+=plus;ssym-=minus;ssym*=times;ssym/=slash;ssym(=lparen;ssym)=rparen;ssym=eql;ssym,=comma;ssym.=period;ssym#=neq;ssym;=semicolon;/* 设置保留字名字 */strcpy(&(word00),begin);strcpy(&(word10),call);strcpy(&(word20),const);strcpy(&(word30),do);s
33、trcpy(&(word40),end);strcpy(&(word50),if);strcpy(&(word60),odd);strcpy(&(word70),procedure);strcpy(&(word80),read);strcpy(&(word90),then);strcpy(&(word100),var);strcpy(&(word110),while);strcpy(&(word120),write);/* 设置保留字符号 */wsym0=beginsym;wsym1=callsym;wsym2=constsym;wsym3=dosym;wsym4=endsym;wsym5=i
34、fsym;wsym6=oddsym;wsym7=procsym;wsym8=readsym;wsym9=thensym;wsym10=varsym;wsym11=whilesym;wsym12=writesym;/* 设置指令名称 */strcpy(&(mnemoniclit0),lit);strcpy(&(mnemonicopr0),opr);strcpy(&(mnemoniclod0),lod);strcpy(&(mnemonicsto0),sto);strcpy(&(mnemoniccal0),cal);strcpy(&(mnemonicinte0),int);strcpy(&(mnem
35、onicjmp0),jmp);strcpy(&(mnemonicjpc0),jpc);/* 设置符号集 */for(i=0;isymnum;i+)declbegsysi=false;statbegsysi=false;facbegsysi=false;/* 设置声明开始符号集 */declbegsysconstsym=true;declbegsysvarsym=true;declbegsysprocsym=true;/* 设置语句开始符号集 */statbegsysbeginsym=true;statbegsyscallsym=true;statbegsysifsym=true;statbeg
36、syswhilesym=true;/* 设置因子开始符号集 */facbegsysident=true;facbegsysnumber=true;facbegsyslparen=true;/* 用数组实现集合的集合运算 */int inset(int e,bool* s)return se;int addset(bool* sr,bool* s1,bool* s2,int n)int i;for(i=0;in;i+)sri=s1i|s2i;return 0;int subset(bool* sr,bool* s1,bool* s2,int n)int i;for(i=0;in;i+)sri=s
37、1i&(!s2i);return 0;int mulset(bool* sr,bool* s1,bool* s2,int n)int i;for(i=0;in;i+)sri=s1i&s2i;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);linell=ch;ll+;printf(n);fprintf(fa1,n);ch=linecc;cc+;return 0;/* 生成一项名字表 */void enter(enum object k,/* 名字种类const
限制150内