C++ 实现自动产生LR1分析器的产生器

1 介绍

对形如 G[E]: E–>E+T | T   的文法,生成它的LR(1)分析表,并根据此LR(1)分析表,对输入的语句 (形如 #i+i×i# ) 进行语法分析判断合法性。
                        T–>T×F | F
                        F–>(E) | i
其中大写字符表示非终结符小写字母表示终结符希腊字母表示文法符号串

生成的LR(1)分析表
LR1分析表
生成的语句i+i*i的LR分析过程
分析过程
 

2 总体思路

  • 将文法拓广为G[M]
  • 计算每个字符的FIRST集合
  • 计算每个闭包的项目集以及GO函数
  • 计算分析表的动作函数ACTION和状态转换函数GOTO
  • 输入需分析的语句,根据已知的分析表,对该语句进行语法分析,并输出该分析过程
     

2.1 拓广文法

  • 读取输入的文法
  • 将文法拓广为G[M],即引入新的开始符号M,并把形如 X–>a | b ··· 的产生式分解为 X–>a 和 X–>b
  • 区分终结符和非终结符
vector< vector<char> > G; //文法G[S]产生式 ,~为空字
unordered_map<char, set<char> > ts; //终结符(char)terminal symbol,及它的first集合(set<char>)
unordered_map<char, set<char> > nts; //非终结符(char)non-terminal symbol,及它的first集合(set<char>)
void read_G() {
    //读取文法G[S]->G'[M],并区分终结符和非终结符 
	char ch; //当前读入的字符
	int i = 0; //当前行读取的第i个字符 
	vector<char> v; //存放输入的一行产生式 
	char X; //若遇到形如X->α|β|……,用于保存X以便消除|
	set<char> m;
	nts['M'] = m;
	while (ch = getchar()) {
    
		if (ch == '#') break;
		if (ch == '\n') {
    //换行 
			if (!v.empty())G.push_back(v);
			v.clear();
			i = 0;
			continue;
		}
		if (ch != ' ' || ch != '\t') {
    //去掉空格等多余字符 
			if (ch == '|') {
    //消除元语言符号'或|'
				G.push_back(v);
				v.clear();
				i = 3;
				v.push_back(X);
				continue;
			}
			i++;
			if (i == 1) {
   
				X = ch;
				nts[ch] = m; //产生式左边(第一个字符)的为非终结符 
			}
			else if (i != 2 && i != 3&&ch!='~') ts[ch] = m; //此时ts里既有非终结符又有终结符
			if (i != 2 && i != 3)v.push_back(ch); //去掉产生式的-> 
		}
	}
	if (G.empty()) exit(0);

	//加入新树根M
	v.clear();
	v.push_back('M');
	v.push_back(G[0][0]);
	G.insert(G.begin(), v);

	//去掉ts中的非终结符
	for (unordered_map<char, set<char> >::iterator it = nts.begin(); it != nts.end(); it++) {
   
		unordered_map<char, set<char> >::iterator iter;
		iter = ts.find(it->first);
		if (iter != ts.end())ts.erase(iter);
	}
}

2.2 计算First集合

  • 终结符的First集合就是它自己
  • 非终结符的First集合构造方法:
      连续使用下述规则,直到每个集合的First不再增大为止。
       (1) 若有产生式X–>a···,则把a加入到First(X)中;若X–>ε ,则把 ε 加入First(X)中。
       (2) 若有X–>Y···,则将First(Y)加入First(X)中,具体步骤如下:
          ① 将First(Y)中所有非 ε 元素都加入First(X)中;
          ② 若First(Y)中存在 ε :
              若X–>Y,则将 ε 加入First(X)中;
              若X–>Ya···,则将 a 加入First(X)中;
              若X–>YZ···,则将First(Z)加入First(X)中,即重复步骤(2);
vector< vector<char> > G; //文法G[S]产生式 ,~为空字
unordered_map<char, set<char> > ts; //终结符(char)terminal symbol,及它的first集合(set<char>)
unordered_map<char, set<char> > nts; //非终结符(char)non-terminal symbol,及它的first集合(set<char>)
void get_First() {
    //得到First集合
	for (auto &it : ts) it.second.insert(it.first);//终结符的first集合是它自己
	
	//求非终结符的First集合
	int r = 0;
	int change = 1;
	while (change) {
   
		if (r == 20)break;
		r++;
		change = 0;
		for (auto &it : nts) {
    //对每个非终结符
			for (unsigned int i = 0; i < G.size(); i++) {
    //遍历产生式
				if (G[i][0] == it.first) {
   
					unsigned int size = it.second.size(); //操作前First(X)的大小
					unordered_map<char, set<char> >::iterator iter=ts.find(G[i][1]);
					if (ts.find(G[i][1]) != ts.end()||G[i][1]=='~') {
    //形如X->a……或X->空字,把a加入First(X)中
						it.second.insert(G[i][1]);
						if (it.second.size() > size) change = 1;
					}
					else {
     //形如X->Y……,把First(Y)加入First(X)
						unsigned int col = 1;
						while(1){
    //若X->Y1Y2……,循环把First(Y)加入First(X)
							int flag = 0; //标记当前First(Y)中是否有空字
							unordered_map<char, set<char> >::iterator itt= nts.find(G[i][col]);
							for(auto &iter:itt->second){
    //遍历First(Y)
								if (iter == '~') flag = 1;
								else it.second.insert(iter);
							}
							if (flag) {
   
								col++;
								if (G[i].size() <= col) {
   
									it.second.insert('~'); //形如X->Y,将空字加入First(X)
									break;
								}
								else if (ts.find(G[i][col]) != ts.end()) {
    //形如X->Ya……,将a加入First(X)
									it.second.insert(G[i][col]);
									break;
								}
								else {
    //形如X->YZ……,将First(Z)加入First(X)
								}
							}
							else break;
						}
						if (it.second.size() > size) change = 1;
					}
				}
			}
		}
	}
}

2.3 计算每个闭包的项目集以及GO函数

  • 生成闭包I0 的第一个项目【M–>•S,#】,其中S为输入的产生式的开始符号(即为拓广前文法G[S]的开始符号)
  • 根据已知的项目,计算闭包CLOSURE(I) 的所有项目:
      (1) I的任何项目都属于CLOSURE(I)。
      (2) 若项目【A–>α•Bβ,a】属于CLOSURE(I) ,且B–>γ是一个产生式,那么对于First(βa)中的每个终结符b,如果【B–>•γ,b】原来不在CLOSURE(I)中,则把它加进去。
      (3) 重复执行步骤(2),直到 CLOSURE(I) 不再增大为止。
  • 根据已生成所有项目的闭包CLOSURE(I),计算它的GO函数,并生成该GO函数对应的新项目(若该新项目属于新闭包,就生成新的闭包;若该新项目属于已有闭包,就将它加入该闭包里)
  • 从闭包CLOSURE(0)开始,对每个闭包,先计算其所有项目,再计算它的GO函数,并生成新项目(可能生成新闭包),直到没有未处理的闭包。
void get_Closure() {
     //计算CLOSURE,包括GO
	int i = 0; //闭包编号
	C clo; //生成第一个闭包(I0)
	c.push_back(clo);
	while(1) {
   
		if (i == c.size()) break; //没有新闭包,跳出循环(即已获得全部闭包及项目)
		if (i == 0) {
    //确定项目集I0的第一个项目
			vector<char> v(G[0]);
			v.insert(v.begin() + 1, ' ');
			c[i].project.push_back(v);
			set<char> m;
			m.insert('#');
			c[i].outlook.push_back(m);
		}
		for (unsigned int j = 0; j < c[i].project.size(); j++){
     //遍历已有项目,生成该闭包所有项目
			for (unsigned int k = 0; k < c[i].project[j].size(); k++) {
     //扫描单个项目,找到当前位置·(这里用空格代替)
				if (c[i].project[j][k] == ' ') {
   
					if (k == c[i].project[j].size() - 1) break;  //形如X->β·,不会生成新的项目
					for (unsigned int x = 0; x < G.size(); x++) {
    //形如X->α·Yβ,  遍历G'[M],查找所有对应的产生式,以求出新的项目并加入项目集
						if (G[x][0] == c[i].project[j][k + 1]) {
    //对应的产生式
							vector<char> v(G[x]); //用于保存新项目
							v.insert(v.begin() + 1, ' '); //计算新项目
							int exist = 0; //标记该新项目是否已存在
							for (unsigned int y = 0; y < c[i].project.size(); y++) {
    //遍历已有项目,判断是新项目还是已有项目
								if (c[i].project[y] == v) {
    //已有项目,只需保存项目下标(用于添加新的展望串)
									exist = y; 
									break;
								}
							}
							if(exist==0) c[i].project.push_back(v); //新项目,加入项目集
							set<char> m; //用于保存新展望串
							//形如【形如X->α·Yβ,a】,计算展望串,即计算First(βa)
							//情况一:β为空字,First(βa)=a
							//情况二:β中第一个字符为结束符b,First(βa)=b
							//情况三:β中第一个字符为非结束符B,若First(B)中没有空字,First(βa)=First(B);
							//                                   若First(B)中包含空字,First(βa)=First(B)+First(γa),其中γ为β除去第一个字符后形成的符号串
							bool kong = true; //标记情况三B中是否有空字
							int t = 0; //表示当前符号串γ为β除去第t个字符
							while (kong) {
    //若为情况三且B含空字,计算First(γa)
								kong = false;
								if (k + t + 1 == c[i].project[j].size() - 1) {
     //情况一
									for (auto it : c[i].outlook[j]) m.insert(it);
								}
								else if (ts.find(c[i].project[j][k + t + 2]) != ts.end()) {
     //情况二
									m.insert(c[i].project[j][k + 2 + t]);
								}
								else {
     //情况三
									set<char> m1((nts.find(c[i].project[j][k + 2 + t]))->second);
									for (auto it : m1) {
   
										if (it== '~') {
     //含空字
											kong = true;
											t++;
										}
										else {
    //不含空字
											m.insert(it);
										}
									}
								}
							}
							if (exist) //已存在项目,将新展望串加入原展望串中
							{
   
								for (auto it : m) {
   
									c[i].outlook[exist].insert(it)
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值