当前位置:文档之家› 编译原理实验报告3-LL(1)文法构造

编译原理实验报告3-LL(1)文法构造

实验3LL(1)文法构造一、实验目的熟悉LL(1)文法的分析条件,了解LL(1)文法的构造方法。

二、实验内容1、编制一个能够将一个非LL(1)文法转换为LL(1)文法;2、消除左递归;3、消除回溯。

三、实验要求1、将一个可转换非LL(1)文法转换为LL(1)文法,要经过两个阶段,1)消除文法左递归,2)提取左因子,消除回溯。

2、提取文法左因子算法:1)对文法G的所有非终结符进行排序2)按上述顺序对每一个非终结符Pi依次执行:for(j=1; j< i-1;j++)将Pj代入Pi的产生式(若可代入的话);消除关于Pi的直接左递归:Pi-> Piα|β,其中β不以Pi开头,则修改产生式为:Pi —>βPi′Pi′—> αPi′|ε3)化简上述所得文法。

3、提取左因子的算法:A —>δβ1|δβ2|…|δβn|γ1|γ2|…|γm(其中,每个γ不以δ开头) 那么,可以把这些产生式改写成A —> δA′|γ1| γ2…|γmA′—>β1|β2|…|βn4、利用上述算法,实现构造一个LL(1)文法:1)从文本文件g.txt中读入文法,利用实验1的结果,存入实验1设计的数据结构;2)设计函数remove_left_recursion()和remove_left_gene()实现消除左递归和提取左因子算法,分别对文法进行操作,消除文法中的左递归和提出左因子;3)整理得到的新文法;4)在一个新的文本文件newg.txt输出文法,文法输出按照一个非终结符号一行,开始符号引出的产生式写在第一行,同一个非终结符号的候选式用“|”分隔的方式输出。

四、实验环境PC微机DOS操作系统或Windows操作系统Turbo C程序集成环境或Visual C++ 程序集成环境五、实验步骤1、学习LL(1)文法的分析条件;2、学习构造LL(1)文法的算法;3、结合实验1给出的数据结构,编程实现构造LL(1)文法的算法;4、结合实验1编程和调试实现对一个具体文法运用上述算法,构造它的LL(1)文法形式;5、把实验结果写入一个新建立的文本文件。

六、测试数据输入数据:编辑一个文本文文件g.txt,在文件中输入如下内容:ﻩ正确结果:本实验的输出结果是不唯一的,根据消除左递归是选择非终结符号的顺序不同,或选择新的非终结符号的不同,可能会得到不同的结果,下面只是可能的一个结果:1、P->P之类的),也不含以ε为右部的产生式。

一个文法要能进行LL(1)分析,那么这个文法应该满足:无二义性,无左递归,无左公因子。

首先需要定义一些规则:1.在程序运行前文法就必须输入进文本文件中,输入的文法只包含其中的所有产生式,并且默认其为可转换的非LL(1)文法,即通过消除左递归和反复提取公共左因子,就转换成了LL(1)文法。

2.输入的产生式为原实验1的结果,即一个非终结符只有一条产生式,候选之间用“|”隔开。

3.产生式与产生式之间只能以换行符或分号隔开。

4.开始符号对应的产生式必须第一个输入,即默认输入的第一个产生式的左部的大写字母是开始符号。

5.输入与输出都会保存在文本文件中文件名分别是g.txt和newg.txt,本实验测试数据时,将两个文本放在了桌面。

6.ε用@代替,输入与输出都使用@。

7.新产生的非终结符统一使用没有在非终结符集合中出现的大写字母。

8.规定产生式最多只有20个。

2、构造LL(1)文法的算法;算法:1)从文本文件g.txt中读入文法,存入结构体中。

将第一个读到的大写字母记为开始符号S,将读到的包括开始符号在内的所有大写字母判定为非终结符,并将第一次出现的存入文法的非终结符集合中,终结符小写字母也一样。

将以换行符或分号隔开的字符串判断为一条产生式存入文法中。

实现函数是scanP()。

2)对文法中的产生式消除左递归。

实现函数是remove_left_recursion()。

3)对文法中的产生式反复提取公共左因子。

实现函数是remove_left_gene()。

4)向newg.txt中输出文法的所有产生式。

3、消除左递归文法和提取左因子算法实现方法;消除左递归文法(包括其中用到其它的子函数):/*字符串分割函数,将产生式右部的候选返回,识别‘|’,从pos位开始分割*/string strsplit(string strTok,int pos ) { ﻩﻩﻩstring str;ﻩsize_t position;position = strTok.find("|",pos);ﻩif(position!= string::npos) {ﻩﻩﻩ//找到了‘|’ﻩstr= strTok.substr(pos,position - pos);ﻩ}ﻩelse {ﻩﻩﻩﻩﻩ//没找到ﻩﻩstr = strTok.substr(pos, strTok.size() - pos);}return str;}/*获得一个文法中尚未定义的非终结符,即特定的大写字母*/char GetWord(char *p){ﻩcharch,word[] ={ 'A', 'B', 'C','D', 'E', 'F','G', 'H','I', 'J','K', 'L','M', 'N','O', 'P','Q',ﻩ'R', 'S','T', 'U', 'V','W', 'X','Y','Z'};intw,x;for (w = 0;w < 26; w++){ﻩch= word[w];ﻩﻩfor (x=0; x<m; x++) {ﻩif (ch == p[x]) {ﻩﻩbreak;ﻩ}ﻩ}ﻩﻩif(x == m)break;ﻩ}ﻩreturn ch;}/*判断非终结符是否已存在*/bool checkWord(charch, string Vn) {int i;ﻩboolflag =true;for(i =0;i < Vn.size(); i++) {ﻩﻩif (ch==Vn[i])ﻩﻩflag = false;}ﻩreturn flag;}/*化简无用产生式*/void simplify(struct grammar *gp) {string str;ﻩﻩ//存储从开始符号可以到达的非终结符的序列int sVn[20];ﻩﻩﻩ//标记在哪个产生式ﻩsVn[0]=0;ﻩstr.insert(0,1,gp->Vn[0]); ﻩ//初始化设置开始符号boolflag[20];flag[0] =false;ﻩﻩﻩﻩ//标记哪个产生式需要删除char ch;ﻩint i,j,k,l,o;ﻩfor(i =0; i <str.size(); i++) {ﻩfor(j = 3; j < gp->P[sVn[i]].size(); j++) {ﻩﻩfor (k= 0;k< m;k++){ﻩﻩﻩif (gp->P[sVn[i]][j] <'A' || gp->P[sVn[i]][j] > 'Z') break;//不是非终结符无需判断ﻩﻩif(gp->P[sVn[i]][j] == gp->Vn[k]) {ﻩﻩﻩ//判断从开始符号可以到达的非终结符在Vn的哪个位置ﻩﻩﻩﻩflag[k] =false;ﻩﻩif (checkWord(gp->Vn[k], str)) { ﻩ//将在str中没有的有用非终结符插入strﻩﻩint e = str.size();ﻩﻩﻩsVn[e] = k;ﻩstr.insert(str.size(),1, gp->Vn[k]);ﻩ}ﻩﻩbreak;ﻩﻩﻩ}ﻩﻩ}ﻩ}}for (l = 0;l < m; l++) {//删除无用的产生式和相应的非终结符ﻩﻩchar ch;ﻩif(flag[l]){ﻩﻩﻩgp->Vn[l] ='';ﻩﻩfor(o= l + 1;o <m; o++){ch = gp->Vn[o - 1];ﻩﻩgp->Vn[o -1] =gp->Vn[o];ﻩﻩﻩﻩgp->Vn[o]=ch;ﻩﻩﻩgp->P[o- 1].clear();ﻩﻩgp->P[o-1].s>P[o]);ﻩﻩ}ﻩm--;ﻩ}}}void remove_left_recursion(struct grammar *gp) { ﻩﻩ//子函数,消除文法左递归ﻩinti, j,num_i,num_j,ipos,jpos;charch_i, ch_j;ﻩfor (i= 1; i<m + 1;i++) {ﻩﻩbool repeat =true; ﻩﻩﻩ//标记相对于本轮循环上轮过程产生式是否有过变化,有则需要重新分割得到候选ﻩﻩnum_i= 0,ipos= 3;ﻩstring str_i[20],restr_i[20], ex = "a";ﻩﻩﻩch_i = gp->Vn[i - 1];ﻩﻩﻩ//获取当前要被处理的非终结符,即Piﻩﻩﻩﻩ//分割产生式,得到右部的所有候选ﻩwhile (ipos != gp->P[i - 1].size() + 1) {ﻩstr_i[num_i]= strsplit(gp->P[i-1], ipos);ﻩrestr_i[num_i]= str_i[num_i];ﻩﻩﻩipos= ipos +str_i[num_i].size() + 1;ﻩnum_i++;ﻩﻩ}ﻩfor(j = 1;j<=i - 1; j++) {ﻩﻩif (!repeat) {ﻩnum_i =0, ipos= 3;ﻩﻩch_i =gp->Vn[i - 1]; ﻩ//重新获取当前要被处理的非终结符,即Piﻩﻩﻩﻩ//分割产生式,得到右部的所有候选ﻩﻩwhile (ipos != gp->P[i- 1].size() +1){ﻩﻩﻩstr_i[num_i]= strsplit(gp->P[i - 1], ipos);ﻩrestr_i[num_i]=str_i[num_i];ﻩﻩipos = ipos + str_i[num_i].size() + 1;ﻩﻩnum_i++;ﻩﻩ}ﻩ}ﻩrepeat= true;ﻩﻩstringstr_j[20];ﻩﻩint l;ﻩﻩjpos = 3;ﻩﻩﻩnum_j =0;ch_j=gp->Vn[j - 1];ﻩﻩﻩﻩ//获取当前要替换的非终结符,即P jﻩﻩﻩ//分割产生式,得到右部的所有候选ﻩﻩﻩﻩwhile(jpos!= gp->P[j - 1].size() + 1){ﻩﻩstr_j[num_j]= strsplit(gp->P[j-1], jpos);ﻩﻩﻩjpos= jpos + str_j[num_j].size() + 1;ﻩﻩﻩnum_j++;ﻩﻩﻩ}ﻩfor(l = 0; l <num_i; l++) {ﻩ//逐个判断Pi的候选中是否含有Pj,有则进行替换ﻩﻩﻩﻩstringchange;ﻩﻩex[0] = ch_j;ﻩﻩsize_t pos = restr_i[l].find(ex); ﻩﻩﻩﻩif(pos ==string::npos){ continue;} //无需替换ﻩﻩelse if (pos ==0){ﻩﻩﻩﻩ//Pj在该候选的第一个字符ﻩﻩrepeat =false;//ﻩﻩstring s=str_i[l].substr(1, str_i[l].size()- 1);ﻩ//得到候选中除Pj外的剩余部分ﻩﻩﻩstr_i[l].s);ﻩﻩﻩ//清空字符串ﻩﻩint link = 0;ﻩwhile(1) {ﻩﻩﻩ//将Pj的所有候选与Pi中匹配的候选的剩余部分连接,中间还添加“|”ﻩif (link == num_j)break;ﻩﻩstr_j[link]+=s; ﻩﻩﻩﻩﻩif(link ==num_j - 1) ﻩstr_i[l]+= str_j[link];ﻩelseﻩstr_i[l] = str_i[l] + str_j[link] +"|";ﻩﻩﻩﻩ link++;ﻩ}ﻩﻩﻩ}ﻩﻩelseif (pos== str_i[l].size() - 1) {//Pj在该候选的最后一个字符ﻩﻩrepeat =false;//ﻩﻩﻩﻩstring s = str_i[l].substr(0, str_i[l].size() - 1);ﻩﻩﻩstr_i[l].s);ﻩintlink =0;ﻩﻩwhile(1) { ﻩﻩﻩif(link ==num_j)ﻩbreak;ﻩﻩﻩﻩ str_j[link] =s+ str_j[link];ﻩﻩﻩif (link == num_j - 1)str_i[l] += str_j[link];ﻩﻩelse str_i[l] = str_i[l]+ str_j[link] +"|";ﻩﻩﻩﻩlink++;ﻩﻩﻩ}}ﻩelse { ﻩﻩﻩ//Pj在该候选的中间ﻩﻩrepeat = false;//ﻩﻩﻩstring s1 =str_i[l].substr(0,pos - 1); //得到该候选中Pj前的字符串ﻩﻩﻩﻩstring s2 = str_i[l].substr(pos+ 1, str_i[l].size() - pos - 1);//得到该候选中Pj后的字符串ﻩﻩﻩstr_i[l].s);ﻩﻩﻩint link = 0;ﻩﻩﻩwhile(1) {ﻩﻩﻩﻩﻩﻩﻩif(link ==num_j)ﻩbreak;ﻩstr_j[link] = s1+str_j[link]+s2;ﻩﻩif (link==num_j -1) str_i[l]+= str_j[link];ﻩelse str_i[l] = str_i[l] + str_j[link]+ "|";ﻩﻩﻩlink++;ﻩﻩﻩ}ﻩﻩﻩ}}ﻩstringstri = "->";ﻩstri.insert(0,1,ch_i);ﻩﻩﻩint index=0;ﻩﻩwhile(1) {ﻩ//将替换后的Pi的所有候选进行重组并存进文法中ﻩﻩif(index==num_i)ﻩbreak;ﻩﻩﻩif (index == num_i -1)ﻩstri = stri +str_i[index];ﻩﻩﻩelse stri=stri +str_i[index] + "|";ﻩﻩﻩindex++;ﻩ}ﻩﻩgp->P[i - 1] = stri;ﻩ}ﻩ//消除直接左递归string splitstr[30], resplitstr[30];ﻩint s = 0,ps = 3,h = 0;ﻩwhile (1){ﻩﻩﻩ//分割替换后的产生式ﻩﻩsplitstr[s] =strsplit(gp->P[i - 1],ps);ﻩﻩresplitstr[s]=splitstr[s];ﻩps = ps + splitstr[s].size()+ 1;ﻩif (ps == gp->P[i- 1].size() + 1) ﻩbreak;ﻩs++;ﻩ}ﻩstring Pi = "->",Pichange ="->";ﻩPi = ch_i+ Pi;ﻩintlink =0,flag= -1;bool flagpos[30];ﻩﻩchar newWord;ﻩﻩfor (; link<= s;link++) { //遍历所有候选,校验其中是否有左递归ﻩﻩsize_t posi;ﻩﻩposi = resplitstr[link].find(ch_i);ﻩif(posi ==0) {ﻩﻩ//存在直接左递归ﻩﻩflag++;ﻩﻩ//对候选标记左递归ﻩﻩif (flag ==0) { ﻩﻩ//处理出现左递归的第一个候选ﻩﻩnewWord =GetWord(gp->Vn);ﻩ//获取一个新的非终结符ﻩﻩgp->Vn[m] = newWord;ﻩﻩﻩPichange =newWord + Pichange;ﻩﻩﻩm++;ﻩﻩﻩﻩsplitstr[link] = splitstr[link].substr(1)+newWord;ﻩﻩflagpos[link] = false;ﻩﻩﻩﻩgp->P[m - 1] = Pichange+ splitstr[link]+ "|";ﻩ}ﻩﻩif(flag > 0) {ﻩﻩsplitstr[link] = splitstr[link].substr(1) + newWord;ﻩﻩﻩflagpos[link] = false;ﻩﻩgp->P[m - 1] =gp->P[m - 1] + splitstr[link] +"|";ﻩﻩﻩ}ﻩﻩ}}//对消除了直接左递归的候选进行重组成为产生式并存入文法ﻩﻩif (flag >-1) {ﻩﻩﻩﻩgp->P[i - 1] ="->";ﻩgp->P[i -1].insert(0, 1,ch_i);ﻩﻩfor (; h <= s;h++) {ﻩﻩif(flagpos[h]) {ﻩﻩﻩsplitstr[h] += newWord;ﻩﻩgp->P[i- 1] = gp->P[i-1] + splitstr[h]+ "|";ﻩ}}gp->P[m- 1]+= "@";ﻩﻩgp->P[i -1].erase(gp->P[i- 1].size()-1,1);ﻩﻩ}ﻩ}simplify(gp);ﻩﻩﻩ//化简无用的产生式}提取左因子(包括辅助函数)://对字符串数组排序void str_sort(string*str, int num) {inti,j;for (i = 0;i < num; i++) {ﻩfor (j = i+ 1; j<num;j++) {ﻩif(str[i] > str[j])ﻩﻩstr[i].s[j]);ﻩ}ﻩ}}/*子函数,提取左因子*/void remove_left_gene(struct grammar*gp) {ﻩﻩﻩﻩﻩintrule_a, i, j, k, l, matchnum,oldmatchnum, resize,size;ﻩcharch, newWord;for(rule_a = 0; rule_a <m;rule_a++) { //遍历所有产生式ﻩintbre = -1;ﻩﻩﻩﻩﻩ//标记已对产生式进行过左因子的提取ﻩintoldpo =0;ﻩﻩint num = 0, ps = 3;ﻩstringstr[30],restr[30];ﻩﻩﻩﻩ//前者用于判断,需要保持原样,后者用于对有公共左因子的候选进行提取,可变ﻩwhile (ps != gp->P[rule_a].size()+1) { ﻩ//分割替换后的产生式ﻩﻩstr[num]=strsplit(gp->P[rule_a], ps);ﻩrestr[num] = str[num];ps = ps + str[num].size() +1;ﻩﻩﻩnum++;}ﻩﻩstr_sort(str,num); ﻩﻩ//对所有候选按ASCII码进行排序,以便于简化对公共左因子的判断,只需先对前面候选判断ﻩstr_sort(restr,num);ﻩint ca_i;ﻩstringPa ="->";ﻩPa.insert(0, 1,gp->Vn[rule_a]);ﻩfor(ca_i =0; ca_i < num; ca_i++) {ﻩ//对排序后的候选进行重组并存入文法ﻩif (ca_i== num -1)ﻩPa +=str[ca_i];ﻩﻩelseﻩPa += str[ca_i] +"|";ﻩﻩ}ﻩgp->P[rule_a] = Pa;ﻩﻩint ipo = 0; //辅助免除对已判断过有左因子的候选的遍历ﻩfor (i = 0;i < num;i++,i += ipo) { //遍历候选ﻩipo = 0;ﻩﻩsize = 0;ﻩresize = 0;ﻩﻩoldmatchnum = 0;inti_s =str[i].size();ﻩfor (j =0;j<i_s;j++) { ﻩﻩ//对候选的逐个字符遍历ﻩﻩﻩﻩmatchnum=0; ﻩﻩﻩ//标记除了本身,有几个候选有公共左因子ﻩﻩﻩch=str[i][j];ﻩﻩint kf = num; ﻩﻩfor(k= i +1;k< num && k<kf;k++) { //对i之后的候选进行判断,是否有与i对应的公共左因子ﻩﻩﻩﻩif(str[k][j] == ch) { ﻩ//有公共左因子ﻩﻩmatchnum++;ﻩﻩﻩ}ﻩelse {ﻩﻩﻩbreak;ﻩ}ﻩﻩ}ﻩif (j == 0) { ﻩ//判断是否有公共左因子是i的第一个字符的情况,有则特别处理ﻩﻩﻩif(matchnum == 0)ﻩﻩbreak;ﻩﻩelseﻩ{oldmatchnum = matchnum; kf= i + 1 + oldmatchnum;} ﻩﻩﻩ}ﻩﻩﻩﻩelse{ﻩﻩﻩﻩﻩﻩif(oldmatchnum!= matchnum) break;ﻩﻩ}ﻩﻩ}ﻩ/*有公共左因子的处理过程*/ﻩif(matchnum !=oldmatchnum|| j == i_s) {ﻩﻩﻩﻩﻩbre++;ﻩﻩstringmatch,repstr,can, newP;ﻩmatch = str[i].substr(0, j);//获取公共左因子ﻩﻩﻩﻩnewWord =GetWord(gp->Vn);ﻩﻩ//得到新的非终结符ﻩﻩﻩgp->Vn[m]=newWord;ﻩ//将新非终结符存入文法ﻩﻩm++;ﻩnewP="->";ﻩﻩﻩnewP.insert(0,1,newWord);ﻩrepstr= match + newWord;ﻩ//得到要被替换的有公共左因子的所有候选ﻩint renum = num;ﻩﻩﻩﻩif(bre> 0) {ﻩ//若对同一产生式还存在另一个公共左因子(之前提取过一次左因子),需进行特别处理ﻩﻩsize = resize = 0;ﻩﻩﻩﻩrenum = 0;ﻩps = 3;ﻩﻩwhile (ps!= gp->P[rule_a].size() +1){ﻩﻩﻩ//分割变化后的产生式ﻩﻩﻩﻩﻩrestr[renum]= strsplit(gp->P[rule_a], ps);ﻩﻩﻩﻩﻩps = ps+ restr[renum].size() +1;ﻩﻩrenum++;ﻩﻩﻩﻩ}ﻩﻩ}ﻩ/*将已经提取过左因子的以候选为单位的字符串重新组合成产生式(包括新产生式)*/ﻩﻩﻩﻩfor (l =0; l <= i- oldpo+ oldmatchnum;l++) {ﻩﻩﻩﻩﻩif(l >=i - oldpo) {ﻩﻩﻩﻩsize += restr[l].size();ﻩﻩcan=restr[l].substr(j);ﻩﻩﻩﻩﻩif (can == "")ﻩﻩcan = "@";ﻩﻩﻩﻩif(l == i -oldpo+ oldmatchnum) newP += can;ﻩﻩﻩﻩelsenewP =newP + can+ "|";ﻩﻩﻩﻩﻩgp->P[m - 1]= newP;ﻩﻩ}ﻩﻩelse{ﻩﻩﻩﻩresize+= restr[l].size();ﻩﻩresize++;ﻩﻩ}ﻩﻩ}ﻩﻩgp->P[rule_a].replace(resize+ 3,size + oldmatchnum,repstr);//原产生式以替换的方式进行改变ﻩﻩif(i + 1+oldmatchnum >num) { break;}ﻩelseoldpo = ipo = oldmatchnum;ﻩ}ﻩ}ﻩ}}4、主程序代码;#include<iostream>#include<string>using namespace std;structgrammar{ﻩﻩﻩ//使用结构体定义文法ﻩchar Vn[20];ﻩﻩﻩﻩ//非终结符ﻩchar Vt[20];ﻩﻩﻩﻩﻩ//终结符ﻩcharS;ﻩﻩﻩ//开始符号string P[20]; ﻩﻩﻩﻩ//产生式};int m = 0, n =0;ﻩﻩﻩ//全局变量,分别表示最近存入结构体的非终结符与终结符是字符数组的第几个位置char GetBC(FILE* fpi){ﻩ//子函数,用于读取一个非空格字符char ch;ﻩdo{ﻩch = fgetc(fpi);ﻩ} while(ch ==' ');return ch;}ﻩ/*ﻩ整型函数,读入一行产生式分析出文法成员,参数分别是输入文本的文件指针、文法结构体的指针ﻩ第几行的产生式ﻩﻩ*/void scanP(FILE* fpi,struct grammar*gp) {ﻩﻩﻩchar ch;stringstr; ﻩﻩﻩﻩ//存入一条产生式ﻩif (feof(fpi))ﻩﻩreturn;ﻩﻩ//到达文件尾则返回ﻩch =GetBC(fpi);ﻩif (ch>='A' && ch<='Z') {ﻩﻩﻩ//读入产生式左部的非终结符ﻩstr +=ch;ﻩgp->Vn[m] = ch;ﻩﻩﻩ//将非终结符存入结构体ﻩﻩm++;ﻩch = GetBC(fpi);ﻩif(ch == '-') {ﻩﻩstr += ch;ﻩﻩch = GetBC(fpi);ﻩﻩif(ch== '>'){ﻩﻩﻩstr +=ch;ﻩﻩwhile (1) {ch = GetBC(fpi);ﻩﻩﻩif (ch=='\n' || ch == ';') ﻩbreak; ﻩ//读入换行符或分号,退出循环ﻩﻩstr+= ch;ﻩﻩﻩﻩﻩif(ch>= 'a' && ch<= 'z') {ﻩﻩ//读入终结符ﻩﻩﻩintnum;ﻩﻩfor (num =0; num< n; num++){ ﻩﻩ//判断该终结符在当前结构体中是否已存在ﻩif(gp->Vt[num]==ch)ﻩﻩﻩﻩﻩbreak;ﻩﻩ}ﻩﻩﻩﻩﻩif(num == n) {ﻩﻩ//存入结构体中未出现的终结符ﻩﻩﻩﻩﻩgp->Vt[n] =ch;ﻩﻩﻩﻩﻩﻩn++;ﻩﻩ}ﻩﻩﻩ}ﻩﻩﻩ}ﻩﻩﻩ}ﻩ}ﻩgp->P[m - 1]+=str;ﻩﻩﻩ//将产生式存入结构体ﻩ}}int main() {ﻩFILE* fpi;ﻩﻩ//定义输入文本指针ﻩFILE* fpo; ﻩﻩﻩﻩ//定义输出文本指针ﻩerrno_t err;ﻩif((err = fopen_s(&fpi,"C:\\Users\\Administrator\\Desktop\\g.txt","r")) !=0){ﻩ//只读方式打开文件ﻩprintf(" not open!\n"); ﻩ//打开文件出错提示ﻩexit(0);ﻩﻩﻩﻩﻩﻩ//安全退出程序ﻩ}struct grammar g,*gp; ﻩﻩgp =&g;ﻩﻩﻩﻩﻩ//定义结构体及其指针//读取第一个大写字母作为开始符号ﻩchar ch;ﻩdo {ﻩch = GetBC(fpi);ﻩ}while (ch<= 'A'|| ch >='Z');ﻩgp->S =ch;fseek(fpi, -1L, 1); ﻩﻩ//搜索指示器回调一个字符while (!feof(fpi)) {ﻩﻩﻩ//从文本文件中读入产生式得到文法四个部分,并存入结构体中ﻩscanP(fpi,gp);ﻩﻩﻩ}fclose(fpi); ﻩﻩﻩ//关闭fpi指向的文件fpi =NULL; ﻩﻩﻩﻩﻩ//避免指向非法内存remove_left_recursion(gp);ﻩremove_left_gene(gp);err=fopen_s(&fpo,"C:\\Users\\Administrator\\Desktop\\newg.txt", "w"); //只写方式打开文件,不存在则自动建立ﻩif(err != 0) {ﻩﻩprintf(" not open!\n");ﻩﻩﻩﻩ//打开文件出错提示ﻩﻩexit(0); ﻩﻩﻩﻩﻩ//安全退出程序ﻩ}int i; ﻩﻩfor (i =0; i< m; i++) {ﻩﻩ//输出处理后的文法到文本中fputs(gp->P[i].data(),fpo);ﻩﻩfputs("\n", fpo);ﻩ}fclose(fpo); ﻩ//关闭fpi指向的文件ﻩfpo =NULL;ﻩﻩﻩﻩ//避免指向非法内存}5、整个测试程序的流程;6、程序的测试结果和问题;实验源文法:ﻩ其它文法:向g.txt中输入文法启动程序main()反复调用scanP()到达文件尾完成文法输入调用remove_left_recursion()调用remove_left_gene()将文法输出到newg.txt程序结束查看结果文法ﻩ书上的文法,不过调换了顺序,且改变开始符号为R,结果会删除关于Q的无用产生式:ﻩﻩ实验中遇到的问题:1.开始设计程序的时候,并没有很好的运用数据结构来简化问题,只是使用了实验一的数据结构,构造了一个结构体。

相关主题