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

📄 compiler.cpp

📁 PL0的一个编译器
💻 CPP
📖 第 1 页 / 共 4 页
字号:
				{
					getsym();					//获取下一个token,这里正好应该是标识符
					constdeclaration();			//声明以当前token为标识符的常量
				}

				if(strcmp(sym,"semicolon")==0)	//如果常量声明结束,应最到分号
					getsym();					//获取下一个token
				else
					error(5);					//否则抛出5号错误
			}while(strcmp(sym,"ident")==0);
		}

		if(strcmp(sym,"varsym")==0)
		{
			getsym();						//获取下一个token,此处正常应为用作变量名的一个标识符
			do
			{
				vardeclaration();			//以当前token为标识符声明一个变量
				while(strcmp(sym,"comma")==0)//如果最到的逗号,则反复声明下一个变量
				{
					getsym();				//获取下一个token
					vardeclaration();		//声明以当前token为标识符的变量
				}
				if(strcmp(sym,"semicolon")==0)//如果变量声明结束,应遇到分号
					getsym();				//获取下一个token,为下一轮循环做好准备
				else
					error(5);				//否则抛出5号错误
			}while(strcmp(sym,"ident")==0);
		}
	
		while(strcmp(sym,"procsym")==0)		//循环声明各子过程
		{
			getsym();						//获取下一个token,此处正常应为作业过程名的标识符
			if(strcmp(sym,"ident")==0)		//如果token确为标识符
			{
				enter(procedur);			//把这个过程登录到名字表中
				getsym();					//获取下一个token,正常情况应为分号
			}
			else 
				error(4);					//如果procsym后不是标识符,抛出4号错误
			if(strcmp(sym,"semicolon")==0)	//如果当前token是分号,获取下一个token
				getsym();						
			else
				error(5);					//否则抛出5号错误

			block(plev+1,add(temp3,fsys));  //递归调用语法分析过程,当前层次加一,同时传递表头索引,合法单词
			lev=lev-1;
			if(strcmp(sym,"semicolon")==0)
			{
				getsym();
				test(add(statbegsys,temp2),fsys,6);//检查当前token是否合法,不合法则用fsys恢复语法分析,同时抛出6号错误
			}
			else
				error(5);					//如果过程声明后的符号不是分号,抛出5号错误
		}
	
		test(add(statbegsys,temp4),declbegsys,7);//检查当前状态是否合汇款单,不合法则且声明开始符号作出错恢复,抛7号错误
	
	}while(in(sym,declbegsys)==1); //直到声明性的源程序分析完毕,继续向下执行,分析主程序
	
	code[table1[tx0].adr].a=cx;	   //把前面生成的跳转语句的跳转位置改成当前位置	
	table1[tx0].adr=cx;			   //地址为当前代码分配地址	
	table1[tx0].size=dx;		   //长度为当前数据代分配位置
	
	gen(ini,0,dx);				   //生成分配空间指令,分配dx个空间
	cx0=cx;						   //记下当代码分配位置
	statement(add(temp1,fsys),plev);//处理当前遇到的语句或语句块
	gen(opr,0,0);					//生成从子程序返回操作指令
	test(fsys,temp5,8);				//用fsys检查当前状态是否合法,不合法则抛出8号错
	listcode(&cx0);					//列出本层的类PCODE代码
	cx0=cx; 
}




//通过静态链求出数据区基地址的函数base
//返回值:要求的数据区基址
int base(int l,int *b,int s[STACKSIZE])
{
	int b1;
	b1=*b;			//首先从当前层开始
	while(l>0)		//如果l大于0,循环通过静态链往前找需要的数据区基址
	{
		b1=s[b1];   //用当前层数据区基址中的内容(正好是静态链SL数据,为上一层的基址)作为新的当前层,即向上找了一层
		l=l-1;		//向上了一层,l减1
	}
	return b1;		//把找到的要求的数据区基址返回
}


void interpret()
{
	int p=0;					//程序指令指针,指向下一条要运行的代码
	int b=1;					//基址指针,指向每个过程被调用时数据区中分配给它的局部变量数据段基址
	int t=0;					//栈顶寄存器,类PCODE,是在一种假想的栈式计算上运行的,这个变量记录这个计算机的当前栈顶位置
	struct instruction i;		//i变量中存放当前在运行的指令
	int s[STACKSIZE];			//s为栈式计算机的数据内存区
	
	cout<<"开始执行PL0文件…………"<<endl<<endl;
	s[0]=0;
	s[1]=0;
	s[2]=0;						//数据内存中为SL,DL,RA三个均为0,标识为主程序
	s[3]=0;						


	do
	{
		i=code[p];				//获取一行目标代码
		p++;					//指令指针加一,指向下一条代码
		switch(i.f)
		{
		case lit:				//如果是lit指令
			t=t+1;				//栈顶指针上移,在栈中分配 一个单元
			s[t]=i.a;			//该单元的内容存入i指令的a操作数,即实现了把常量值放到运行栈栈顶
			break;
		case opr:				//如果是opr指令
			switch(i.a)			//根据a操作数不同,执行不同的操作
			{
			case 0:				//0操作为从子过程返回操作
				t=b-1;			//释放这段子过程占用的数据内存空间
				p=s[t+3];		//把指令指针取到RA的值,指向的是返回地址
				b=s[t+2];		//把数据段基址取到DL的值,指向调用前子过程的数据段基址
				break;
			case 1:				//1号操作为栈顶数据取反操作
				s[t]=-s[t];		//对栈顶数据进行取反
				break;
			case 2:				//2号操作为栈顶两个数据加法操作
				t--;			//栈顶指针下移
				s[t]=s[t]+s[t+1];//把两单元数据相加存入栈顶
				break;
			case 3:				//3号操作为栈顶两个数据减法操作
				t--;			//栈顶指针下移
				s[t]=s[t]-s[t+1];//把两单元数据相减存入栈顶
				break;
			case 4:				//4号操作为栈顶两个数据乘法操作
				t--;	
				s[t]=s[t]*s[t+1];
				break;
			case 5:				//5号操作为栈顶两个数据除法操作
				t--;
				if(s[t+1]==0)
				{
					cout<<"运行时错误,除数不能为0!!"<<endl;
					exit(0);
				}
				else
					s[t]=s[t]/s[t+1];
				break;
			case 6:				//6号操作为奇偶判断操作
				s[t]=s[t]%2;
				break;
			case 8:				//8号操作为栈顶两个数据判等操作
				t--;
				s[t]=(s[t]==s[t+1]);//相等置1,不等置0
					break;
			case 9:				//9号操作为判不等操作
				t--;
				s[t]=(s[t]!=s[t+1]);
				break;
			case 10:			//10号操作为栈顶两个数据判小于操作
				t--;
				s[t]=(s[t]<s[t+1]);
				break;
			case 11:			//11号操作为栈顶两个数据判大于等于操作
				t--;
				s[t]=(s[t]>=s[t+1]);
				break;	
			case 12:			//12号操作为栈顶两个数据判大于操作	
				t--;
				s[t]=(s[t]>s[t+1]);
				break;
			case 13:			//13号操作为栈顶两个数据判小于等于操作
				t--;
				s[t]=(s[t]<=s[t+1]);
				break;
			case 14:			//14号操作为输出栈顶值操作
				cout<<"运行结果: "<<s[t]<<endl;//输出栈顶值
				fprintf(fa2,"运行结果: %d\n",s[t]);//同时打印到文件
				t--;			//栈顶下移
				break;
			case 15:			//15号操作为输出换行操作
				cout<<endl;
				fprintf(fa2,"\n");
			case 16:			//16号操作是接受键盘值输入到栈顶
				t++;			//栈顶上移,分配空间
				cout<<"输入数字:"<<endl;
				fprintf(fa2,"执行程序所用的整数为: ");
				char inputChar;
				cin>>inputChar;
				while(!isdigit(inputChar))
				{
					cout<<"请输入数字"<<endl;
					cin>>inputChar;
				}
				s[t]=inputChar-48;
				cout<<endl;
				fprintf(fa2,"%d\n\n",s[t]);
				break;
			}
			break;
		case lod:			//如果是lod指令,将变量放到栈顶
			t++;			//栈顶上移,开辟空间
			s[t]=s[base(i.l,&b,s)+i.a];//通过数据层差1和偏移地址a找到变量的数据,存入上面开辟的新空间(即栈顶)
		    break;
		case sto:			//如果是sto指令
			s[base(i.l,&b,s)+i.a]=s[t];//把栈顶的值存入位置在数据区层差1偏移地址a的变量内存
			t--;
			break;
		case cal:			//如果是cal指令
			s[t+1]=base(i.l,&b,s);	//在栈顶压入静态链SL
			s[t+2]=b;				//然后压入当前数据区基址,作为动态链DL
			s[t+3]=p;				//最后压入当前的断点,作为返回地址RA
			//以上工作即为过程调用前的现场保护

			b=t+1;					//把当前数据区基址指向SL所在位置
			p=i.a;					//从a指位置开始继续执行指令,即实现了程序执行的跳转
			break;
		case ini:					//如果是ini指令
			t=t+i.a;				//栈顶上移a个空间,即开辟a个新的内存单元
			break;
		case jmp:					//如果是jmp指令
			p=i.a;					//把jmp指令操作数a的值作为下一次要执行的指令地址,实现无条件跳转
			break;
		case jpc:					//如果是jpc指令
			if(s[t]==0)				//判栈顶值
				p=i.a;				//如果是0就跳转
			t--;					//释放栈顶空间
			break;
		}
	}while(p!=0);					//如果p等于0,意味着在主程序运行时遇到了从子程序返回指令,也就是整个程序运行的结束
	cout<<endl;
	cout<<"PL0执行结束!!"<<endl<<endl;
	
	fclose(fa2);
}

	
void main()
{
	int i=0,j=0;
	char *declbeg[]={"constsym","varsym","procsym",NULL};
	char *statbeg[]={"beginsym","callsym","ifsym","whilesym",NULL};
	char *facbeg[]={"ident","number","lparen",NULL};
	char *tempset[]={"period","constsym","varsym","procsym",NULL};
	declbegsys=(struct node*)malloc(sizeof(struct node));
	statbegsys=(struct node*)malloc(sizeof(struct node));
	facbegsys=(struct node*)malloc(sizeof(struct node));
	tempsetsys=(struct node*)malloc(sizeof(struct node));
	
	while(declbeg[j]!=NULL)
		declbegsys->pa[i++]=declbeg[j++];
	declbegsys->pa[i]=NULL;

	i=0;
	j=0;
	while(statbeg[j]!=NULL)
		statbegsys->pa[i++]=statbeg[j++];
	statbegsys->pa[i]=NULL;
	
	i=0;
	j=0;
	while(facbeg[j]!=NULL)
		facbegsys->pa[i++]=facbeg[j++];
	facbegsys->pa[i]=NULL;

	i=0;
	j=0;
	while(tempset[j]!=NULL)
		tempsetsys->pa[i++]=tempset[j++];
	tempsetsys->pa[i]=NULL;
	
	if((fa1=fopen("Analyse.txt","w"))==NULL)
	{
		cout<<"打开存放分析文件过程信息文件时出错!"<<endl;
		exit(0);
	}


	cout<<"要编译的源文件: ";
	cin>>fname;
	while((fin=fopen(fname,"r"))==NULL)
	{
		cout<<"无法打开文件!"<<endl;
		cout<<"再一次打开文件(Y/N)"<<endl;
		char trychar;
		cin>>trychar;
		if(trychar=='Y'||trychar=='y')
		{
			system("cls");  //清屏
			cout<<"要编译的源文件: "<<endl;
			cin>>fname;
		}
		else
			exit(0);
	}

	fprintf(fa1,"对pascal源程序的分析为:\n(源程序来自:%s)\n\n",fname);
	
	cout<<"是否列出伪代码?(Y/N)"<<endl;
	char mychar;
	cin>>mychar;
	if(mychar=='Y'||mychar=='y')
	{
		listswitch=true;
		cout<<endl;
		cout<<"伪代码如下:"<<endl<<endl;
	}
	else
	{
		listswitch=false;
		cout<<endl;
		cout<<"源程序如下:"<<endl<<endl;
	}
	
	//初始化
	err=0;
	cc=1;
	cx=0;
	ll=0;
	ch=' ';
	kk=AL;

	getsym();
	if((fa=fopen("ObjectCode.txt","w"))==NULL)
	{
		cout<<"无法打开伪代码存放文件!"<<endl;
		exit(0);
	}
	else
		fprintf(fa,"伪代码如下:\n\n");
	
	if((fa2=fopen("Result.txt","w"))==NULL)
	{
		cout<<"打开程序执行结果文件失败!"<<endl;
		exit(0);
	}

	block(0,add(statbegsys,tempsetsys));
	fclose(fa);
	if(strcmp(sym,"period")!=0)
		error(9);
	if(err==0)
	{
		cout<<"编译通过,无语法错误!"<<endl;
		interpret();
	}

	else
	{
		cout<<endl<<"   "<<err<<"个错误在该pascal源程序中!"<<endl;
		cout<<"请修改后重新编译"<<endl;
	}
	fclose(fa1);
	fclose(fin);
}
	



	







		












  
 



     
       

 


        
        
      


⌨️ 快捷键说明

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