LL(1)的实现:
1.输入文法
2.求first集
3.求follow集
4.求select集
5.分析串
求各个集合的过程:
1、判断各非汇总结符是否可以推出空
(1)将各非终结符出示状态置为“未知”
(2)按顺序扫描各产生式右部。分为下面几种情况:
a、若遇到符号“ε”,检查左部非终结符状态,若不是“空”,将其置为“空”,继续扫描下一产生式;
b、若遇到终结符,检查左部非终结符状态,若为“未知”,将其置为“非空”,继续扫描下一产生式;
c、若遇到非终结符,检查该非终结符状态,若为“未知”,则跳过当前产生式,扫描下一产生式;若为“非空”,检查左部非终结符状态,若为“未知”,将其置为“非空”,继续扫描下一产生式;
若为“空”,继续扫描本产生式的下一符号;
d、若遇到“\0”,检查左部非终结符状态,若不是“空”,将其置为“空”,继续扫描下一产生式;
e、若上述a,b,c,d过程引起了非终结符状态的改变,则跳到(2)处继续循环,否则跳出循环,判断结束。
2、求非终结符的first集
扫描以要求first集的非终结符为左部的各产生式的右部,分为下面几种情况:
A、若遇到终结符,将该终结符加入左部非终结符的first集,继续扫描下一产生式;
B、若遇到符号“ε”,将“ε”加入左部非终结符的first集,继续扫描下一产生式;
C、若遇到非终结符,将该非终结符的 first集— {ε} 加入左部非终结符的first集,然后检查该非终结符是否可以推出空,若可以为空,则扫描本产生式的下一符号;若不为空,则继续扫描下一产生式;
D、若遇到“\0”,将“ε”加入左部非终结符的first集,继续扫描下一产生式;
3、求某一非终结符的FOLLOW集
(1)在产生式右部找到该非终结符,扫面它后面的符号,分为下面几种情况:
A、若是终结符,则将该终结符加入该非终结符的folow集;
B、若是非终结符,将该非终结符的 first集— {ε} 加入该非终结符的folow集,然后检查该非终结符是否可以推出空,若可以为空,则扫描本产生式的下一符号;
C、若是“\0”,则将该产生式的左部非终结符的follow集加入它的follow集。
(2)在产生式的右部继续查找该非终结符,若找到,转(1)步。
4、求某产生式的select集
扫描产生式的右部,分为下面几种情况:
- 若是终结符,则将该终结符加入该产生式的select集;
- 若是“ε”,将左部非终结符的follow集加入该产生式的select集;
- 若是非终结符,将该非终结符的 first集— {ε} 加入该产生式的select集,然后检查该非终结符是否可以推出空,若可以为空,则扫描本产生式的下一符号;
- 若是“\0”,则将该产生式的左部非终结符的follow集加入该产生式的select集。
具体实现:
#include<iostream>
#include<map>
#include<stack>
#include<unordered_map>
#include<unordered_set>
#include<string.h>
using namespace std;
char chan[100][50];
string S;
unordered_map<char,int> mp;
unordered_map<char,int> mpx;
unordered_set <char> Fir[50];
unordered_set <char> Foll[50];
unordered_set <char> Sele[50];
stack<char> st;
int fei; //非终结符数量
int cntchan; //产生式数量
int Cal[50][50]; //预测分析表
int f[50]; //能否推出空的标记数组
void mune();
void input();
void output();
void JudgeIsNull();
void displayNULL();
void First();
void displayFirst();
void Follow();
void displayFollow();
void Select();
void displaySele();
void Analyse();
int main()
{
int sel;
while(1)
{
mune();
cin>>sel;
switch(sel)
{
case 1:
input();
break;
case 2:
output();
break;
case 3:
displayNULL();
break;
case 4:
displayFirst();
break;
case 5:
displayFollow();
break;
case 6:
displaySele();
break;
case 7:
Analyse();
break;
}
system("cls");
}
}
void mune()
{
printf("1.输入LL(1)文法\n");
printf("2.显示输入的LL(1)文法\n");
printf("3.判定非终结符是否为空\n");
printf("4.输出非终结符的First集\n");
printf("5.输出非终结符的Follow集\n");
printf("6.输出产生式的Sellect集\n");
printf("7.分析过程演示\n");
}
void input()
{
cntchan=0; //此处考虑新增产生式,而非从头开始
system("cls");
int n;
int k=1;
printf("输入文法产生式的个数:");
cin>>n;
printf("输入各产生式(每行一条,空用'@'表示)中间用'->'隔开,如 A->b:\n");
for(int i=0; i<n; i++)
{
printf("%d) ",i);
cin>>chan[i];
if(!mp[chan[i][0]])
mp[chan[i][0]]=k++;
cntchan++;
}
JudgeIsNull();
First();
Follow();
Select();
system("pause");
}
void output()
{
system("cls");
for(int i=0; i<cntchan; i++)
cout<<chan[i]<<endl;
system("pause");
}
void JudgeIsNull()
{
/*
1、判断各非汇总结符是否可以推出空
(1)将各非终结符出示状态置为"未知"
(2)按顺序扫描各产生式右部。分为下面几种情况:
a、若遇到符号"ε",检查左部非终结符状态,若不是"空",将其置为"空",继续扫描下一产生式;
b、若遇到终结符,检查左部非终结符状态,若为"未知",将其置为"非空",继续扫描下一产生式;
c、若遇到非终结符,检查该非终结符状态,若为"未知",则跳过当前产生式,扫描下一产生式;若为"非空",检查左部非终结符状态,若为"未知",将其置为"非空",继续扫描下一产生式;
若为"空",继续扫描本产生式的下一符号;
d、若遇到"\0",检查左部非终结符状态,若不是"空",将其置为"空",继续扫描下一产生式;
e、若上述a,b,c,d过程引起了非终结符状态的改变,则跳到(2)处继续循环,否则跳出循环,判断结束。
2、求非终结符的first集
扫描以要求first集的非终结符为左部的各产生式的右部,分为下面几种情况:
A、若遇到终结符,将该终结符加入左部非终结符的first集,继续扫描下一产生式;
B、若遇到符号"ε",将"ε"加入左部非终结符的first集,继续扫描下一产生式;
C、若遇到非终结符,将该非终结符的 first集- {ε} 加入左部非终结符的first集,然后检查该非终结符是否可以推出空,若可以为空,则扫描本产生式的下一符号;若不为空,则继续扫描下一产生式;
D、若遇到"\0",将"ε"加入左部非终结符的first集,继续扫描下一产生式;
*/
//0->空 1->非空 -1->未知
int i,j;
for(i=1; i<=cntchan; i++)
f[i]=-1;
while(1)
{
for(i=0; i<cntchan; i++)
{
int indexA=mp[chan[i][0]];
for(j=3; j<strlen(chan[i]); j++)
{
if(chan[i][j]=='@')
{
f[indexA]=0;
break;
}
if(chan[i][j]<'A'||chan[i][j]>'Z')
{
if(f[indexA]==-1)
f[indexA]=1;
break;
}
if(chan[i][j]>='A'&&chan[i][j]<='Z')
{
if(f[mp[chan[i][j]]]==-1)
break;
if(f[mp[chan[i][j]]]==1)
{
if(f[indexA]==-1)
f[indexA]=1;
break;
}
else if(f[mp[chan[i][j]]]==0)
continue;
}
}
if(j==strlen(chan[i]))// \0
if(f[indexA]!=0)
f[indexA]=0;
}
for(i=0; i<cntchan; i++)
if(f[mp[chan[i][0]]]==-1)
break;
if(i==cntchan)
break;
}
unordered_map<char,int> :: iterator it;
for(it=mp.begin(); it!=mp.end(); it++)
fei++;
}
void displayNULL()
{
unordered_map<char,int> :: iterator it;
for(it=mp.begin(); it!=mp.end(); it++)
printf("%c->%d\n",it->first,f[it->second]);
system("pause");
}
void First()
{
// JudgeIsNull();
/*
2、求非终结符的first集
扫描以要求first集的非终结符为左部的各产生式的右部,分为下面几种情况:
A、若遇到终结符,将该终结符加入左部非终结符的first集,继续扫描下一产生式;
B、若遇到符号“ε”,将“ε”加入左部非终结符的first集,继续扫描下一产生式;
C、若遇到非终结符,将该非终结符的 first集— {ε} 加入左部非终结符的first集,
然后检查该非终结符是否可以推出空,若可以为空,则扫描本产生式的下一符号;若不为空,则继续扫描下一产生式;
D、若遇到 “\0” ,将“ε”加入左部非终结符的first集,继续扫描下一产生式;
*/
int J[50];
int i,j;
Foll[mp[chan[0][0]]].insert('#');
while(1)
{
for(i=1; i<=fei; i++)
J[i]=Fir[i].size();
for(i=0; i<cntchan; i++)
{
char left=chan[i][0];
int indexL=mp[left];
int len=strlen(chan[i]);
// Foll[indexL].insert('#');
for(j=3; j<len; j++)
{
char now=chan[i][j];
int indexR=0;
if(now>='A'&&now<='Z')
indexR=mp[now];
if(now<'A'||now>'Z') //终结符 a
{
Fir[indexL].insert(now);
break;
}
if(now=='@')
{
Fir[indexL].insert(now);
break;
}
if(now>='A'&&now<='Z')//非终结符
{
if(Fir[indexR].find('@')!=Fir[indexR].end())
Fir[indexR].erase(Fir[indexR].find('@'));
Fir[indexL].insert(Fir[indexR].begin(),Fir[indexR].end());
if(f[indexR]==0)
continue;
if(f[indexR]==1)
break;
}
}
if(j==len) // \0
Fir[indexL].insert('@');
}
int ff=0;
for(i=1; i<=fei; i++)
if(J[i]!=Fir[i].size())
ff=1;
if(ff==0)
break;
}
}
void displayFirst()
{
for(auto ii : mp)
{
int index = ii.second;
printf("First(%c) = { ",ii.first);
for(auto it : Fir[index])
printf("%c ",it);
printf("}\n");
}
system("pause");
}
void Follow()
{
// First();
/*
3、求某一非终结符的FOLLOW集
(1)在产生式右部找到该非终结符,扫面它后面的符号,分为下面几种情况:
A、若是终结符,则将该终结符加入该非终结符的folow集;
B、若是非终结符,将该非终结符的 first集— {ε} 加入该非终结符的folow集,
然后检查该非终结符是否可以推出空,若可以为空,则扫描本产生式的下一符号;
C、若是“\0”,则将该产生式的左部非终结符的follow集加入它的follow集。
(2)在产生式的右部继续查找该非终结符,若找到,转(1)步。
*/
int i,j;
int J[50];
while(1)
{
for(i=1; i<=fei; i++)
J[i]=Foll[i].size();
for(auto ii : mp)
{
int index = ii.second;
char now = ii.first;
for(i=0; i<cntchan; i++)
{
int len = strlen(chan[i]);
int indexL = mp[chan[i][0]];
int ff=0;
for(j=3; j<len; j++)
{
if(ff==1||chan[i][j]==now)
{
char c = chan[i][j+1];
int indexc = 0;
if(c==0) // \0
{
Foll[index].insert(Foll[indexL].begin(),Foll[indexL].end());
break;
}
if(c>='A'&&c<='Z') //非终结符
{
indexc = mp[c];
if(Fir[indexc].find('@')!=Fir[indexc].end())
Fir[indexc].erase(Fir[indexc].find('@'));
Foll[index].insert(Fir[indexc].begin(),Fir[indexc].end());
if(f[indexc] == 0)
{
ff=1;
continue;
}
else
break;
}
if(c<'A'||c>'Z')//终结符
{
Foll[index].insert(c);
break;
}
}
}
}
}
int fff=0;
for(i=1; i<=fei; i++)
if(J[i]!=Foll[i].size())
fff=1;
if(fff==0)
break;
}
}
void displayFollow()
{
for(auto ii : mp)
{
int index = ii.second;
printf("Follow(%c) = { ",ii.first);
for(auto it : Foll[index])
printf("%c ",it);
printf("}\n");
}
system("pause");
}
void Select()
{
/*
4、求某产生式的select集
扫描产生式的右部,分为下面几种情况:
(1)若是终结符,则将该终结符加入该产生式的select集;
(2)若是“ε”,将左部非终结符的follow集加入该产生式的select集;
(3)若是非终结符,将该非终结符的 first集— {ε} 加入该产生式的select集,
然后检查该非终结符是否可以推出空,若可以为空,则扫描本产生式的下一符号;
(4)若是“\0”,则将该产生式的左部非终结符的follow集加入该产生式的select集。
*/
int i,j,index;
for(i=0; i<cntchan; i++)
{
int indexL = mp[chan[i][0]];
int len = strlen(chan[i]);
for(j=3; j<len; j++)
{
char c = chan[i][j];
if(c=='@')
{
Sele[i].insert(Foll[indexL].begin(),Foll[indexL].end());
break;
}
if(c <'A'||c>'Z')
{
printf("\n%c\n",c);
Sele[i].insert(c);
break;
}
if(c>='A'&&c<='Z')
{
index = mp[c];
if(Fir[index].find('@')!=Fir[index].end())
Fir[index].erase(Fir[index].find('@'));
Sele[i].insert(Fir[index].begin(),Fir[index].end());
if(f[index]==0)
continue;
else
break;
}
}
if(j==len)
Sele[i].insert(Foll[indexL].begin(),Foll[index].end());
}
}
void displaySele()
{
for(int i=0; i<cntchan; i++)
{
printf("SELECT(%s)\t= { ",chan[i]);
for(auto it : Sele[i])
printf("%c ",it);
printf("}\n");
}
system("pause");
}
void Analyse()
{
int i,j,k=1;
for(i=0; i<50; i++)
for(j=0; j<50; j++)
Cal[i][j]=-1;
for(i=0; i<cntchan; i++)
{
int len=strlen(chan[i]);
for(j=3; j<len; j++)
if((chan[i][j]<'A'||chan[i][j]>'Z')&&!mpx[chan[i][j]])
mpx[chan[i][j]]=k++;
}
mpx['#']=k;
for(k=0; k<cntchan; k++)
{
int i = mp[chan[k][0]];
for(auto ii : Sele[k])
{
int j = mpx[ii];
Cal[i][j] = k;
}
}
string now ;//模拟输出串
cin>>S;
//处理字符串,结尾加 #
S+='#';
//初始化栈
now+='#';
now+=chan[0][0];
st.push('#');
st.push(chan[0][0]);
int ind = 0;
k=1;
printf("步骤\t分析栈\t剩余输入串\t推导产生式或匹配\n");
while(1)
{
printf("%d\t",k++);
cout<<now<<'\t'<<S.substr(ind,S.size()-ind)<<'\t'<<'\t';
if(S[ind]!='#') //当剩余串不为空
{
char Top = st.top();
char Sheng = S[ind];
if(Top == Sheng) //匹配
{
printf("'%c'匹配\n",Top);
st.pop();
ind++;
now = now.substr(0,now.size()-1);
continue;
}
int indexT = 0;
int indexS = 0;
if(Top >= 'A'&& Top <= 'Z') //分析栈顶为非终结符
indexT = mp[Top];
else
indexT = mpx[Top];
if(Sheng >= 'A'&& Sheng <= 'Z') //剩余串头为非终结符
indexS = mp[Sheng];
else
indexS = mpx[Sheng];
if(Cal[indexT][indexS]==-1) //不匹配
{
printf("分析错误!\n");
break;
}
else //匹配
{
char ss[50];
strcpy(ss,chan[Cal[indexT][indexS]]);
printf("%s\n",ss);
st.pop();
now = now.substr(0,now.size()-1);
if(ss[strlen(ss)-1]!='@')
{
for(i=strlen(ss)-1; i>=3; i--)
{
st.push(ss[i]);
now+=ss[i];
}
}
}
}
else
{
if(st.size()!=1) //当剩余串为空,分析栈不空时
{
char Top = st.top();
char Sheng = '@';
int indexT = mp[Top];
if(f[indexT]==0)
{
printf("%c->@\n",Top);
}
st.pop();
now = now.substr(0,now.size()-1);
}
else if(st.top()==S[ind])
{
printf("接受\n");
break;
}
else
{
printf("分析错误\n");
break;
}
}
}
system("pause");
}
/*
E->TX
X->+TX
X->@
T->FY
Y->*FY
Y->@
F->i
F->(E)
*/
// -std=c++11