Pl0编译器扩充

PL0编译器扩充(注释 else 数组)

注释的扩充

注释的扩充写在getch中,就是词法分析,在getch中每次会读取一行放入数组line中然后进行读取,那么注释的基本思想就是遇到注释就不放入line中,直接跳过就好。

int getch()
{
	if (cc == ll) /* getch使用的计数器,cc表示当前字符(ch)的位置 */
	{
		/* feof()只用于测试流文件的结束,当到达结尾时,返回非0;
		当文件内部位置指针指向文件结束时,并未立即置位FILE结构中的文件结束标记,
		只有再执行一次读文件操作,才会置位结束标志,此后调用feof才会返回为真。*/
		//如果到达文件结尾返回-1
		if (feof(fin))
		{
			printf("program incomplete");
			return -1;
		}
		ll=0;
		cc=0;
		printf("%d ", cx);/*cx 虚拟机代码指针, 取值范围[0, cxmax-1] 输出序号*/
		fprintf(fa1,"%d ", cx);  /* 输出源文件及其各行对应的首地址 */
		ch = ' ';
		//回车
		while (ch != 10)
		{
			//fscanf(fin,"%c", &ch)
			//richard
			if (EOF == fscanf(fin,"%c", &ch))
			{
				line[ll] = 0;
				break;
			}
			//end richard
			//注释扩充
			if(ch=='{'){
				//line[ll-1]='';
				//遇到注释输出并跳过
				printf("%c", ch);
				while(ch!='}'){
					
					if (EOF == fscanf(fin,"%c", &ch))
					{
						error(31);//如果文件结束也没有读到右括号,报错
						line[ll] = 0;
						break;
					}
					printf("%c", ch);
				}
			}//注释扩充结束
			else{
				printf("%c", ch);
				//printf("%c", '#');
				fprintf(fa1, "%c", ch);//把ch写入fal
				line[ll] = ch;
				ll++;
			}
		}
		printf("\n");
		fprintf(fa1, "\n");
	}
	ch = line[cc];
	cc++;
	return 0;
}

扩充else

else 在statement 语句处理中,初始化init和getsym中需要添加的东西比较简单,大家自己加就行。在statement 原本的if是通过jpc条件跳转指令来查看是否需要跳过then语句,gen函数主要作用就是存储指令,cx记录指令code中指令位置,增加else语句之后我们如果条件成立就直接跳到else语句,不成立的话肯定会顺序执行,那么就需要在then语句之后增加一条无条件跳转指令jmp用于跳过else语句。所以用cx1记录jpc指令位置,cx2记录语句执行完的位置。就像下面的一段指令:
7和8是then语句jpc如果条件成立就会直接跳到10执行else语句,要不就顺序执行到jmp指令跳过else语句从27继续执行。

6 jpc 0 10
7 lod 1 3
8 sto 1 4
9 jmp 0 27
10 lod 1 3
11 lit 0 10
12 opr 0 10
13 jpc 0 21
14 lit 0 2
15 lod 1 3
16 opr 0 4
17 lit 0 1
18 opr 0 3
19 sto 1 4
20 jmp 0 27
21 lit 0 3
22 lod 1 3
23 opr 0 4
24 lit 0 11
25 opr 0 3
26 sto 1 4
27 opr 0 0

if (sym == ifsym)   /* 准备按照if语句处理 */
	{
						getsymdo;
                        memcpy(nxtlev,fsys,sizeof(bool)*symnum);
                        nxtlev[thensym]=true;
                        nxtlev[dosym]=true;    /*后跟符号为then或do*/
						nxtlev[elsesym] = true;/* 后跟符号为else */
                        conditiondo(nxtlev,ptx,lev);   /*调用条件处理(逻辑运算)函数*/
                        if(sym==thensym)
                        {
                            getsymdo;
                        }
                        else
                        {
                            error(16);          /*缺少then*/
                        }
                        cx1=cx;                /*保存当前指令地址*/
                        gendo(jpc,0,0);        /*生成条件跳转指令,跳转地址暂写0*/
						/*指令 “JPC  0  A”
						条件转移指令
						若栈顶为 0,则转移至地址 A,即置指令地址寄存
						器为A ;T 减1
						*/
                        statementdo(fsys,ptx,lev);   /*处理then后的语句*/
						
                        //添加处理else语句
                        if(sym == elsesym){

							cx2=cx;//记录jmp指令位置
							gendo(jmp,0,0);//将来会直接跳转到else语句后面

							code[cx1].a=cx;/* 经statement处理后,cx为then后语句执行完的位置,它正是前面未定的跳转地址 *///为jpc指令赋值
						
                            getsymdo;
                            statementdo(fsys,ptx,lev);
                            code[cx2].a=cx;  //当前是else后面的语句结束位置,if语句执行后应当跳转至此 为jmp指令最后一个赋值
                        }
						else
							code[cx1].a = cx;

数组扩充

数组扩充分为三块:(1)数组是变量 那么数组的声明需要在 vardeclaration中进行补充,声明处理主要是向名字表中添加。 名字表中原来的变量添加是
case variable: /* 变量名字 */
table[(*ptx)].level = lev;//层数
table[(*ptx)].adr = (*pdx);//基地址
(*pdx)++;//偏移量加一
break;
对于数组偏移量是(上界-下界+1)那么原来加入名字表的函数肯定不能用
因为我们的pdx需要增加的偏移量是(上界-下界+1),所以需要增加新的变量名称array和新的增加名字表函数enterArray。

int vardeclaration(int* ptx,int lev,int* pdx)
{
	if (sym == ident)
	{
		int low=0,/*下界*/upper=0;/*上界*/
		bool flag=false;//判断是否找到下界
		getsymdo;
		if(sym==lparen){//如果下一个字符是左括号则可能是数组
			char array_name[11];//存储变量名字,用于填写名字表
			memcpy(array_name,id,11);//将变量名放入
			getsymdo;//继续分析
			if(sym==number){//下一个是数字//下界
				low=num;
			}
			else if(sym==ident){//判断下一个是否为常量
				low=isConst(position(id,*ptx));
				if(low==-1){
					flag=true;
					error(31);//没有找到常量
				}
			}
			else{
				flag=true;
				error(31);//既不是数字也不是常量
			}
			if(!flag){//找到下界后继续分析
				getsymdo;
				if(sym==colon){//下一个是“:”
					getsymdo;
					if(sym==number){
						upper=num;
					}
					else if(sym==ident){
						upper=isConst(position(id,*ptx));
						if(upper==-1){
							flag=true;
							error(31);
						}
					}
					else{
						flag=true;
						error(31);
					}
					if(!flag){
						if(low<upper){//下界小于上界
							getsymdo;
							if(sym==rparen){//数组最后一个是右括号 完美
								enterArray(ptx,lev,pdx,low,upper,array_name);//加入名字表·
							}
							else{
								error(31);//缺少右括号
							}
						}
						else{
							error(31);//下界大于上界
						}
					}

				}
				else{
					error(31);//不是冒号
				}
				getsymdo;
			}
			
		}
		else{
			enter(variable, ptx, lev, pdx); // 填写名字表
		}
	}
	else
	{
		error(4);   /* var后应是标识 */
	}
	return 0;
}

(2)语句处理,即读、写和赋值在statement语句处理中添加。
(1)read处理
  例如read(a(1+2))那么中间的需要通过表达式处理进行分析
  expressiondo这个函数会把表达式的值放到栈顶,这并不符合我们的要求,我们希望他给我们返回一个值,
然后使用sto指令,把地址传进去。那我们怎么解决这个问题?答案就是我们也把处理延后。
既然偏移量在栈顶,我们把基地址也放到栈顶,然后相加,那么栈顶的就是真实地址,
然后我们就会发现sto指令并不能满足我们,因为它使用的是名字表的地址,而我们的地址在栈顶,
所以我们自己再写一个指令sto2来完成我们需要的功能*/

if (sym == readsym) /* 准备按照read语句处理 */
		{
			getsymdo;
			if (sym != lparen)//左括号
			{
				error(34);  /* 格式错误,应是左括号 */
			}
			else
			{
				do {
					getsymdo;
					if (sym == ident)
					{
						i = position(id, *ptx); /* 查找要读的变量 */
					}
					else
					{
						i=0;
					}

					if (i == 0)
					{
						error(35);  /* read()中应是声明过的变量名 */
					}
					else if (table[i].kind != variable && table[i].kind!=array)//增加数组
					{
						error(32);	/* read()参数表的标识符不是变量, thanks to amd */
					}
					else
					{
						if(table[i].kind == variable)//如果是普通变量read(abc)
						{
							gendo(opr, 0, 16);  /* 生成输入指令,读取值到栈顶 */
							gendo(sto, lev-table[i].level, table[i].adr);   /* 储存到变量 */
							getsymdo;
						}
						else if(table[i].kind == array){//read(a(1))
							/*现在考虑我们读入数组,假如我们有定义a(0:3),将来要read(a(1))。
							我们在名字表中能查找到的只有a,也就是a(0)的地址,那我们怎么知道a(1)在哪里存储呢?
							其实就是基地址+偏移量。基地址就是数组首地址,即a的地址,偏移量就是1,a(1)的地址就是a.addres+1。
							考虑到可能有read(a(0+1))这样的情况,我们使用原有的表达式处理函数。
							expressiondo这个函数会把表达式的值放到栈顶,这并不符合我们的要求,我们希望他给我们返回一个值,
							然后使用sto指令,把地址传进去。那我们怎么解决这个问题?答案就是我们也把处理延后。
							既然偏移量在栈顶,我们把基地址也放到栈顶,然后相加,那么栈顶的就是真实地址,
							然后我们就会发现sto指令并不能满足我们,因为它使用的是名字表的地址,而我们的地址在栈顶,
							所以我们自己再写一个指令sto2来完成我们需要的功能*/
							getsymdo;//左括号
							expressiondo(nxtlev, ptx,true,lev,i);//括号内的表达式,将偏移量放到栈顶
					
							gendo(lit, 0, table[i].adr);//基地址 将数组首地址放到栈顶
							gendo(opr, 0, 2);//当前栈顶是真实地址 次栈顶与栈顶相加
							gendo(opr, 0, 16);  /* 生成输入指令,读取值到栈顶 */
							gendo(sto2, lev - table[i].level, 0);
							

						}
					}

				} while (sym == comma); /* 一条read语句可读多个变量 */
			}
			if(sym != rparen)
			{
				error(33);  /* 格式错误,应是右括号 */
				while (!inset(sym, fsys))   /* 出错补救,直到收到上层函数的后跟符号 */
				{
					getsymdo;
				}
			}
			else
			{
				getsymdo;
			}
		}

int expression(bool* fsys, int* ptx,bool isArray, int lev,int index)
{
	enum symbol addop;  /* 用于保存正负号 */
	bool nxtlev[symnum];

	if(sym==plus || sym==minus) /* 开头的正负号,此时当前表达式被看作一个正的或负的项 */
	{
		addop = sym;    /* 保存开头的正负号 */
		getsymdo;
		memcpy(nxtlev, fsys, sizeof(bool)*symnum);
		nxtlev[plus] = true;
		nxtlev[minus] = true;
		termdo(nxtlev, ptx, lev);   /* 处理项 */
		if (addop == minus)
		{
			gendo(opr,0,1); /* 如果开头为负号生成取负指令 */
		}
	}
	else    /* 此时表达式被看作项的加减 */
	{
		memcpy(nxtlev, fsys, sizeof(bool)*symnum);
		nxtlev[plus] = true;
		nxtlev[minus] = true;
		termdo(nxtlev, ptx, lev);   /* 处理项 */
	}
	while (sym==plus || sym==minus)
	{
		addop = sym;
		getsymdo;
		memcpy(nxtlev, fsys, sizeof(bool)*symnum);
		nxtlev[plus] = true;
		nxtlev[minus] = true;
		termdo(nxtlev, ptx, lev);   /* 处理项 */
		if (addop == plus)
		{
			gendo(opr, 0, 2);   /* 生成加法指令 */
		}
		else
		{
			gendo(opr, 0, 3);   /* 生成减法指令 */
		}
	}
	//如果是数组,下标应该减去下界值才能得到真实的地址值
	if (isArray == true){
		gendo(lit, 0, table[index].low);//取数组下界到栈顶
		gendo(opr, 0, 3);/*次栈顶的值减去栈顶的值,结果存入次栈顶   T 减 1*/
	}
	return 0;
}

对于写的对应处理并不是在statement中处理的。而是在因子处理中进行的。

int factor(bool* fsys, int* ptx, int lev)
{
	int i;
	bool nxtlev[symnum];
	testdo(facbegsys, fsys, 24);    /* 检测因子的开始符号 */
	/* while(inset(sym, facbegsys)) */  /* 循环直到不是因子开始符号 */
	if(inset(sym,facbegsys))    /* BUG: 原来的方法var1(var2+var3)会被错误识别为因子 */
	{
		if(sym == ident)    /* 因子为常量或变量 */
		{
			bool ar = false;
			i = position(id, *ptx); /* 查找名字 */
			if (i == 0)
			{
				error(11);  /* 标识符未声明 */
			}
			else
			{
				switch (table[i].kind)
				{
				case constant:  /* 名字为常量 */
					gendo(lit, 0, table[i].val);    /* 直接把常量的值入栈 */
					break;
				case variable:  /* 名字为变量 */
					gendo(lod, lev-table[i].level, table[i].adr);   /* 找到变量地址并将其值入栈 */
					break;
				case procedur:  /* 名字为过程 */
					error(21);  /* 不能为过程 */
					break;
				case array:  //数组 a(1) , a(1*2+3)等等
					getsymdo; //忽略左小括号(
					getsymdo; //获取数组下标
					expressiondo(nxtlev, ptx, true,lev,i); //下标的表达式,将相对偏移量(已经减掉下界值)放到栈顶
					gendo(lit, 0, table[i].adr); //将基地址放到栈顶
					gendo(opr, 0, 2); //下标的相对偏移量+基地址=相对地址
					gendo(lod2, lev - table[i].level, 0);
					break;
				}
			}
			getsymdo;
		}
		else
		{
			if(sym == number)   /* 因子为数 */
			{
				if (num > amax)
				{
					error(31);
					num = 0;
				}
				gendo(lit, 0, num);
				getsymdo;
			}
			else
			{
				if (sym == lparen)  /* 因子为表达式 */
				{
					getsymdo;
					memcpy(nxtlev, fsys, sizeof(bool)*symnum);
					nxtlev[rparen] = true;
					expressiondo(nxtlev, ptx,false, lev,0);
					if (sym == rparen)
					{
						getsymdo;
					}
					else
					{
						error(22);  /* 缺少右括号 */
					}
				}
				testdo(fsys, facbegsys, 23);    /* 因子后有非法符号 */
			}
		}
	}
	return 0;
}

赋值和read中写的差不多


```cpp
if (sym == ident)   /* 准备按照赋值语句处理 */
	{
		/*
		* 查找名字的位置.
		* 找到则返回在名字表中的位置,否则返回0.
		*
		* idt:    要查找的名字
		* tx:     当前名字表尾指针
		*/
		i = position(id, *ptx);
		if (i == 0)
		{
			error(11);  /* 变量未找到 */
		}
		else
		{
			if(table[i].kind != variable&& table[i].kind != array)
			{
				error(12);  /* 赋值语句格式错误 */
				i = 0;
			}
			else
			{
				if (table[i].kind == variable){
					getsymdo;
					if (sym == becomes)
					{
						getsymdo;
					}
					else
					{
						error(13);  /* 没有检测到赋值符号 */
					}
					memcpy(nxtlev, fsys, sizeof(bool)*symnum);
					expressiondo(nxtlev, ptx,false, lev,i); /* 处理赋值符号右侧表达式 */
					if (i != 0)
					{
						/* expression将执行一系列指令,但最终结果将会保存在栈顶,执行sto命令完成赋值 */
						gendo(sto, lev - table[i].level, table[i].adr);
					}
				}
				else
				{
					getsymdo;
					
					expressiondo(nxtlev, ptx, true,lev,i);//下标的表达式,将偏移量(已经减掉下界值)放到栈顶
					
					gendo(lit, 0, table[i].adr);//将基地址放到栈顶
					gendo(opr, 0, 2);//当前栈顶是真实地址
					if (sym == becomes){
						getsymdo;
					}
					else{
						error(13);
					}
					memcpy(nxtlev, fsys, sizeof(bool)*symnum);
					expressiondo(nxtlev, ptx,false, lev,i); /* 处理赋值符号右侧表达式 */
					if (i != 0)
					{
						/* expression将执行一系列指令,但最终结果将会保存在栈顶,执行sto命令完成赋值 */
						gendo(sto2, lev - table[i].level, 0);
					}
				}
			}
		}//if (i == 0)
	}

扩充好的资源已经上传至https://download.csdn.net/download/qq_41979507/12097302

  • 2
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值