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

📄 pl0.c

📁 本程序实现编译原理中
💻 C
📖 第 1 页 / 共 3 页
字号:
#include<stdio.h>
#include <memory.h>
#include"string.h"
#include"pl0.h"

#define stacksize 500

int main()
{
	bool nxtlev[symnum];
	printf("Input PL0 file's name:");
	scanf("%s",fname);
	fin = fopen(fname,"r");
	if(fin)
	{
		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');
		fa1 = fopen("fa1.tmp","w");

		init();//初始化变量
		
		err = 0;//错误计数
		cc = cx = ll = 0;
		ch = ' ';
		//首先取得一个符号,若成功则开始调用block()开始编译。
		if(-1 != getsym())
		{
			fa = fopen("fa.tmp","w");
			fas = fopen("fas.tmp","w");

			addset(nxtlev,declbegsys,statbegsys,symnum);//语句和声明所对应的行为true

			nxtlev[period] = true;//period代表“.”,即PL/0程序的结束符

			if(-1 == block(0,0,nxtlev))//block parameter:int lev,int tx,bool* fsys;
			{
				fclose(fa);
				fclose(fa1);
				fclose(fas);
				fclose(fin);
				printf("\n");
				return 0;
			}
			fclose(fa);
			fclose(fa1);
			fclose(fas);
			if(sym != period)
			{
				error(9);
			}
			if(err == 0)
			{
				fa2 = fopen("fa2.tmp","w");
				interpret();
				fclose(fa2);
			}
			else
			{
				printf("There are %d errors in pl0 program!\n",err);
			}
		}
		fclose(fin);
	}
	else
	{
		printf("Can't open file!\n");
	}
	printf("\n");
	return 0;
}

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]),  "cos");
	strcpy(&(word[4][0]),  "do");
	strcpy(&(word[5][0]),  "end");
	strcpy(&(word[6][0]),  "exp");
	strcpy(&(word[7][0]),  "if");
	strcpy(&(word[8][0]),  "odd");
	strcpy(&(word[9][0]),  "procedure");
	strcpy(&(word[10][0]), "read");
	strcpy(&(word[11][0]), "sin");
	strcpy(&(word[12][0]), "tan");
	strcpy(&(word[13][0]), "then");
	strcpy(&(word[14][0]), "var");
	strcpy(&(word[15][0]), "while");
	strcpy(&(word[16][0]), "write");
/*根据字母表,设置保留字符号*/
	wsym[0] = beginsym;
	wsym[1] = callsym;
	wsym[2] = constsym;
	wsym[3] = cossym;
	wsym[4] = dosym;
	wsym[5] = endsym;
	wsym[6] = expsym;
	wsym[7] = ifsym;
	wsym[8] = oddsym;
	wsym[9] = procsym;
	wsym[10] = readsym;
	wsym[11] = sinsym;
	wsym[12] = tansym;
	wsym[13] = thensym;
	wsym[14] = varsym;
	wsym[15] = whilesym;
	wsym[16] = writesym;
	/*word数组的行数和wsym的行数相等*/
 /*设置指令名称*/
	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");
	strcpy(&(mnemonic[fun][0]),"fun");
/*设置符号集*/
	for(i=0;i<symnum;i++)
	{
		declbegsys[i] = false;
		statbegsys[i] = false;
		facbegsys[i] = false;
	}
/*设置声明符号集*/
	declbegsys[constsym] = true;
	declbegsys[varsym] = true;
	declbegsys[procsym] = true;
/*设置语句开始符号集*/
	statbegsys[beginsym] = true;
	statbegsys[callsym] = true;
	statbegsys[ifsym] = true;
	statbegsys[whilesym] = true;
/*设置因子开始符号集*/
	facbegsys[ident] = true;
	facbegsys[number] = true;
	facbegsys[lparen] = true;
	facbegsys[cossym] = true;
	facbegsys[sinsym] = true;
	facbegsys[tansym] = true;
	facbegsys[expsym] = true;

	numlen = 0;
}

int inset(int e,bool*s)
{

	if(s == NULL)
	{
		printf("NULL pointer error!(in inset function)\n");
		exit(1);
	}
	return s[e];
}

int addset(bool* sr,bool* s1,bool* s2,int n)
{
	int i;
	for(i=0;i<n;i++)
	{
		sr[i] = s1[i]||s2[i];//语句和声明所对应的行为true
	}
	return 0;
}

int subset(bool* sr,bool* s1,bool* s2,int n)
{
	int i;
	for(i=0;i<n;i++)
	{
		sr[i] = s1[i]&&(!s2[i]);
	}
	return 0;
}

int mulset(bool* sr,bool* s1,bool* s2,int n)
{
	int i;
	for(i=0;i<n;i++)
	{
		sr[i] = s1[i]&&s2[i];
	}
	return 0;
}
//else
void error(int n)//n记录出错位置
{
	char space[81];

	memset(space,32,81);
	space[cc-1] = '\0';
	printf("%s ^ %d\n",space,n);
	fprintf(fa1,"%s ^ %d\n",space,n);
	err++;
}

/*
函数功能简述:为getsym()函数返回一个字符,该函数在cc==ll的时候从文件中读取一行数据,
放在line[]中,以后每次从line[]中取一个字符然后返回,当取完时(cc==ll)在读取。
cc==ll有两种情况,一是第一次调用,二是由于不断的调用已将line中的数据取完。
*/
int getch()
{
	//printf("I come into the function getch!\n");
	if(cc == ll)
	{
		if(feof(fin))//程序还没有遇到句点,但文件已经结束,编译程序认为程序不完整,并返回-1.
		{
			printf("program incomplete");
			return -1;
		}
		ll = 0;
		cc = 0;
		printf("%d ",cx);
		fprintf(fa1,"%d ",cx);
		ch = ' ';
		while(ch != 10)//10是回车换行,32是空格
		{
			//由于该循环是先把字符存放在line里,在检查。
			//因此回车符也被存放在了line里边。
			if(EOF == fscanf(fin,"%c",&ch))//EOF表示文件结束
			{
				line[ll] = '\0';//空终止符
				break;
			}
			printf("%c",ch);
			fprintf(fa1,"%c",ch);
			line[ll] = ch;
			ll++;
		}

	}
	ch = line[cc];
	cc++;
	return 0;
}
/*
输入符号的所有可能情况:
标识符(变量名、常量名、过程名)、数字,关系运算符( < <= > >= ),单字符(+ - * / ( ) , . ; = #)
*/
int getsym()
{	

	
	int i,j,k;	

	//滤去空白符
	while(ch == ' '||ch == 9 || ch == 10)//9=='\t' and 10 == '\n';
	{
		getchdo;
	}
	if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))//字母
	{
		k=0;
		do
		{
			if(k<MAX_IDENT_LENGTH)//MAX_IDENT_LENGTH(=10) is the longest length of the identifier.
			{
				a[k] = ch;//a 临时符号集,记录获得的完整的字符串或数字串
				k++;
			}
			getchdo;
		}while((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')||(ch>='0'&&ch<='9'));
		a[k] = 0;
		strcpy(id,a);//char id[MAX_IDENT_LENGTH+1],得到标识符;
		i = 0;
		j = NUM_OF_KEYWORD -1;//NUM_OF_KEYWORD's value is 17.保留字
		do//折半查找,找到读入字符串的位置
		{
			k = (i+j)/2;
			/*	
				下面两句话比较关键,在这里简单说明一下:
				当 id==word[k] 时,两个if语句都会执行,于是
				j = k - 1 while i = k + 1;
				假如 k=8的话,则当两个if都执行之后,j = 7 while i = 8;
				于是 当 i-1 > j 时,word[k] 肯定是我们要找的
			*/
			if(strcmp(id,word[k])<=0)//临时串的首字母与symbol中的某个字符的首字母比较
			{
				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')//数字
		{
			k = 0;
			num = 0;
			sym = number;//标记数字
			do//读一个完整的数字串
			{
				num = 10*num +ch-'0';
				k++;
				getchdo;
			}while(ch>='0'&&ch<='9');
			k--;
			numlen = k;//add by daka
			if(numlen > MAX_NUM_LENGTH)//读入的数字串的长度大于最大允许长度,抛出异常
			{
				error(31);
			}
		}
		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(sym == '=')
						{
							sym = geq;//标记>=
							getchdo;//继续读
						}
						else
						{
							sym = gtr;//>
						}
					}
					else
					{
						sym = ssym[ch];//标记“,”或“;”或“(”或“)”
						if(sym != period)//如果不等结束符“.”
						{
							getchdo;
						}
					}
				}
			}
		}
	}


	return 0;
}

/*
f: 功能码
l: 层次差(标识符引用层减去定义层)
a:	根据不同的指令有所区别
*/
int gen(enum fac x,int y,int z)
{
	if(cx > MAX_CODE_NUM)//当产生的代码数量超出code数组的长度时,编译程序报错,并返回-1.
	{
		printf("Program too long");
		return -1;
	}
	
	code[cx].f = x;
	code[cx].l = y;
	code[cx].a = z;
	cx++;//地址递增

	return 0;
}

int test(bool* s1,bool* s2,int n)
{

	bool bs1,bs2;


	if(s1 == NULL || s2 == NULL)
	{
		printf("NULL pointer error!\n");
		exit(1);
	}
	if(! inset(sym,s1))
	{

		error(n);
		bs2 = inset(sym,s2);
		bs1 = inset(sym,s1);
		
		while((!bs1)&&(!bs2))
		{
			getsymdo;
			bs2 = inset(sym,s2);
			bs1 = inset(sym,s1);
		}
	}
	return 0;
}

/*
block会对一个分程序进行分析,分析对象包括,常量声明,变量声明,过程声明
当遇到过程声明时会再次递归调用block继续深入分析。
<分程序> ::= [<常量说明部分>][<变量说明名>][<过程说明部分><语句>
前三个部分都是可选,只有语句是必须的。

int tx(名字表的索引)是作为参数传进去的,而且传的值,于是所有的修改
在函数返回的时候恢复原样,相应的它曾今在名字表中占的空间将被别人覆盖(可能)

lev:当前分程序所在的层
tx:名字表当前尾指针
fsys:当前模块后跟符号集合
*/
int block(int lev,int tx,bool* fsys)//数组fsys中,声明,语句和结束符“.”所对应的位置值为true
{

	int i;
	int dx;//名字分配到的相对地址
	int tx0;//保留初始的tx
	int cx0;//保留初始的cx
	bool nxtlev[symnum];/*在下级函数的参数中,符号集合均为参数,但由于使用数组实现,传进来的是指针,
						为防止下级函数改变上级函数的集合,特开辟新的空间传递给下级函数
						*/
/*
调用该函数,有两种情况1、程序刚开始运行,读入第一个符号后开始调用该函数。(见mian函数)
2、在分析程序的过程中遇到了过程说明,会递归调用这个函数。下面我们仅对第二中情况进行讨论,
当遇到过程说明符时,于是读入过程的标识符,并将该标识符登记到名字表中,紧接着开始调用block()
这时tx指向名字表的末尾(正好是过程标识符所在),tx0将这个位置保存下来,供以后显示名字表使用。
*/

	dx = 3;
	tx0 = tx;
//每个过程的第一条指令一定是一条跳转指令,跳到程序体的开始
	table[tx0].adr = cx;//modified by daka tx---->tx0
						//cx的值就是下面gen(jmp,0,0)中jmp指令的地址。
	cx0 = cx;

	gendo(jmp,0,0);//把 jmp 0 0 加入虚拟代码中,成功继续执行


	if(lev > MAX_LEVEL)//大于最大允许层,抛出异常
	{
		error(32);
	}
	do
	{
		if(sym == constsym)//常量
		{
			getsymdo;//成功读取一个单词
			do
			{
				constdeclarationdo(&tx,lev,&dx);//是常量声明,继续执行
				while(sym == comma)//","
				{
					getsymdo;
					constdeclarationdo(&tx,lev,&dx);
				}
				if(sym == semicolon)//";"
				{
					getsymdo;
				}
				else
				{

⌨️ 快捷键说明

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