實驗任務:構造一個LL(1)文法分析器
思路是:
1.用map<char,int>逐個對終結符與非終結符標識
2.把產生式壓棧
3.得到Fisrt和Follow集
4.構建預測分析表
5.分析輸入的合法性
整體代碼如下
#include<iostream>
#include<stdio.h>
#include<vector>
#include<string>
#include<stack>
#include<map>
#include<cstring>
#include<cstdlib>
#include <bits/ios_base.h>
using namespace std;
map<char,int>getnum;
vector<char>getzf;
vector<string>proce(10);
vector<string>first(20);
vector<string>follow(20);
int table[100][100]; //預測分析表
int num;
int numv;//終結符的數量-1
char j[2];
void read()//讀終結符、非終結符、產生式
{
char c;
int i=0;
int n=0;
cout<<"輸入產生式集合(空字用‘@’表示),輸入一條後換行,以‘end’結束:"<<endl;
string ss;
string dd;
int j=0;
int y=0;
while(cin>>ss&&ss!="end")
{
dd.clear();
dd+=ss[0];
proce[j]+=dd;
for(i=3;i<ss.length();i++)
{
if(ss[i]!='|'){
dd.clear();
dd+=ss[i];
proce[j]+=dd;
}
else
{
dd.clear();
dd+=ss[0];
dd+=ss[++i];
proce[++j]+=dd;
}
}
j++;
}
getnum['#']=0;//#代表結束標誌
// getzf[0]='#';沒有定義數組大小的時候這樣輸入是錯誤的
getzf.push_back('#');
//終結符壓棧
for(int i=0;i<proce.size();i++)
{
for(int k=0;k<proce[i].length();k++)
{
if(proce[i][k]!='-'&&proce[i][k]!='|')
{
if(proce[i][k]<64||proce[i][k]>90)
{
for( y=0;y<getzf.size();y++)
{
if(proce[i][k]==getzf[y]) break;
}
if(y==getzf.size()&&k!=2){//這裏讓k!=2是不讓第三位置的>進去
getnum[proce[i][k]]=++n;
getzf.push_back(proce[i][k]);
}
}
}
}
}
getnum['@']=++n;
numv=n;//終結符的數量等於當前n的值
getzf.push_back('@');
//非終結符壓棧
for(int i=0;i<proce.size();i++)
{
for(int k=0;k<proce[i].length();k++)
{
if(proce[i][k]!='-'&&proce[i][k]!='|'&&proce[i][k]!='>')
{
if(proce[i][k]>64&&proce[i][k]<91)
{
for( y=0;y<getzf.size();y++)
{
if(proce[i][k]==getzf[y]) break;
}
if(y==getzf.size()){
getnum[proce[i][k]]=++n;
num=n;//終結符加非終結符的數量等於當前i的值
getzf.push_back(proce[i][k]);
}
}
}
}
}
}
void get_firstT()//給終結符的first數組賦值
{
int i;//不能在下面int
//先給終結符的first數組賦值
for( i=1;i<=numv;i++)
{
itoa(i,j,10);
first[i]=j;//之前寫的是first[i].push_back(j[0])是錯的,字符串數組的輸入不需要加下標,且如果是j[0]一個字符不能裝到一個字符串當中去
}
}
string get_firstF(int *a)//給非終結符的first數組賦值
{//犯了一個錯誤,下面的a沒有加*
for(int i=0;i<proce.size();i++)
{
if(getnum[proce[i][0]]==*a)
{
if(getnum[proce[i][1]]<=numv)
{
itoa(getnum[proce[i][1]],j,10);
first[*a]+=j;
}
else
{
//first[getnum[proce[i][0]]].clear();
first[getnum[proce[i][0]]]=get_firstF(&getnum[proce[i][1]]);
}
}
}
return first[*a];
}
void get_follow(int *a){//得到follow集
//犯了一個錯誤,以stirng開頭但是沒有返回值
int i,j1;
int flag=0;
for(i=0;i<proce.size();i++)
{
for(j1=1;j1<proce[i].length();j1++)
{
if(getnum[proce[i][j1]]==*a)//這地方應該是j1我寫成了k
{
if(j1==proce[i].length()-1)
{
if(getnum[proce[i][j1]]!=getnum[proce[i][0]])
follow[*a]+=follow[getnum[proce[i][0]]];
}
else
{
if(getnum[proce[i][j1+1]]<=numv)
{
itoa(getnum[proce[i][j1+1]],j,10);
follow[*a]+=j;
}
else
{
for(int jj=0;jj<first[getnum[proce[i][j1+1]]].size();jj++)
{
if(atoi(&first[getnum[proce[i][j1+1]]][jj])==numv)//等於空時
follow[*a]+=follow[getnum[proce[i][0]]];
else
follow[*a]+=first[getnum[proce[i][j1+1]]][jj];
}
flag=1;//標誌位,如果已經找到*a後面的非終結符就可以停止了
}
}
}
}
if(flag==1) break; //停止尋找
}
}
void get_table() //得預測分析表
{
memset(table,-1,sizeof(table));//剛開始tableM沒有初始化,導致本該是空格的地方出現E->TA
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(atoi(&temps[j])!=numv)
{
// table[getnum[proce[i][0]]][atoi(&temps[j])]=i;//atoi不能這麼用,他表示的是從當前位一直到末位
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 ss;
ss+=proce[i][0];
ss+="->"; //把->要加上
for(int j=1;j<proce[i].size();j++)
ss+=proce[i][j];
return ss;
}
void print_table()//輸出預測分析表
{
cout<<"預測分析表:"<<endl;
for(int i=0;i<numv;i++)
cout<<'\t'<<getzf[i];
cout<<endl;
for(int i=numv+1;i<=num;i++)
{
cout<<endl<<"________________________________________________________"<<endl;
cout<<getzf[i];
for(int j=0;j<numv;j++)
{
cout<<'\t'<<get_proce(table[i][j])<<"";
}
}
cout<<endl<<"________________________________________________________"<<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;
}
void scanf() //輸入字
{
cout<<"請輸入字:"<<endl;
cin>>word;
if(analyze())
cout<<"該字有效,所用產生式如上!"<<endl;
else
cout<<"出錯了!"<<endl;
}
int main()
{
int k;
int j;
read();
//終結符的first集
get_firstT();
//非終結符的first集
for(k=numv+1;k<=num;k++) //犯了一個錯誤,numv的位置寫成7
{
get_firstF(&k);
}
follow[numv+1]+='0'; //這地方加的是0而不是#
for(k=numv+1;k<=num;k++) //犯了一個錯誤,numv的位置寫成7
{
get_follow(&k);
}
cout<<endl;
get_table();
print_table();
scanf();
return 0;
}
一個LL(1)文法的例子:
E->TA
A->+TA|@
T->FB
B->FB|@
F-|i
(這裏面本是(E),但出錯,換成就可以了,不知道爲什麼)
輸入i+ii
例二:
S->iEtSB|a
B->eS|@
E->b
例三:
S->|a
L->SB
B->,SB|@