编译原理-LL1语法分析器(消除左递归+消除回溯)

本文档详细介绍了如何构造和实现LL1语法分析器,包括消除左递归和回溯的过程。实验要求涵盖文法构造、LL(1)分析表的生成、预测分析程序的编写以及文法数据结构的设计。文中还提供了具体的代码实现,展示了消除左递归前后的对比,并给出了两个测试样例的分析过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

编译原理-LL1语法分析器(消除左递归+消除回溯)

实验要求:

要求一

1、 给出文法如下:
G[E]:
E->T|E+T;
T->F|T*F;
F->i|(E);

2、 根据该文法构造相应的LL(1)文法及LL(1)分析表,并为该文法设计预测分析程序,利用C语言或C++语言或Java语言实现;
3、 利用预测分析程序完成下列功能:
1) 手工将测试的表达式写入文本文件,每个表达式写一行,用“;”表示结束;
2) 读入文本文件中的表达式;
3) 调用实验一中的词法分析程序搜索单词;
4) 把单词送入预测分析程序,判断表达式是否正确(是否是给出文法的语言),若错误,应给出错误信息;
5) 完成上述功能,有余力的同学可以进一步完成通过程序实现对非LL(1)文法到LL(1)文法的自动转换

要求二

1、 将一个可转换的非LL(1)文法转换为LL(1)文法,要经过两个阶段,1)消除文法左递归,2)提取左因子,消除回溯。
2、 提取文法左因子算法:
1)对文法G的所有非终结符进行排序
2)按上述顺序对每一个非终结符Pi依次执行:
for( j=1; j< i-1;j++)
将Pj代入Pi的产生式(若可代入的话);
消除关于Pi的直接左递归:
Pi -> Piα|β ,其中β不以Pi开头,则修改产生式为:
Pi —> βPi′
Pi′—> αPi′|ε
3)化简上述所得文法。
3、 提取左因子的算法:
A —> δβ1|δβ2|…|δβn|γ1|γ2|…|γm
(其中,每个γ不以δ开头)
那么,可以把这些产生式改写成
A —> δA′|γ1| γ2…|γm
A′—>β1|β2|…|βn
4、 利用上述算法,实现构造一个LL(1)文法:
1) 读入文法,利用实验二附加资料2的实验要求将文法存入设计的数据结构;
2) 设计函数remove_left_recursion()和remove_left_gene()实现消除左递归和提取左因子算法,分别对文法进行操作,消除文法中的左递归和提出左因子;
3) 整理得到的新文法;
4) 在一个新的文本文件输出文法,文法输出按照一个非终结符号一行,开始符号引出的产生式写在第一行,同一个非终结符号的候选式用“|”分隔的方式输出。

要求三

1、 了解文法定义的4个部分:
G(Vn, Vt, S, P)
Vn 文法的非终结符号集合,在实验中用大写的英文字母表示;
Vt 文法的终结符号集合,在实验中用小写的英文字母表示;
S 开始符号,在实验中是Vn集合中的一个元素;
P 产生式,分左部和右部,左部为非终结符号中的一个,右部为终结符号或非终结符号组成的字符串,如S->ab|c
2、 根据文法各个部分的性质,设计一个合理的数据结构用来表示文法,
1) 若使用C语言编写,则文法可以设计成结构体形式,结构体中应包含上述的4部分,
2) 若使用C++语言编写,则文法可以设计成文法类形式,类中至少含有4个数据成员,分别表示上述4个部分
文法数据结构的具体设计由学生根据自己想法完成,并使用C或C++语言实现设计的数据结构。
3、 利用完成的数据结构完成以下功能:
1) 从文本文件中读入文法(文法事先应写入文本文件);
2) 根据文法产生式的结构,分析出文法的4个部分,分别写入定义好的文法数据结构的相应部分;
3) 整理文法的结构;
4) 在计算机屏幕或者文本框中输出文法,文法输出按照一个非终结符号一行,开始符号引出的产生式写在第一行,同一个非终结符号的候选式用“|”分隔的方式输出。

代码

#include <iostream>
#include <string>
#include <fstream>
#include <set>
#include <map>
#include <iomanip>
#include <stack>
using namespace std;
const int maxnlen = 1e4;
class Grammar {
private:
	set<char>Vn;
	set<char>Vt;
	char S;
	map<char, set<string> > P;
	map<char,set<char> >FIRST;
	map<char,set<char> >FOLLOW;
	map<string, string>Table;
public:
	Grammar(string filename) {
		Vn.clear();
		Vt.clear();
		P.clear();
		FIRST.clear();
		FOLLOW.clear();
		ifstream in(filename);
		if (!in.is_open()) {
			cout << "文法  文件打开失败" << endl;
			exit(1);
		}
		char *buffer = new char[maxnlen];
		in.getline(buffer, maxnlen, '#');
		string temps = "";
		bool is_sethead = 0;
		for (int i = 0; i < strlen(buffer); i++) {
			if (buffer[i] == '\n' || buffer[i] == ' ')continue;
			if (buffer[i] == ';') {
				if (!is_sethead) {
					this->setHead(temps[0]);
					is_sethead = 1;
				}
				this->add(temps);
				temps = "";
			}
			else
				temps += buffer[i];
		}
		delete buffer;
		/*
			输出Vn,Vt,set
			
		*/
		
	}
	void setHead(char c) {
		S = c;
	}
	void add(string s) {
		char s1 = s[0];
		string s2="";
		int num = 0;
		for (int i = 0; i < s.length() ; i++) {
			if (s[i] == '>')num=i;
			if (num == 0)continue;
			if (i > num)
				s2 += s[i];
		}
		s2 += ';';
		Vn.insert(s1);
		string temp = "";
		for (char s : s2) {
			if (!isupper(s) && s != '|'&&s != ';'&&s!='@')Vt.insert(s);
			if (s == '|' || s == ';') {
				P[s1].insert(temp);
				temp = "";
			}
			else {
				temp += s;
			}
		}
	}
	void print() {
		cout << "当前分析文法为:" << endl << endl;
		for (set<char>::iterator it = Vn.begin(); it != Vn.end(); it++) {
			char cur_s = *it;
			for (set<string>::iterator it1 = P[cur_s].begin(); it1 != P[cur_s].end(); it1++) {
				string cur_string = *it1;
				cout << cur_s << "->" << cur_string << endl;
			}
		}
	}
	void getFirst() {
		FIRST.clear();
		//判断迭代次数
		int iter = 4;
		while (iter--) {
			for (set<char>::iterator it = Vn.begin(); it != Vn.end(); it++) {
				char cur_s = *it;
				/*
				cur_s->cur_string[0]
					a加到A的FIRST集
				cur_s->cur_string[0]
					B的FITRST集加到A的FIRST集
				*/
				for (set<string>::iterator it1 = P[cur_s].begin(); it1 != P[cur_s].end(); it1++) {
					string cur_string = *it1;
					if (!isupper(cur_string[0])) {
						FIRST[cur_s].insert(cur_string[0]);
					}
					else {
						char nxt_s = cur_string[0];
						for (set<char>::iterator it2 = FIRST[nxt_s].begin(); it2 != FIRST[nxt_s].end(); it2++) {
							if ((*it2) != '@') {
								FIRST[cur_s].insert(*it2);
							}
						}
					}
				}
			}
		}
		//输出FIRST集
		cout << "FIRST集为:" << endl << endl;
		for (set<char>::iterator it = Vn.begin(); it != Vn.end();it++) {
			char cur_s = *it;
			cout << "FIRST()   " << cur_s ;
			for (set<char>::iterator it1 = FIRST[cur_s].begin(); it1 != FIRST[cur_s].end(); it1++) {
				 cout<<"       " << *it1 ;
			}
			cout << endl;
		}
	}
	void getFollow() {
		FOLLOW.clear();
		FOLLOW[S].insert('#');
		//判断迭代次数
		int iter = 4;
		while (iter--) {
			for (set<char>::iterator it = Vn.begin(); it != Vn.end(); it++) {
				char cur_s = *it;
				/*
				cur_s->cur_string[0]
				a加到A的FIRST集
				cur_s->cur_string[0]
				B的FITRST集加到A的FIRST集
				*/
				for (set<string>::iterator it1 = P[cur_s].begin(); it1 != P[cur_s].end(); it1++) {
					string cur_string = *it1;
					for (int i = 0; i < cur_string.length() - 1; i++) {
						/*
							B->Ac
							将c加到A的follow集
						*/
						if (isupper(cur_string[i]) && !isupper(cur_string[i + 1])) {
							FOLLOW[cur_string[i]].insert(cur_string[i + 1]);
						}
						/*
							B->AC
							将C的first集加到A的follow集
						*/
						if (isupper(cur_string[i]) && isupper(cur_string[i + 1])) {
							//遍历C的first去除@,加到A的follow集
							for (auto it2 = FIRST[cur_string[i + 1]].begin(); it2 != FIRST[cur_string[i + 1]].end(); it2++) {
								if((*it2)!='@')
								FOLLOW[cur_string[i]].insert(*it2);
							}
						}
						
					}
					/*
					AC/ACK为最后两个或者三个
					B->AC
					B->ACK(K的first集含有@)
					将B的follow集加入到C的follow集
					*/
					int len = cur_string.length();
					if ( (len>=1&&isupper(cur_string[len - 1])) ) {
						for (auto it2 = FOLLOW[cur_s].begin(); it2 != FOLLOW[cur_s].end(); it2++) {
							if(isupper(cur_string[len - 1]))
								FOLLOW[cur_string[len - 1]].insert(*it2);
						}
					}
					if ((len >= 2 && isupper(cur_string[len - 2]) && isupper(cur_string[len - 1]) && FIRST[cur_string[len - 1]].count('@') > 0)) {
						for (auto it2 = FOLLOW[cur_s].begin(); it2 != FOLLOW[cur_s].end(); it2++) {	
							FOLLOW[cur_string[len - 2]].insert(*it2);
						}
					}
				}
			}
		}
		//输出FOLLOW集
		cout << "FOLLOW集为:" << endl << endl;
		for (set<char>::iterator it = Vn.begin(); it != Vn.end(); it++) {
			char cur_s = *it;
			cout << "FOLLOW()  " << cur_s;
			for (set<char>::iterator it1 = FOLLOW[cur_s].begin(); it1 != FOLLOW[cur_s].end(); it1++) {
				cout << "       " << *it1;
			}
			cout << endl;
		}
	}
	void getTable() {
		set<char>Vt_temp;
		for (char c : Vt) {
			Vt_temp.insert(c);
		}
		Vt_temp.insert('#');
		for (auto it = Vn.begin(); it != Vn.end(); it++) {
			char cur_s = *it;
			for (auto it1 = P[cur_s].begin(); it1 != P[cur_s].end(); it1++) {
				/*
				产生式为
					cur_s->cur_string
				*/
				string cur_string = *it1;
				if (isupper(cur_string[0])) {
					char first_s = cur_string[0];
					for (auto it2 = FIRST[first_s].begin(); it2 != FIRST[first_s].end(); it2++) {
						string TableLeft = "";
						TableLeft = TableLeft +cur_s + *it2;
						Table[TableLeft] = cur_string;
					}
					
				}
				else {
					string TableLeft = "";
					TableLeft = TableLeft+ cur_s + cur_string[0];
					Table[TableLeft] = cur_string;
				}	
			}
			if (FIRST[cur_s].count('@') > 0) {
				for (auto it1 = FOLLOW[cur_s].begin(); it1 != FOLLOW[cur_s].end(); it1++) {
					string TableLeft = "";
					TableLeft =TableLeft+ cur_s + *it1;
					Table[TableLeft] = "@";
				}
			}
		}
		/*
			检查出错信息:即表格中没有出现过的
		*/
		
		
		for (auto it = Vn.begin(); it != Vn.end(); it++) {
			for (auto it1 = Vt_temp.begin(); it1 != Vt_temp.end(); it1++) {
				string TableLeft = "";
				TableLeft =TableLeft+ *it + *it1;
				if (!Table.count(TableLeft)) {
					Table[TableLeft] = "error";
				}
			}
		}
		
		/*
			显示Table
		*/
		cout << "显示table表:" << endl << endl;
		cout.setf(std::ios::left);
		for (auto it1 = Vt_temp.begin(); it1 != Vt_temp.end(); it1++) {
			if (it1 == Vt_temp.begin())cout << setw(10) << " ";
			cout << setw(10) << *it1;
		}
		cout << endl;
		for (auto it = Vn.begin(); it != Vn.end(); it++) {
			for (auto it1 = Vt_temp.begin(); it1 != Vt_temp.end(); it1++) {
				string TableLeft="";
				if (it1 == Vt_temp.begin())cout << setw(10) << *it;
				TableLeft =TableLeft+ *it + *it1;
				cout << *it << "->" << setw(7) << Table[TableLeft];
			}
			cout << endl;
		}
	}
	/*
		每一次分析一个输入串
		Sign为符号栈,出栈字符为x
		输入字符串当前字符为a
	*/
	bool AnalyzePredict(string inputstring){
		stack<char>Sign;
		Sign.push('#');
		Sign.push(S);
		int StringPtr = 0;
		char a = inputstring[StringPtr++];
		bool flag = true;
		while (flag) {
			char x = Sign.top();
			Sign.pop();
			//如果是终结符,直接移出符号栈
			if (Vt.count(x)) {
				if (x == a)a = inputstring[StringPtr++];
				else
					return false;
			}
			else {
				//如果不是终结符,
				//如果是末尾符号
				if (x == '#') {
					if (x == a)flag = false;
					else
						return false;
				}
				else {
					//如果是非终结符,需要移进操作
					string left = "";
					left += x;
					left += a;
					if (Table[left] != "error") {
						string right = Table[left];
//						cout << left << "---->" << right << endl;
						for (int i = right.length() - 1; i >= 0; i--) {
							Sign.push(right[i]);
						}
						
					}
					else {
						return false;
					}
				}
			}		
		}
		return true;
	}
	/*
		消除左递归
	*/
	void remove_left_recursion(){
		string tempVn = "";
		for (auto it = Vn.begin(); it != Vn.end(); it++) {
			tempVn += *it;
		}
		
		for (int i = 0; i < tempVn.length(); i++) {
			char pi = tempVn[i];
			set<string>NewPRight;
			for (auto it = P[pi].begin(); it != P[pi].end(); it++) {
				bool isget = 0;
				string right = *it;
				for (int j = 0; j < i; j++) {
					char pj = tempVn[j];
					if (pj == right[0]) {
						isget = 1;
						for (auto it1 = P[pj].begin(); it1 != P[pj].end(); it1++) {
							string s = *it1 + right.substr(1);
							NewPRight.insert(s);
						}
					}
				}
				if (isget == 0) {
					NewPRight.insert(right);
				}
			}
			/*
			for (int j = 0; j < i; j++) {
				char pj=tempVn[j];
				for (auto it = P[pi].begin(); it != P[pi].end(); it++) {
					string right = *it;
					if (right[0] == pj) {
						for (auto itpj = P[pj].begin(); itpj != P[pj].end(); itpj++) {
							string s = *itpj + right.substr(1);
							NewPRight.insert(s);
						}
					}
					else {
						NewPRight.insert(right);
					}
				}
			}
			*/
			if(i!=0)
				P[pi] = NewPRight;

			remove_left_gene(pi);
		}
	}
	/*
		提取左因子
	*/
	void remove_left_gene(char c) {
		char NewVn;
		for (int i = 0; i < 26; i++) {
			NewVn = i + 'A';
			if (!Vn.count(NewVn)) {
				break;
			}
		}
		bool isaddNewVn = 0;
		for (auto it = P[c].begin(); it != P[c].end(); it++) {
			string right = *it;
			
			if (right[0] == c) {
				isaddNewVn = 1;
				
				break;
			}
		}
		if (isaddNewVn) {
			set<string>NewPRight;
			set<string>NewPNewVn;
			for (auto it = P[c].begin(); it != P[c].end(); it++) {
				string right = *it;
				if (right[0] != c) {
					right += NewVn;
					NewPRight.insert(right);
				}
				else {
					right = right.substr(1);
					right += NewVn;
					NewPNewVn.insert(right);
				}
			}
			Vn.insert(NewVn);
			NewPNewVn.insert("@");
			P[NewVn] = NewPNewVn;
			P[c] = NewPRight;
		}
	}
	void ShowByTogether() {
		for (auto it = Vn.begin(); it != Vn.end(); it++) {
			cout << *it << "->";
			char c = *it;
			for (auto it1 = P[c].begin(); it1 != P[c].end(); it1++) {
				if (it1 == P[c].begin())cout << *it1;
				else
					cout << "|" << *it1;
					
			}
			cout << endl;
		}
	}
};
int main() {
	/*
	文法测试
	E->T|E+T;
	T->F|T*F;
	F->i|(E);

	A->+TA|@;
	B->*FB|@;
	E->TA;
	F->(E)|i;
	T->FB;
	直接将上面两个测试样例放在Gramar_test.txt中
	*/
	string filename_gramer = "D:\\c++Project\\fundamentals_of_compiling\\Parsing\\Gramar_test.txt";
	Grammar *grammar=new Grammar(filename_gramer);
	cout << "/-------------------------没有消除左递归-----------------------------/" << endl;
	cout << "规格显示:" << endl;
	grammar->ShowByTogether();
	cout << endl;
	grammar->getFirst();
	cout << endl;
	grammar->getFollow();
	cout << endl;
	grammar->getTable();
	
	cout << "/--------------------------------------------------------------------/" << endl<<endl<<endl;


	cout << "/-------------------------已经消除左递归-----------------------------/" << endl;
	/*
	消除左递归之后的判断
	*/
	grammar->remove_left_recursion();
	cout << "规格显示:" << endl;
	cout << endl;
	grammar->ShowByTogether();
	grammar->getFirst();
	cout << endl;
	grammar->getFollow();
	cout << endl;
	grammar->getTable();
	
	cout << "/--------------------------------------------------------------------/" << endl << endl << endl;
	cout << "/-------------------------对词法进行分析----------------------------/" << endl;
	/*
		目前的想法是使用第一个实验分离出不同的单词,对单词操作,
		如果单词为+,*,等于他本身,否则等于i;
		以下直接使用实验一的输出文本
	*/
	string filename= "D:\\c++Project\\fundamentals_of_compiling\\Parsing\\out.txt";
	ifstream in(filename);
	char buffer[200];
	string inf="";
	int num = 0;
//	cout << "文法分析结果为:" << endl << endl;
	if (in.is_open()) {
		while (!in.eof()) {
			in.getline(buffer, 100);
//			cout << buffer << endl;
			inf += buffer;
			num++;
		}
	}
	string row = "";
	for (int i = 0; i < num-1; i++) {
		int ptr = i * 13;
		string temp = "";
		for (int j = 1; j <= 5; j++) {
			if (inf[j+ptr] == ' ')continue;
			else
				temp += inf[ptr+j];
		}
		if (temp == "+" || temp == "-" || temp == "*" || temp == "/" || temp == ">" || temp == "<" || temp == "=" || temp == "(" || temp == ")") {
			row += temp;
		}
		else {
			if (temp == ";") {
				row += "#";
				cout << "当前row为:   " << row << endl;
				if (grammar->AnalyzePredict(row)) {
					cout << "                                         正确" << endl;
				}
				else
					cout << "                                         错误" << endl;
				row = "";
			}
			else {
				row += "i";
			}
		}
	}
	cout << "/--------------------------------------------------------------------/" << endl << endl << endl;
	system("pause");
	return 0;
}

实验截图

实验代码结果显示部分非常详细,其中我实现了自动生成FITST集,FOLLOW集,显示table表,消除左递归。
另外为了对比,我将未消除左递归的结果和已经消除的结果做对比,我们会发现不同。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


非常感谢大家对我博客的喜爱,之前确实写得比较粗略,我在补充点运行的细节。
一、代码中的包含两个测试样例,分别为
测试样例a:

E->T|E+T;
T->F|T*F;
F->i|(E);

测试样例b:

A->+TA|@;
B->*FB|@;
E->TA;
F->(E)|i;
T->FB;

将他们分别放在两个txt文件里头,例如代码中注释的,我将测试样例a放在了D:\\c++Project\\fundamentals_of_compiling\\Parsing\\Gramar_test.txt
同时,注意windows系统的文件格式。
测试 测试样例b时,更换文件内容即可。
二、关于代码中的第二部分(对词法进行分析)
代码内的文件D:\\c++Project\\fundamentals_of_compiling\\Parsing\\out.txt是将D:\\c++Project\\fundamentals_of_compiling\\Parsing\\Gramar_test.txt作为输入,运行上一篇博客的代码,得到的保存运行结果的文件。

### 回答1LL(1)文法分析器可以通过递归下降法实现。下面是一个简单的C语言LL(1)文法分析器的示例代码: ```c #include <stdio.h> #include <ctype.h> #define MAX_LENGTH 100 char lookahead; char input[MAX_LENGTH]; int index = 0; int error = 0; void match(char c) { if (lookahead == c) { lookahead = input[index++]; } else { error = 1; } } void program(); void statement(); void expression(); void term(); void factor(); void program() { statement(); if (!error && lookahead == '\0') { printf("Parsing successful!\n"); } else { printf("Parsing failed.\n"); } } void statement() { if (lookahead == 'i') { match('i'); match('d'); match('='); expression(); } else { error = 1; } } void expression() { term(); while (!error && (lookahead == '+' || lookahead == '-')) { match(lookahead); term(); } } void term() { factor(); while (!error && (lookahead == '*' || lookahead == '/')) { match(lookahead); factor(); } } void factor() { if (isdigit(lookahead)) { match(lookahead); } else if (lookahead == '(') { match('('); expression(); match(')'); } else { error = 1; } } int main() { printf("Enter an arithmetic expression: "); fgets(input, MAX_LENGTH, stdin); lookahead = input[index++]; program(); return 0; } ``` 该程序可以识别类似于下面的算术表达式: ``` id = 3 + (4 * 5) / 6 ``` 如果输入的表达式符合LL(1)文法规则,程序将输出"Parsing successful!",否则将输出"Parsing failed."。 ### 回答2: LL1文法分析器是一种逐步分析和解析输入的字符串的工具,它是根据LL1文法规则来构建的。在C语言中,我们可以使用LL1文法分析器来验证C代码的语法正确性。 首先,编写一个词法分析器来将输入的C代码转换成一个个的Token,每个Token代表了代码中的一个原子元素,比如:关键字、标识符、常量等。然后,根据C语言的LL1文法规则,构建一个语法分析表。 语法分析表是一个维数组,其中的每一个格子都代表了分析器在某个状态下对某个输入Symbol(Token)的处理。通过预测分析法(Predictive Parsing Algorithm)和LL1文法规则,可以构建语法分析表。 接下来,我们可以使用LL1文法分析器对输入的Token串进行语法分析。从开始符号(比如程序(Program))开始,依次读取Token串中的每一个Token,并根据语法分析表中的指导进行相应的操作,比如推导规则(Production Rule)的选择、符号的匹配等。 如果在分析的过程中发现了不符合LL1文法规则的错误,那么分析器将会报错,并指示发现错误的位置和类型。这种方式能够帮助开发者及早发现和修正代码中的语法错误,在程序编译或解析阶段就可以减少后续的错误和调试难度。 总结来说,LL1文法分析器是一种用于检验C语言代码语法正确性的工具。通过将C代码转换为Token串、构建语法分析表和使用预测分析法,我们可以逐步解析并验证代码的语法,减少错误和调试难度。
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

胖头鱼爱算法

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

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

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

打赏作者

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

抵扣说明:

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

余额充值