C语言小子集编译程序-递归下降分析

上机实习目的

理解编译程序的构造原理,掌握编译程序的构造方法与技术。通过实习,使学生既加深对编译原理基础理论的理解,又提高动手能力,特别是提高软件设计能力。

上机实习要求

在理解编译原理基本思想的基础上,选择一个自己熟悉的程序设计语言,完成编译程序的设计和实现过程。
编译程序的设计可以采用自顶向下和自底向上两种不同的方法。由于许多高级语言(如PASCAL,C)中的语法成分都是递归定义的,所以本实习要求学生采用递归下降分析技术,这是一种自顶向下的的编译方法,其基本思想是对语言的每个(或若干个)语法成分编制一个处理子程序,从处理<程序>这个语法成分的子程序开始,在分析过程中调用一系列过程或函数,对源程序进行语法和语义分析,直到整个源程序处理完毕为止。
本上机实习是为C语言(子集)设计一个编译程序,完成词法分析、语法分析、语义分析等功能,并生成某种机器上的目标代码(汇编语言)或中间代码(四元式)。

上机实习步骤

1.阅读《上机实习指导书》。
2.根据设计要求写算法,画程序框图
3.根据框图编写编译程序
4.输入编译程序并上机调试
5.撰写上机实习报告
四、上机实习内容

题目:C语言小子集编译程序的实现
C语言小子集的文法规则:

<程序>::=main(){<分程序>}
<分程序>::=<变量说明部分>;<语句部分>
<变量说明部分>::=<变量说明><标识符表>
<变量说明>::=int
<标识符表>::=<标识符表>,<标识符>
<标识符表>::=<标识符>
<标识符>::=<字母>
<标识符>::=<标识符><字母>
<标识符>::=<标识符><数字>
<语句部分>::=<语句部分>;<语句>|<语句>
<语句>::=<赋值语句>|<条件语句>|<循环语句>|
<赋值语句>::=<标识符>=<表达式>
<条件>::=<表达式><关系运算符><表达式>
<表达式>::=<项>|<表达式><加法运算符><项>
<项>::=<因子>|<项><乘法运算符><因子>
<因子>::=<标识符>|<常量>|(<表达式>)
<常量>::=<无符号整数>
<无符号整数>::=<数字序列>
<数字序列>::=<数字序列><数字>
<数字序列>::=<数字>
<加法运算符>::=+|-
<乘法运算符>::=
|/
<关系运算符>::=<
|>|!=|>=|<=|==
<复合语句>::={<语句部分>}
<语句1>::=<语句>|<复合语句>
<条件语句>::=if(<条件>)<语句1>else<语句1>
<循环语句>::=while(<条件>)do<语句1>
<字母>::=a *|b *|c *|d *|e *|f *|g *|h *|i *|j *|k *|l *|m *|n *|o *|p *|q *|r *|s *|t *|u *|v *|w *|x *|y *|z
<数字>::=0|1|2|3|4|5|6|7|8|9

实现功能:
  1. 词法分析
    扫描源程序,根据词法规则,识别单词,填写相应的表。如果产生词法错误,则显示错误信息、位置,并试图从错误中恢复。简单的恢复方法是忽略该字符(或单词)继续扫描。
  2. 语法分析
    对源程序作语法分析,确定是否属于C语言小子集,同时揭示出程序的内在结构。
  3. 语法错误检查
    根据C语言小子集的文法规则设置检测手段,通过查错子程序或一些查错语句,报告源程序出错位置、性质等,直至整个程序结束为止。
  4. 语义分析与目标代码生成
    在语法分析的基础上,进行语义分析,生成输入源程序的目标代码。输入源程序的目标代码可以建立在一个假想的处理机(虚拟机)上,或者以所学的汇编语言为基础,也可以生成四元式序列。

上机过程

功能流程图

在这里插入图片描述

词法分析器

(1)读到空格则略过,读下一个字符;若读到的是字母,就再接着读,直到读到的既不是字母也不是数字也不是下划线,并将读到的写入到token数组;

(2)若读到的是数字,直到读到的不是数字或小数点,将读到的写入到token数组;

(3)若读到的是<|>|=,则再读入下一位,若为=,则该运算符为<=|>=|==,若为其他字符,则返回<|>|=的种别码;若读到的是/,则读下一位,若为*,则说明之后为注释内容,一直读入直到读入*,并判断下一位是否为/,若是则注释结束,不是继续往下一位读入;若读入\n,则行数加一,若读入的字符与以上都不匹配,则报错,并输出出错行数。

在这里插入图片描述

单词符号种别码单词符号种别码
#0+18
Num1-19
Letter2*20
main3/21
if4=22
else5>23
do6>=24
while7<25
for8<=26
swtich9;27
case10"28
int11++29
double1230
float13/*31
long14*/32
void15{33
(16}34
)17
文法转化为LL(1)文法

首先主要为消除左递归,无需提取左公共因子
需要消除左递归的文法:

<标识符表>::=<标识符表>,<标识符>

<标识符表>::=<标识符>

<语句部分>::=<语句部分>;<语句>|<语句>

<表达式>::=<项>|<表达式><加法运算符><项>

<项>::=<因子>|<项><乘法运算符><因子>

消除左递归后的文法

<标识符表>::=<标识符><子标识符表>

<子标识符表>::=,<标识符><子标识符表>|ε

<语句部分>::=<语句><子语句部分>

<子语句部分>::=;<语句><子语句部分>|ε

<表达式>::=<项><子表达式>

<子表达式>::=<加法运算符><项><子表达式>|ε

<项>::=<因子><子项>

<子项>::=<乘法运算符><因子><子项>|ε

语义分析

首先得求出每项的Select集,比如对于子项和子表达式的Select集求起来就稍显复杂。由于报告上已经说明,这里就展示下具体的程序。

//表达式 
void M(){
	N();
	M1();
}
//子表达式 
void M1(){
	if(strcmp(w[word_cnt].value,";") == 0 || strcmp(w[word_cnt].value,")") == 0 
	|| L() || strcmp(w[word_cnt].value,"}") == 0 || strcmp(w[word_cnt].value,"else") == 0){
		return;
	}
	else if(!O()){
		error("miss + or -");
		//word_cnt--;
	}
	else{
		op_stack.push(w[word_cnt].value);
		word_cnt++;
		//word_stack.push(w[word_cnt].value);
		op_num++; 
		//getWord();
		//strcpy(b[b_num++].bds,new_w.value);
		N();
		M1();

		if(op_stack.size() > 1 && e == 0){
			op = op_stack.top();
			op_stack.pop();
			s2 = word_stack.top();
			word_stack.pop(); 
			s1 = word_stack.top();
			word_stack.pop();
			stringstream ss;
			ss << op_num;
			res = "t"+ss.str();
			word_stack.push(res);
			siYuan(op,s1,s2,res);
		}
		
	}
}
报错处理

错误处理主要包含两个方面的任务:一是报错,发现错误时应尽可能准确指出错误的属性;二是错误恢复,尽可能校正,使得编译工作可以继续下去,提高程序调试的效率。
在这里我主要举一个因子的例子。

<因子>::=<标识符>|<常量>|(<表达式>)

//因子
void Z(){
	//cout<<w.value<<"  "<<w.type<<endl;
	//非数字或标识符 
	if(w[word_cnt].type!=1 && w[word_cnt].type!=2 &&strcmp(w[word_cnt].value,"(")){
		error("expression has a error");
		return;
	}
	//标识符
	if(w[word_cnt].type == 1||w[word_cnt].type == 2){
		if(w[word_cnt].type == 2){
			is_say(w[word_cnt].value);
		}
		word_stack.push(w[word_cnt].value);
		word_cnt++;
		//equ[equ_num++] = w.value;
		return;
	}
	else if(strcmp(w[word_cnt].value,"(") == 0){
		word_cnt++;
		//word_stack.push(w[word_cnt].value);
		M();
		if(e == 0){
			op = op_stack.top();
			op_stack.pop();
			s2 = word_stack.top();
			word_stack.pop(); 
			s1 = word_stack.top();
			word_stack.pop();
			stringstream ss;
			ss << op_num;
			res = "t"+ss.str();
			word_stack.push(res);
			siYuan(op,s1,s2,res);
		}
		
		if(strcmp(w[word_cnt].value,")") == 0){
			word_cnt++;
			return;
		}
		else{
			error("miss )");
		}
	}
	return;
}

主要有两次错误处理,如果非数字和标识符或(则报表达式错误;第二个就是因子到表达式时对缺少‘)’的错误处理。

语义分析生成四元式

首先四元式的构造如下所示

//四元式
struct si_Yuan{
	string op;//运算符
	string s1;
	string s2;
	string res;//结果 
}sy[105];

然后我用两个栈分别保存标识符和运算符,由于在实验报告中已经举例分析,我在这直接给出下代码。

对于赋值语句的入栈出栈操作

//赋值语句
void I(){
	if(strcmp(w[word_cnt].value,"=") == 0){
		word_cnt++;
		//word_stack.push(w[word_cnt].value); 
		M();
		if(e > 0){
			return;
		}
		if(op_stack.size() > 0){
			op = op_stack.top();
			op_stack.pop();
			s2 = word_stack.top();
			word_stack.pop();
			s1 = word_stack.top();
			word_stack.pop();
			res = word_stack.top();
			word_stack.pop();
			siYuan(op,s1,s2,res);
		}
		else{
			s1 = word_stack.top();
			word_stack.pop();
			res = word_stack.top();
			word_stack.pop();
			siYuan("=",s1,"",res);
		}
		
	}
	else{
		error("miss = ");
		word_cnt--; 
	}
} 

对于循环语句的构造四元式:

//while语句
void K(){
	if(strcmp(w[word_cnt].value,"while")){
		error("miss while");
		word_cnt--;
	}
	word_cnt++;
	if(strcmp(w[word_cnt].value,"(")){
		error("miss (");
		word_cnt--;
	}
	word_cnt++;
	//word_stack.push(w[word_cnt].value);
	X();
	sent_id = sy_num;
	
	if(op_stack.size()>0 && e == 0){
		op = op_stack.top();
		op_stack.pop();
		s2 = word_stack.top();
		word_stack.pop();
		s1 = word_stack.top();
		word_stack.pop();
		siYuan(op,s1,s2,res);
	}
	
	//cout<<w[word_cnt].value<<endl;
	if(strcmp(w[word_cnt].value,")")){
		error("miss )");
		word_cnt--;
	}
	word_cnt++;
	if(strcmp(w[word_cnt].value,"do")){
		error("miss do");
		word_cnt--;
	}
	word_cnt++;
	Y();	
}

实验结果

测试程序1.

在这里插入图片描述

测试结果1:

在这里插入图片描述

测试程序2:

在这里插入图片描述

测试结果2:

在这里插入图片描述

实验小结

主要的实验小结已经附在纸质文档上。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

由于篇幅有限,本篇博客只能给出部分代码。想要完整代码。附上下载链接(代码功能实现比较完善,但是由于听老师要求是用堆栈实现的(这里给老师一个锅)。所以对于a-1+b这种情况有一点点小缺陷。但是对于复杂情况实现效果都挺好,诸如(a+b+c)*(a+c+1))。

下载链接

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值