1.项目要求
文法要求:
(1)从文件读入,每条产生式占用一行
(2)文法为LL(1)文法
从文件中读入文法,从键盘上输入待分析的符号串,采用 LL(1)分析算法判断该符号串是否为该文法的句子。
2.实验思路:
首先实现集合FIRST(X)构造算法和集合FOLLOW(A)构造算法,再根据FIRST和FOLLOW集合构造出预测分析表,并对指定的句子打印出分析栈的分析过程,判断是否为该文法的句子。
3.实验原理
(1)first集的算法思想
如果产生式右部第一个字符为终结符,则将其计入左部first集
如果产生式右部第一个字符为非终结符执行以下步骤
求该非终结符的first集
将该非终结符的非$first集计入左部的first集
若存在$,则将指向产生式的指针右移
若不存在$,则停止遍历该产生式,进入下一个产生式
若已经到达产生式的最右部的非终结符,则将$加入左部的first集
处理数组中重复的first集中的终结符
(2)follow集的算法思想
对于文法G中每个非终结符A构造FOLLOW(A)的办法是,连续使用下面的规则,直到每个FOLLOW不在增大为止.
对于文法的开始符号S,置#于FOLLOW(S)中;
若A->aBb是一个产生式,则把FIRST(b)\{ε}加至FOLLOW(B)中;
若A->aB是一个产生式,或A->aBb是一个产生式而b=>ε(即ε∈FIRST(b))则把FOLLOW(A)加至FOLLOW(B)中
(3)生成预测分析表的算法思想
构造分析表M的算法是:
对文法G的每个产生式A->a执行第二步和第三步;
对每个终结符a∈FIRST(a),把A->a加至M[A,a]中;
若ε∈FIRST(a),则把任何b∈FOLLOW(A)把A->a加至M[A,b]中;
把所有无定义的M[A,a]标上出错标志.
(4)对符号串的分析过程
预测分析程序的总控程序在任何时候都是按STACK栈顶符号X和当前的输入符号行事的,对于任何(X,a),总控程序
每次都执行下述三种可能的动作之一;
若X=a=”#”,则宣布分析成功,停止分析过程.
若X=a≠”#”,则把X从STACK栈顶逐出,让a指向下一个输入符号.
若X是一个非终结符,则查看分析表M,若M[A,a]中存放着关于X的一个产生式,那么,首先把X逐出STACK栈顶,然后把产生式的右部符号串按反序一一推进STACK栈(若右部符号为ε,则意味着不推什么东西进栈).在把产生式的右部符号推进栈的同时应做这个产生式相应得语义动作,若M[A,a]中存放着”出错标志”,则调用出错诊察程序ERROR.
4.实验代码
#include<iostream> #include<string> #include<map> #include<vector> #include<stack> #include<set> #include<cstring> using namespace std; map<char,int>getnum; char getchar[100]; //获得对应字符 vector<string>proce; int table[100][100]; //预测分析表 int num=0;int numvt=0; //numvt是终结符集合,0是‘#’,numvt表空字 string first[100]; string follow[200]; void readin() { memset(table,-1,sizeof(table)); getnum['#']=0; getchar[0]='#'; cout<<"请输入所有的终结符:"<<endl; char x; do { cin>>x; getnum[x]=++num; getchar[num]=x; }while(cin.peek()!='\n'); numvt=++num; getnum['@']=numvt; //kong zi getchar[num]=('@'); cout<<"请输入所有非终结符:"<<endl; do { cin>>x; getnum[x]=++num; getchar[num]=x; }while(cin.peek()!='\n'); cout<<"输入产生式集合(空字用‘@’表示),以‘end’结束:"<<endl; string pro; while(cin>>pro&&pro!="end") { string ss; ss+=pro[0]; for(int i=3;i<pro.size();i++) { if(pro[i]=='|') { proce.push_back(ss); ss.clear();ss+=pro[0]; } else { ss+=pro[i]; } } proce.push_back(ss); } } void jiaoji(string &a,string b) //a=a or b 取a,b交集赋值给a { set<char>se; for(int i=0;i<a.size();i++) se.insert(a[i]); for(int i=0;i<b.size();i++) se.insert(b[i]); string ans; set<char>::iterator it; for(it=se.begin();it!=se.end();it++) ans+=*it; a=ans; } string get_f(int vn,int & has_0) //dfs:vn能推出的不含空字的vt集合,并且判断vn能否推出空字 { if(vn==numvt)has_0=1; if(vn<numvt)return first[vn]; string ans; for(int i=0;i<proce.size();i++) { if(getnum[proce[i][0]]==vn) ans+=get_f(getnum[proce[i][1]],has_0); } return ans; } void getfirst() { for(int i=1;i<=numvt;i++) //终结符,first集是其本身。 { first[i]+=('0'+i); } for(int j=0;j<proce.size();j++) //扫描所有产生式 { int k=0;int has_0=0; //k扫瞄该产生式 do{ has_0=0; k++; if(k==proce[j].size()) //推到最后一个了,则附加空字 { first[getnum[proce[j][0]]]+=('0'+numvt); break; } //合并之 jiaoji(first[getnum[proce[j][0]]],get_f(getnum[proce[j][k]],has_0)); } while(has_0); //到无法推出空字为止 } } void print_first() { cout<<"first集:"<<endl; for(int i=1;i<=num;i++) { cout<<"first ["<<getchar[i]<<"]: "; for(int j=0;j<first[i].size();j++) cout<<getchar[first[i][j]-'0']<<" "; cout<<endl; } cout<<endl; } void getfollow() { jiaoji(follow[getnum[proce[0][0]]],"0"); //先添加‘#’; for(int j=0;j<proce.size();j++) //扫所有产生式 { for(int jj=1;jj<proce[j].size();jj++) //每个非终结符的follow集 { if(getnum[proce[j][jj]]<=numvt)continue; //vt无follow集 int k=jj; int has_0; do { has_0=0; k++; if(k==proce[j].size()) //都能推出空字,follow集=产生式左边的vn, { jiaoji(follow[getnum[proce[j][jj]]],follow[getnum[proce[j][0]]]); break; } jiaoji(follow[getnum[proce[j][jj]]],get_f(getnum[proce[j][k]],has_0)); }while(has_0); } } } void gettable() //得预测分析表 { for(int i=0;i<proce.size();i++) //扫所有产生式 { if(proce[i][1]=='@') //直接推出空字的,特判下(follow集=产生式左边的vn中元素填) { string flw=follow[getnum[proce[i][0]]]; for(int k=0;k<flw.size();k++) { table[getnum[proce[i][0]]][flw[k]-'0']=i; } } string temps=first[getnum[proce[i][1]]]; for(int j=0;j<temps.size();j++) //考察first集 { if(temps[j]!=('0'+numvt)) { table[getnum[proce[i][0]]][temps[j]-'0']=i; } else //有空字的,考察follw集 { string flw=follow[getnum[proce[i][1]]]; for(int k=0;k<flw.size();k++) { table[getnum[proce[i][0]]][flw[k]-'0']=i; } } } } } string get_proce(int i) //由对应下标获得对应产生式。 { if(i<0)return " "; //无该产生式 string ans; ans+=proce[i][0]; ans+="->"; //ans+=(proce[i][0]+"->"); 注意这样不行!思之即可。 for(int j=1;j<proce[i].size();j++) ans+=proce[i][j]; return ans; } void print_table() { cout<<"预测分析表:"<<endl; for(int i=0;i<numvt;i++) cout<<'\t'<<getchar[i]; cout<<endl; for(int i=numvt+1;i<=num;i++) { cout<<getchar[i]; for(int j=0;j<numvt;j++) { cout<<'\t'<<get_proce(table[i][j]); } cout<<endl; } cout<<endl; } void print_follow() { cout<<"follow集:"<<endl; for(int i=numvt+1;i<=num;i++) { cout<<"follow ["<<getchar[i]<<"]: "; for(int j=0;j<follow[i].size();j++) cout<<getchar[follow[i][j]-'0']<<" "; cout<<endl; } cout<<endl; } string word; bool analyze() //总控,分析字word的合法性,若合法,输出所有产生式。 { stack<char>sta; sta.push('#');sta.push(proce[0][0]); int i=0; while(!sta.empty()) { int cur=sta.top(); sta.pop(); if(cur==word[i]) //是终结符,推进 { i++; } else if(cur=='#') //成功,结束 { return 1; } else if(table[getnum[cur]][getnum[word[i]]]!=-1) //查表 { int k=table[getnum[cur]][getnum[word[i]]]; cout<<proce[k][0]<<"->"; for(int j=1;j<proce[k].size();j++) cout<<proce[k][j]; cout<<endl; for(int j=proce[k].size()-1;j>0;j--) //逆序入栈 { if(proce[k][j]!='@') sta.push(proce[k][j]); } } else //失败! { return 0; } } return 1; } int main() { readin(); getfirst(); getfollow(); getfollow(); gettable(); print_first(); print_follow(); print_table(); cout<<"请输入字:"<<endl; cin>>word; if(analyze()) cout<<"succeed!该字有效,所用产生式如上。"<<endl; else cout<<"error!"<<endl; return 0; }
实验输入及程序运行输出结果: