代码:
#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; } }