【实验目的】
1.熟悉判断LL(1)文法的方法及对某一输入串的分析过程。
2.学会构造表达式文法的预测分析表。
【实验内容】
编写一个语法分析程序,对于给定的输入串,能够判断识别该串是否为给定文法的句型。
【实验要求】
- 输入一个LL(1)文法,构造相应的LL(1)预测分析表。
- 从键盘读入输入串,由算法判断该输入串是否为该文法的句子,若正确,就通过,若错误就报错。
【参考说明】
语法分析主要是将从词法分析那里得来的记号构成一棵语法树。例:
SHMA#
adbe#
S->aH
H->aMd
H->d
M->Ab
M->@
A->aM
A->e
分析例句:aaabd#
/*
a d b e
S H M A
S->aH
H->aMd
H->d
M->Ab
M->@
A->aM
A->e
end
aaabd#
*/
#include<iostream>
#include<string>
#include<map>
#include<vector>
#include<stack>
#include<set>
#include<cstring>
using namespace std;
map<char, int>getnumber;//map函数是一种映射,将任何基本类型映射到任何基本类型
char yszf[100]; //获得对应字符
vector<string>proce; //长度根据需要自动改变的数组
int database[500][500]; //预测分析表
int number = 0;
int numbervt = 0; //numvt是终结符集合,0是'#',numvt表空字
string first[100];//FIRST集合
string follow[200];//FOLLOW集合
void readall()//先加入终结符,再加入非终结符
{
memset(database, -1, sizeof(database));//将database中所有的字符用-1替换并返回table
getnumber['#'] = 0;//把#映射给0
yszf[0] = '#';//#储存在第一个数组
cout << "请输入所有的终结符:" << endl;
char m;
do
{
cin >> m;
getnumber[m] = ++number;//number从0开始计数,把yszf数组装满
yszf[number] = m;//将m赋值给第number个数组
} while (cin.peek() != '\n');//循环直到碰到换行符结束
numbervt = ++number;//numbervt比number大1
getnumber['@'] = numbervt; //空字
yszf[number] = ('@');//numbervt表示空字
cout << "请输入所有非终结符:" << endl;
do//一样的循环结构
{
cin >> m;
getnumber[m] = ++number;
yszf[number] = m;
} while (cin.peek() != '\n');//循环直到碰到换行符结束
cout << "输入产生式集合(空字用'@'表示),以'end'结束:" << endl;
string pro;
while (cin >> pro&&pro != "end")//字符串pro以end结束
{
string xx;
int i;
xx += pro[0];
for (i = 3; i<pro.size(); i++)
{
if (pro[i] == '|')//当pro中有|时
{
proce.push_back(xx);//push_back在vector尾部加入一个数据
xx.clear(); //删除ss中的所有元素
xx += pro[0];//重新开始赋值
}
else
{
xx += pro[i];//其他时候就直接赋值给ss
}
}
proce.push_back(xx);
}
}
void jjjh(string &a, string b) //a=a or b 取a,b交集赋值给a
{
set<char>dd;//插入删除操作时仅仅需要指针操作节点即可完成,不涉及到内存移动和拷贝,不允许有重复元素,自动被排序
for (int i = 0; i<a.size(); i++)
dd.insert(a[i]);//从a的开头开始插入,将a的所有字符全部插入到se
for (int i = 0; i<b.size(); i++)//重复元素自动被删掉,并且se字符自动排序
dd.insert(b[i]);//从b的开头开始插入,将b的所有字符全部插入到se
string uu;
set<char>::iterator yy;//迭代器
for (yy = dd.begin(); yy != dd.end(); yy++)
uu += *yy;//begin()就是指向容器第一个元素的迭代器,end()是指向容器最后一个元素的下一个位置的迭代器
a = uu;//最后赋值给a
}
string getuu(int vn, int & determine) //vn能推出的不含空字的vt集合,并且判断vn能否推出空字
{
int i;
if (vn == numbervt)
determine = 1;//numbervt为空字,vn为空,determine赋一个值为1,推出空字就进行循环
if (vn<numbervt)
return first[vn];//如果vn<numbervt就返回vn的FIRST集合,推不出空字直接返回
string uu;
for (i = 0; i<proce.size(); i++)
{
if (getnumber[proce[i][0]] == vn)//getnumber获得proce数组中的每一行的第一个字符如果与vn匹配就进行迭代getuu函数,下一次循环就一直是每一行的第二个字符
uu += getuu(getnumber[proce[i][1]], determine);
}
return uu;//进行到这一步uu的值为1
}
void getfirst()
{
for (int i = 1; i <= numbervt; i++) //1到numvt属于终结符,终结符的first集合是其本身
{
first[i] += ('0' + i);//空字代表0,其他字符都记为数字
}
for (int j = 0; j<proce.size(); j++) //扫描所有产生式
{
int k = 0; int determine = 0; //k扫描该产生式
do{
determine = 0;
k++;
if (k == proce[j].size()) //推到最后一个了,则附加空字
{
first[getnumber[proce[j][0]]] += ('0' + numbervt);//用0加上从终结符之后开始的数字将其赋值给FIRST集合
break;
}
jjjh(first[getnumber[proce[j][0]]], getuu(getnumber[proce[j][k]], determine));//将get_f得到的字符串与FIRST集合进行交集,最后返回FIRST集合
} while (determine); //到无法推出空字为止
}
}
void firstputout()
{
cout << "first集:" << endl;//输出FIRST集合
for (int i = 1; i <= number; i++)//从1一直到非终结符的最后全部显示
{
cout << "first [" << yszf[i] << "]: ";
for (int j = 0; j<first[i].size(); j++)
cout << yszf[first[i][j] - '0'] << " ";
cout << endl;
}
cout << endl;
}
void getfollow()
{
jjjh(follow[getnumber[proce[0][0]]], "0"); //先给follow集合开始符号添加'#';
for (int j = 0; j<proce.size(); j++) //扫描所有产生式
{
for (int tt = 1; tt<proce[j].size(); tt++) //每个非终结符的follow集合从第二个字符开始判断
{
if (getnumber[proce[j][tt]] <= numbervt)
continue; //终结符没有follow集合
int k = tt;
int determine;
do
{
determine = 0;
k++;
if (k == proce[j].size()) //都能推出空字,follow集合就等于产生式左边的非终结符的follow集合
{
jjjh(follow[getnumber[proce[j][tt]]], follow[getnumber[proce[j][0]]]);//返回第一个字符的follow集合与后面所有字符的follow集合的交集
break;
}
jjjh(follow[getnumber[proce[j][tt]]], getuu(getnumber[proce[j][k]], determine));//推不出空字返回follow集合与终结符的first集合的交集
} while (determine);
}
}
}
void followputout()
{
cout << "follow集:" << endl;
for (int i = 1+numbervt; i <= number; i++)
{
cout << "follow [" << yszf[i] << "]: ";
for (int j = 0; j<follow[i].size(); j++)
cout << yszf[follow[i][j] - '0'] << " ";
cout << endl;
}
cout << endl;
}
void getdatabase() //获得预测分析表
{
for (int i = 0; i<proce.size(); i++) //扫描所有产生式
{
if (proce[i][1] == '@') //直接推出空字的,特判下(follow集合等于产生式左边的非终结符中元素填)
{
string kkk = follow[getnumber[proce[i][0]]];//FOLLOW集合的每一行的第一个字符赋值给kkk
for (int k = 0; k<kkk.size(); k++)
{
database[getnumber[proce[i][0]]][kkk[k] - '0'] = i;//将i赋值给database行数为getnumber中第i行的产生式的下标和列数为kkk中减去0的下标
}
}
string ggg = first[getnumber[proce[i][1]]];
for (int j = 0; j<ggg.size(); j++) //检查FIRST集合
{
if (ggg[j] != ('0' + numbervt))
{
database[getnumber[proce[i][0]]][ggg[j] - '0'] = i;
}
else //有空字的,检查follw集合
{
string kkk = follow[getnumber[proce[i][1]]];
for (int k = 0; k<kkk.size(); k++)
{
database[getnumber[proce[i][0]]][kkk[k] - '0'] = i;
}
}
}
}
}
string ifproce(int i) //由对应下标获得对应产生式
{
if (i<0)
return " "; //无该产生式
string pl;
pl += proce[i][0];
pl += "->";
for (int j = 1; j<proce[i].size(); j++)
pl += proce[i][j];
return pl;
}
void databaseputout()
{
cout << "预测分析表:" << endl;
for (int i = 0; i<numbervt; i++)
cout << '\t' << yszf[i];//非终结符的打印
cout << endl;
for (int i = numbervt + 1; i <= number; i++)
{
cout << yszf[i];//先打印一个终结符,在这一行的接下来打印分析内容
for (int j = 0; j<numbervt; j++)
{
cout << '\t' << ifproce(database[i][j]);
}
cout << endl;
}
cout << endl;
}
string word;
bool analyze() //总控,分析字word的合法性,若合法,输出所有产生式
{
stack<char>box;
box.push('#');
box.push(proce[0][0]);//将#入栈,将proce[0][0]入栈
int i = 0;
while (!box.empty())//检测是否为空返回bool
{
int sor = box.top();//获得栈顶元素
box.pop();//弹出栈顶元素
if (sor == word[i]) //是终结符就推进
{
i++;
}
else if (sor == '#') //成功就结束
{
return 1;
}
else if (database[getnumber[sor]][getnumber[word[i]]] != -1) //查表
{
int k = database[getnumber[sor]][getnumber[word[i]]];
cout << proce[k][0] << "->";//第一个字符输出->
for (int j = 1; j<proce[k].size(); j++)
cout << proce[k][j];//输出proce数组
cout << endl;
for (int j = proce[k].size() - 1; j>0; j--) //按照逆序入栈
{
if (proce[k][j] != '@')//不等于空就入栈
box.push(proce[k][j]);
}
}
else //失败!
{
return 0;
}
}
return 1;
}
int main()
{
readall();//调用读取终结符与非终结符的函数
getfirst();//调用得到FIRST集合的函数
getfollow();//调用得到FOLLOW集合的函数
getdatabase();//调用得到预测分析表的函数
firstputout();//调用打印FIRST集合的函数
followputout();//调用打印FOLLOW集合的函数
databaseputout();//调用打印预测分析表的函数
cout << "请输入句子:" << endl;
cin >> word;
while(word!="end")
{
if (analyze())
cout << "succeed!该句子是文法给定的句子,所用的产生式如上" << endl;
else cout << "error!该句子不是文法给定的句子" << endl;
cout << "请输入句子或以'end'结束程序:" << endl;
cin >> word;
}
return 0;
}