编译原理-TEST编译器实现

“编译原理,经过对普通编译原理分析,形成了以词典为核心,结合词的数据结构完成编译过程的自成体系技术理论。通过分析词法生成词典(词典发生器),分析语法生成局部代码,并建立数据指针(翻译机),组装局部代码生成目标机器语言流并建立程序调用过的对象、指针等数据流空间,最终完成可执行程序(装配器)三个模糊过程(因为这些过程都离不开词典,编译中间会发生相互影响),把高级计算机语言加工成为计算机机器语言代码流,这就是我们提出的编译原理。”

经过一学期的编译原理学习,对这门课的认识也从一知半解到逐渐理解。学习过程中,做了许多内容相关的实验,如词法分析程序、语法分析程序、语义分析程序,以及TEST虚拟机。这些部分组合成了一个简易的TEST语言编译器。现将代码上传至博客。如果对你有帮助,欢迎点赞。

词法分析:

词法分析是编译器前端设计的基础阶段, 在这一阶段, 编译器会根据设定的语法规则, 对源程序进行标记, 在标记的过程中, 每一处记号都代表着一类单词, 在做记号的过程中, 主要有标识符、关键字、特殊符号等类型, 编译器中包含词法分析器、输入源程序、输出识别记号符, 利用这些功能可以将字号转化为熟悉的单词。

 建议使用DEVC++作为编程工具,可避免不必要的错误。

#include<bits/stdc++.h>   //这是一个万能头文件。不想使用的话可更改为自己需要的
#define keyNum 12
using namespace std;
char keyWord[keyNum][11]={"call","do","else","enum","for","function","if","int","main","read","while","write"};
char singleWord[]="+-*();,{}";//纯单分界符
char doubleWord[]=">=<!";
FILE *Fin,*Fout;//文件指针 
char cifa_in[300];//输入文件名
char cifa_out[300];
int init();//初始化
int scan();//输入

int isSaved(char *s){//是否为保留字 (二分) 
	int l=0,r=keyNum;
	while(l<r){
		int mid=(l+r)/2;
		if(strcasecmp(s,keyWord[mid])==0) return 1;
		if(strcasecmp(s,keyWord[mid])>0) l=mid+1;
		else r=mid;
	}
	return 0;
}

int init(){
	cout<<"输入文件名称:"<<endl;
	cin>>cifa_in; 
	strcat(cifa_in,".txt");
	if((Fin=fopen(cifa_in,"r"))==NULL){
		cout<<"打开输入文件出错"<<endl;
		return 0;
	}
	strcpy(cifa_out,"out_cifa_");
	strcat(cifa_out,cifa_in);
	if((Fout=fopen(cifa_out,"w"))==NULL){
		cout<<"创建词法输出文件出错"<<endl;
		return 0;
	}
	return 1;
}

int scan(){//词法分析 
	char ch,buff[40];
	int es=1,j;
	int cnt=1;//用于记录当前行号 
	ch=getc(Fin);
	while(ch!=EOF){
		while(ch==' '||ch=='\t'||ch=='\n'){
			if(ch=='\n') cnt++;
			ch=getc(Fin);
		} 
		if(ch==EOF) break;
		if(isalpha(ch))
		{//如果是标识符 
		
		
			buff[0]=ch;
			j=1;
			ch=getc(Fin);
			while(isalnum(ch))
			{
				buff[j++]=ch;
				ch=getc(Fin);
			}
			buff[j]='\0';
			int num;
			
			if(isSaved(buff))
			 fprintf(Fout,"%s\t%s\n",buff,buff);
			else  fprintf(Fout,"%s\t%s\n","ID",buff);
		}
		else if(isdigit(ch)){//如果是数字 
			buff[0]=ch;ch=getc(Fin);j=1;
			while(isdigit(ch)){
				buff[j++]=ch;
				ch=getc(Fin);
			}
			buff[j]='\0';
			fprintf(Fout,"%s\t%s\n","NUM",buff);
		}
		else if(strchr(singleWord,ch)>0){//单分界符 
			buff[0]=ch;
			buff[1]='\0';
			ch=getc(Fin);
			fprintf(Fout,"%s\t%s\n",buff,buff);
		}
		
		else if(strchr(doubleWord,ch)>0)
		{//双 
			 buff[0]=ch; ch=getc(Fin);
			 if(ch=='='){
			 	buff[1]=ch;buff[2]='\0';
			 	ch=getc(Fin);
			 }
			 else buff[1]='\0';
			 
			 fprintf(Fout,"%s\t%s\n",buff,buff);
		}
		else if(ch=='/'){
			ch=getc(Fin);
			if(ch=='*'){
				char ch1;
				ch1=getc(Fin);
				do{
					ch=ch1;ch1=getc(Fin);
				}while((ch!='*'||ch1!='/')&&ch1!=EOF);
				
				ch=getc(Fin);
			}
			else{	
				buff[0]='/';buff[1]='\0';
				fprintf(Fout,"%s\t%s\n",buff,buff);
			}
		}
		else if(ch=='&'){
			buff[0]=ch;
			ch=getc(Fin);
			if(ch=='&'){
				buff[1]=ch;buff[2]='\0';
				fprintf(Fout,"%s\t%s\n",buff,buff);
				ch=getc(Fin);
			}
			else{
				buff[1]='\0';
				ch=getc(Fin);
				es=0;
				fprintf(Fout,"%s\t%d\t%s\n","ERROR at line",cnt,buff);
			} 
		}
		else if(ch=='|'){
			buff[0]=ch;
			ch=getc(Fin);
			if(ch=='|'){
				buff[1]=ch;buff[2]='\0';
				fprintf(Fout,"%s\t%s\n",buff,buff);
				ch=getc(Fin);
			}
			else{
				buff[1]='\0';
				ch=getc(Fin);
				es=0;
				fprintf(Fout,"%s\t%d\t%s\n","ERROR at line ",cnt,buff);
			} 
		}
		else{
			buff[0]=ch;buff[1]='\0';
			ch=getc(Fin);
			es=0;
			fprintf(Fout,"%s\t%d\t%s\n","ERROR at line ",cnt,buff);
		}
	}
	fclose(Fin);
	fclose(Fout);
	return es;
}

语法分析:

语法分析是指利用设定的语法规则, 对记号中的结构进行标识, 这包括句子、短语等方式, 在标识的过程中, 可以形成特殊的结构语法树。语法分析对编译器功能的发挥有着重要影响, 在设计的过程中, 一定要保证标识的准确性。

语法分析主要是以单词串形式的源程序作为分析与输入对象, 其最为根本的目标和任务就是为了以设计语言的语法规则为标准, 对源程序的语法结构进行具体的分析, 根据设计语言的语法规则, 对组成这些源程序的语法成分进行分析, 如函数、下标变量、各种程序语句、各种表达式等等, 并且要通过正确性的语法检查, 将中间代码进行阶段处理。但是要注意的一点是根据需要进行了归约处理, 必然存在着相应语法错误, 那么可以将其中全部输入的符号删除, 改变上述格局, 进行移进和归约分析, 并且在此基础上, 不断地寻找一个相应的策略, 从而形成有效的语法分析方法 

 语义分析,生成中间代码:

语义分析也需要借助语法规则, 在对语法单元的静态语义进行检查时, 要保证语法规则设定的准确性。在对词法或者语法进行转化时, 一定要保证语法结构设置的合法性。在对语法、词法进行检查时, 语法结构设定不合理, 则会出现编译错误的问题。前端设计对精确性要求比较好, 设计人员能够要做好校对工作, 这会影响到编译的准确性, 如果前端设计存在失误, 则会影响C语言编译的效果。

将编译程序转换为一种内部表现形式后, 我们将该种内部表现形式称之为中间语言或者是中间代码, 它含义明确、结构简单, 属于一种记号系统。比如一些编译程序, 基本上没有中间代码, 但是为了在实际应用中, 确保机器的独立运行, 易于实现目标代码的优化, 在许多的编译程序中均设置了中间语言。这种中间语言介于机器语言和源程序语言中, 程序相对复杂, 而C语言编译器却在很大程度上改变以上现状, 其语义分析和语法分析相对成熟, 理论和算法比较完善, 可仍旧存在的问题是没有公认的形式系统, 中间代码仍旧接近于形式化的方法。

同教材一样,我将语法以及语义分析中间代码生成写成一体,方便修改编辑。

代码如下(需要以头文件的形式包含前面的词法分析程序):

#include<bits/stdc++.h>
#include"词法分析.h"

using namespace std;
const int maxvartablep=500;
char token[20],token1[40];
char yuyi_in[300];//词法分析文件名
char yuyi_out[300];
FILE *fin,*fout;
int program();
int declaration_list();
int declaration_stat();
int statement_list();
int statement();
int if_stat();
int while_stat();
int for_stat();
int do_while_stat();
int read_stat();
int write_stat();
int compound_stat();
int expression_stat();
int expression();
int bool_expr();
int additive_expr();
int term();
int factor();
int fun_declaration();//
int fun_body();//
int main_declaration();//
int call_stat();//
int name_def(char *name,int isenum);


char enum_name[15];				//存enum类型的枚举名 
int isenum=0;
//连续查错 
int line = 3;  //定义行数,用于错误处理 从2开始,一般main和{分行写 2指向{ 
int errorFlag=0; //errorFlag表示是否检测到"可连续查错的"错误  为0表示没有,为1表示有 
char errors[50][50];  //errors数组用来储存编译过程中出现的错误
char errorNum = 0; //用来储存错误序号
//sprintf(errors[errorNum], "第%d行:   未定义的符号", line);
//			errorNum++;



struct{
	char name[8];
	int address;
	int xisenum; 
} vartable[maxvartablep],enum_chart[maxvartablep];
int vartablep=0,labelp=0,datap=0;
int e_vartablep=0,e_labelp=0;		//用来插入enum符号表 

//把枚举值存入enum符号表 
int insert_enum(char *name)
{
	int i,es=0;
	if(e_vartablep>=maxvartablep) return(26);
	for(i=e_vartablep-1;i>=0;i--)			
	{				
		if(strcmp(enum_chart[i].name,name)==0)
		{			
			es=25;						//枚举值重复	 
			break;	
		}	
	}	

	if(es>0) return(es);
	strcpy(enum_chart[e_vartablep].name,name);
	e_vartablep++;
	return(es);
} 

//插入符号表动作 
int name_def(char*name,int isenum)		
{
	
	int i,es=0;
	if(vartablep>=maxvartablep) return(21);
	for(i=vartablep-1;i>=0;i--)			
	{
		if(strcmp(vartable[i].name,name)==0)
		{
			es=22;	
			break;	
		}	
	}	

	if(es>0) return(es);
	strcpy(vartable[vartablep].name,name);
	vartable[vartablep].address=datap;
	vartable[vartablep].xisenum=isenum;
	datap++;
	vartablep++;
	return(es);
}

//查枚举变量赋值是否在enum_chart中 
int lookenum(char *name)
{
	int i,es=0;
	for(i=0;i<e_vartablep;i++)
	{
		if(strcmp(enum_chart[i].name,name)==0)
		{
			fprintf(fout,"		LOADI %s\n",name);	
			return(es); 
		} 
	}
	es=30;									//值不在枚举值中 
	return(es);
}

int lookup(char* name,int* paddress){
    int i,es=0;
    for(i=0;i<vartablep;i++){
        if(strcmp(vartable[i].name,name)==0){
            *paddress=vartable[i].address;
            return es;
        }
    }
    return es=23;
}

int TESTparse(){
	int es=0;
	strcpy(yuyi_in,cifa_out);
	if((fin=fopen(yuyi_in,"r"))==NULL){
		printf("打开%s文件错误\n",yuyi_in);
		es=10;
	}
	strcpy(yuyi_out,"out_yuyi_");
	strcat(yuyi_out,cifa_in);
	if((fout=fopen(yuyi_out,"w"))==NULL){
		printf("打开yuyi_out文件错误\n");
		es=10;
	}
	if(es==0) es=program();
	printf("=====语法分析结果:======\n");
	switch(es){
		case 0:printf("语法分析成功\n");break;
		case 10:cout<<"打开输入文件失败"<<endl;break;
		case 1:cout<<"缺少{!"<<endl;break;
		case 2:cout<<"缺少}!"<<endl;break;
		case 3:cout<<"缺少标识符!"<<endl;break;
		//case 4:cout<<"缺少分号!"<<endl;break;
		case 5:cout<<"缺少(!" <<endl;break;
		case 6:cout<<"缺少)!"<<endl;break;
		case 7:cout<<"缺少操作数!"<<endl;break;
		case 9:cout<<"缺少main函数"<<endl;break;
		case 11:cout<<"非法声明,缺少fun/main"<<endl;break;
		case 21:cout<<"符号表溢出"<<endl;break;
		case 22:cout<<"重复定义"<<endl;break;
		case 23:cout<<"变量未声明"<<endl;break;
		case 24:printf("do后缺少while!\n"); break;
		case 25: printf("枚举值重复!\n"); break;
		case 26: printf("枚举值个数溢出!\n"); break;
		case 27: printf("不允许给枚举值赋值!\n"); break;
		case 30: printf("给枚举变量赋的值只能为%s枚举值中的一个!\n",enum_name); break;  
		default: cout<<es<<endl;
	}
	return es;
}

int program(){
	int es=0;
	fscanf(fin,"%s %s\n",token,token1);
	if(strcmp(token,"function")&&strcmp(token,"main")){
		return es=11;
	}
	while(strcmp(token,"function")==0){

		es=fun_declaration();
		if(es>0) return es;
	}
	if(strcmp(token,"main")){
		return es=9;
	}
	es=main_declaration();
	fprintf(fout,"  	STOP\n");
	if(es>0) return es;
 	printf("         符号表\n");
    printf("         名字         地址\n");
    for(int i=0;i<vartablep;i++)
    {
    	printf("           %s           %d\n",vartable[i].name,vartable[i].address);
	}
	return es;
}
int fun_declaration(){
	int es=0;
	fscanf(fin,"%s %s\n",token,token1);
	if(strcmp(token,"ID")) return es=3;
	es=name_def(token1,0);
	fscanf(fin,"%s %s\n",token,token1);
	if(strcmp(token,"(")) return es=5;
	fscanf(fin,"%s %s\n",token,token1);
	if(strcmp(token,")")) return es=5;
	es=fun_body();
	if(es>0) return es;

	return es;
}
int main_declaration(){
	int es=0;
	fscanf(fin,"%s %s\n",token,token1);
	if(strcmp(token,"(")) return es=5;
	fscanf(fin,"%s %s\n",token,token1);
	if(strcmp(token,")")) return es=6;
	es=fun_body();
	if(es>0) return es;
	fscanf(fin,"%s %s\n",token,token1);

	return es;

}
int fun_body(){
	int es=0;
	fscanf(fin,"%s %s\n",token,token1);
	if(strcmp(token,"{")) return es=1;
	fscanf(fin,"%s %s\n",token,token1);
	es=declaration_list();
	if(es>0) return es;
	es=statement_list();
	if(es>0) return es;
	if(strcmp(token,"}")) return es=2;
	fscanf(fin,"%s %s\n",token,token1);
	return es;
}
int declaration_list(){
	int es=0;
	while(strcmp(token,"int")==0||strcmp(token,"enum")==0){
		es=declaration_stat();
		if(es>0) return es;
	}
	return es;
}
int declaration_stat(){
	int es=0;
	if(strcmp(token,"int")==0)		//是int 
	{
		fscanf(fin,"%s %s\n",token,token1);
		if(strcmp(token,"ID")) return es=3;
		es=name_def(token1,0);
		if(es>0) return es;
		fscanf(fin,"%s %s\n",token,token1);
		
		if(strcmp(token,";")){ //读到不是;的符号时,写入错误文件,再继续向下取   减少一步fscanf
			errorFlag=1; //置全局错误标志为1 
			sprintf(errors[errorNum], "第%d行:   语句末尾缺少分号", line);
			line++;
			errorNum++;
		}	
		if(!strcmp(token,";")){  //读到;了,正常继续fscanf
		fscanf(fin,"%s %s\n",token,token1);
		line++;
		}		
		return es;
	}
	
	if(strcmp(token,"enum")==0)
    {
    	fscanf(fin, "%s %s\n", token, token1);				//识别枚举名 存入enum_name数组 
	    printf("%s %s\n",token,token1);
	    strcpy(enum_name,token1);
	    
	    fscanf(fin, "%s %s\n", token, token1);				//识别枚举名后面的枚举值 
	    printf("%s %s\n",token,token1);
	    
		if (strcmp(token,"{")) {
	    return (es=4);
	    }
	    
	    fscanf(fin, "%s %s\n", token, token1);	
	    printf("%s %s\n",token,token1);
	    while(strcmp(token,"}"))							//枚举值存入符号表 
	    {
	    	if((strcmp(token,",")==0))
	    	{
	    		fscanf(fin, "%s %s\n", token, token1);	 
	    		printf("%s %s\n",token,token1);
			}
			else
			{
	    		es=insert_enum(token1);					//扫描进枚举符号表
	    		if(es>0) return(es);
	    		fscanf(fin, "%s %s\n", token, token1);	 
	    		printf("%s %s\n",token,token1);
			}
		}
		fscanf(fin, "%s %s\n", token, token1);	
	    printf("%s %s\n",token,token1);
	    
	    while(strcmp(token,";"))
	    {
	    	if((strcmp(token,",")==0))
	    	{
	    		fscanf(fin, "%s %s\n", token, token1);	 
	    		printf("%s %s\n",token,token1);
			}
			else
			{
	    		es=name_def(token1,1);//扫描进符号表并标识xisenum=1 
	    		if(es>0) return(es);
	    		fscanf(fin, "%s %s\n", token, token1);	 
	    		printf("%s %s\n",token,token1);
			}
		}
		fscanf(fin, "%s %s\n", token, token1);
		printf("%s %s\n",token,token1);
		return(es);
}
}
int statement_list(){
	int es=0;
	while(strcmp(token,"}")){
		es=statement();
		if(es>0) return es;
	}
	return es;
}
int statement(){
	int es=0;
	if(es==0&&strcmp(token,"if")==0) es=if_stat();
	if(es==0&&strcmp(token,"while")==0) es=while_stat();
	if(es==0&&strcmp(token,"for")==0) es=for_stat();
	if(es==0&&strcmp(token,"read")==0) es=read_stat();
	if(es==0&&strcmp(token,"write")==0) es=write_stat();
	if(es==0&&strcmp(token,"{")==0) es=compound_stat();
	if(es==0&&strcmp(token,"call")==0) es=call_stat();
	if(es==0&&(strcmp(token,"ID")==0||strcmp(token,"NUM")==0||strcmp(token,"(")==0)) es=expression_stat();
	if(es == 0 && strcmp(token, "do") == 0) es = do_while_stat(); //<do_while语句>

	return es;
}
int if_stat(){
	int es=0,label1,label2;
	fscanf(fin,"%s %s\n",token,token1);
	if(strcmp(token,"(")) return es=5;
	fscanf(fin,"%s %s\n",token,token1);
	es=expression();
	if(es>0) return es;
	if(strcmp(token,")")) return es=6;
	label1=labelp++;
	fprintf(fout,"		BRF LABEL%d\n",label1);
	fscanf(fin,"%s %s\n",token,token1);
	es=statement();
	if(es>0) return es;
	label2=labelp++;
	fprintf(fout,"		BR LABEL%d\n",label2);
	fprintf(fout,"LABEL%d:\n",label1);
	if(strcmp(token,"else")==0){
		fscanf(fin,"%s %s\n",token,token1);
		es=statement();
		if(es>0) return es;
	}
	fprintf(fout,"LABEL%d:\n",label2);
	return es;
}

int do_while_stat(){
	int es = 0,label1,label2;
	label1=labelp++;  //BR
	 
	fscanf(fin,"%s %s\n",token,token1);			
	if(strcmp(token,"{")) //少{ 
		return(es=1);
	//设置LABEL1:
	line++;
	fprintf(fout,"LABEL%d:\n",label1); 
	
	fscanf(fin,"%s %s\n",token,token1);	
	es=statement(); 	// statement语句,do内部 	
	if(strcmp(token,"}")) //少} 
		return(es=2);	  //do的执行范围是从第一句一直到}左边为止 
		
	fscanf(fin,"%s %s\n",token,token1);
	if(strcmp(token,"while"))  //少while 
		return(es=24);
		
	fscanf(fin,"%s %s\n",token,token1);
	if(strcmp(token,"("))  //少( 
	return(es=5);
	
	fscanf(fin,"%s %s\n",token,token1); //while()括号内表达式 expression语句 
	es=expression();
	if(es>0) return(es);	
	if(strcmp(token,")"))  //少) 
	return(es=6); 
	label2=labelp++; 	//存BRF 
		
	fscanf(fin,"%s %s\n",token,token1);
	if(strcmp(token,";")){ //读到不是;的符号时,写入错误文件,再继续向下取   减少一步fscanf
		errorFlag=1; //置全局错误标志为1 
		sprintf(errors[errorNum], "第%d行:   语句末尾缺少分号", line);
		line++;
		errorNum++;
	}	

	//设置LABEL2:
	fprintf(fout,"		BRF LABEL%d:\n",label2);	
	fprintf(fout,"		BR LABEL%d:\n",label1);	 
	fprintf(fout,"LABEL%d:\n",label2);
		 					
	if(!strcmp(token,";")){  //读到;了,正常继续fscanf
	fscanf(fin,"%s %s\n",token,token1);
	line++;
	}  //少; 
	
	return(es);
}
int while_stat()
{
	int es=0,label1,label2;
	label1=labelp++;
	fprintf(fout,"LABEL%d:\n",label1);
	fscanf(fin,"%s %s\n",token,token1);

	if(strcmp(token,"(")) return es=5;
	fscanf(fin,"%s %s\n",token,token1);
	es=expression();
	if(es>0) return es;
	if(strcmp(token,")")) return es=6;
	label2=labelp++;
	fprintf(fout,"		BRF LABEL%d\n",label2);
	fscanf(fin,"%s %s\n",token,token1);
	es=statement();
	if(es>0)    return es;
	fprintf(fout,"		BR LABEL%d\n",label1);
	fprintf(fout,"LABEL%d:\n",label2);
	return es;
}
int for_stat(){
	int es=0,label1,label2,label3,label4;
	fscanf(fin,"%s %s\n",token,token1);
	if(strcmp(token,"(")) return es=5;
	fscanf(fin,"%s %s\n",token,token1);
	es=expression();
	if(es>0) return es;
	fprintf(fout,"		POP\n");
		if(strcmp(token,";")){    //少分号 
		errorFlag=1; //置全局错误标志为1 
		sprintf(errors[errorNum], "第%d行:   语句末尾缺少分号", line);
		line++;
		errorNum++;
	}
	label1=labelp++;
	fprintf(fout,"LABEL%d:\n",label1);
		
	if(!strcmp(token,";")){  //读到;了,正常继续fscanf
	fscanf(fin,"%s %s\n",token,token1);
	line++;
	}  
	
	es=expression();
	if(es>0) return es;
	label2=labelp++;
	fprintf(fout,"		BRF LABEL%d\n",label2);
	label3=labelp++;
	fprintf(fout,"		BR LABEL%d\n",label3);
	if(strcmp(token,";")){   //少分号 
		errorFlag=1; //置全局错误标志为1 
		sprintf(errors[errorNum], "第%d行:   语句末尾缺少分号", line);
			errorNum++;
			line++;
	}
	label4=labelp++;
	fprintf(fout,"LABEL%d:\n",label4);
	
	if(!strcmp(token,";")){  //读到;了,正常继续fscanf
	fscanf(fin,"%s %s\n",token,token1);
	line++;
	}   
	
	es=expression();
	if(es>0) return es;
	fprintf(fout,"		POP\n");
	fprintf(fout,"		BR LABEL%d\n",label1);
	if(strcmp(token,")")) return es=6;
	fprintf(fout,"LABEL%d:\n",label3);
	fscanf(fin,"%s %s\n",token,token1);
	es=statement();
	if(es>0)    return es;
	fprintf(fout,"		BR LABEL%d\n",label4);
	fprintf(fout,"LABEL%d:\n",label2);
	return es;
}
int write_stat(){
	int es=0;
	fscanf(fin,"%s %s\n",token,token1);
	es=expression();
	if(es>0) return es;
		if(strcmp(token,";")){ //少分号 
		errorFlag=1; //置全局错误标志为1  
		sprintf(errors[errorNum], "第%d行:   语句末尾缺少分号", line);
			errorNum++;
			line++;
	}
	fprintf(fout,"  	OUT\n");
	
	if(!strcmp(token,";")){  //读到;了,正常继续fscanf
	fscanf(fin,"%s %s\n",token,token1);
	line++;
	}   
	
	return es;
}
int read_stat(){
	int address;
	int es=0;
	fscanf(fin,"%s %s\n",token,token1);
	if(strcmp(token,"ID")) return es=3;
	es=lookup(token1,&address);
	if(es>0) return es;
	fprintf(fout,"  IN  \n");
	fprintf(fout,"  STO %d\n",address);
	fprintf(fout,"  POP\n");

	fscanf(fin,"%s %s\n",token,token1);
		if(strcmp(token,";")){  //少分号 
		errorFlag=1; //置全局错误标志为1 
		sprintf(errors[errorNum], "第%d行:   语句末尾缺少分号", line);
			errorNum++;
			line++;
	}
	if(!strcmp(token,";")){  //读到;了,正常继续fscanf
	fscanf(fin,"%s %s\n",token,token1);
	line++;
	} 
	
	return es;
}
int compound_stat(){
	int es=0;
	fscanf(fin,"%s %s\n",token,token1);
	es=statement_list();
	if(strcmp(token,"}")) return es=2;
	fscanf(fin,"%s %s\n",token,token1);
	return es;
}
int expression_stat(){
	int es=0;
	if(strcmp(token,";")==0){   //有; 
		fscanf(fin,"%s %s\n",token,token1);
		return es;
	}
	es=expression();
	if(es>0) return es;
	fprintf(fout,"  	POP\n");
	if(es==0&&strcmp(token,";")==0){
		fscanf(fin,"%s %s\n",token,token1);
		line++;
		return es;
	}
	if(es==0&&(strcmp(token,";")!=0)){  //没取到; 
		errorFlag=1; //置全局错误标志为1 
		sprintf(errors[errorNum], "第%d行:   语句末尾缺少分号", line);
		line++;
		errorNum++;
		return es;
	}
}
int call_stat(){
	int es=0;
	fscanf(fin,"%s %s\n",token,token1);
	if(strcmp(token,"ID")) return es=3;
	fscanf(fin,"%s %s\n",token,token1);
	if(strcmp(token,"(")) return es=5;
	
	fscanf(fin,"%s %s\n",token,token1);
	if(strcmp(token,")")) return es=6;
	
	fscanf(fin,"%s %s\n",token,token1);

	return es;
}
int expression(){
	int es=0,fileadd;
	char token2[20],token3[40];
	if(strcmp(token,"ID")==0){
		for(int i=0;i<e_vartablep;i++)				//遍历枚举值符号表,看是不是在给枚举值赋值,是的话返回错误信息,退出 
		{	
			if(strcmp(token1,enum_chart[i].name)==0)
			{
				return (es=27);
			}
		}
		
		fileadd=ftell(fin);
		fscanf(fin,"%s %s\n",token2,token3);
		if(strcmp(token2,"=")==0){
            int address;
            es=lookup(token1,&address);
            if(es>0) return es;
            
			if(vartable[address].xisenum==1)		//看前面的ID是普通变量还是enum变量
				{
					fscanf(fin, "%s %s\n", token, token1);  
	    			printf("%s %s\n",token,token1);
	    			
					es=lookenum(token1);
	    			if(es>0)
	    				return(es);
	    			fprintf(fout,"		STO %d\n",address);	
	    			fscanf(fin, "%s %s\n", token, token1);  
					printf("%s %s\n",token,token1);
				}
				else
				{
					fscanf(fin, "%s %s\n", token, token1);  
		    		printf("%s %s\n",token,token1);
		    		
					es=bool_expr();
			    	if(es>0) 
			        	return (es);
			        fprintf(fout,"		STO %d\n",address);
				}
		}
		else{
			fseek(fin,fileadd,0);
			es=bool_expr();
			if(es>0) return es;
		}
	}
	else es=bool_expr();
	return es;
}
int bool_expr(){
	int es=0;
	es=additive_expr();
	if(es>0) return es;
	if(strcmp(token,">")==0||strcmp(token,">=")==0||strcmp(token,"<")==0||strcmp(token,"<=")==0||strcmp(token,"==")==0||strcmp(token,"!=")==0){
			char token2[20];
			strcpy(token2,token);
			fscanf(fin,"%s %s\n",token,token1);
			es=additive_expr();
			if(es>0) return es;
			if(strcmp(token2,">")==0)fprintf(fout," 	GT\n");
			if(strcmp(token2,">=")==0)fprintf(fout," 	GE\n");
			if(strcmp(token2,"<")==0)fprintf(fout," 	LES\n");
			if(strcmp(token2,"<=")==0)fprintf(fout," 	LE\n");
			if(strcmp(token2,"==")==0)fprintf(fout," 	EQ\n");
			if(strcmp(token2,"!=")==0)fprintf(fout," 	NOTEQ\n");
	}
	return es;
}
int additive_expr(){
	int es=0;
	es=term();
	if(es>0) return es;
	while(strcmp(token,"+")==0||strcmp(token,"-")==0){
		char token2[20];
		strcpy(token2,token); 
		fscanf(fin,"%s %s",token,token1);
//		printf("%s %s\n",token,token1);
		es=term();
		if(es>0) return es;
		if(strcmp(token2,"+")==0) fprintf(fout,"	a	ADD\n");
		if(strcmp(token2,"0")==0) fprintf(fout,"		SUB\n");
	}
	return es;
}
int term(){
	int es=0;
	es=factor();
	if(es>0) return es;
	while(strcmp(token,"*")==0||strcmp(token,"/")==0){
		char token2[20];
		strcpy(token2,token);
		fscanf(fin,"%s %s\n",token,token1);
		es=factor();
		if(es>0) return es;
		if(strcmp(token2,"*")==0) fprintf(fout,"		MULT\n");
		if(strcmp(token2,"/")==0) fprintf(fout,"		DIV\n");
	}
	return es;
}
int factor(){
	int es=0;

	if(strcmp(token,"(")==0){
		fscanf(fin,"%s %s\n",token,token1);
		es=expression();
		if(es>0) return es;
		if(strcmp(token,")")) return es=6;
		fscanf(fin,"%s %s\n",token,token1);
	}
	else{

		if(strcmp(token,"ID")==0){
			int address;
			es=lookup(token1,&address);
			if(es>0) return es;
			fprintf(fout,"		LOAD %d\n",address);
			fscanf(fin,"%s %s\n",token,token1);
			return es;
		}
		if(strcmp(token,"NUM")==0){
			fprintf(fout,"		LOADI %s\n",token1);
			fscanf(fin,"%s %s\n",&token,&token1);
			return es;
		}
		else{
			es=7;
			return es;
		}
	}
	return es;
}

int main()
{
	if(!init()){cout<<"词法分析错误"<<endl;return 0;}
	int flag=scan();
	if(flag) cout<<"词法分析成功"<<endl;
	else cout<<"词法分析失败"<<endl;

	int es=0;
	if(flag)  es=TESTparse();
	if(es==0&&flag){
		printf("语义分析成功\n");
	}
	else if(flag&&es) printf("语法分析失败\n");
		if (errorFlag!=0)	{
	printf("有遗漏性错误\n"); 
	printf("\n错误数量:  %d \n", errorNum);
	for (int i = 0; i < errorNum; i++)
		printf("%s\n", errors[i]);
}

	fclose(fin);
	fclose(fout);
	return 0;
 }

TEST虚拟机:

#include<bits/stdc++.h>
int TESTmachine(){
	int es=0,i=0,k=0,codecount,stack[1000],stacktop=0;
	int data[1000];
	char Codein[300],code[1000][20];
	int label[100]={0};
	char lno[4];
	FILE* fin;
	printf("输入文件名:");
	scanf("%s",Codein);
	strcat(Codein,".txt");
	char s[300]="out_yuyi_";
	strcat(s,Codein);strcpy(Codein,s);
	if((fin=fopen(Codein,"r"))==NULL){
		printf("打开%s文件出错\n",Codein);
		es=10;
		return 10;
	}
	codecount=0;
	i=fscanf(fin,"%s",&code[codecount]);
	while(!feof(fin)){
		i=strlen(code[codecount])-1;
		if(code[codecount][i]==':'){
			i=i-5;
			strncpy(lno,&code[codecount][5],i);
			lno[i]='\0';
			label[atoi(lno)]=codecount;
//			printf("label[%d]=%d\n",atoi(lno),label[atoi(lno)]);
			code[codecount][0]=':';
			code[codecount][1]='\0';
			strcat(code[codecount],lno);
			k++;
		}
		codecount++;
		i=fscanf(fin,"%s",&code[codecount]);
	}
	fclose(fin);
//	for(i=0;i<codecount;i++) printf("code[%d]  %s\n",i,code[i]);
	for(i=0;i<codecount;i++){
		int l;
		l=strlen(code[i]);
		
		if((l>1)&&(code[i][1]=='A')){
			strncpy(lno,&code[i][5],l-5);
			lno[l-5]='\0';
			itoa(label[atoi(lno)],code[i],10);
		}
	}	
//	for(i=0;i<codecount;i++) printf("code[%d]  %s\n",i,code[i]);
	i=0;
	while(i<codecount){
//		printf("code %d   %s\n",i,code[i]);
		if(strcmp(code[i],"LOAD")==0){
			i++;
			stack[stacktop]=data[atoi(code[i])];
			stacktop++;
		}
		if(strcmp(code[i],"LOADI")==0){
			i++;
			stack[stacktop++]=atoi(code[i]);	
		}
		if(strcmp(code[i],"STO")==0){
			i++;
			data[atoi(code[i])]=stack[stacktop-1];
		}
		if(strcmp(code[i],"POP")==0){
			stacktop--;
		}
		if(strcmp(code[i],"ADD")==0){
			stack[stacktop-2]=stack[stacktop-2]+stack[stacktop-1];
			stacktop--;
		}
		if(strcmp(code[i],"SUB")==0){
			stack[stacktop-2]=stack[stacktop-2]-stack[stacktop-1];
			stacktop--;	
		}
		if(strcmp(code[i],"MULT")==0){
			stack[stacktop-2]=stack[stacktop-2]*stack[stacktop-1];
			stacktop--;	
		}
		if(strcmp(code[i],"DIV")==0){
			stack[stacktop-2]=stack[stacktop-2]/stack[stacktop-1];
			stacktop--;	
		}
		if(strcmp(code[i],"BR")==0){
			i++;
			i=atoi(code[i]);
		}
		if(strcmp(code[i],"BRF")==0){
			i++;
			if(stack[stacktop-1]==0) i=atoi(code[i]);
			stacktop--;
		}
		if(strcmp(code[i],"EQ")==0){
			stack[stacktop-2]=stack[stacktop-2]==stack[stacktop-1];
			stacktop--;
		}
		if(strcmp(code[i],"NOTEQ")==0){
			stack[stacktop-2]=stack[stacktop-2]!=stack[stacktop-1];
			stacktop--;
		}
		if(strcmp(code[i],"GT")==0){
			stack[stacktop-2]=stack[stacktop-2]>stack[stacktop-1];
			stacktop--;
		}
		if(strcmp(code[i],"LES")==0){
			stack[stacktop-2]=stack[stacktop-2]<stack[stacktop-1];
			stacktop--;
		}
		if(strcmp(code[i],"GE")==0){
			stack[stacktop-2]=stack[stacktop-2]>=stack[stacktop-1];
			stacktop--;
		}
		if(strcmp(code[i],"LE")==0){
			stack[stacktop-2]=stack[stacktop-2]<=stack[stacktop-1];
			stacktop--;
		}
		if(strcmp(code[i],"AND")==0){
			stack[stacktop-2]=stack[stacktop-2]&&stack[stacktop-1];
			stacktop--;
		}
		if(strcmp(code[i],"OR")==0){
			stack[stacktop-2]=stack[stacktop-2]||stack[stacktop-1];
			stacktop--;
		}
		if(strcmp(code[i],"NOT")==0){
			stack[stacktop-1]=!stack[stacktop-1];
			stacktop--;
		}
		if(strcmp(code[i],"IN")==0){
			printf("输入:");
			scanf("%d",&stack[stacktop]);
			stacktop++; 
		}
		if(strcmp(code[i],"OUT")==0){
			printf("程序输出##%d\n",stack[stacktop-1]);
			stacktop--;
		}
		if(strcmp(code[i],"STOP")==0){
			break;
		}
		i++;
	}
	return es;
} 

int main()
{
	int es=TESTmachine();
	return 0;
}

结语:编译原理这门课对科班学生还是很重要的。知道了编译器是如何实现的,当自己的代码出问题后可以更好的定位问题,解决问题。同时,学习这门课不建议单单为了考试而背课件,而是老老实实做实验。虽然过程很艰难,但是收获真的很大。

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值