【编译原理】 C++实现LL(1)文法(超详细,掰碎了给你吃!)FIRST集,FOLLOW集,预测分析表算法思想

实验目的:

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

简单要求:

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

(1)E->TG

(2)G->+TG

(3)G->ε

(4)T->FS

(5)S->*FS

(6)S->ε

(7)F->(E)

(8)F->I

高要求:

(1)手动输入文法;

(2)显示文法的非终结符的FOLLOW集;

(3)显示规则的可选集合;

(4)构造预测分析表;

(5)对任意输入的符号串进行分析

实验过程:

first集的算法思想:
如果产生式右部第一个字符为终结符,则将其计入左部first集  如果产生式右部第一个字符为非终结符执行以下步骤求该非终结符的first集 将该非终结符的非$first集计入左部的first集
若存在$,则将指向产生式的指针右移
若不存在$,则停止遍历该产生式,进入下一个产生式
若已经到达产生式的最右部的非终结符,则将$加入左部的first集
处理数组中重复的first集中的终结符

follow集的算法思想:
   对于文法G中每个非终结符A构造FOLLOW(A)的办法是,连续使用下面的规则,直到每个FOLLOW不在增大为止.
  对于文法的开始符号S,置#于FOLLOW(S)中;
  若A->aBb是一个产生式,则把FIRST(b)\{ε}加至FOLLOW(B)中;

若A->aB是一个产生式,或A->aBb是一个产生式而b=>ε(即ε∈FIRST(b))则把FOLLOW(A)加至FOLLOW(B)中

生成预测分析表的算法思想:
构造分析表M的算法是:
对文法G的每个产生式A->a执行第二步和第三步;
对每个终结符a∈FIRST(a),把A->a加至M[A,a]中;
若ε∈FIRST(a),则把任何b∈FOLLOW(A)把A->a加至M[A,b]中;
把所有无定义的M[A,a]标上出错标志.

对符号串的分析过程:
 预测分析程序的总控程序在任何时候都是按STACK栈顶符号X和当前的输入符号行事的,对于任何(X,a),总控程序
 每次都执行下述三种可能的动作之一;
若X=a=”#”,则宣布分析成功,停止分析过程.
若X=a≠”#”,则把X从STACK栈顶逐出,让a指向下一个输入符号.
若X是一个非终结符,则查看分析表M,若M[A,a]中存放着关于X的一个产生式,那么,首先把X逐出STACK栈顶,然后把产生式的右部符号串按反序一一推进STACK栈(若右部符号为ε,则意味着不推什么东西进栈).在把产生式的右部符号推进栈的同时应做这个产生式相应得语义动作,若M[A,a]中存放着”出错标志”,则调用出错诊察程序ERROR.

实验代码:

#include<iostream>
#include<string>
#include<map>
#include<vector>
#include<stack>
#include<set>
#include<cstring>
using namespace std;

map<char, int>getnum;        //map容器有键跟键对应的值两个变量,组成一个pair对象。
char getchar_[100];         //获得对应字符
vector<string>proce;       //定义了一个容器向量   用来存储产生式的。
int table[100][100];      //构建预测分析表
int num = 0;
int numvt = 0;     //numvt是终结符集合,0是‘#’,numvt表空字
string first[100];
string follow[200];
void readin()//将所需要的各类资料读取进去.
{
	memset(table, -1, sizeof(table));//将table的地址空间全部置-1,memset是内存重置函数,第一个参数是要重置的首地址,第二个是要重置的结果,第三个是重置的大小,sizeof是计算table内存空间大小的意思
	getnum['#'] = 0;
	getchar_[0] = '#';
	cout << "请输入所有的终结符:" << endl;
	char x;
	do
	{
		cin >> x;
		getnum[x] = ++num;
		getchar_[num] = x;
	} while (cin.peek() != '\n');//cin.peek()的返回值是一个char型的字符,其返回值是指针指向的当前字符,但它只是观测指针停留在当前位置并不后移;	
	numvt = ++num;
	getnum['@'] = numvt;        //空字,因为ε无法显示所以用@来代替
	getchar_[num] = ('@');
	cout << "请输入所有非终结符:" << endl;
	do
	{
		cin >> x;
		getnum[x] = ++num;
		getchar_[num] = x;
	} while (cin.peek() != '\n');
	cout << "输入产生式集合(空字用‘@’表示),以‘end’结束:" << endl;
	string pro;
	while (cin >> pro && pro != "end")
	{
		string ss;
		ss += pro[0];
		for (int i = 3; i < pro.size(); i++)//从3开始是因为前面有?->
		{
			if (pro[i] == '|')
			{
				proce.push_back(ss);
				ss.clear();
				ss += pro[0];
			}
			else
			{
				ss += pro[i];
			}
		}
		proce.push_back(ss);//作用是字符串之后插入一个字符。
	}
}
void bingji(string& a, string b)  //a=a or b   取a,b并集赋值给a
{
	set<char>se;
	for (int i = 0; i < a.size(); i++)
		se.insert(a[i]);
	for (int i = 0; i < b.size(); i++)
		se.insert(b[i]);
	string ans;
	set<char>::iterator it;//这条语句定义了一个名为it的变量,iterator为迭代器:提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节。 
	for (it = se.begin(); it != se.end(); it++)
		ans += *it;
	a = ans;
}
string get_f(int vn, int& has_0)     //推出vn能推出的不含空字的vt集合,并且判断vn能否推出空字
{
	if (vn == numvt)has_0 = 1;       //为@  则赋值使得可以继续下去
	if (vn < numvt)return first[vn];//为终结符,归入first中
	string ans;
	for (int i = 0; i < proce.size(); i++)
	{
		if (getnum[proce[i][0]] == vn)//如果产生式左部等于非终结符,就进入非终结符中继续查询
			ans += get_f(getnum[proce[i][1]], has_0);
	}
	return  ans;
}
void getfirst()
{
	for (int i = 1; i <= numvt; i++)     //终结符,first集是其本身。
	{
		first[i] += ('0' + i);
	}
	for (int j = 0; j < proce.size(); j++)    //扫描所有产生式
	{
		int k = 0;        //k扫瞄该产生式
		int has_0 = 0;
		do {
			has_0 = 0;
			k++;
			if (k == proce[j].size())  //推到最后一个了,则附加空字
			{
				first[getnum[proce[j][0]]] += ('0' + numvt);
				break;
			}                     //合并之
			bingji(first[getnum[proce[j][0]]], get_f(getnum[proce[j][k]], has_0));//如果前面没有空集,就将空集排除
		} while (has_0);  //到无法推出空字为止
	}
}
void print_first()
{
	cout << "first集:" << endl;
	for (int i = numvt + 1; i <= num; i++)
	{
		cout << "first [" << getchar_[i] << "]: ";
		for (int j = 0; j < first[i].size(); j++)
			cout << getchar_[first[i][j] - '0'] << " ";
		cout << endl;
	}
	cout << endl;
}
void getfollow()
{
	bingji(follow[getnum[proce[0][0]]], "0");  //先添加‘#’;
	for (int j = 0; j < proce.size(); j++)       //扫所有产生式
	{
		for (int jj = 1; jj < proce[j].size(); jj++)   //每个非终结符的follow集
		{
			if (getnum[proce[j][jj]] <= numvt)continue;  //为终结符,vt无follow集
			int k = jj; int has_0;
			do
			{
				has_0 = 0;
				k++;
				if (k == proce[j].size())   //都能推出空字,follow集=产生式左边的vn,
				{
					bingji(follow[getnum[proce[j][jj]]], follow[getnum[proce[j][0]]]);
					break;
				}
				bingji(follow[getnum[proce[j][jj]]], get_f(getnum[proce[j][k]], has_0));
			} while (has_0);
		}
	}
}
void gettable()          //得预测分析表
{
	for (int i = 0; i < proce.size(); i++)   //扫所有产生式
	{
		if (proce[i][1] == '@')     //直接推出空字的,把follow集加进去(follow集=产生式左边的follow中元素填)
		{
			string flw = follow[getnum[proce[i][0]]];
			for (int k = 0; k < flw.size(); k++)
			{
				table[getnum[proce[i][0]]][flw[k] - '0'] = i;
			}
		}
		string temps = first[getnum[proce[i][1]]];
		for (int j = 0; j < temps.size(); j++)               //考察first集
		{
			if (temps[j] != ('0' + numvt))//没有空字终结符
			{
				table[getnum[proce[i][0]]][temps[j] - '0'] = i;//字符型减'0'可以得到整数型
			}
			else                                     //有空字终结符的,考察follw集
			{
				string flw = follow[getnum[proce[i][1]]];
				for (int k = 0; k < flw.size(); k++)
				{
					table[getnum[proce[i][0]]][flw[k] - '0'] = i;
				}
			}
		}
	}
}
string get_proce(int i)  //由对应下标获得对应产生式。
{
	if (i < 0)return " ";    //无该产生式
	string ans;
	ans += proce[i][0];
	ans += "->";
	for (int j = 1; j < proce[i].size(); j++)
		ans += proce[i][j];
	return ans;
}
void print_table()
{
	cout << "预测分析表:" << endl;
	for (int i = 0; i < numvt; i++)
		cout << '\t' << getchar_[i];
	cout << endl;
	for (int i = numvt + 1; i <= num; i++)
	{
		cout << getchar_[i];
		for (int j = 0; j < numvt; j++)
		{
			cout << '\t' << get_proce(table[i][j]);
		}
		cout << endl;
	}
	cout << endl;
}
void print_follow()
{
	cout << "follow集:" << endl;
	for (int i = numvt + 1; i <= num; i++)
	{
		cout << "follow [" << getchar_[i] << "]: ";
		for (int j = 0; j < follow[i].size(); j++)
			cout << getchar_[follow[i][j] - '0'] << " ";
		cout << endl;
	}
	cout << endl;
}
string word;
string shuchu;
bool analyze()       //总控,分析字word的合法性,若合法,输出所有产生式。
{
	stack<char>sta;
	sta.push('#');
	sta.push(proce[0][0]);
	shuchu.push_back('#');
	shuchu.push_back(proce[0][0]);
	int i = 0;
	while (!sta.empty())
	{
		int cur = sta.top();//取出栈顶元素
		sta.pop();        //删除栈顶元素
		if (cur == word[i])       //是终结符,推进
		{
			word[i] = NULL;
			if (!shuchu.empty())
				shuchu.pop_back();
			i++;
		}
		else  if (cur == '#')   //成功,结束
		{
			return 1;
		}
		else  if (table[getnum[cur]][getnum[word[i]]] != -1) //查表,最初预测分析表全定义为-1
		{
			int k = table[getnum[cur]][getnum[word[i]]];
			cout << shuchu << "   ";
			cout << word << "#" << " ";
			cout << proce[k][0] << "->";
			for (int j = 1; j < proce[k].size(); j++)
				cout << proce[k][j];
			cout << endl; shuchu.pop_back();
			for (int j = proce[k].size() - 1; j > 0; j--)  //逆序入栈
			{
				if (proce[k][j] != '@') {
					sta.push(proce[k][j]);
					shuchu.push_back(proce[k][j]);
				}
			}
		}
		else      //失败!
		{
			return 0;
		}
	}
	return 1;
}
int main()
{
	readin();
	getfirst();
	getfollow();
	getfollow();
	gettable();
	print_first();
	print_follow();
	print_table();
	cout << "请输入字:" << endl;
	cin >> word;
	if (analyze())
		cout << "succeed!该字有效,所用产生式如上。" << endl;
	else   cout << "error!" << endl;
	system("pause");
	return 0;
}

实验结果:

  • 22
    点赞
  • 168
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
文法的FirstFollow编译原理中的重要概念,它们可以用来帮助分析和处理文法,对于学习编译原理的同学来说是非常重要的。下面是一个简单的算法来求解文法的FirstFollow: 1. First求解算法动态演示 输入:文法G 输出:文法G的所有非终结符和对应的First 步骤: 1. 初始化每个非终结符的First为空 2. 对于文法G中的每个终结符a,将a加入到对应的非终结符的First中 3. 对于文法G中的每个产生式A -> B1B2...Bn,按照以下规则求出A的First: a. 如果B1是终结符或者空串,则将B1加入到A的First中 b. 如果B1是非终结符,则将B1的First中除空串之外的所有元素加入到A的First中 c. 如果B1可以推出空串,则将B2的First中除空串之外的所有元素加入到A的First中 d. 重复上述过程,直到Bn的First被处理完毕 4. 返回所有非终结符的First 2. Follow求解算法动态演示 输入:文法G和开始符号S 输出:文法G的所有非终结符和对应的Follow 步骤: 1. 初始化每个非终结符的Follow为空 2. 将S的Follow中加入$符号 3. 对于文法G中的每个产生式A -> B1B2...Bn,按照以下规则求出B1、B2、...Bn的Follow: a. 如果B1是非终结符,则将A的Follow中除空串之外的所有元素加入到B1的Follow中 b. 如果B1可以推出空串,则将A的Follow中的所有元素加入到B2的Follow中,重复此过程直到Bn的Follow被处理完毕 c. 如果B1是终结符,则无需处理 4. 对于文法G中的每个产生式A -> B1B2...Bn,按照以下规则求出B2、B3、...Bn的Follow: a. 如果Bi是非终结符,则将Bi+1的First中除空串之外的所有元素加入到Bi的Follow中 b. 如果Bi可以推出空串,则将A的Follow中的所有元素加入到Bi的Follow中,重复此过程直到Bn的Follow被处理完毕 c. 如果Bi是终结符,则无需处理 5. 返回所有非终结符的Follow

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小树ぅ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值