编译原理实验,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后就什么都不管了,至少把函数和变量名换了呀~~***