【编译原理】实验二:实现递归下降分析器

实验内容

用高级语言实现课本3.2文法的递归下降分析程序。
课本3.2文法要求:
可使用书上提供的输入串i1*(i2+i3),也可以是自己定义的其他符号串;输出栈中所有内容,并给出分析结果。

开发环境

windows 10
visual studio 2019

数据结构

1 符号栈

栈是一种有存取限制的线性表,它只允许在一端进行插入或删除操作。遵循“先进先出”原则。
栈示意图
递归下降分析法是一种自顶向下的分析方法,文法的每个非终结符对应一个递归过程(函数),可用栈来表示分析过程。

2 分析过程

序号 符号栈 待分析字符 表达式
0 # i*(i+i)#
1 #E i*(i+i)# E→TE’
2 #E’T i*(i+i)# T→FT’
3 #E’T’F i*(i+i)# F→i
4 #E’T’ i*(i+i)# T’→*FT’
5 #E’T’F i*(i+i)# F→(E)
6 #E’T’E i*(i+i)# E→TE’
7 #E’T’E’T i*(i+i)# T→FT’
8 #E’T’E’T’F i*(i+i)# F→i
9 #E’T’E’T’ i*(i+i)# T’→ε
10 #E’T’E’ i*(i+i)# E’→+TE’
11 #E’T’E’T i*(i+i)# T→FT’
12 #E’T’E’T’F i*(i+i)# F→i
13 #E’T’E’T’ i*(i+i)# T’→ε
14 #E’T’E’ i*(i+i)# E’→ε
15 #E’T’ i*(i+i)# T’→ε
16 #E’ i*(i+i)# E’→ε
17 #

 i*(i+i)分析栈示意图

3 数组

1、分析串数组char token[token_size]:存放待分析串。
2、符号栈char stack[10] = {’#’}:存放符号栈内容,初始压入‘#’。

实验步骤

分析与设计

教材上的例题已然分析地较详细:
1、 已消除左递归。
2、 无回溯。
我们直接进行递归下降分析器的构造。
递归下降分析法大致流程图

编程

1 全局变量

① token_len:int类型变量,存放待分析串的长度。
② step:int类型变量,记录当前分析步数。

2 栈

① char stack[10] = {’#’}:char类型数组,存放符号栈内容,初始压入‘#’。
② stack_top:int类型变量,为符号栈的栈顶指针。

3 数组

token[token_size]:cha类型数组,存放待分析串。

4 函数

① 非终结符表达式:
void E();
void E1();
void T();
void T1();
void F();
② 打印函数:void print()。

5 伪代码

见《编译原理教程(第四版)》P52-53

void match(token t){
	if (lookahead == t)
			lookahead = nexttoken;
	else
			error();
}

void E(){
	T();
	E();
}

void E'(){
	if (lookahead == '+'){
		match('+');
		T();
		E'();
	}
}

void T(){
	F();
	T'();
}

void T'(){
	if (lookahead == '*'){
		match('*');
		F();
		T'();
	}
}

void F(){
	if (lookahead == 'i')
		match('i');
	else if (lookahead == '('){
		match('(');
		E();
		if (lookahead == ')')
			match(')');
		else
			error();
	}
	else
		error();
}

运行与调试

根据调试情况进行相应的代码修改。

运行结果

i*(i+i)为合法字符串
++为非法字符串

所遇问题与解决方案

1 E’和T’的压栈

因为E’不属于一个字符,在考虑存放的时候本来是准备实现字符串数组的,问题有点多,所以就直接按照’E’和’\’’两个字符来存储E’了,T’同理。

2 栈的实现

心得体会

对递归下降分析器的构造有了更深的理解,也更熟练地掌握了栈的实现。

实验代码

1 主函数main()

int main(void){
    
    
	printf("请输入字符串(以#结束):");		//输入待分析字符串
	while (token[lookahead] != '#'){
    
    		//如果没有按下#结束
		char scan_char;						//扫描字符
		do{
    
    									//若没有扫描到结束符#,就继续扫描
			scanf_s("%c", &scan_char,1);
			token[token_len] = scan_char;	//将扫描到的字符保存到待分析串数组中
			token_len++;
		} while (scan_char != '#');
		printf("\n");
		printf("序号\t");
		printf("符号栈\t\t");
		printf("待分析字符\t");
		printf("表达式\n");
		print();							//打印当前步骤信息
		printf("\n");						//换行
		stack[++stack_top] = 'E';			//将E()压入栈中,开始分析
		step++;								//分析步数+1
		E();
		if (token[lookahead] == '#')		//若待分析字符为#,说明符合文法
			printf("\n分析成功,合法字符串!");
		else
			printf("\n分析失败,非法字符串!");
	}
	return 0;
}

2 E()

/*****************************************************************************
 *函数名称:E()
 *函数类型:void
 *参数:void
 *功能:分析文法,输出信息
 *****************************************************************************/
void E(){
    
    
	print();					//打印当前步骤信息
	printf("E->TE'\n");			//输出表达式
	stack[stack_top] = 'E';		//将产生式右→左压栈
	stack[++stack_top] = '\'';	//代表E'
	stack[++stack_top] = 'T';
	step++;						//分析步数+1
	T();
	E1();
}

3 E1()

/*****************************************************************************
 *函数名称:E1()
 *函数类型:void
 *参数:void
 *功能:分析文法,输出信息
 *****************************************************************************/
void E1(){
    
    
	if (token[lookahead] == '+'){
    
    		//若待分析字符匹配‘+’
		print();						//打印当前步骤信息
		printf("E'->+TE'\n");			//输出表达式
		stack[--stack_top] = 'E';		//将产生式右→左压栈
		stack[++stack_top] = '\'';
		stack[++stack_top] = 'T';
		step++;							//分析步数+1
		lookahead++;					//因为匹配到一个终结符,所以分析下一个字符
		T();
		E1();
	}
	else{
    
    
		print();						//打印当前步骤信息
		printf("E'->ε\n");				//输出表达式
		stack[stack_top--] = NULL;		//出栈
		stack[stack_top--] = NULL;		//E'虽然为一个非终结符,但占两个字符,T'同
		step++;							//分析步数+1
	}
}

4 T()

/*****************************************************************************
 *函数名称:T()
 *函数类型:void
 *参数:void
 *功能:分析文法,输出信息
 *****************************************************************************/
void T(){
    
    
	print();					//打印当前步骤信息
	printf("T->FT'\n");			//输出表达式
	stack[stack_top] = 'T';		//将产生式右→左压栈
	stack[++stack_top] = '\'';
	stack[++stack_top] = 'F';
	step++;						//分析步数+1
	F();
	T1();
}

5 T1()

/*****************************************************************************
 *函数名称:T1()
 *函数类型:void
 *参数:void
 *功能:分析文法,输出信息
 *****************************************************************************/
void T1(){
    
    
	if (token[lookahead] == '*'){
    
    		//若待分析字符匹配‘*’
		print();						//打印当前步骤信息
		printf("T'->*FT'\n");			//输出表达式
		stack[--stack_top] = 'T';		//将产生式右→左压栈
		stack[++stack_top] = '\'';
		stack[++stack_top] = 'F';
		step++;							//分析步数+1
		lookahead++;					//因为匹配到一个终结符,所以分析下一个字符
		F();
		T1();
	}
	else{
    
    
		print();						//打印当前步骤信息
		printf("T'->ε\n");				//输出表达式
		stack[stack_top--] = NULL;		//出栈
		stack[stack_top--] = NULL;
		step++;							//分析步数+1
	}
}

6 F()

/*****************************************************************************
 *函数名称:F()
 *函数类型:void
 *参数:void
 *功能:分析文法,输出信息
 *****************************************************************************/
void F(){
    
    
	if (token[lookahead] == 'i'){
    
    		//若待分析字符匹配‘i’
		print();						//打印当前步骤信息
		printf("F->i\n");				//输出表达式
		stack[stack_top--] = NULL;		//出栈
		step++;							//分析步数+1
		lookahead++;					//因为匹配到一个终结符,所以分析下一个字符
	}
	else if (token[lookahead] == '('){
    
    	//若待分析字符匹配‘(’
		print();						//打印当前步骤信息
		printf("F->(E)\n");				//输出表达式
		stack[stack_top] = 'E';			//将产生式右→左压栈
		step++;							//分析步数+1
		lookahead++;					//因为匹配到一个终结符,所以分析下一个字符
		E();
		if (token[lookahead] == ')'){
    
    	//若待分析字符匹配‘)’
			lookahead++;				//因为匹配到一个终结符,所以分析下一个字符
		}
		else{
    
    
			printf("没有')'匹配!\n");
			return;
		}
	}
	else{
    
    
		printf("error\n");
		return;
	}
}

7 print()

/*****************************************************************************
 *函数名称:print()
 *函数类型:void
 *参数:void
 *功能:打印信息
 *****************************************************************************/
void print(){
    
    
	int i;
	printf("%d\t", step);						//打印分析第step步
	for (i = 0; i <= stack_top; i++)			//输出分析栈中内容
		printf("%c", stack[i]);
	if (stack_top < 7)							//每列对齐
		printf("\t\t");
	else
		printf("\t");
	for (i = 0; i < lookahead; i++){
    
    			//若字符已被分析,它的位置置空,保持列对齐
		token[i] = ' ';
		printf("%c", token[i]);
	}
	for (i = lookahead; i < token_len; i++)		//输出剩余待分析字符
		printf("%c", token[i]);
	printf("\t");								//为输出表达式做准备
}

猜你喜欢

转载自blog.csdn.net/qq_44714521/article/details/106972173
今日推荐