编译原理增加数据类型.docx
《编译原理增加数据类型.docx》由会员分享,可在线阅读,更多相关《编译原理增加数据类型.docx(67页珍藏版)》请在冰豆网上搜索。
编译原理增加数据类型
//pl0.h
///这个文件中的代码与课本的一致
//对于使用C++的编译器,这句话可以注释掉,但对于C语言不行
//typedefenum{FALSE,TRUE,}bool;
#definenorw16//关键字的个数
#definetxmax100//名字表的容量
#definenmax14//数字的最大位数
#defineal10//符号的最大长度
#defineamax2047//地址上界
#definelevmax3//最大允许的嵌套声明层数
#definecxmax200//最多的虚拟机代码数
enumsymbol{
//枚举类型:
//nul=0,ident=1,number=2,plus=3,minus=4
//times=5,依次类推,procsym=31
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,procsym,
integersym,charactersym,doubsym,
charnum,integercon,doubnum
};
#definesymnum38
enumobject{
constant,
variable,
procedur,
integer,
character,
doub,
};
enumfct{
lit,opr,lod,
sto,cal,inte,
jmp,jpc,
};
#definefctnum11
structinstruction
{
enumfctf;//虚拟机代码指令
intl;//引用层与声明层的层差
doublea;//根据f的不同而不同,参考课本
};
FILE*fas;//输出名字表
FILE*fa;//输出虚拟机代码
FILE*fa1;//输出源文件及其各行对应的首地址
FILE*fa2;//输出结果
boollistswitch;
booltableswitch;
charch;//获取字符的缓冲区
enumsymbolsym;
charid[al+1];
doublenum;
intcc,ll;//cc表示当前字符的位置
intcx;//虚拟机代码指针
charline[81];//读取行缓冲区
chara[al+1];//临时符号,多出的一个字节用于表示字符串的结尾
structinstructioncode[cxmax];//存放虚拟机代码的数组
charword[norw][al];//保留字
enumsymbolwsym[norw];//保留字对应的符号值
enumsymbolssym[256];//单符号的符号值
charmnemonic[fctnum][5];//虚拟机代码指令的名称
booldeclbegsys[symnum];//表示声明开始的符号集合
boolstatbegsys[symnum];//表示语句开始的符号集合
boolfacbegsys[symnum];//表示因子开始的符号集合
//名字表结构
structtablestruct
{
charname[al];//名字
//enumdatatypetype;//数据类型:
integer,character,doub,
enumobjectkind;//类型:
const,var,array,procedure
doubleval;//数值,仅const使用,使用双精度浮点型
intlevel;//所处层
intadr;//地址
intsize;//需分配的数据空间
};
structtablestructtable[txmax];//名字表
FILE*fin;
FILE*fout;
charfname[al];
interr;//错误计数器,每出一次错误,其加一
#definegetsymdoif(-1==getsym())return-1
#definegetchdoif(-1==getch())return-1
#definetestdo(a,b,c)if(-1==test(a,b,c))return-1
#definegendo(a,b,c)if(-1==gen(a,b,c))return-1
#defineexpressiondo(a,b,c)if(-1==expression_r(a,b,c))return-1
#definefactordo(a,b,c)if(-1==factor(a,b,c))return-1
#definetermdo(a,b,c)if(-1==term(a,b,c))return-1
#defineconditiondo(a,b,c)if(-1==condition(a,b,c))return-1
#definestatementdo(a,b,c)if(-1==statement(a,b,c))return-1
#defineconstdeclarationdo(a,b,c)if(-1==constdeclaration(a,b,c))return-1
#definevardeclarationdo(a,b,c)if(-1==vardeclaration(a,b,c))return-1
#defineintegerdeclarationdo(a,b,c)if(-1==integerdeclaration(a,b,c))return-1
#definecharacterdeclarationdo(a,b,c)if(-1==characterdeclaration(a,b,c))return-1
#definedoubdeclarationdo(a,b,c)if(-1==doubdeclaration(a,b,c))return-1
voiderror(intn);
intgetsym();
intgetch();
voidinit();
intgen(enumfctx,inty,doublez);
inttest(bool*s1,bool*s2,intn);
intinset(inte,bool*s);
intaddset(bool*sr,bool*s1,bool*s2,intn);
intsubset(bool*sr,bool*s1,bool*s2,intn);
intmulset(bool*sr,bool*s1,bool*s2,intn);
intblock(intlev,inttx,bool*fsys);
voidinterpret();
intfactor(bool*fsys,int*ptx,intlev);
intterm(bool*fsys,int*ptx,intlev);
intcondition(bool*fsys,int*ptx,intlev);
intexpression_r(bool*fsys,int*ptx,intlev);
intstatement(bool*fsys,int*ptx,intlev);
voidlistcode(intcx0);
intvardeclaration(int*ptx,intlev,int*pdx);
intintegerdeclaration(int*ptx,intlev,int*pdx);
intcharacterdeclaration(int*ptx,intlev,int*pdx);
intdoubdeclaration(int*ptx,intlev,int*pdx);
intconstdeclaration(int*ptx,intlev,int*pdx);
intposition(char*idt,inttx);
voidenter(enumobjectk,int*ptx,intlev,int*pdx);
intbase(intl,int*s,intb);
//pl0.cpp
#include
#include"pl0.h"
#include"string.h"
//这个宏定义的意思表示是运行时的数据栈大小,这个会在interpret函数中出现,可以先不用管它
#definestacksize500
/*********************************************************************************
**在看源程序之前,请始终记住**
**sym表示的是当前字符串的类型,**
**num表示的数字,**
**id中存放的是字符串,**
**记住这点在看源程序的时候非常重要。
**
**另外,还要记住的是nxtlev数组中存放的是后继符号集**
**********************************************************************************/
intmain()
{
boolnxtlev[symnum];//这个数组表示的是当前语句的后继符号集合,一共有个
printf("Inputpl/0file?
");//输入文件的路径
scanf("%s",fname);
fin=fopen(fname,"r");//以只读打开文件,fin文件指针
if(fin)
{
printf("Listobjectcode?
(Y/N)");
scanf("%s",fname);
listswitch=(fname[0]=='y'||fname[0]=='Y');//listswitch用于列举代码
printf("Listsymboltable?
(Y/N)");
scanf("%s",fname);
tableswitch=(fname[0]=='y'||fname[0]=='Y');//tableswitch表示是否输出table表
//该表类似于页的表
fa1=fopen("fa1.tmp","w");//以只读格式打开文件,对于本程序其实是新建一个文件
//fa1文件中存储的是源程序,运行一次,看看这个文件就明白啦
fprintf(fa1,"Inputpl/0file?
");//将"Inputpl/0file?
"输入文件中
fprintf(fa1,"%s\n",fname);//同上
init();//初始化
err=0;
cc=cx=ll=0;
ch='';
if(-1!
=getsym())
{
//如果程序开始是对的,则往下执行
//fa存放的是模拟程序的代码
//fas存放的是table表中的数据
//运行一次代码看一下文件即明白
fa=fopen("fa.tmp","w");//以只读形式打开
fas=fopen("fas.tmp","w");//同上
//函数原型:
intaddset(bool*sr,bool*s1,bool*s2,intn);
//boolnxtlev[32];
/*
declbegsys[constsym]=true;
declbegsys[varsym]=true;
declbegsys[procsym]=true;
statbegsys[beginsym]=true;
statbegsys[callsym]=true;
statbegsys[ifsym]=true;
statbegsys[whilesym]=true;
执行该函数的结果为对应的下标在nxtlev中上述值均为true;
*/
//在分程序开始的时候,其后继符号可以是
//const,var,procedure,begin,call,if,while
//所以在nxtlev中将上述这些置为true
//当然在整个程序可以只有一个'.',即程序是空的也可以,所以会有nxtlev[period]=true;
addset(nxtlev,declbegsys,statbegsys,symnum);//symnum=32;
nxtlev[period]=true;
//block是编译程序的主程序,是分程序的分析处理过程,其里面调用了大部分程序
//耐心看
//请记住在block程序开始的时候,只读取了源程序的一个语句单位
if(-1==block(0,0,nxtlev))
{
//当程序出错的时候,则关闭以下文件,并退出程序
fclose(fa);
fclose(fa1);
fclose(fas);
fclose(fin);
printf("\n");
return0;
}
fclose(fa);
fclose(fa1);
fclose(fas);
//如果在源程序的结尾没有'.',则报错
if(sym!
=period)
{
error(9);
}
//err=0表示源程序没有错误
if(err==0)
{
//在源程序没有错误的前提下,打开fa2文件,依次执行里面的语句,输出源程序结果
fa2=fopen("fa2.tmp","w");
//模拟计算机执行源程序
interpret();
fclose(fa2);
}
else
{
//如果远程有错的话,会这样
printf("Errorsinpl/0program");
}
}
fclose(fin);
}
else
{
printf("Can'topenfile!
\n");
}
printf("\n");
return0;
}
voidinit()
{
inti;
for(i=0;i<=255;i++)
{
ssym[i]=nul;//nul为,将ssym初始化为
}
//ssym大小为,可以表示所有的ASCLL码
ssym['+']=plus;
ssym['-']=minus;
ssym['*']=times;
ssym['/']=slash;
ssym['(']=lparen;
ssym[')']=rparen;
ssym['=']=eql;
ssym[',']=comma;
ssym['.']=period;
ssym['#']=neq;
ssym[';']=semicolon;
//将保留字输入word数组中,为了使用二分查找,这个设置顺序是按照字母序
/*strcpy(&(word[0][0]),"begin");
strcpy(&(word[14][0]),"character");
strcpy(&(word[1][0]),"call");
strcpy(&(word[2][0]),"const");
strcpy(&(word[3][0]),"do");
strcpy(&(word[15][0]),"doub");
strcpy(&(word[4][0]),"end");
strcpy(&(word[5][0]),"if");
strcpy(&(word[13][0]),"integer");
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");*/
strcpy(&(word[0][0]),"begin");
strcpy(&(word[1][0]),"call");
strcpy(&(word[2][0]),"character");
strcpy(&(word[3][0]),"const");
strcpy(&(word[4][0]),"do");
strcpy(&(word[5][0]),"doub");
strcpy(&(word[6][0]),"end");
strcpy(&(word[7][0]),"if");
strcpy(&(word[8][0]),"integer");
strcpy(&(word[9][0]),"odd");
strcpy(&(word[10][0]),"procedure");
strcpy(&(word[11][0]),"read");
strcpy(&(word[12][0]),"then");
strcpy(&(word[13][0]),"var");
strcpy(&(word[14][0]),"while");
strcpy(&(word[15][0]),"write");
//wsym内放有保留字所对应的枚举变量的值
wsym[0]=beginsym;
wsym[1]=callsym;
wsym[2]=charactersym;
wsym[3]=constsym;
wsym[4]=dosym;
wsym[5]=doubsym;
wsym[6]=endsym;
wsym[7]=ifsym;
wsym[8]=integersym;
wsym[9]=oddsym;
wsym[10]=procsym;
wsym[11]=readsym;
wsym[12]=thensym;
wsym[13]=varsym;
wsym[14]=whilesym;
wsym[15]=writesym;
//mnemonic中存放的是模拟程序的命令符
//大小是个,因为只有中命令符
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");
//初始化这些数组,全部为false
for(i=0;i{
declbegsys[i]=false;
statbegsys[i]=false;
facbegsys[i]=false;
}
//下面的这三块内容的功能在后续的介绍中会有,
//分别是声明开始符号集,语句开始符号集,因子开始符号集
declbegsys[constsym]=true;
declbegsys[varsym]=true;
declbegsys[procsym]=true;
declbegsys[integersym]=true;
declbegsys[charactersym]=true;
declbegsys[doubsym]=true;
statbegsys[beginsym]=true;
statbegsys[callsym]=true;
statbegsys[ifsym]=true;
statbegsys[whilesym]=true;
facbegsys[ident]=true;
facbegsys[number]=true;
facbegsys[lparen]=true;
facbegsys[charnum]=true;
facbegsys[doubnum]=true;
}
//该函数的功能是返回符号e是否在符号集s中
//是则返回true,否则返回false
intinset(inte,bool*s)
{
returns[e];
}
//对于下标i,如果s1或s2是true,则sr也是true
//这个函数其实是初始化后继符号集,即nxtlev数组
intaddset(bool*sr,bool*s1,bool*s2,intn)
{
inti;
for(i=0;i{
sr[i]=s1[i]||s2[i];
}
return0;
}
//下面的函数不知干什么用,我没找到这两个函数的使用地方,不知道是不是作者弄错啦
intsubset(bool*sr,bool*s1,bool*s2,intn)
{
inti;
for(i=0;i{
sr[i]=s1[i]&&(!
s2[i]);
}
return0;
}
intmulset(bool*sr,bool*s1,bool*s2,intn)
{
inti;
for(i=0;i{
sr[i]=s1[i]&&s2[i];
}
return0;
}
//当出问题的时候,仅仅输出错误,并不退出程序
//对于出错码,可以参考课本页
voiderror(intn)
{
charspace[81];
memset(space,32,81);//将space全部置为空格
space[cc-1]=0;//出错时当前符号已经读完,所以是CC-1
printf("****%s!
%d\n",space,n);
fprintf(fa1,"****%s!
%d\n",space,n);
err++;
}
intgetch()
{
if(cc==ll)
{
if(feof(fin))//fin未知
{
printf("programincomplete"