编译原理LL(1)文法分析

一、实验目的: 

根据某一文法编制调试LL(1)分析程序,以便对任意输入的符号串进行分析。本次实验的目的主要是加深对预测分析LL(1)分析法的理解。

二、实验说明

1、LL(1)分析法的功能

LL(1)分析法的功能是利用LL(1)控制程序根据显示栈栈顶内容、向前看符号以及LL(1)分析表,对输入符号串自上而下的分析过程。

2、LL(1)分析法的前提

改造文法:消除二义性、消除左递归、提取左因子,判断是否为LL(1)文法,

3、LL(1)分析法实验设计思想及算法

三、实验要求

(一)准备: 

1.阅读课本有关章节,

2.考虑好设计方案;

3.设计出模块结构、测试数据,初步编制好程序。

(二)上课上机: 

将源代码拷贝到机上调试,发现错误,再修改完善。第二次上机调试通过。

(三)程序要求:

程序输入/输出示例: 

对下列文法,用LL(1)分析法对任意输入的符号串进行分析: 

(1)E->TG

(2)G->+TG|-TG

(3)G->ε

(4)T->FS

(5)S->*FS|/FS

(6)S->ε

(7)F->(E)

(8)F->i

输出的格式如下:

(1)LL(1)分析程序,编制人:姓名,学号,班级

(2)输入一以#结束的符号串(包括+—*/()i#):在此位置输入符号串 

(3)输出过程如下:

步骤 

分析栈 

剩余输入串 

所用产生式 

1

E

i+i*i#

E->TG

 (4)输入符号串为非法符号串(或者为合法符号串)

(1)在“所用产生式”一列中如果对应有推导则写出所用产生式;如果为匹配终结符则写明匹配的终结符;如分析异常出错则写为“分析出错”;若成功结束则写为“分析成功”。

(2) 在此位置输入符号串为用户自行输入的符号串。

(3)上述描述的输出过程只是其中一部分的。 

注意:1.表达式中允许使用运算符(+-*/)、分割符(括号)、字符i,结束符#; 

2.如果遇到错误的表达式,应输出错误提示信息(该信息越详细越好);

3.对学有余力的同学,测试用的表达式事先放在文本文件中,一行存放一个表达式,同时以分号分割。同时将预期的输出结果写在另一个文本文件中,以便和输出进行对照;

 (四)程序思路

模块结构:

(1)定义部分:定义常量、变量、数据结构。

(2)初始化:设立LL(1)分析表、初始化变量空间(包括堆栈、结构体、数组、临时变量等);

(3)控制部分:从键盘输入一个表达式符号串;

(4)利用LL(1)分析算法进行表达式处理:根据LL(1)分析表对表达式符号串进行堆栈(或其他)操作,输出分析结果,如果遇到错误则显示错误信息。

(五)练习该实验的目的和思路: 

程序相当复杂,需要利用到大量的编译原理,也用到了大量编程技巧和数据结构,通过这个练习可大大提高软件开发能力。

(六)为了能设计好程序,注意以下事情:

1.模块设计:将程序分成合理的多个模块(函数),每个模块做具体的同一事情。

2.写出(画出)设计方案:模块关系简图、流程图、全局变量、函数接口等。

3.编程时注意编程风格:空行的使用、注释的使用、缩进的使用等。

  • 实验环境

Visual Studio 2019, Windows 10

  • 实验步骤
  • 对此文法写出分析表;

i

+

-

*

/

(

)

#

E

TG

TG

G

+TG

-TG

ε

ε

T

FS

FS

S

ε

ε

*FE

/FE

ε

ε

F

i

(i)

  •   代码实现如下:
char Vn[M] = { 'E', 'G', 'T', 'S', 'F' };
char Vt[N] = { 'i', '+', '-', '*', '/', '(', ')', '#' };
string L[M][N] = {
	{ "TG", "ERROR", "ERROR", "ERROR", "ERROR", "TG", "ERROR", "ERROR" },
	{ "ERROR", "+TG", "-TG", "ERROR", "ERROR", "ERROR", "NULL", "NULL" },
	{ "FS", "ERROR", "ERROR", "ERROR", "ERROR", "FS", "ERROR", "ERROR" },
	{ "ERROR", "NULL", "NULL", "*FS", "/FS", "ERROR", "NULL", "NULL" },
	{ "i", "ERROR", "ERROR", "ERROR", "ERROR", "(i)", "ERROR", "ERROR" }
};
  • 栈输出函数设计

定义了一个暂存栈,先将分析栈的数据逐个弹出并压入暂存栈。全部存完后,将分析栈的数据逐个弹出输出并回分析栈。

代码实现如下:

void outstack()
{
	//定义一个栈暂存数据
	stack<char> temp;
	//存放
	int l = analyse.size();
	for (int i = 0; i < l; i++)
	{
		char a = analyse.top();
		analyse.pop();
		temp.push(a);
	}
	//放回输出
	l = temp.size();
	for (int i = 0; i < l; i++)
	{
		char a = temp.top();
		temp.pop();
		analyse.push(a);
		cout << a;
	}
}
  • LL(1)分析算法设计如下:
  1. 先将‘#’与文法开始符E压入分析栈,并使输入字符指向input的第一位;
  2. 弹出栈顶元素v,判断v是否与输入字符相同,是转至3,否转至4;
  3. 输入字符移向input下一位,转至3;若已为最后一位转至8;
  4. 判断v是否为非终结符,是转至5;否转至9;
  5. 判断v与输入字符是否有产生式,是转至6,否转至9;
  6. 判断产生式是否为ε是转至2,否转至7;
  7. 将产生式逆序压入分析栈中,转至2;
  8. 输出过程步骤,以及为合法字符串;
  9. 输出错误位置并结束;

代码实现如下:

void LL_1(string input)
{
	//压入#和开始符
	analyse.push('#');
	analyse.push(Vn[0]);
	//
	cout << "步骤" << "\t剩余分析栈" << "\t剩余输入串" << "\t所用产生式" << "\t动作" << endl;
	cout << '0' << "\t";
	outstack();
	cout << "\t\t" << input;
	cout << "\t\t\t\t" << "初始化" << endl;

	int i = 0;
	for (int j = 1; i < input.length(); j++)
	{
		//取出栈顶
		char v = analyse.top();
		analyse.pop();
		//判断是否匹配
		if (v == input[i])
		{
			cout << j << "\t";
			outstack();
			cout << "\t\t";
			outinput(input, i);
			cout << "\t\t" << input[i] << "匹配" << "\t\tPOP" << endl;
			i++;
			continue;
		}
		else
		{
			int m, n;
			m = -1;
			//未匹配时,判断栈弹出的字符是否为非终结符
			for (int j = 0; j < M; j++)
			{
				if (v == Vn[j])
				{
					m = j;
					break;
				}

			}
			if (m == -1)
			{
				cout << "fzjf";
				cout << "第" << i << "个字符" << input[i] << "匹配错误" << endl;
				cout << input << "为非法符号串" << endl;
				break;
			}
			//栈弹出的字符为非终结符
			for (int j = 0; j < N; j++)
			{
				if (input[i] == Vt[j])
				{
					n = j;
					break;
				}
			}
			//匹配情况
			if (L[m][n] == "ERROR")
			{
				cout << "第" << i << "个字符" << input[i] << "匹配错误" << endl;
				cout << input << "为非法符号串" << endl;
				break;
			}
			if (L[m][n] == "NULL")
			{
				cout << j << "\t";
				outstack();
				cout << "\t\t";
				outinput(input, i);
				cout << "\t\t\t\t" << v << "->" << "ε" << endl;
				continue;
			}
			else
			{
				//
				string t = "";
				for (int j = L[m][n].length() - 1; j >= 0; j--)
				{
					t += L[m][n][j];
					analyse.push(L[m][n][j]);
				}
				cout << j << "\t";
				outstack();
				cout << "\t\t";
				outinput(input, i);
				cout << "\t\t" << v << "->" << L[m][n];
				cout << "\t\tPOP,PUSH(" << t << ")";
			}
		}
		cout << endl;
	}
	if(i == input.length())
		cout << input << "为合法符号串" << endl;
}

运行结果:

代码总览:

#include<iostream>
#include<string>
#include<stack>
#define M 5	//非终结符数
#define N 8	//终结符数

using namespace std;

stack<char> analyse;
char Vn[M] = { 'E', 'G', 'T', 'S', 'F' };
char Vt[N] = { 'i', '+', '-', '*', '/', '(', ')', '#' };
string L[M][N] = {
	{ "TG", "ERROR", "ERROR", "ERROR", "ERROR", "TG", "ERROR", "ERROR" },
	{ "ERROR", "+TG", "-TG", "ERROR", "ERROR", "ERROR", "NULL", "NULL" },
	{ "FS", "ERROR", "ERROR", "ERROR", "ERROR", "FS", "ERROR", "ERROR" },
	{ "ERROR", "NULL", "NULL", "*FS", "/FS", "ERROR", "NULL", "NULL" },
	{ "i", "ERROR", "ERROR", "ERROR", "ERROR", "(i)", "ERROR", "ERROR" }
};

void outstack()
{
	//定义一个栈暂存数据
	stack<char> temp;
	//存放
	int l = analyse.size();
	for (int i = 0; i < l; i++)
	{
		char a = analyse.top();
		analyse.pop();
		temp.push(a);
	}
	//放回输出
	l = temp.size();
	for (int i = 0; i < l; i++)
	{
		char a = temp.top();
		temp.pop();
		analyse.push(a);
		cout << a;
	}
}

void outinput(string s, int n)
{
	for (; n < s.length(); n++)
		cout << s[n];
}

void LL_1(string input)
{
	//压入#和开始符
	analyse.push('#');
	analyse.push(Vn[0]);
	//
	cout << "步骤" << "\t剩余分析栈" << "\t剩余输入串" << "\t所用产生式" << "\t动作" << endl;
	cout << '0' << "\t";
	outstack();
	cout << "\t\t" << input;
	cout << "\t\t\t\t" << "初始化" << endl;

	int i = 0;
	for (int j = 1; i < input.length(); j++)
	{
		//取出栈顶
		char v = analyse.top();
		analyse.pop();
		//判断是否匹配
		if (v == input[i])
		{
			cout << j << "\t";
			outstack();
			cout << "\t\t";
			outinput(input, i);
			cout << "\t\t" << input[i] << "匹配" << "\t\tPOP" << endl;
			i++;
			continue;
		}
		else
		{
			int m, n;
			m = -1;
			//未匹配时,判断栈弹出的字符是否为非终结符
			for (int j = 0; j < M; j++)
			{
				if (v == Vn[j])
				{
					m = j;
					break;
				}

			}
			if (m == -1)
			{
				cout << "fzjf";
				cout << "第" << i << "个字符" << input[i] << "匹配错误" << endl;
				cout << input << "为非法符号串" << endl;
				break;
			}
			//栈弹出的字符为非终结符
			for (int j = 0; j < N; j++)
			{
				if (input[i] == Vt[j])
				{
					n = j;
					break;
				}
			}
			//匹配情况
			if (L[m][n] == "ERROR")
			{
				cout << "第" << i << "个字符" << input[i] << "匹配错误" << endl;
				cout << input << "为非法符号串" << endl;
				break;
			}
			if (L[m][n] == "NULL")
			{
				cout << j << "\t";
				outstack();
				cout << "\t\t";
				outinput(input, i);
				cout << "\t\t\t\t" << v << "->" << "ε" << endl;
				continue;
			}
			else
			{
				//
				string t = "";
				for (int j = L[m][n].length() - 1; j >= 0; j--)
				{
					t += L[m][n][j];
					analyse.push(L[m][n][j]);
				}
				cout << j << "\t";
				outstack();
				cout << "\t\t";
				outinput(input, i);
				cout << "\t\t" << v << "->" << L[m][n];
				cout << "\t\tPOP,PUSH(" << t << ")";
			}
		}
		cout << endl;
	}
	if(i == input.length())
		cout << input << "为合法符号串" << endl;
}

int main()
{
	cout << "输入一以#结束的符号串(包括+—*/()i#):";
	string in;
	cin >> in;
	cout << "匹配过程如下" << endl;
	LL_1(in);
	system("pause");
}

  • 18
    点赞
  • 136
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值