目录
开篇废话
假如你来到了这........因为代码管理的很乱, 可能你们看都不会看, 不看没关系, 如果你急需的话, 这份代码可以让你复制黏贴起来应付老师..........大家都懂的....^_^
这是我自己做的一个很水很水的一个LL(1)文法分析器,代码鲁棒性不强, 比如对文件中存放的产生式的右部的输入有要求, 详情看如下的测试文件说明。 需要的话可以自己看看。 思路书本都有, 先求first集,后求follow集合, 然后再自动生成分析表等等....
假如我的程序在你的机器跑的结果不对, 或者跑到程序中途崩溃了, 那是我的代码对输入数据格式有要求,请原谅我, 没错, 就是我太菜了, 没有加防止胡乱输入的限制。 在心里骂几句 “我***,什么**程序也发出来” ,然后消消气, 请看回 “重要说明” 和 "测试文件说明" 那里, 当然还有运行环境那里, 确认自己的数据文件格式是不是和我的一样。 ^_^
运行环境说明
win10, 编译器vs2010
重要说明
LL(1)分析器的使用的前提是消除了左递归和最左公因子, 此程序不完善, 没有加入消除左递归和最左公因子的功能, 因此输入的测试文法必须自己消除了左递归和最左公因子!可以按照我下面的expression.txt那个文件的格式来测试。
测试文件说明
- expression.txt 存放产生式。产生式左部,中部, 右部之间用空格或者TAB分隔开。
- 注意产生式右部的各个侯选式要紧挨在一起, 比如
- F -> (E)|i 的右部 (E)|i 就不要写成 (E) | i
- 也就是产生式右部的各个侯选式不要加空格。 。。我这样是为了方便程序处理拆开以 | 分隔的各个侯选式。 同时产生式的空串用@ 表示。
- 如下的expression.txt是一个实现乘法和加法的文法。
- 还有千万不要在文件末尾加多余的空格和空行!比如如下图只要让你的鼠标光标最后只能在我图中的那个 i 后面。
- 还有就是每行的结尾不要加多余的空格了!
- final.txt是用来存储产生式终结符号。 以end表示文件结束。 比如上面expression.txt 里面的产生式的终结符如下图片。
- notFinal.txt 是用来存放产生式的非终结符。 同样以end结尾。 比如上面expression.txt 里面的产生式的终结符如下图片
程序设计思路
first集合设计思路:
- 对产生式X->a....。 且a属于终结符, 那么就直接把a加入FIRST[X]
- 对产生式X->ε, 把ε加入FIRST[X]
- 对产生式X->Y..... 且Y是非终结符。 那么就把FIRST(Y)去除ε的结果加入FIRST[X]中。 如果发现X->Y1Y2Y3.....Yi-1 Y1,...Yi-1都是非终结符, 并且Y1...Yi-1的first集合都有ε,那么就把Y1...Yi 的去掉 ε的first集合加入FIRST[X]. 如果Y1....Yn的first集都有ε,那么就把ε加入到FIRST[X].
follow集合设计思路:
把 # 直接置于文法开始符(比如E)的FOLLOW集合中
对每个非终结符,比如Z, 找到它在产生式的位置, 看他后面有没有什么符号:
(1) 如果其后面是终结符, 那么就直接把这个终结符加入到该非终结符号Z的FOLLOW集合中。 比如E->FE) 那么把 ) 加入E的follow集合中。
(2)如果后面什么都没有, 比如F->KE ,E的后面什么都没有, 那么就把该非终结符 所在的产生式的左部的那个符号的FOLLOW集合加入到该非终结符号的FOLLOW中。 比如F->KE中的求E的FOLLOW集合就要把FOLLOW(F)加入到FOLLOW(E)
(3)如果后面有一个非终结符。 那么就把该非终结符的FIRST集合去掉ε然后再加入Z的FOLLOW集合中, 比如F->KH。求K的FOLLOW集合要把FIRST(H)\{ε}再加入FOLLOW(K)。
特别地,假如上面的产生式中H可以推出ε,也就是first(H)包含ε,那么就要把产生式左部的FOLLOW集加入, 如上的产生式,要把FOLLOW(F)加入到FOLLOW(K)中。
预测分析表自动生成思路:
遍历每个非终结符的first集合里的的每个元素 ,比如对非终结符A的first集合, 如果 a是属于first(A):
【1】如果 a不是空字@, 并且产生A右部只有唯一的候选式,比如A->α, 那么分析表mp[A][a]就填入α;
如果A->α|β|θ......也就是A右边有多个候选式, 那么就逐个扫描右边候选式,看哪个候选式能够推出a,在分析 表mp[A][a]填入能推出a的候选式。 比如 β-->a..... , 那么就mp[A][a]=β
【2】如果first[A]有a,且a是空字@, 那么遍历follow[A]的每个元素(记为fol),在mp[A][fol]填入空字@
控制程序设计思路: 如下图
重要数据结构
#define path "expression.txt" //产生式路径
map<string, set<string>>first;///first集
map<string, set<string>>follow; ///follow集
map<string, map<string, string>>mp;//预测分析表
string final[101];//终结符集
string notFinal[101];//非终结符集
int pf = 0; //终结符数量
int pnf = 0; //非终结符数量
stack<string>st; //分析栈
string inputString; //输入串
struct production{///产生式结构, 左中右三部分
string left;
string middle;
string right;
//production():left("null"),middle("null"), right("null"){};
vector<string>rightString; /*存放产生式各个右部 比如E->T|F。
rightString 就放T, F。 */
};
vector<production*>vt; //存产生式
实验代码
#include<iostream>
#include<vector>
#include<fstream>
#include<string>
#include<cstring>
#include<map>
#include<stack>
#include<set>
#include<iomanip>
using namespace std;
#define path "expression.txt" //产生式路径
map<string, set<string>>first;///first集
map<string, set<string>>follow; ///follow集
map<string, map<string, string>>mp;//预测分析表
string final[101];//终结符集
string notFinal[101];//非终结符集
int pf = 0; //终结符数量
int pnf = 0; //非终结符数量
stack<string>st; //分析栈
string inputString; //输入串
struct production{///产生式结构, 左中右三部分
string left;
string middle;
string right;
//production():left("null"),middle("null"), right("null"){};
vector<string>rightString; /*存放产生式各个右部 比如E->T|F。
rightString 就放T, F。 */
};
vector<production*>vt; //存产生式
void readFile(){/*从文件读入产生式*/
ifstream infile;
infile.open(path, ios::in);
if(!infile){
cout<<"error in opening expression.txt"<<endl;
system("pause");
exit(-1);
}
while(!infile.eof()){/*读到产生式的左部, 中部, 右部*/
production * tmp = new production();
string s1, s2, s3;
infile>>s1>>s2>>s3;
tmp->left = s1;
tmp->middle = s2;
tmp->right = s3;
//cout<<"middle: "<<s2<<endl;
vt.push_back(tmp);
}
cout<<"从文件读入的产生式是:"<<endl;
int size = vt.size();
for(int i = 0; i < size; i++){
cout<<vt[i]->left<<"\t"<<vt[i]->middle<<"\t"<<vt[i]->right<<endl;
//cout<<vt[i]->left<<vt[i]->middle<<vt[i]->right<<endl;
}
}
vector<string> split(const string &st){/*以 | 为分隔符截取字符串*/
vector<string>res;
string tmp="";
int length = st.size();
int i = 0;
string token = "|";
while(i < length){
while(i < length && st[i] != token[0]){
tmp += st[i];
i++;
}
res.push_back(tmp);
//cout<<"tmp "<<tmp<<endl;
tmp = "";
i++;
}
return res;
}
void callSplit(){
int size = vt.size();
for(int i = 0; i < size; i++){//对每条产生式
vt[i]->rightString = split(vt[i]->right);//以 | 为分隔符截取产生式右部
}
//cout<<"check 分隔后的串"<<endl;
//for(int i = 0; i < size; i++){
// int lg = vt[i]->rightString.size();
// //cout<<vt[i]->left<<"这个产生式右部分解结果和每隔串的长度 ";
// for(int j = 0; j < lg; j++){
// cout<<vt[i]->rightString[j]<<":"<<vt[i]->rightString[j].length()<<" ";
// }
// cout<<endl;
//}
}
bool isBelongFinal(const char & x){//判断是否属于终结符号
string tmp = "";
tmp += x;
if(tmp == "@") //返回true把 @ 也当作终结符一遍直接加入first集合
{
return true;
}
for(int i = 0; i < pf; i++){
if(final[i] == tmp)
return true;
}
return false;
}
void solveFirst(string s){/*求s的first集,即是first[s]*/
int size = vt.size();
for(int i = 0; i < size; i++){
if(s == vt[i]->left){//找到s所在的产生式
int lg = vt[i]->rightString.size();//s产生式右部的侯选式个数
for(int j = 0 ; j < lg; j++){//对每条产生式右边的的候选式子
//cout<<"first!! "<<"i: "<<i<<" j: "<<j<<" 第一个字符 "<< vt[i]->rightString[j][0]<<endl;
if(isBelongFinal(vt[i]->rightString[j][0])){//终结符加入first
string tmp = "";
tmp += vt[i]->rightString[j][0];
//cout<<"s: "<<s<<" tmp: "<<tmp<<endl;
//first[vt[i]->left].insert(vt[i]->rightString[j][0]);
first[s].insert(tmp);
}
else{
int numCh = vt[i]->rightString[j].size();//侯选式的长度
int flag = 1; /*要把候选式多少个连续的符号的去除空字后的first集加入到
first[s].
比如E->TE'。 flag若为为2表示把first[T]/@,first[E']/@
加入first[s]
*/
for(int k = 0; k < numCh; k++){
if(isBelongFinal(vt[i]->rightString[j][k])){//终结符直接加入first
string tmp = "";
tmp += vt[i]->rightString[j][k];
first[vt[i]->left].insert(tmp);
break;
}
//非终结符
string tmp = "";
tmp += vt[i]->rightString[j][k];
solveFirst(tmp);//非终结符递归求其first集
if(first[tmp].find("@") != first[tmp].end()){//first[tmp]有空字
flag++;
continue;
}
else
break;
}
//cout<<"flag: "<<flag<<endl;
for(int h = 0; h < flag && h < numCh; h++){
/*char 转换为 string*/
string tmp = "";
tmp += vt[i]->rightString[j][h];
set<string>::iterator it;
set<string>nw;
nw.insert(first[tmp].begin(), first[tmp].end());
/*去除空字*/
it = nw.find("@");
if(it != nw.end()){
nw.erase(it);
}
/*去除空字后的结果加入first[s]*/
first[s].insert(nw.begin(), nw.end());
}
if(flag == numCh +1)//侯选式里的每个符号的first集都有空字
first[s].insert("@");
}
}
}
}
}
void callFirst(){
callSplit();
for(int i = 0; i < pnf; i++)//求每个非终结符的first集
solveFirst(notFinal[i]);
cout<<"-----------------检查各个非终结符的first集-------------"<<endl;
for(int i =0; i < pnf; i++){
//cout<<"in..."<<endl;
set<string>::iterator st;
cout<<"first["<<notFinal[i]<<"]"<<" ";
for(st = first[notFinal[i]].begin(); st != first[notFinal[i]].end(); st++){
cout<<*st<<" ";
}
cout<<endl;
}
}
int getIndex(const string & orgin, const string & target){//返回target匹配orgin的最后一个位置
int olength = orgin.length();
int tlength = target.length();
if(tlength == 1){//比如E 后面没有单引号'
for(int i = 0; i <olength; i++){
if(orgin[i] == target[0]){
if(i + 1 < olength && orgin[i+1] != '\'')
return i;
}
}
}
else{//有单引号比如E'
for(int i = 0; i < olength; i++){
if(orgin[i] == target[0]){
if(i + 1 < olength && orgin[i + 1] == '\'')
return i + 1;
}
}
}
return -1;
}
set<string> clearNull(set<string>arg){///返回集合arg去掉空字
set<string>::iterator it;
it = arg.find("@");
if(it != arg.end()){
arg.erase(it);
}
return arg;
}
void musterInsert(set<string>&a, set<string>b){//集合b的first去掉空字后的结果加入集合a
set<string>::iterator it;
it = b.find("@");
if(it != b.end()){//集合b去掉空字
b.erase(it);
}
a.insert(b.begin(), b.end());
}
bool hasNull(const set<string> & st){/*集合st是否有空字*/
set<string>::iterator it;
it = st.find("@");
if(it != st.end()){
return true; //有空字
}
return false;
}
set<string> solveFollow(string arg){//求argfollow集
int sz = vt.size();
for(int i = 0; i < sz; i++){
//cout<<"当前要求follw["<<arg<<"]"<<endl;
int lg = vt[i]->rightString.size();
for(int j = 0; j < lg; j++){//对vt[i]这个产生式右部的每个候选式
int numCh = vt[i]->rightString[j].size();//该侯选长度
int index = getIndex(vt[i]->rightString[j], arg);//匹配位置
if(index == -1 )//该候选式没有arg
continue;
if(index >= 0 && index < numCh - 1){//求arg后面的那个符号去除空字后的结果
string tmp = "";
tmp += vt[i]->rightString[j][index+1];
if(index + 2 < numCh && vt[i]->rightString[j][index + 2] == '\'')
{
tmp += '\'';//比如E' 后面的‘ 不能漏掉
//i++;
}
//cout<<"tmp: "<<tmp<<endl;
set<string>tmpSet;
if(isBelongFinal(tmp[0]))//arg后面是终结符,直接加入tmpSet集合
tmpSet.insert(tmp);
else
tmpSet = clearNull(first[tmp]);//first[tmp]集合去掉空字
musterInsert(follow[arg], tmpSet);//去掉空字后的结果加入follow[arg]集合
if(hasNull(first[tmp])){//tmp可以推出空字@
set<string>left;
if(vt[i]->left != notFinal[0] && follow[vt[i]->left].size())
left = follow[vt[i]->left];//产生式左部的follow集已求
else{
if(vt[i]->left != arg)//将产生式左部的follow集加入left
left = solveFollow(vt[i]->left);
}
musterInsert(follow[arg], left);//把left去掉空字加入到follow[arg]
}
//break;
}
if(index == numCh - 1){//比如K->FE 来求follow(E)E后面没有东西
//那么将产生式左部follow集合加入follow[arg]
set<string>left;
if(vt[i]->left != notFinal[0] && follow[vt[i]->left].size())
left = follow[vt[i]->left];//产生式左部follow集已求
else
left = solveFollow(vt[i]->left);
//set<string>left = solveFollow(vt[i]->left);
musterInsert(follow[arg], left);//把follow[left]加入follow[arg]
}
/*cout<<"当前follow["<<arg<<"]"<<endl;
for(set<string>::iterator it = follow[arg].begin(); it != follow[arg].end(); it++){
cout<<*it<<" ";
}
cout<<endl;
cout<<"finsih output follow["<<arg<<"]====="<<endl; */
//break;
}
}
return follow[arg];
}
void startCalFollow(){/*开始求follow集*/
follow[notFinal[0]].insert("#");
for(int i = 0; i < pnf; i++){
if(i !=0 && follow[notFinal[i]].size()>0)//该符号的follow集已求
continue;
solveFollow(notFinal[i]);
}
cout<<"-----------检查各个元素的follow集合----------------"<<endl;
for(int i = 0; i < pnf; i++){
cout<<"follow["<<notFinal[i]<<"]:"<<" ";
set<string>::iterator it;
for(it = follow[notFinal[i]].begin(); it != follow[notFinal[i]].end();it++){
cout<<*it<<" ";
}
cout<<endl;
//cout<<"finish this "<<notFinal[i]<<" 的follow"<<endl;
}
return ;
//solveFollow();
}
void in(){/*读入终结符合非终结符*/
ifstream infile;
infile.open("final.txt", ios::in);
if(!infile){
cout<<"error in opening final.txt"<<endl;
system("pause");
exit(-1);
}
string tmp;
while(!infile.eof()){
infile>>tmp;
if(tmp == "end")
break;
final[pf++] = tmp;
}
infile.close();//关闭
//------读入非终结符
infile.open("notFinal.txt", ios::in);
if(!infile){
cout<<"error in opening notFinal.txt"<<endl;
system("pause");
exit(-1);
}
while(!infile.eof()){
infile>>tmp;
if(tmp == "end")
break;
notFinal[pnf++] = tmp;
}
infile.close();
for(int i = 0; i < pnf; i++){//分析表全部初始化为error
for(int j = 0; j < pf; j++){
mp[notFinal[i]][final[j]] = "error";
}
}
cout<<"----------检查终结符号------------"<<endl;
for(int i = 0; i < pf; i++)
cout<<final[i]<<" ";
cout<<endl;
cout<<"-----------检查非终结符号-------------"<<endl;
for(int i = 0; i < pnf; i++)
cout<<notFinal[i]<<" ";
cout<<endl;
}
void outTable(){//输出分析表
cout<<"输出分析表"<<endl;
cout<<" ";
for(int i = 0; i < pf; i++){
//cout<<"in.."<<endl;
cout<<final[i]<<" ";
}
cout<<endl;
for(int i = 0; i < pnf; i++){//输出分析表内容
cout<<notFinal[i]<<" ";
for(int j = 0; j < pf; j++){
cout<<mp[notFinal[i]][final[j]]<<" ";
}
cout<<endl;
}
return ;
}
bool isInfer(const string & tmp, const string & target){
//tmp是候选式,判断tmp能否推出target
int length = tmp.length();
for(int i = 0; i < length; i++){
string t = "";
t += tmp[i];
if(first[t].find(target) != first[t].end())//tmp可以推出target
return true;
else{
if(first[t].find("@") == first[t].end())//tmp[i]不能推出target又没有空串@顶替
return false;
else{//first[t]有空串。 跳过tmp[i],看后面的能不能推出target
continue;
}
}
}
return false; //tmp不能推出target
}
void initContent(){//填写预测分析表
int sz = vt.size();
for(int i = 0; i < sz; i++){/*对分析表的每个非终结符*/
set<string>::iterator it;
for(it = first[vt[i]->left].begin(); it != first[vt[i]->left].end(); it++){
//对每个非终结符的first集合的每个元素*it
if(*it != "@"){//不是空字
if(vt[i]->rightString.size() == 1){//右边只有一个侯选式
mp[vt[i]->left][*it] = vt[i]->rightString[0];
}
else{//右边有多个侯选式子
string ch = *it;
for(int j = 0; j < vt[i]->rightString.size(); j++){//对每个侯选式
string tmp = vt[i]->rightString[j];//获得候选式
string col = *it;
//cout<<"这个候选式: "<<tmp<<" 要推出 "<<*it<<endl;
//如果候选式首字符和*it匹配或者
//或者侯选式tmp可以推出以*it开头 那么表格就填入侯选式tmp
if(tmp[0] == col[0] || isInfer(tmp, *it)){
mp[vt[i]->left][*it] = tmp;//选中这个候选式填入表
break;
}
}
}
}
else if(*it == "@"){//first集合有空串
//就把A->@ 写入到mp[A][fol](A是产生式左部,fol是follow集合的元素)
set<string>::iterator fol;
for(fol = follow[vt[i]->left].begin(); fol != follow[vt[i]->left].end(); fol++){
mp[vt[i]->left][*fol] = "@";
}
}
}
}
outTable();//输出分析表
return ;
}
string outStack(stack<string>target){/*输出分析栈内容*/
string res = "";
while(target.size()){
res = target.top() + res;
target.pop();
}
return res;
}
string getRestString(const string & st, const int & index){
///从st的index处截取字符串到末尾
int length = st.length();
string res = "";
for(int i = index ; i < length; i++)
res += st[i];
return res;
}
bool analyze(){//文法分析
//用户忘记输入串没有结尾加入 # 就让程序加上去。
if(inputString[inputString.length()-1] != '#')
inputString += '#';
cout<<"---------文法分析过程如下---------------"<<endl;
cout<<setw(16)<<setiosflags(ios::left)<<"符号栈"<<" "<<setw(16)<<setiosflags(ios::left)<<"当前输入符号"<<" "
<<setw(16)<<setiosflags(ios::left)<<"输入串"<<" "<<setiosflags(ios::left)<<"说明"<<endl;
st.push("#");
st.push(notFinal[0]);
int length = inputString.length();
string x;//栈顶
for(int i = 0; i < length; i++){
//cout<<"in...."<<endl;
if(st.size() <= 0){
cout<<"符号栈为空!!"<<endl;
system("pause");
exit(-1);
}
x = st.top();
string sinput = "";
sinput += inputString[i];
if(sinput == " ") // 比如 i + i ,跳过i和+之间的的空格
continue;
cout<<setw(16)<<setiosflags(ios::left)<<outStack(st)<<setw(16)<<setiosflags(ios::left)
<<sinput<<setw(16)<<setiosflags(ios::left)<<getRestString(inputString, i + 1);
//
if( !isBelongFinal(sinput[0])){
cout<<"分析表不存在mp["<<x<<","<<sinput<<"]!!文法不匹配"<<endl;
return false;
}
if(x == sinput && x == "#"){
cout<<setiosflags(ios::left)<<"匹配, 分析成功"<<endl;
return true;
}
if(x == sinput && x != "#"){
st.pop();
//cout<<setw(64)<<setiosflags(ios::left)<<"弹出栈顶符号"<<x<<",因为mp["<<
cout<<setiosflags(ios::left)<<"匹配,弹出栈顶符号"<<x<<"并读出输入串的下一个符号"<<getRestString(inputString , i +1)[0]<<endl;
continue;
}
//!!如果 x是终结符且x != sinput返回错误
if(isBelongFinal(x[0]) && x != sinput)
{
cout<<x<<" 和 "<<sinput<<"不匹配"<<endl;
return false;
}
//!!是非终结符号
if(mp[x][sinput] == "error")//没有找到产生式
{ //cout<<"文法不匹配!!"<<endl;
cout<<"mp["<<x<<","<<sinput<<"]"<<"的值是error, 不匹配!!"<<endl;
return false;
}
//找到产生式定义为空串
//也就是是A->@ @用@表示
if(mp[x][sinput] == "@"){
st.pop();
i--; //保持i仍然指向当前的输入串的位置
cout<<"弹出"<<x<<"因mp["<<x<<","<<sinput<<"]中为"
<<x<<"->"<<"@ ,固不压栈"<<endl;
continue;
}
//从分析表中找到产生式不是A->@
st.pop();
cout<<setiosflags(ios::left)<<"弹出栈顶符号 "<<x
<<" 将mp["<<x<<","<<sinput<<"]中"<<x<<"->"<<mp[x][sinput]<<"的"
<<mp[x][sinput]<<"逆序压入栈"<<endl;
int k = mp[x][sinput].length();
string token = "";
for(int j = k - 1; j >= 0;){//逆序压入栈
token += mp[x][sinput][j];
if(token == "'"){
//token += mp[x][sinput][j - 1];
token = mp[x][sinput][j-1]+ token;
j -= 2;
st.push(token);
//cout<<"当前压 "<<token<<" 逆序入栈"<<endl;
token = "";
}
else{
st.push(token);
//cout<<"当前压 "<<token<<" 逆序入栈"<<endl;
token = "";
j--;
}
}
i--; //保持要识别的串指针不变
}
return false;
}
void resetStack(stack<string> &s){//清空分析栈
s = stack<string>();
}
int main(){
in();//读入终结符和非终结符
readFile();//读入产生式
cout<<"求first集合"<<endl;
callFirst();
cout<<"求follow集合"<<endl;
startCalFollow();
initContent();//填写分析表
cout<<"输入要分析的串, 串结尾要加入 # "<<endl;
while(getline(cin, inputString)){
if(inputString == ""){
cout<<"请不要输入空串"<<endl;
continue;
}
if(inputString == "finish")
break;
if(analyze())
{
cout<<"输入串 "<<inputString<<" 文法匹配"<<endl;
}
else{
cout<<"输入串 "<<inputString<<" 文法不匹配"<<endl;
}
resetStack(st);//清空栈
cout<<"继续输入要分析的串或者输入 finish 退出程序"<<endl;
}
system("pause");
}
运行结果