博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

消除左递归和提取左因子,消除回溯

Posted on 2019-11-25 20:31  淼哥学习园地  阅读(427)  评论(0编辑  收藏  举报

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>

#define isupper(x) (x >= 'A' && x <= 'Z')
#define islow(x) (x >= 'a' && x <= 'z')

using namespace std;

const int maxn = 100;

//产生式数据结构的设计 
struct Node {
    string s;         //记录此产生式的非终结符 
    string str[maxn]; //记录每一个候选式 
    int cnt;          //记录候选的个数 
}node[maxn];

//文法的数据结构的设计
struct G {
    char Vn[maxn];             //非终结符集合
    int cnt_Vn;                //非终结符的个数 
    char Vt[maxn];             //终结符集合 
    int cnt_Vt;                //终结符的个数 
    string S;                  //开始符 
    Node production[maxn];     //产生式 
}g;

int cnt;                           //用来记录产生式的个数  
map<string, int> mp;               //将一个字符串映射到一个整形数字 
bool vis[400];                     //用来记录出现的字符 
bool st[30];                       //用来记录大写字母的使用情况 
string str_recursion_pop[100];     //用来暂时存储消除左递归时候的字符串;需要消除的  
string str_recursion_push[100];    //用来暂时存储消除左递归时候的字符串;留下的     
int cnt_recursion_pop;             //用于存储暂时存储的消除左递归时候字符串的个数;消除的 
int cnt_recursion_push;            //用于存储暂时存储的消除左递归时候字符串的个数;剩下的 
vector<int> cnt_gene_out;          //用于暂时存储下标;用于提取左公共因子时 
string str_gene_pop[100];          //用来暂时存储消除公共因子时候的字符串;需要消除的  
string str_gene_push[100];         //用来暂时存储消除左公共因子时候的字符串;留下的 
int cnt_gene_pop;                  //用于存储暂时存储的除左公共因子时候字符串的个数;消除的 
int cnt_gene_push;                 //用于存储暂时存储的除左公共因子时候字符串的个数;剩下的 
bool vis_gene[100];                //用于标记需要消除的字符串的下标;在消除左公共因子的时候 


void input(void);                     //输入函数的声明 
void output(void);                    //输出函数的声明 
void converse(void);                  //得到非终结符集,终结符集合的函数的声明 
void out_gather(void);                //输出非终结符集, 终结符集 
void out_tmp(void);                   //输出所有产生式,输出中间过程中处理的文法 
void remove_left_recursion(void);     //消除左递归算法 
void remove_candidate(int x, int k, int y);     //用来移动候选式的数组
void remove_production(int idx, string str);    //用来修改产生式,在解决直接左递归的时候 
void remove_left_gene(void);                    //提取左公共因子算法 
void remove_gene_production(int idx, vector<int> x, string str);     //用来修改产生式,在解决除左

int main(void) {
    //用文件输入 
    freopen("g.txt", "r", stdin);
    //输入 
    input();    
    
    //标记已经使用过的大写字母 
    for(int i = 1; i <= cnt; i ++)
        st[node[i].s[0] - 'A'] = true;
    //初步整理 
    converse();
    printf("初步整理后的文法:"); 
    out_tmp();
    //消除左递归 
    printf("消除左递归后;");
    remove_left_recursion();
    out_tmp();
    
    //消除左公共因子 
    printf("消除左公共因子后:");
    remove_left_gene();
    out_tmp();
    
    //得到所有的产生式;得到终结符集合 以及 非终结符集合 
    converse(); 
    output();
    out_gather();

    //关闭文件 
    fclose(stdin);    
    return 0;
}


//输入函数的定义
inline void input(void) {
    string tmp, permt;
    int flag = false;
    while(cin >> tmp) {
        permt = "";
        int len = tmp.size(); //获取输入的长度 
        if(len == 0) break;
        
        //得到 -> 后面的候选字符串 
        int idx = 0;
        for(int i = 0; ;i ++)
            if(tmp[i] != '-') permt += tmp[i]; //permt是Vn 
            else {
                idx = i + 2;
                break;
            }
        
        //若是permt这个非终结符已经有了就不再开空间去存,否则再开一个存储单元去存储 
        if(mp[permt] == 0) mp[permt] = ++ cnt;
        node[mp[permt]].s = permt;
        if(!flag) g.S = permt, flag = true;  //得到开始的Vn 
        
        //将后面的字符放入到这个非终结符对应的结构体中
        int count = mp[permt]; permt = "";
        for(int i = idx; i < len; i ++) {
            if(tmp[i] != '|' && tmp[i] != ';') permt += tmp[i];
            else {
                node[count].str[++ node[count].cnt] = permt;
                permt = "";
            }
        } 
    }
} 

//得到非终结符集,终结符集合的函数的定义
inline void converse(void){
    for(int i = 1; i <= cnt; i ++) {
        
        //得到产生式 
        g.production[i] = node[i];
        
        //得到非终结符集合 和 终结符集合
        //处理候选式的左边 
        for(int j = 0; j < node[i].s.size(); j ++) {
            char tmp = node[i].s[j];
            if(isupper(tmp)) {
                if(!vis[tmp - 'A']) 
                    g.Vn[++ g.cnt_Vn] = tmp, vis[tmp - 'A'] = true;
            } else if(!vis[tmp - 'a' + 26])
                    g.Vt[++ g.cnt_Vt] = tmp, vis[tmp - 'a' + 26] = true;
        }
        
        //处理候选式的右边 
        for(int j = 1; j <= node[i].cnt; j ++) {
            for(int k = 0; k < node[i].str[j].size(); k ++) {
                char tmp = node[i].str[j][k];
                if(isupper(tmp)) {
                    if(!vis[tmp - 'A']) 
                        g.Vn[++ g.cnt_Vn] = tmp, vis[tmp - 'A'] = true;
                } else if(!vis[tmp - 'a' + 26])
                        g.Vt[++ g.cnt_Vt] = tmp, vis[tmp - 'a' + 26] = true;
                }
        }
        
    }
    
}

//输出整理后的文法的函数的定义
inline void output(void) {
    printf("构造出的LL(1)文法:\n");
    for(int i = 1; i <= cnt; i ++) {
        printf("%s->", g.production[i].s.c_str());
        for(int j = 1; j <= g.production[i].cnt; j ++)
            if(j == g.production[i].cnt) printf("%s;\n", g.production[i].str[j].c_str());
            else printf("%s|", g.production[i].str[j].c_str());
    }
    puts("");
} 

//输出非终结符集, 终结符集 的函数的定义 
inline void out_gather(void) {
    //非终结符 
    printf("非终结符集合:");
    for(int i = 1; i <= g.cnt_Vn; i ++) {
        printf("%c ", g.Vn[i]);
    }
    puts("");

    //终结符 
    printf("终结符集合:");
    for(int i = 1; i <= g.cnt_Vt; i ++)
        printf("%c ", g.Vt[i]);
}

//输出所有产生算式,用于调试 
inline void out_tmp(void) {
    puts("");
    for(int i = 1; i <= cnt; i ++) {
        printf("%s->", node[i].s.c_str());
        for(int j = 1; j <= node[i].cnt; j ++)
            if(j == node[i].cnt) printf("%s;\n", node[i].str[j].c_str());
            else printf("%s|", node[i].str[j].c_str());
    }
    puts("");
}  

//将一个终结符号的产生算式加到另一个终结符号的产生算式中,用于消除左递归
inline void remove_candidate(int x, int k, int y) {  //第x 个产生式的第 k 个候选,用第y个产生式来替换 
    int tmp = node[x].cnt;
    node[x].cnt += node[y].cnt - 1;
    
    //移动node[y].cnt - 1 个空位置出来;即:k --- (k + node[y].cnt - 1) 
    for(int i = node[x].cnt, j = tmp; j >= k + 1; i --, j --) 
        node[x].str[i] = node[x].str[j];
    
    string permt = node[x].str[k];
    //将y的候选式添加到x的候选中;即:k --- (k + node[y].cnt - 1) 
    for(int i = k, j = 1; j <= node[y].cnt; j ++, i ++) {
        node[x].str[i] = node[y].str[j];
        for(int k = 1; k < permt.size(); k ++) //加上去除第一个Vn后的字母 
            node[x].str[i] += permt[k]; 
    }
} 

//修改产生式 函数的定义 
inline void remove_production(int idx, string str) {  //idx == i (第i条产生式) str == tmp(新非终结符) 
    cnt ++;
    
    //移动数组,用来空出2个位置(只用空一个位置,因为原来的一个位置可以重复利用) 
    for(int i = cnt; i >= idx + 2; i --)
        node[i] = node[i - 1];
        
    //保留原来的并加上一个新添加的非终结符号 
    node[idx].cnt = cnt_recursion_push;
    for(int i = 1; i <= cnt_recursion_push; i ++) {
        node[idx].str[i] = str_recursion_push[i];
        node[idx].str[i] += str;
    }

    //新添加一个产生式
    node[idx + 1].s = str;
    node[idx + 1].cnt = cnt_recursion_pop + 1;  //加入一个@  
    for(int i = 1; i <= cnt_recursion_pop; i ++) {
        for(int j = 1; j < str_recursion_pop[i].size(); j ++)
            node[idx + 1].str[i][j - 1] = str_recursion_pop[i][j];
        node[idx + 1].str[i] += str;
    }
    node[idx + 1].str[cnt_recursion_pop + 1] = '@';
}


//消除左递归算法 函数的定义 
void remove_left_recursion(void) {
    
    //消除间接左递归 
    for(int i = cnt; i >= 1; i --) {     //每个个产生式的非终结符 
        for(int k = 1; k <= node[i].cnt; k ++) {  //此产生式的候选式的第一个字符 
            for(int j = cnt; j > i; j --) {        //前面每个产生式的左部 
                if(node[i].str[k][0] == node[j].s[0]) 
                    remove_candidate(i, k, j);      //第 i 个产生式 第 k 个候选式,可以用第j个产生式替代    
            }
        } 
    }
    
    //消除直接左递归
    for(int i = 1; i <= cnt; i ++) {     //每个产生式 
        bool flag = false;
        
        cnt_recursion_pop = 0;      //用于存储暂时存储的消除左递归时候字符串的个数;消除的 
        cnt_recursion_push = 0;     //用于存储暂时存储的消除左递归时候字符串的个数;剩下的 
        //获取一个没有使用过的大写字母 
        string tmp = "";          
        for(int i = 0; i < 26; i ++) 
            if(!st[i]) {
                tmp += ('A' + i); //tmp存储新生成的左端非终结符 
                break;
            }
            
        for(int k = 1; k <= node[i].cnt; k ++) {
            if(node[i].s[0] == node[i].str[k][0]) {
                flag = true;
                str_recursion_pop[++ cnt_recursion_pop] = node[i].str[k];  //存储存在直接左递归的候选式 
            } else str_recursion_push[++ cnt_recursion_push] = node[i].str[k];//存储不存在直接左递归的候选式 
        }
        
        if(flag) {
            st[tmp[0] - 'A'] = true;
            remove_production(i, tmp);  //传入存在直接左递归的 i 和新生成的 Vn tmp 
        }
    } 
}   

//用来修改产生式,在解决除左公共因子的时候           
void remove_gene_production(int idx, vector<int> x, string str) {   //第idx个产生式   x 是含有公因子的候选式下标集  str的公因子 
    cnt_gene_pop = cnt_gene_push = 0;
    memset(vis_gene, false, sizeof vis_gene);
    
    //标记需要提取因子的候选式 
    for(int i = 0; i < x.size(); i ++) vis_gene[x[i]] = true;  //哈希表来记录 
    
    //暂时存储这2类候选式 
    for(int i = 1; i <= node[idx].cnt; i ++) {
        if(vis_gene[i]) str_gene_pop[++ cnt_gene_pop] = node[idx].str[i]; //含有公因子的候选式 
        else str_gene_push[++ cnt_gene_push] = node[idx].str[i];   //不含公因子的候选式 
    }
    
    //得到一个未用过的大写字母
    string tmp = "";
    for(int i = 0; i < 26; i ++) 
        if(!st[i]) {
            tmp += ('A' + i);
            st[tmp[0]] = true;
            break;
        }
    
    //修改当前产生式的候选式
    node[idx].str[1] = str + tmp;
    for(int i = 2, j = 1; j <= cnt_gene_push; j ++, i ++)
        node[idx].str[i] = str_gene_push[j];
    node[idx].cnt = cnt_gene_push + 1;
    
    //产生式个数加1 
    cnt ++;
    
    //移动一个空位置出来 
    for(int i = cnt; i >= idx + 2; i --)
        node[i] = node[i - 1];
        
    //增加一个新的产生式 
    node[idx + 1].s = tmp;
    
    for(int i = 1; i <= cnt; i ++)
        node[idx + 1].str[i].clear();
     
    node[idx + 1].cnt = cnt_gene_pop; 
    
    for(int i = 1; i <= node[idx + 1].cnt; i ++) {
        if(str.size() == str_gene_pop[i].size()) node[idx + 1].str[i] = '@';
        else {
            string permt = "";
            for(int k = str.size(); k < str_gene_pop[i].size(); k ++) {
                node[idx + 1].str[i] += str_gene_pop[i][k];
                permt += str_gene_pop[i][k];
            }
        }        
    }        
}   

//提取左公共因子算法;函数的定义 
inline void remove_left_gene(void) { 
    int idx1 = 1, idx2 = 1, permt = 1; //idx1 产生式的 下标  idx2 候选式的下标 
    while(idx1 <= cnt) {
        bool flag = false;
        string tmp = "";
        while(idx2 <= node[idx1].cnt) {
            int cnt_gene_fi = 1;
            cnt_gene_out.clear();
            cnt_gene_out.push_back(idx2);  //记录存在公因子的下标 
            if(isupper(node[idx1].str[idx2][0])) {  //如果第一个候选第一个字母大写 直接退出(肯定不存在公因子) 
                idx2 ++;
                continue;
            }

            for(int i = idx2 + 1; i <= node[idx1].cnt; i ++) {  //找到所有含有相同公因子的候选式,将下标存储起来 
                if(node[idx1].str[idx2][0] == node[idx1].str[i][0]) {
                    flag = true;
                    cnt_gene_out.push_back(i);
                }
            }
            
            if(flag) {
                tmp += node[idx1].str[idx2][0];
                for(int i = 1; i < node[idx1].str[idx2].size(); i ++) {     //去匹配每一个字符 
                    for(int j = 1; j < cnt_gene_out.size(); j ++) {
                        if(node[idx1].str[idx2][i] != node[idx1].str[cnt_gene_out[j]][i]) break;
                        else if(j == cnt_gene_out.size() - 1) tmp += node[idx1].str[idx2][i];    //从含有公因子的候选式集合中提取更多的公因子 
                    }
                }

                remove_gene_production(idx1, cnt_gene_out, tmp);  //第 idx1 个产生式, cnt_gene_out含有公因子的集合,tmp 公因子 
            }
            
            tmp = "";
            idx2 ++;
        }
        if(flag) idx1 = permt;
        else permt ++, idx1 = permt;
    } 
}