消除无用符号
消除无用符号算法:
对于任意上下文无关文法G=(V,T,P,S),w∈L(G),X∈V,若存在a,b∈(VUT)*使得S经过若干步推出aXb,aXb经过若干步推出w,则称X为有用符号,否则为无用符号。
- 首先计算“产生的”符号集N:每个T中的符号都是产生的,若A→a∈P且a中符号都是产生的,则A是产生的。
伪代码:
for (int i=0;i<V.num;i++)
for(int j=0;j<v.num;j++)//v表示V的拷贝
if(p中存在v[j] →a&&a中的每个符号都属于N)
{将v[j]从v中删除并加到N中,同时跳出内层循环}
- 接着计算“可达的”符号集M:开始符号S是可达的,A→a∈P且A是可达的,则a中的符号都是可达的。
伪代码:
Reach(S){
if(P中存在S→a)
{将a中的所有字符加入到M中,
if(a中存在非终结符B)
Reach(B)}
return }
- 最后先消除全部非“产生的”符号,在消除全部非“可达的”符号,剩下的都是有用符号。最后将无用字符和无用产生式都删除
伪代码:
for(int i=0;i<Q.num;i++){//Q是VUT的集合
for(int j=0;j<N.num;j++){
if(Q[i]不在N中)
将其加入到非“产生的”符号集N1中}}
// 消除全部非“可达的”符号
for(int i=0;i<N1.num;i++){
for(int j=0;j<M.num;j++){
if(N1[i]不在M中)
将其加入到无用符号集NM中}}
//消除产生式和无用符号,因结构类似只写了一个
for(int i=0;i<P.num;i++){
if(P[i]个产生式中含有NM符号集的元素)
将该条产生式删除}}
注意产生集和可达集不能颠倒顺序,否则会消除不干净
消除空产生式
消除ε产生式:称形如A→ε的产生式为ε产生式。
思路:
- 由文法推出满足定义(A∈V,且A能在有限步推出ε)的非终结符集合V1。
伪代码:for(int i=0;i<V.num;i++)
for(int j=0;j<V.num;j++){
if(左部为V[i]的产生式右部所有符号都在V1中)
将V[i]加入V1中,跳出内层循环;
- 若产生式B→a0B1…Bkak∈P,其中aj∈(VUT)*,Bi∈V1,那么用ε和Bi本身代替Bi。然后将这些产生式都加入到新的产生式集合中,不满足上述产生式的直接将空产生式扣除后加入到新产生式集合中。若有S→ε,则引入S|→ε|S。S|为新的开始符号。
伪代码:for(int i=0;i<P.num;i++)
if(存在产生式B→a0B1...Bkak∈P,aj∈(VUT)*,Bi∈V1)
以Bi或者空代替Bi,并将形成的产生式加入到P|中;
else if(产生式左部属于V1)
将ε产生式去除后将其加入P|中;
else if(若有S→ε)
引入S|→ε|S。S|为新的开始符号;}
消除单一产生式
思路:如果存在产生式A→B,则将B作为A的子节点加入图集合X中,若存在B→C,则同样将C作为B的子节点加入到X中。所有的非终结符都进行这样的处理,然后对图集合X进行遍历,所有子节点加入到祖宗节点的链集合中,比如A的子节点有B、C、D,则将B、C、D加入A的链集合中。对于每个非终结符的链集合中若存在非终结符(假设为B),且B→w属于P ,w不属于V,则将A→w加入到P|中。遍历所有的链集合后便完成了单一产生式的消除。
伪代码:
for(int i=0;i<P.num;i++)
if(存在单一产生式(A→B))
将B作为A的子节点加入到图中;
遍历图找到每个非终结符的链集合;
遍历每个非终结符的链集合:
如果非终结符的链集合不为空(例如B在A的链集合中):
且B→w属于P,w不属于V;
将A→w加入到P|中;
如果非终结符的链集合为空则不进行处理。
消除左递归
消除左递归:间接左递归和直接左递归。首先消除间接左递归,转化为直接左递归,在消除所有的直接左递归。
1.消除直接左递归:形如A→Aa。
对于A→Aa|b(b可为空)。因为推导结束后一定有个b在开始位置,故改为:A→bB,B→aB。
2.消除间接左递归:形如:P→Aa,A→Pb。
首先对所有的非终结符进行随机编号排序。
1)若消除过程中出现了直接左递归,就按照直接左递归的方法,来消除。
2)若产生式右部最左的符号是非终结符,且这个非终结符序号大于等于左部非终结符,则暂不处理(后面会处理到)
3)若序号小于左部的非终结符,则用之前求到的式子的右部来替换
伪代码:
所有非终结符按任意顺序排列编号[V1,…,Vn]
按上面的排列顺序,对这些非终结符进行遍历
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= n - 1; ++j) {
遍历产生式,若V[j]序号小于等于产生式右部非终结符按规则3)
进行替换(序号大于的按规则2)处理) }
消除i序号的非终结符的直接左递归(如果存在的话)
}
这里需要注意的是,消除左递归后会产生空产生式和无用符号,
所以消除左递归后需要在将空产生式和无用符号处理一遍。