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)分析表:
生成的语句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)