⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pl0c.bak

📁 这是一个PL/0的编译器
💻 BAK
📖 第 1 页 / 共 2 页
字号:
/*  
Windows 下c语言PL/0编译程序 
在Visual C++ 6.0和Visual C.NET上运行通过 
使用方法: 
运行后输入PL/0源程序文件名 
回答是否输出虚拟机代码 
回答是否输出名字表 
fa.tmp输出虚拟机代码 
fa1.tmp输出源文件及其各行对应的首地址 
fa2.tmp输出结果 
fas.tmp输出名字表 
*/ 
#include <stdio.h> 
#include "pl0c.h" 
#include "string.h" 

/* 解释执行时使用的栈 */ 
#define stacksize 500  
int 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=%d size=%d\n",table[i].level,table[i].adr,table[i].size); 
fprintf(fas,"    %d proc  %s ",i,table[i].name); 
fprintf(fas,"lev=%d addr=%d size=%d\n",table[i].level,table[i].adr,table[i].size); 
break; 
} 
} 
printf("\n"); 
} 

/* 语句后跟符号为分号或end */ 
memcpy(nxtlev,fsys,sizeof(bool)*symnum); /* 每个后跟符号集和都包含上层后跟符号集和,以便补救 */ 
nxtlev[semicolon]=true; 
nxtlev[endsym]=true; 
statementdo(nxtlev,&tx,lev); 
gendo(opr,0,0); /* 每个过程出口都要使用的释放数据段指令 */ 
memset(nxtlev,0,sizeof(bool)*symnum); /*分程序没有补救集合 */ 
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"); 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -