编译原理LL(1)文法

编译原理实验,LL(1)文法的代码实现

总要为后来人留下点什么吧,毕竟也是从先前人那里得来的东西,不能自己完成任务了就不管了,嘿嘿


备注一下,所有代码都是由VS2019编写,语言是C++,编译运行是没有问题的(如果有,那就~~)。为了把各部分的功能区分开,我采用了一个功能一个类的方式,这样看起来有点烦,但是便于调试和修改,没有使用继承来优化,因为我觉得没必要(其实就是懒)

总头文件

*创建名叫“main.h”*的头文件,里面写上下面的代码

#pragma once
using namespace std;
#include <iostream>
#include <queue>
#include <stack>
#include <list>
#include <algorithm>
#include <vector>
#include <string>
#include <unordered_map>
#include <unordered_set>

创建这个头文件后,以后写任何东西只需要包含这个头文件就可以了,虽然很low,但是很香

InPut类

*创建名叫“InPut.h”*的头文件,里面写上下面的代码

#pragma once
#include "main.h"
//进行文法写入,得到新文法,非终结符,终结符,开始符号
class InPut
{
private:
	vector<string> grammarCollection;							//文法集合
	unordered_map<char, vector<string>>	newGrammarCollection;	//处理后的文法集合,格式为,“非终结符+产生式”,不含“|”
	unordered_set<char> alphaofVn;								//非终结符集合
	unordered_set<char> alphaofVt;								//终结符集合
	char alphofBegin = NULL;									//开始符号
public:
	//写入文法
	void readGrammar()	
	{
		cout << "请输入文法(end表示输入结束,$代表空):" << endl;
		while (1)
		{	
			string temp;
			cin >> temp;
			if (temp == "end")
				break;
			else
				grammarCollection.push_back(temp);
		}
	}

	//处理输入的文法,识别开始符号,非终结符号,终结符号,并得到处理后的文法
	void analysisGrammar()
	{
		alphofBegin = grammarCollection[0][0];		//识别开始符号

		for (auto& i : grammarCollection)			//识别非终结符
			alphaofVn.insert(i[0]);

		for (auto& i : grammarCollection)			//识别终结符号并处理文法
		{
			string temp;
			for (size_t j = 3; j < i.size(); j++)
			{
				if (i[j] == '|')
				{
					newGrammarCollection[i[0]].push_back(temp);
					temp.clear();
					continue;
				}
				temp.push_back(i[j]);
				if (!alphaofVn.count(i[j]))
					alphaofVt.insert(i[j]);
			}
			newGrammarCollection[i[0]].push_back(temp);
		}
	}

	//得到非终结符集合
	unordered_set<char> getAlphaofVn()
	{
		return alphaofVn;
	}

	//得到终结符集合
	unordered_set<char> getAlphaofVt()
	{
		return alphaofVt;
	}

	//得到处理后的文法集合
	unordered_map<char, vector<string>> getNewGrammarCollection()
	{
		return newGrammarCollection;
	}

	//得到开始符号
	char getAalphofBegin()
	{
		return alphofBegin;
	}

	//打印开始符号,非终结符集合,终结符集合,处理后的文法集合
	void printingData()
	{
		cout << "开始符号:" << "\t\t" << alphofBegin;
		cout << endl << "非终结符集合:" << "\t\t";
		for (auto& i : alphaofVn)
			cout << i << " ";
		cout << endl << "终结符集合:" << "\t\t";
		for (auto& i : alphaofVt)
			cout << i << " ";
		cout << endl << "处理后的文法集合:" << endl;	
		for (auto& i : alphaofVn)
			for (auto& j : newGrammarCollection[i])
				cout << i << "->" << j << endl;
	}
};

得到新文法的意思是把旧文法里的 ‘|’ 去掉,这样有利于在后期的处理中可以不用考虑这个部分的代码

CreateFirst类

*创建名叫“CreateFirst.h”*的头文件,里面写上下面的代码

#pragma once
#include "main.h"
//构造FIRST集合,得到FIRST集合
class CreateFirst
{
private:
	unordered_map<char, unordered_set<char>> firstofAlph;		//FIRST集合
	unordered_map<char, vector<string>> newGrammarCollection;	//新文法集合
	unordered_set<char> alphaofVn;								//非终结符集合
	unordered_set<char> alphaofVt;								//终结符集合

	//构造非终结符FIRST集合
	unordered_set<char> buildVnFirst(char charVn)
	{
		if (firstofAlph.count(charVn))return firstofAlph[charVn];
		for (auto& i : newGrammarCollection[charVn])
			for (auto& j : i)
			{
				unordered_set<char> temp = buildVnFirst(j);
				for (auto& k : temp)
					if (k != '$')firstofAlph[charVn].insert(k);		//添加非空的first集
				if (!temp.count('$'))
					break;
				if (j == i.back())firstofAlph[charVn].insert('$');	//添加空
			}
		return firstofAlph[charVn];
	}
public:
	CreateFirst(unordered_map<char, vector<string>> newGrammarCollection, unordered_set<char> alphaofVn, unordered_set<char> alphaofVt)
	{
		this->newGrammarCollection = newGrammarCollection;
		this->alphaofVn = alphaofVn;
		this->alphaofVt = alphaofVt;
	}

	//构造FIRST集合
	void buildFirst()
	{
		for (auto& i : alphaofVt)
			firstofAlph[i].insert(i);
		unordered_set<char> flagSet;
		for (auto& i : alphaofVn)
			buildVnFirst(i);
	}

	//得到FIRST集合
	unordered_map<char, unordered_set<char>> getFirstofAlph()
	{
		return firstofAlph;
	}

	//打印非终结符FIRST集合
	void printingFirstofAlph()
	{
		cout << "非终结符FIRST集:" << endl;
		for (auto& i : alphaofVn)
		{
			cout << "FIRST(" << i << ") = { ";
			for (auto& j : firstofAlph[i])
				cout << j << " ";
			cout << "}" << endl;
		}
	}
};

这部分就是专门构造FIRST集的

CreateFollow类

*创建名叫“CreateFollow.h”*的头文件,里面写上下面的代码

#pragma once
#include "main.h"
//构造FOLLOW集合,得到FOLLOW集合
class CreateFollow
{
private:
	unordered_map<char, unordered_set<char>> firstofAlph;		//FIRST集合
	unordered_map<char, unordered_set<char>> followofAlph;		//FOLLOW集合
	unordered_map<char, vector<string>> newGrammarCollection;	//新文法集合
	unordered_set<char> alphaofVn;								//非终结符集合
	char alphofBegin;											//开始符号
	vector<pair<char, char>> insertPointer;						//插入指针向量

	//构造非终结符FOLLOW集合
	bool buildVnFOLLOW(char& charVn, string& nString, size_t countIndex)
	{
		if (!alphaofVn.count(charVn))return false;
		for (size_t i = countIndex; i < nString.size(); i++)
		{
			unordered_set<char> temp = firstofAlph[nString[countIndex]];
			for (auto& k : temp)
				if (k != '$')followofAlph[charVn].insert(k);		//添加非空的first集
			if (!temp.count('$'))
				return false;
		}
		return true;
	}

	//A->aB情况,把follow(A)添加至follow(B)
	void addElement()
	{
		for (auto& i : alphaofVn)									//为了防止文法输入没有严格按照先后顺序,所以
			for (auto& j : insertPointer)
				followofAlph[j.second].insert(followofAlph[j.first].begin(), followofAlph[j.first].end());
	}
public:
	CreateFollow(unordered_map<char, vector<string>> newGrammarCollection, char alphofBegin, unordered_set<char> alphaofVn, unordered_map<char, unordered_set<char>> firstofAlph)
	{
		this->newGrammarCollection = newGrammarCollection;	
		this->alphofBegin = alphofBegin;
		this->alphaofVn = alphaofVn;
		this->firstofAlph = firstofAlph;
	}

	//构造FOLLOW集合
	void buildFollow()
	{
		followofAlph[alphofBegin].insert('#');									//文法的开始符号的FOLLOW集添加'#'
		for (auto& f_charVn : alphaofVn)
			for (auto& nString : newGrammarCollection[f_charVn])
				for (size_t count = 0; count < nString.size(); count++)
					if (buildVnFOLLOW(nString[count], nString, count + 1) && f_charVn != nString[count])
						insertPointer.push_back({ f_charVn,nString[count] });

		int a = 10;
		addElement();
	}

	//得到FOLLOW集合
	unordered_map<char, unordered_set<char>> getFollowofAlph()
	{
		return followofAlph;
	}

	//打印FOLLOW集合
	void printingFollowtofAlph()
	{
		cout << "FOLLOW集:" << endl;
		for (auto& i : alphaofVn)
		{
			cout << "FOLLOW(" << i << ") = { ";
			for (auto& j : followofAlph[i])
				cout << j << " ";
			cout << "}" << endl;
		}
	}
};

重点看一下下面这个函数

bool buildVnFOLLOW(char& charVn, string& nString, size_t countIndex)

在处理插入指针向量的时候,并不是只完成一次映射,而是n次,也就是非终结符的个数。其实这是为了保证文法无顺序排列时(不包括第一条产生式哦)仍可以正确构建FOLLOW集,如果按照正确的顺序的话,大体上两次就可以了

CreateTable类

*创建名叫“CreateTable.h”*的头文件,里面写上下面的代码

#pragma once
#include "main.h"
//构造分析表,得到分析表
class CreateTable
{
private:
	unordered_map<char, vector<string>> newGrammarCollection;		//新文法集合
	unordered_set<char> alphaofVn;									//非终结符集合
	unordered_set<char> alphaofVt;									//终结符集合
	unordered_map<char, unordered_set<char>> firstofAlph;			//FIRST集合
	unordered_map<char, unordered_set<char>> followofAlph;			//FOLLOW集合		
	unordered_map<char, unordered_map<char, string>> analysisTable;	//预测分析表
	void buildVnAnalysisTable(char charVn, string nString)
	{
		unordered_set<char> temp = firstofAlph[nString[0]];
		for (auto& i : temp)
			if (i != '$')analysisTable[charVn][i] = nString;
		if (temp.count('$'))
			for (auto& i : followofAlph[charVn])
				analysisTable[charVn][i] = nString;
	}
public:
	CreateTable(unordered_map<char, vector<string>> newGrammarCollection, unordered_set<char> alphaofVn, unordered_set<char> alphaofVt, unordered_map<char, unordered_set<char>> firstofAlph, unordered_map<char, unordered_set<char>> followofAlph)
	{
		this->newGrammarCollection = newGrammarCollection;
		this->alphaofVn = alphaofVn;
		this->alphaofVt = alphaofVt;
		this->firstofAlph = firstofAlph;
		this->followofAlph = followofAlph;
	}
	//构造分析表
	void buildAnalysisTable()
	{
		for (auto& i : alphaofVn)						//表格初始化为空
		{
			for (auto& j : alphaofVt)
				if (j != '$')analysisTable[i][j] = "";
			analysisTable[i]['#'] = "";
		}
		for (auto& charVn : alphaofVn)
			for (auto& nString : newGrammarCollection[charVn])
				buildVnAnalysisTable(charVn, nString);
	}

	//得到分析表
	unordered_map<char, unordered_map<char, string>> getAnalysisTable()
	{
		return analysisTable;
	}

	//打印分析表
	void printingAnalysisTable()
	{
		cout << "分析表:" << endl << "  ";
		for (auto& i : alphaofVt)
			if(i != '$')cout << "\t" << i;
		cout << "\t" << '#' << endl;
		for (auto& i : alphaofVn)
		{
			cout << i;
			for (auto& j : alphaofVt)
				if (j != '$')
				{
					if (analysisTable[i][j].empty())cout << "\t" << "   ";
					else cout << "\t" << i << "->" << analysisTable[i][j];
				}
			if (analysisTable[i]['#'].empty())cout << "\t" << "   " << endl;
			else cout << "\t" << i << "->" << analysisTable[i]['#'] << endl;
		}
	}
};

这个类主要是生成分析预测表

Implement类

*创建名叫Implement.h”*的头文件,里面写上下面的代码

#pragma once
#include "main.h"
//对输入的字符串进行分析
class Implement
{
private:
	char alphofBegin;												//开始符号
	unordered_set<char> alphaofVn;									//非终结符集合	
	unordered_map<char, unordered_map<char, string>> analysisTable;	//预测分析表
	string receiveString;											//需要分析的字符串

	//标准打印格式
	void outofLine(int countofStep, string string_dataofStack, int countofReceive, string receiveString, string createFormular, string behave)
	{
		cout << countofStep << "\t\t" << string_dataofStack << "\t\t";
		for (int i = 0; i < (int)receiveString.size(); i++)
		{
			if (i < countofReceive)cout << " ";
			else cout << receiveString[i];
		}
		cout << "\t\t" << createFormular << "\t\t\t" << behave << endl;
	}
public:
	Implement(char alphofBegin, unordered_set<char> alphaofVn, unordered_map<char, unordered_map<char, string>> analysisTable)
	{
		this->alphofBegin = alphofBegin;
		this->alphaofVn = alphaofVn;
		this->analysisTable = analysisTable;
	}

	//输入需要分析的字符串
	void inputString()
	{
		cout << "请输入字符串:";
		cin >> receiveString;
		receiveString.push_back('#');		//字符串尾添加'#'
	}

	//字符串分析程序总控
	void controlAnalysis()
	{
		stack<char> dataofStack;			//初始化符号栈
		string string_dataofStack;			//初始化符号栈内字符串
		int countofStep = 0;				//步骤计数器
		int countofReceive = 0;				//即将处理输入串的字符下标

		dataofStack.push('#');
		dataofStack.push('E');
		string_dataofStack.append("#E");	//对符号栈进行初始化

		cout << "步骤" << "\t\t" << "符号栈" << "\t\t" << "输入串" << "\t\t" << "所用产生式" << "\t\t" << "动作" << endl;		//打印表头
		outofLine(countofStep, string_dataofStack, countofReceive, receiveString, "    ", "初始化");
		while (1)
		{
			countofStep++;
			if (!alphaofVn.count(dataofStack.top()))
			{
				if (dataofStack.top() == receiveString[countofReceive])
				{
					if (dataofStack.top() == '#')
					{
						outofLine(countofStep, string_dataofStack, countofReceive, receiveString, "    ", "success");
						break;
					}
					else
					{
						countofReceive++;													//下标加一
						dataofStack.pop();													//移除栈顶元素
						string_dataofStack.pop_back();										//移除栈字符
						outofLine(countofStep, string_dataofStack, countofReceive, receiveString, "    ", "GETNEXT(I)");
					}
					continue;
				}
				outofLine(countofStep, string_dataofStack, countofReceive, receiveString, "    ", "error");
				break;
			}

			string temp = analysisTable[dataofStack.top()][receiveString[countofReceive]];	//得到分析表里面的内容
			if (temp.empty())
			{
				outofLine(countofStep, string_dataofStack, countofReceive, receiveString, "    ", "error");
				break;																		//内容为空,报错跳出
			}
			string createString;												//产生式
			createString.push_back(dataofStack.top());
			createString.append("->");
			createString.append(temp);
			dataofStack.pop();													//移除栈顶元素
			string_dataofStack.pop_back();										//移除栈字符
			if (temp.back() == '$')
			{
				outofLine(countofStep, string_dataofStack, countofReceive, receiveString, createString, "POP");
				continue;
			}

			string behave = "POP,PUSH(";										//动作字符串初始化
			for (int i = temp.size() - 1; i >= 0; i--)								//将产生式右部倒插入栈及栈字符以及生成动作字符串
			{
				dataofStack.push(temp[i]);
				string_dataofStack.push_back(temp[i]);
				behave.push_back(temp[i]);
			}
			behave.push_back(')');
			outofLine(countofStep, string_dataofStack, countofReceive, receiveString, createString, behave);
		}
	}
};

这个类就是所谓的总控程序了,负责字符串的输入和分析

源.cpp

*创建名叫”源.cpp“的文件(随便你叫什么名字都可以),里面写上下面的代码

#include "main.h"
#include "InPut.h"
#include "CreateFirst.h"
#include "CreateFollow.h"
#include "CreateTable.h"
#include "Implement.h"
int main()
{
	InPut inPut;
	inPut.readGrammar();							//写入文法
	inPut.analysisGrammar();						//分析输入的文法
	inPut.printingData();							//打印分析结果

	CreateFirst createFirst(inPut.getNewGrammarCollection(), inPut.getAlphaofVn(), inPut.getAlphaofVt());
	createFirst.buildFirst();						//创建FIRST集
	createFirst.printingFirstofAlph();				//输出FIRST集

	CreateFollow createFollow(inPut.getNewGrammarCollection(), inPut.getAalphofBegin(), inPut.getAlphaofVn(), createFirst.getFirstofAlph());
	createFollow.buildFollow();						//创建FOLLOW集
	createFollow.printingFollowtofAlph();			//打印FOLLOW集

	CreateTable createTable(inPut.getNewGrammarCollection(), inPut.getAlphaofVn(), inPut.getAlphaofVt(), createFirst.getFirstofAlph(), createFollow.getFollowofAlph());
	createTable.buildAnalysisTable();				//创建分析表
	createTable.printingAnalysisTable();			//打印分析表

	Implement implement(inPut.getAalphofBegin(), inPut.getAlphaofVn(), createTable.getAnalysisTable());
	implement.inputString();						//输入需要解析的字符串
	implement.controlAnalysis();					//总控程序


	return 0;
}

这个负责所有的类的使用,可以在这里调用某一块进行调试,或者全部调用成为一个完整的分析程序

这里附带一个测试截图
在这里插入图片描述

***代码其实还有很多可以精简的地方,希望大家不要CTRL+C,V后就什么都不管了,至少把函数和变量名换了呀~~***
  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值