编译原理 实验2 自顶向下的语法分析程序

1 运行结果

(1)测试文法一(输入文件产生式右部与句子全部以单个空格隔开,测试文法必须是LL(1)的):
E->T E’
E’->+ T E’
E’->#
T->F T’
T’->* F T’
T’->#
F->i
F->( E )
测试句子:i + i * i
测试结果如下图所示(由于图太大,分割成几部分展示)。
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
(2)测试文法二:
E->T E’
E’->+ E
E’->#
T->F T’
T’->T
T’->#
F->P F’
F’->* F’
F’->#
P->( E )
P->a
P->b
P->^
测试句子:( a ^ b + a ) * b

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
以下是分析过程与结果输出到文件的测试。经测试,程序运行的过程和结果(包含计算First集Follow集Select集、构造预测分析表分析表、分析过程等)能正常输出到文件里。
在这里插入图片描述

2 项目代码

只有一个文件main.cpp:

#include<cstdio>
#include<map>
#include<iostream>
#include<fstream>
#include<iomanip>
#include<vector>
#include<set>
#include<string>
using namespace std;
const int wordnumPerLine = 7;
bool writeFile = false;
FILE* grammerFile;
char fname[50],outfname[50],c,reply[50];
string analyzedString;

#define readLine if( fscanf(grammerFile,"%[^\n]%c",theLine,&c)==EOF)	return;
#pragma region 工具函数
inline bool isInVector(vector<string> vec,string val) {
	vector<string>::iterator iter = find(vec.begin(), vec.end(), val);
	if (iter == vec.end())
		return false;
	return true;
}
inline set<string> getNoneEmptySet(set<string> s) {//得到无空集
	set<string> ans = s;
	set<string>::iterator it = ans.find("#");
	if (it!= ans.end()) {
		ans.erase(it);
	}
	return ans;
}
inline bool isNoneTerminal(string x) {
	return x.length() > 0 && x[0] >= 'A'&&x[0] <= 'Z';
}
inline vector<string> splitString(string oriString, char splitChar = ' ') {
	vector<string> vec;
	oriString += ' ';
	int splitpos;
	while (splitpos = oriString.find(splitChar)!=string::npos) {
		vec.push_back(oriString.substr(0, splitpos));
		oriString = oriString.substr(splitpos+1);
	}
	return vec;
}
inline string vectorToString(vector<string> vec,bool reverse = false) {
	string ansStr = "";
	if (reverse) {
		for (int i = vec.size()-1; i >=0; i--) {
			ansStr += vec[i] + " ";
		}
	}
	else {
		for (int i = 0; i < vec.size(); i++) {
			ansStr += vec[i] + " ";
		}
	}
	return ansStr;
}
#pragma endregion

struct Production{
	string left;
	vector<string>right;
	bool isEmpty = false;
	string getRight() {
		string ans = "";
		for (int i = 0; i < right.size() - 1; i++)
			ans += right[i] + " ";
		if (!right.empty()) {
			ans += right.back();
		}
		return ans;
	}
	string getProduction() {
		return left + "->" + getRight();
	}
};

struct Grammer
{
	vector<Production> productions;//文法产生式
	vector<string> nonTerminal;//文法非终结符
	vector<string> terminal;//文法终结符
	map<string,set<string> > firstSet;//first集
	map<string,set<string> > followSet;//follow集
	vector<set<string> > select;//第i条产生式的select集
	map<string, bool> canReachEmpty;//非终结符表
	string startSymbol= "";//开始符号
	map<pair<string, string>, int> predictionMap;//非终结符A遇到终结符b时,使用第i条产生式
	friend ostream & operator <<(ostream & os, const Grammer & g);
}grammer;

struct Description
{
	string analyzeStr;
	string remainStr;
	string matchStr;
	Description() {}
	Description(string analyzeStr, string remainStr) {
		this->analyzeStr = analyzeStr;
		this->remainStr = remainStr;
	}
};
struct Sentence
{
	vector<string> analyzeStack;
	vector<string> remainString;
	vector<Description > description;
	string sentenceContent;
	void analyzeString() {//分析串
		int productionIndex;
		string nAnaStr, nRemStr;
		vector<string> nRight;
		analyzeStack.clear();//原有分析栈清空
		description.clear();//原有描述清空
		remainString = splitString(sentenceContent + " #");//获取剩余输入串(默认以空格隔开)
		analyzeStack.push_back("#");//压入#
		analyzeStack.push_back(grammer.startSymbol);//压入开始符号
		while (true) {
			nAnaStr = analyzeStack.back(), nRemStr = remainString.front();
			description.push_back(Description(vectorToString(analyzeStack),vectorToString(remainString)));
			if (nAnaStr == nRemStr) {//遇到匹配情况
				analyzeStack.pop_back();//分析栈弹栈
				remainString.erase(remainString.begin());//剩余符号串移除队首
				if (analyzeStack.empty() && remainString.empty()) {//分析完成,接受
					description.back().matchStr = ("接受");
					break;
				}
				description.back().matchStr = ("\"" + nAnaStr + "\"" + "匹配");
			}
			else if (grammer.predictionMap.find(pair<string, string>(nAnaStr, nRemStr)) != grammer.predictionMap.end()) {
				productionIndex = grammer.predictionMap[pair<string, string>(nAnaStr, nRemStr)];//根据分析表取得产生式
				description.back().matchStr = grammer.productions[productionIndex].getProduction();//压入描述
				analyzeStack.pop_back();//分析栈弹栈
				nRight = grammer.productions[productionIndex].right;//获取产生式右部
				if (nRight[0] == "#") {//产生式右部为空,分析栈不做处理

				}
				else {
					for (int i = nRight.size() - 1; i >= 0; i--) {//反向压栈
						analyzeStack.push_back(nRight[i]);
					}
				}
			}
			else {
				//报错
				analyzeStack.push_back("错误:无法匹配,分析结束。");
				break;
			}
		}
	}
}sentence;

ostream & operator <<(ostream & os, Grammer & g) {
	filebuf fb;
	ostream* pos;
	if (writeFile) {
		fb.open(outfname,ios::out);
		pos = new ostream(&fb);
	}
	else {		
		pos = &os;
	}
	*pos << "非终结符:\n";
	for (int i = 0; i < g.nonTerminal.size(); i++) {
		*pos<< g.nonTerminal[i] << "\t" << " ";
		if ((i+1)%wordnumPerLine == 0)
			*pos << endl;
	}
	*pos << endl;
	*pos << "终结符:\n";
	for (int i = 0; i < g.terminal.size(); i++) {
		*pos<< g.terminal[i] << "\t" << " ";
		if ((i+1)%wordnumPerLine == 0)
			*pos << endl;
	}
	*pos << endl;
	*pos << "文法:\n";
	for (int i = 0; i < g.productions.size(); i++) {
		*pos << g.productions[i].left << "->";
		for (int j = 0; j < g.productions[i].right.size(); j++) {
			*pos << " "<< g.productions[i].right[j];
		}
		*pos << endl;
	}
	*pos << "非终结符表:\n";
	for (map<string, bool>::iterator it = grammer.canReachEmpty.begin(); it != grammer.canReachEmpty.end(); ++it) {
		*pos << it->first << ":\t" << ((it->second)?"Y":"N")<<endl;
	}
	*pos << "计算First集:\n";
	for (map<string, set<string> >::iterator it = grammer.firstSet.begin();it!= grammer.firstSet.end();++it) {
		*pos << it->first << ":\t";
		for (set<string>::iterator it2 = grammer.firstSet[it->first].begin(); it2!= grammer.firstSet[it->first].end(); ++it2) {
			*pos << *it2 << "\t";
		}
		*pos << endl;
	}
	*pos << "计算Follow集:\n";
	for (map<string, set<string> >::iterator it = grammer.followSet.begin(); it != grammer.followSet.end(); ++it) {
		*pos << it->first << ":\t";
		for (set<string>::iterator it2 = grammer.followSet[it->first].begin(); it2 != grammer.followSet[it->first].end(); ++it2) {
			*pos << *it2 << "\t";
		}
		*pos << endl;
	}
	*pos << "计算Select集:\n";
	*pos << fixed<<right;
	for (int i = 0; i < g.productions.size(); i++) {
		*pos <<setw(15) <<g.productions[i].left + "->"+ g.productions[i].getRight()+":";
		for (set<string>::iterator it = grammer.select[i].begin(); it != grammer.select[i].end(); ++it) {
			*pos << *it << "\t";
		}
		*pos << endl;
	}
	*pos << "得到预测分析表:\n";
	*pos<<left <<setw(10) <<" ";
	vector<string> terminalAndEmpty(g.terminal);
	terminalAndEmpty.push_back("#");
	for (int i = 0; i < terminalAndEmpty.size(); i++) {//终结符表头
		*pos << setw(10)<<terminalAndEmpty[i];
	}
	*pos << endl;
	//终结符对应的产生式
	for (int i = 0; i < g.nonTerminal.size(); i++) {
		string A = g.nonTerminal[i];
		*pos << setw(10) << A ;
		for (int j = 0; j < terminalAndEmpty.size(); j++) {
			pair<string, string> pkey(A,terminalAndEmpty[j]);
			if (g.predictionMap.find(pkey) != g.predictionMap.end()) {
				int index = g.predictionMap[pkey];
				*pos << setw(10) << g.productions[index].getRight();
				//grammer.predictionMap[pair<string, string>(firstKey, secondKey)] = i;
			}
			else{
				*pos <<setw(10) << "";
			}
		}
		*pos << endl;
	}
	*pos << "开始分析句子:"+sentence.sentenceContent<<endl<<left;
	*pos << setw(30) << "步骤" << setw(30) << "分析栈" << setw(30) << "剩余输入串" << setw(30) << "推导所用的产生式或匹配"<<endl;
	for (int i = 0; i < sentence.description.size();i++) {
		*pos << setw(30) << i+1 << setw(30) << sentence.description[i].analyzeStr << setw(30) << sentence.description[i].remainStr << setw(22) << sentence.description[i].matchStr<<endl;
	}
	if (writeFile)
		fb.close();
	return *pos;
}

//1.获取产生式2.把开头为的空和非终结符加入first集合3.设置直接可达空的非终结符表4.查找开始符号
Production lineToProduction(string line) {
	Production production;
	string rightPart = "";//当前产生式右部的一个单词
	int leftIndex = line.find_first_of('-');
	production.left = line.substr(0, leftIndex);
	if (grammer.startSymbol == "")//开始符号为空则将production.left做为开始符号
		grammer.startSymbol = production.left;
	line = line.substr(leftIndex + 2);//当前line为只剩右部的字符串
	line += ' ';
	if (line[0]=='#'){//右部为空
		production.isEmpty = true;
		production.right.push_back("#");
		grammer.firstSet[production.left].insert("#");//能推导出空,则将其加入first集合
		grammer.canReachEmpty[production.left] = true;
		return production;
	}
	for (int i = 0; i < line.length(); i++) {
		if (line[i] == ' ') {
			if (isNoneTerminal(rightPart)) {//非终结符以大写字母开头
				if (!isInVector(grammer.nonTerminal, rightPart)) {//此非终结符不在文法非终结符里面
					grammer.nonTerminal.push_back(rightPart);
				}
			}
			else {//终结符以小写字母开头
				if (production.right.empty()) {//若该单词为第一条产生式,且为终结符,则将其加入first集。
					grammer.firstSet[production.left].insert(rightPart);
				}
				if (!isInVector(grammer.terminal, rightPart)) {//此终结符不在文法终结符里面
					grammer.terminal.push_back(rightPart);
				}
			}
			production.right.push_back(rightPart);
			rightPart = "";
			continue;
		}
		rightPart += line[i];
	}
	return production;
}
//计算非终结符表
void calNonTerminalTable() {
	bool flag = true;//记录当前是否有表的扩大
	while (flag) {//有表的扩大,就继续循环
		flag = false;//初始化flag
		for (int i = 0; i < grammer.productions.size(); i++) {
			Production nowProduction = grammer.productions[i];//获取当前产生式
			string nowLeft = nowProduction.left;//获取当前产生式的左部
			if (grammer.canReachEmpty[nowLeft]) {//如果已经可达空,则无需计算,continue
				continue;
			}
			bool canReach = true;
			for (int j = 0; j < nowProduction.right.size(); j++) {//获取产生式的所有右部
				if (isNoneTerminal(nowProduction.right[j])) {//此元素为非终结符
					if (!grammer.canReachEmpty[nowProduction.right[j]])//当前非终结符无法到达空,标志置为false,break这个产生式
					{
						canReach = false;
						break;
					}
				}
				else {//若不为终结符,则为非终结符,则肯定不能达到空,标志置为false。break这个产生式
					canReach = false;
					break;
				}
			}
			if (canReach)//说明该产生式能推出空
			{
				grammer.canReachEmpty[nowLeft]= true;
				flag = true;
			}
		}
	}
}

void getFirst() {
	//先将能达到#的#填充入个非终结符的first集合
	for (int i = 0; i < grammer.nonTerminal.size(); i++) {
		if (grammer.canReachEmpty[grammer.nonTerminal[i]]){
			grammer.firstSet[grammer.nonTerminal[i]].insert("#");
		}
	}
	bool isExpanded = true;
	int orilen;//原始的first集合大小
	while (isExpanded) {//First集有扩张,则继续扩充
			isExpanded = false;//初始化为没有扩张
		for (int i = 0; i < grammer.productions.size(); i++) {
			Production nowProduction = grammer.productions[i];//获取当前产生式
			if (nowProduction.isEmpty)//右部为空的产生式不参与计算
				continue;
			string nowLeft = nowProduction.left;//获取当前产生式的左部
			orilen = grammer.firstSet[nowLeft].size();//获取扩充前该非终结符长度
			for (int j = 0; j < nowProduction.right.size(); j++) {//从左向右遍历该产生的每一个字符
				if (!isNoneTerminal(nowProduction.right[j])) {//该右部的这个字符为终结符
					grammer.firstSet[nowLeft].insert(nowProduction.right[j]);//插入并跳出
					break;
				}
				else {//为非终结符,则将其插入当前非终结符的first集
					//isExistedNull记录是否存在#
					set<string> insertedSet = getNoneEmptySet(grammer.firstSet[nowProduction.right[j]]);//获取非空集
					grammer.firstSet[nowLeft].insert(insertedSet.begin(), insertedSet.end());//插入非空集
					set<string>::iterator sit = grammer.firstSet[nowLeft].find("#");

					if (!grammer.canReachEmpty[nowProduction.right[j]]){//该非终结符不能到达空,结束,break
						break;
					}
				}
			}
			if (grammer.firstSet[nowLeft].size() > orilen) {//如果该first集扩大了
				isExpanded = true;
			}
		}
	}
}

void getFollow() {
	//在开始符号的follow集里面加入#(默认开始符号为加入的第一个非终结符)
	grammer.followSet[grammer.startSymbol].insert("#");
	//按照2及以后的规则扩充follow集
	bool isExpanded = true;
	int orilen;//原始的Follow集合大小
	while (isExpanded) {//Follow集有扩张,则继续扩充
		isExpanded = false;//初始化为没有扩张
		for (int i = 0; i < grammer.productions.size(); i++) {
			Production nowProduction = grammer.productions[i];//获取当前产生式
			if (nowProduction.isEmpty)//右部为空的产生式不参与计算
				continue;
			string nowLeft = nowProduction.left;//获取当前产生式的左部
			for (int j = 0; j < nowProduction.right.size(); j++) {//从左向右遍历该产生的每一个字符
				if (isNoneTerminal(nowProduction.right[j])){//nowProduction.right[j]为非终结符
					string A = nowProduction.right[j];
					orilen = grammer.followSet[A].size();
					bool betaCanEmpty = true;
					for (int k = j + 1; k < nowProduction.right.size(); k++) {//遍历之后的所有字串
						string beta = nowProduction.right[k];
						if (isNoneTerminal(beta)) {//β为非终结符
							set<string> insertedSet = getNoneEmptySet(grammer.firstSet[beta]);//得到beta非空的first集							
							grammer.followSet[A].insert(insertedSet.begin(), insertedSet.end());//插入first集
							if (!grammer.canReachEmpty[beta]) {//不能星推导出空,follow集计算也到此为止,break跳出
								betaCanEmpty = false;
								break;
							}
						}
						else {//β为终结符,直接加入,break跳出
							betaCanEmpty = false;
							grammer.followSet[A].insert(beta);
							break;
						}
					}
					if (betaCanEmpty) {//说明beta能推导出空,把nowLeft的Follow集加入A的Follow集				
						grammer.followSet[A].insert(grammer.followSet[nowLeft].begin(), grammer.followSet[nowLeft].end());
					}
					if (grammer.followSet[A].size() > orilen) {//有扩充
						isExpanded = true;
					}
				}
			}
		}
	}
}

void getInput() {
	char theLine[100];//这一行产生式
	while (true){
		readLine;
		Production production = lineToProduction(theLine);		
		grammer.productions.push_back(production);
		if (!isInVector(grammer.nonTerminal, production.left))
			grammer.nonTerminal.push_back(production.left);
		memset(theLine, 0, sizeof(theLine));

	}
}

void getSelect() {
	for (int i = 0; i < grammer.productions.size(); i++) {
		Production nowProduction = grammer.productions[i];//获取当前产生式
		set<string> nowSelect;
		if (nowProduction.isEmpty) {//右部为空,则直接把左部的Follow集加入该产生式的Select集
			nowSelect.insert(grammer.followSet[nowProduction.left].begin(), grammer.followSet[nowProduction.left].end());
		}
		else {//右部非空
			bool canEmpty = true;
			for (int j = 0; j < nowProduction.right.size(); j++) {
				string A = nowProduction.right[j];
				if (isNoneTerminal(A)) {//A为非终结符
					set<string> insertedSet = getNoneEmptySet(grammer.firstSet[A]);
					nowSelect.insert(insertedSet.begin(), insertedSet.end());
					if (!grammer.canReachEmpty[A]) {//A不能推出空
						canEmpty = false;
						break;
					}
				}
				else {//为终结符,则加入Select集
					canEmpty = false;
					nowSelect.insert(A);
					break;
				}
			}
			if (canEmpty) {//能星推导出空,把左部的Follow集也加入该产生式的Select集
				nowSelect.insert(grammer.followSet[nowProduction.left].begin(), grammer.followSet[nowProduction.left].end());
			}
		}
		grammer.select.push_back(nowSelect);
	}
}

void getPredictMap() {//预测分析表只是将Select重新组织成一种易于查找的模式
	string firstKey, secondKey;
	for (int i = 0; i < grammer.productions.size(); i++) {
		firstKey = grammer.productions[i].left;
		for (set<string>::iterator it = grammer.select[i].begin();it!= grammer.select[i].end(); ++it) {
			secondKey = *it;
			//firstkey遇到secondkey则使用产生式i(从0开始编号)
			grammer.predictionMap[pair<string,string>(firstKey,secondKey)] = i;
		}
	}
}

int main() {
	printf("Input grammer file?\n");
	scanf("%s", fname);
	printf("Write in file?(Y/N)\n");
	scanf("%s", reply);
	if (reply[0] == 'Y' || reply[0] == 'y') {
		printf("filename?(Y/N)\n");
		scanf("%s",outfname);
		writeFile = true;
	}
	grammerFile = fopen(fname, "r");
	if (&grammerFile) {//文件读取成功
		getInput();//读入文法生成grammer
		calNonTerminalTable();//1.计算非终结符表
		getFirst();//2.计算first集
		getFollow();//3.计算Follow集
		getSelect();//3.计算Select集
		getPredictMap();//4.计算预测分析表
	}
	printf("Input the string?\n");//输入分析串
	getchar();
	getline(cin,analyzedString);
	sentence.sentenceContent = analyzedString;
	sentence.analyzeString();//开始分析
	cout << grammer;
	getchar();
	getchar();
	return 0;
}
//( a ^ b + a ) * b
//i + i * i

3 实验内容

3.1 实验内容说明

(1)实验要求
你的程序应具有通用性,能够识别由词法分析得出的词法单元序列是否是给定文法的正确句子(程序),并能够输出分析过程和识别结果。
(2)输入格式
一个包含源代码的文本文件,此时需要和词法分析程序进行对接,通过一次扫描同时完成词法分析和语法分析;
(3)输出格式
实验二要求通过标准输出打印程序的运行结果(包括 First 集、Follow 集、LL(1)分析表),
此外,要求可以保存 LL(1)分析表。
你的程序需要输出语法分析过程和相应的分析结果(即此串是否为 LL(1)文法的句子)。

3.2 算法描述

本实验要求根据LL(1)分析法编写一个语法分析分析程序,书本上的分析过程主要分为五步,我在此基础上加入了文件的读入和输出与具体语句三个模块。
针对本实验的步骤设计与步骤对应的函数如下:

1.	读取文法文件:getInput();
此函数会调用lineToProduction函数,主要作用:
(1)获取产生式。
(2)把开头为的空和非终结符加入first集合。
(3)设置直接可达空的非终结符。
函数伪代码如下:
void getInput() {//读取函数
		读一行,若读到终止符号跳出
		Production production = lineToProduction(line)//获取该行产生式
		文法的产生式集.push_back(production);
		if (若产生左部不在文法的非终结符里)
			文法的非终结符表.push_back(production.left);
	}
2.	计算非终结符表:calNonTerminalTable();
void calNonTerminalTable() {
		while 有新的符号可达空 
		for 每个产生式
			if 产生式左部可达空)
				continue;
			for 产生式的所有右部
				if 此右部元素为非终结符
					if 当前非终结符无法到达空					
						canReach = false;
						break;									
				else 为非终结符
					canReach = false;
					break;
			if canReach//说明该产生式能推出空
				grammer.canReachEmpty[nowLeft]= flag=canReach;
}
3.	计算Fisrt集:getFirst();
先根据上一步计算非终结符表的结果将能推出空的非终结符的first集加入空。
当有first有扩张时,应重复执行:对每一条产生式的所有右部从左到右遍历,当遇到终结符或不能推出空的非终极符截止。期间将相关的符号填入First集。伪代码如下:
void getFirst() {
	将能达到#的#填充入文法非终结符的first集合
	while (isExpanded)//First集有扩张 
		for 所有产生式 
			if 右部为空
				continue;
			获取扩充前该非终结符first集长度
			for 该产生式所有右部
				if 此右部元素为终结符
该右部元素插入左部的first集
				else 此右部元素为非终结符
将其first集插入当前非终结符的first集
					if 该右部非终结符不能到达空
						break;		
			if 现在产生式的左部的大小与操作前不同 //如果该first集扩大了
				isExpanded = true;
}
4.	计算Follow集:getFollow();
void getFollow() {
	在开始符号的follow集里面加入#
	while isExpanded//Follow集有扩张,则继续扩充
		for 文法的每个产生式
			if 右部为空
				continue;
			for 右部的所有元素
				if 该元素为非终结符j
					for 此产生式该元素后面的所有元素
						string beta = nowProduction.right[k];
						if β为非终结符
							将beta非空的first集插入first集		
							if beta不能星推导出空
								break;						
						else β为终结符
							加入FOLLOW集
							break;						
					if beta能推导出空				
						产生式左部的FOLLOW集插入该非终结符j
					if 现在产生式的左部的大小与操作前不同 
				isExpanded = true;
}
5.	计算Select集:getSelect();
void getSelect() {
	for 文法的每个产生式
		set<string> nowSelect;
		if 右部为空
把左部的Follow集加入该产生式的Select集
		else 右部非空
			bool canEmpty = true;
			for 产生式右部的所有元素,记元素当前为A
				if A为非终结符
					nowSelect.insert(A的非空first集);
					if A不能推出空
						break;					
				else A为终结符
					nowSelect.insert(A);
					break;				
			if 右部能星推导出空
				把左部的Follow集也加入该产生式的Select集			
}
6.	计算预测分析表:getPredictMap();
预测分析表只是将Select重新组织成一种易于查找的模式,我的算法是将上一步生成的Select集重新组织成
map<pair<string, string>, int>的形式。利用map的特性,根据pair<A,b>(非终结符A遇到终结符b时,使用
第i条产生式)来找到使用产生式的编号,即可达成目的。
7.	输出结果:ostream & operator <<(ostream & os, Grammer & g);
将整个文法定义成结构体Grammer,当调用cout << grammer时会输出所有的分析过程。在扩展功能测试里面,我会讲解具体的实现方法和采用这种重载来输出的原因。
8.	分析语句:Sentence::analyzeString();
	void analyzeString() {//分析串
		获取剩余输入串
		分析栈压入#和开始符号
		while (true) {
				if 剩余符号串头与分析栈顶匹配
				分析栈弹栈
				剩余符号串队首元素移除
				if 分析栈和剩余串都为空 
					接受
					break;
		else if 能根据分析表获取所需要的产生式
				根据分析表取得产生式
				分析栈弹栈
				if 产生式右部为空,分析栈不做处理{}				
				else 
					将产生式右部反向压栈					
				else 
				报错
				break;
	}

3.3 程序结构

在这里插入图片描述

3.4 主要变量说明

在这里插入图片描述

3.5 工具函数

inline bool isInVector(vector<string> vec,string val);//判断vec里是否有val
inline set<string> getNoneEmptySet(set<string> s);//得到非空集
inline bool isNoneTerminal(string x);//判断是否是非终结符
inline vector<string> splitString(string oriString, char splitChar = ' ');//分割字符串
inline string vectorToString(vector<string> vec,bool reverse = false)//将vector类型的变量转化成输出所需的字符串。

  • 15
    点赞
  • 164
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
这个里面的都是测试数据,总共得分5分。从控制台输入,不能从文件中读取。实现了基本功能,加分项目都没有去实现,没有函数数组这些的实现。这是用C++语言写的,新建parser类别要选C++,其他对于VS的配置和C语言一样。for语句用的是枚举所有情况,你可以自行修改。 对预备工作中自然语言描述的简化C编译器的语言特性的语法,设计上下文无关文法进行描述 借助Yacc工具实现语法分析器 考虑语法树的构造: 1.语法树数据结构的设计:节点类型的设定,不同类型节点应保存哪些信息,多叉树的实现方式 2.实现辅助函数,完成节点创建、树创建等功能 3.利用辅助函数,修改上下文无关文法,设计翻译模式 4.修改Yacc程序,实现能构造语法树的分析器 考虑符号表处理的扩充 1.完成语法分析后,符号表项应增加哪些标识符的属性,保存语法分析的结果 2.如何扩充符号表数据结构,Yacc程序如何与Lex程序交互,正确填写符号表项 以一个简单的C源程序验证你的语法分析器,可以文本方式输出语法树结构,以节点编号输出父子关系,来验证分析器的正确性,如下例: main() { int a, b; if (a == 0) a = b + 1; } 可能的输出为: 0 : Type Specifier, integer, Children: 1 : ID Declaration, symbol: a Children: 2 : ID Declaration, symbol: b Children: 3 : Var Declaration, Children: 0 1 2 4 : ID Declaration, symbol: a Children: 5 : Const Declaration, value:0, Children: 6 : Expr, op: ==, Children: 4 5 7 : ID Declaration, symbol: a Children: 8 : ID Declaration, symbol: b Children: 9 : Const Declaration, value:1, Children: 10: Expr, op: +, Children: 8 9 11: Expr, op: =, Children: 7 10 12: if statement, Children: 6 11 13: compound statement, Children: 3 12
语法分析编译原理中的重要部分,它的作用是将词法分析阶段得到的词法单元序列转换成抽象语法树(AST)或语法分析树(Parse Tree),以便于后续的语义分析、中间代码生成和目标代码生成等环节的进行。在本次实验中,我们将使用Java语言实现一个简单的语法分析器。 实验要求: 1. 实现自顶向下的递归下降分析器。 2. 支持的文法如下: ``` <program> ::= <stmts_list> <stmts_list> ::= <stmt> | <stmts_list> <stmt> <stmt> ::= <if_stmt> | <while_stmt> | <assign_stmt> <if_stmt> ::= if <condition> then <stmts_list> end <while_stmt> ::= while <condition> do <stmts_list> end <assign_stmt> ::= <id> = <expr> <condition> ::= <expr> <relop> <expr> <expr> ::= <term> | <expr> <addop> <term> <term> ::= <factor> | <term> <mulop> <factor> <factor> ::= <id> | <number> | '(' <expr> ')' <relop> ::= '<' | '>' | '=' | '<=' | '>=' | '<>' <addop> ::= '+' | '-' <mulop> ::= '*' | '/' <id> ::= <letter> | <id> <letter> | <id> <digit> <number> ::= <digit> | <number> <digit> <letter> ::= A | B | ... | Z | a | b | ... | z <digit> ::= 0 | 1 | ... | 9 ``` 注意:文法中的关键字 if、then、end、while、do、and 等均为保留字。 3. 实现的语法分析器应具备以下功能: - 能够识别出语法正确的程序,并输出相应的语法分析树或抽象语法树。 - 能够识别出语法错误的程序,并给出相应的错误提示信息。 - 能够处理注释和空格等无意义的字符。 4. 实验提交要求: - 实验报告,包括程序设计和实验结果分析。 - 程序源代码。 实验设计思路: 1. 根据给定的文法,设计语法分析器的语法规则和对应的产生式。 2. 编写相应的Java代码,将文法转换为递归下降分析器所需要的形式。 3. 实现从输入的源代码中读取词法单元序列的功能。 4. 实现递归下降分析器的核心算法,对输入的词法单元序列进行语法分析,并构建相应的语法分析树或抽象语法树。 5. 在语法分析过程中,需要处理注释和空格等无意义的字符,以便于正确识别语法错误。 6. 在语法分析过程中,需要对输入的源代码进行错误检查,并给出相应的错误提示信息。 7. 输出语法分析树或抽象语法树,以便于后续的语义分析、中间代码生成和目标代码生成等环节的进行。 实验结果分析: 经过实验测试,我们的语法分析器能够正确地识别出合法的程序,并输出相应的语法分析树或抽象语法树。同时,它也能够正确地识别出语法错误的程序,并给出相应的错误提示信息。总的来说,本次实验取得了较好的实验效果。 实验源代码: 见下方代码框:

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值