C/C++实现编译原理自上而下语法分析(非递归,查LL(1)分析表)

文法产生式如下:

E->TE'

E'->+TE'|空

T->FT'

T'->*FT'|空

F->(E)|i

非递归实现,必须使用一个栈来完成。

总体思路:根据栈顶符号和输入串的的当前符号,来决定下一步的动作。如果栈顶是一个终结符,那么尝试将该终结符和输入串的的当前符号进行匹配,如果栈顶是一个非终结符,则查找LL(1)分析表,如果找到了某个产生式,则产生式左部符号出栈,右部符号逆序进栈(参照陈意云的编译原理教材)。程序中N表示空串。

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

#define VtNum 5
#define VnNum 5
#define ProNum 13
#define MaxSrcLength 20	//输入串最大长度

struct Table
{
	string leftAndCurChar;	//产生式左部符号和当前输入串符号。用string,不用char,可以直接存放、处理E'和T’
	vector<string> right;	//产生式右部,T和E'分别存在两个string中,一起放入vector
};							//T和E'如果连在一起存放成一个string TE',将来要分割出文法符号T和E',会比较困难。


int IsVt(const string vt[], string x, int n);					//判断x是否是终结符
void Print(stack<string> s);									//打印栈
void Print(const string& src, int i);							//打印输入串
void Print(const string& left, const vector<string>& right);	//打印产生式
void Error();


int main()
{
	//终结符集合
	const string vt[VtNum] = { "i","+","*","(",")" };
	//非终结符集合
	const string vn[VnNum] = { "E","E'","T","T'","F" };
	//LL(1)分析表,如果这个表的规模大,可以动态申请内存,等会把这个表的内容存放到map之后,可以delete申请的内存
	Table table[ProNum] = {
		{"Ei",{"T","E'" }},		//E和i的交叉位置有一个产生式E->TE'		
		{"E(",{"T","E'"}},
		{"E'+",{"+","T","E'"}},
		{"E')",{"N"}},
		{"E'$",{"N"}},
		{"Ti",{"F","T'"}},
		{"T(",{"F","T'"}},
		{"T'+",{"N"}},
		{"T'*",{"*","F","T'"}},
		{"T')",{"N"}},
		{"T'$",{"N"}},
		{"Fi",{"i"}},
		{"F(",{"(","E",")"}}
	};
	//用map存储LL(1)分析表,如果map的底层实现是hash表,那么查找LL(1)表的时间复杂度是O(1),和表的大小规模无关。
	//如果是sorted map,那么查找的时间复杂度是O(log2N),此处N=13。
	map<string, vector<string>> table_map;	//string:E和i(Ei一起作为key)	vector<string>:T和E'
	for (int i = 0; i < ProNum; i++)
	{
		table_map.insert(pair<string, vector<string>>(table[i].leftAndCurChar, table[i].right));
	}

	//开始分析
	string src = "i*i+i*i+i+i$";
	stack<string> s;
	s.push("$");
	s.push("E");

	string x;	//栈顶符号,例如E
	string a;	//当前输入串符号,例如i
	string xa;	//例如Ei

	int i = 0;
	int step = 0;

	//输出“输入串”时,占MaxSrcLength宽度
	cout << "步骤" << "\t\t" << "栈" << "\t\t" << setw(MaxSrcLength) << "输入串" << "\t\t" << "动作" << endl;

	cout << step++ << "\t\t";
	Print(s);
	Print(src, i);
	cout << endl;

	bool flag = true;
	while (flag)
	{
		cout << step++ << "\t\t";
		x = s.top();  //E
		a = string(1, src[i]);	//i
		xa = x + a;		//Ei
		s.pop();

		if (IsVt(vt, x, VtNum))
		{
			if (x == a)
			{
				i++;
				a = src[i];
				Print(s);
				Print(src, i);
				cout << endl;
			}
			else
				Error();
		}
		else if (x == "$")
		{
			if (x == a)
			{
				cout << "accept!" << endl;
				flag = false;
			}
			else
				Error();
		}
		else if (table_map.contains(xa))   
		{								
			vector<string> right = table_map[xa];
			if (right.at(0) != "N")		//不是空产生式
			{
				for (int i = right.size() - 1; i >= 0; i--)
				{
					s.push(right.at(i));
				}
			}
			Print(s);
			Print(src, i);
			Print(x, right);
		}
		else
		{
			Error();
		}
	}
	return 0;
}


int IsVt(const string vt[], string x, int n)
{
	for (int i = 0; i < n; i++)
	{
		if (vt[i] == x)
		{
			return 1;
		}
	}
	return 0;
}


void Print(stack<string> s)
{
	vector<string> reverse;	//逆序存放原来的stack

	while (s.size() > 0)
	{
		reverse.push_back(s.top());
		s.pop();
	}

	for (int i = reverse.size() - 1; i >= 0; i--)
	{
		cout << reverse.at(i);
	}

	cout << "\t\t";
}

void Print(const string& src, int i)
{
	string temp(src.length() - i, ' ');

	for (unsigned int j = i, k = 0; j < src.length(); j++, k++)
		temp[k] = src[j];

	cout << right << setw(MaxSrcLength) << temp;	//输出“输入串”时,右对齐,占MaxSrcLength宽度
	cout << "\t\t";
}

void Print(const string& left, const vector<string>& right)
{
	cout << left << "->";

	for (unsigned int i = 0; i < right.size(); i++)
	{
		cout << right.at(i);
	}
	cout << endl;
}

void Error()
{
	cout << "error!" << endl;
	exit(-1);
}

运行结果如下,其中N表示空串。

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
LL(1)语法分析器是一种自顶向下的语法分析器,它可以根据一个文法推导出输入串是否符合该文法。在C++中,可以使用递归下降法来实现LL(1)语法分析器。 递归下降法是一种简单的语法分析方法,它将每个非终结符示为一个函数,并且每个函数对应于一个产生式。在分析过程中,每次调用一个非终结符函数,就相当于使用该非终结符对应的产生式进行推导。 具体实现上,我们可以使用一个预测分析来辅助递归下降法进行分析。预测分析是一个二维数组,其中行示非终结符,列示终结符,每个单元格中存储了一个产生式编号。根据当前的非终结符和输入符号,就可以在预测分析找到对应的产生式编号,进而调用相应的函数进行推导。 在实现LL(1)语法分析器时,需要注意以下几个问题: 1. 预测分析的构建:需要对文法进行预处理,计算出每个非终结符对应的FIRST集合和FOLLOW集合,进而得到预测分析中每个单元格中应该存储的产生式编号。 2. 错误处理:在分析过程中,如果发现输入串不符合该文法,需要进行错误处理。常见的错误处理方式包括跳过当前输入符号、插入缺失的符号、替换错误的符号等。 3. 性能优化:递归下降法的性能可能会受到文法的复杂度、递归深度等因素的影响。为了提高分析效率,可以使用一些优化技巧,比如缓存预测分析、避免重复计算、使用尾递归等。 总之,LL(1)语法分析器是一个比较基础的语法分析实现,但在实际应用中也有一定的局限性。如果需要处理更加复杂的语法或者需要更高的性能,可以考虑使用其他类型的语法分析器,比如LR分析器、Earley分析器等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值