编译原理之文法推导

 

    已知文法G:
            <表达式> ::= <项> | <表达式>+<项>
    		<项> ::= <因子> | <项>*<因子>
    		<因子> ::= (<表达式>) | i
    试给出下列表达式的推导。
    (1) i				(2) (i)			(3) i*i			(4) i*i+i
    (5) i+(i+i)			(6) i+i*i		(7) (i+i)*i		(8) i+i
    (9) i*i+i*i			(10)i*i*i

 

3. 算法思想
描述的是大概的过程,具体实现的细节见代码注释。

(1)定义输入字符串E,保存需要推导的句子;定义输出字符串out=”E”和kout=”E”,out用于输出主推导(不是括号中表达式的推导 ) 时的推导过程,kout用于输出括号推导时的推导过程;定义栈S,用于保存括号中待推导的表达式。
(2)定义推导阶段标志kflag=0,kflag=0表示为主推导,kflag≠0表示为从右向左第kflag个括号推导。定义输出函数print(),根据kflag的值选择性输出推导过程(具体实现可见源代码),(3)中输出均为调用print()函数。
(3)推导方法如下: 
① 扫描字符串E,计算E中除去括号中的符号外“+”的个数,记录每个“()”的位置(位置相对每个“()”为一个整体来看)保存到数组pos,同时将“()”中的符号串压入栈S。
② E中除去括号中的符号外有多少个“+”,就在E的末尾添加几次“+I”(E→E+I),最后将E用I替换(E+I…→I+I…),同时输出。如果没有“+”,用I替换E(E→I),同时输出。
③ 倒序(从右向左)扫描E,以“+”为分割点分成若干区间,从右向左依次扫描每个区间,计算区间内除去括号中的符号外有几个“*”,从右向左扫描out(不重复扫描,每次接着上次的位置继续向左扫描),记录“I”的位置Ipos,有几个“*”就在out中Ipos位置的‘I’的后面插入“*T”(I→I*T),同时输出。 
④ 倒序扫描E,将位置i=pos[](pos数组中任意某个值)的字符“I”或“T”替换成“(E)”(I→T,T→(E)),同时输出。 
⑤当栈S不空,令E=S.top(),S栈顶出栈,推导阶段标志kflag++,重复②~⑤过程。直到S栈空,结束。

4. 不足之处
① 对于文法并没有用相应的数据结构去存储,推导过程仅仅只是依据输入的表达式中的算符来判断,并且只针对2.中给定的文法推导。
②输入检测不够严谨,仅仅只对输入能够接受的字符进行了限制,并没有检查句子合法性。
③推导过于依赖“+*()”符号,所以整体不是最左推导也不是最右推导;并且对应存在嵌套括号(括号中还有括号)的表达式不能推导,能够推导大部分的多级括号(式中有多个括号,但每个括号中无括号),但也有少部分不能推导。
 

    #include<iostream>
    #include<string>
    #include<stack>
    using namespace std;

    int kflag=0;//推导阶段标志,0为主推导,非0表示从右向左第几个括号推导
    string out,kout;//保存括号推导过程,保存全部推导过程

    void shift()//存储的字母输出为汉字
    {
    	cout<<"	→";
    	if(kflag==0)//主推导直接输出out
    	{
    		cout<<out;
    		for(int j=0;j<30-out.length();j++)
    			cout<<" ";
    		cout<<"|	→";
    		for(int i=0;i<out.length();i++)
    		{
    			if(out[i]=='E')
    				cout<<"<表达式>";
    			else if(out[i]=='I')
    				cout<<"<项>";
    			else if(out[i]=='T')
    				cout<<"<因子>";
    			else
    				cout<<out[i];
    		}
    		cout<<endl;
    	}
    	else//括号推导输出kout
    	{
    		cout<<kout;
    		for(int j=0;j<30-kout.length();j++)
    			cout<<" ";
    		cout<<"|	→";
    		for(int i=0;i<kout.length();i++)
    		{
    			if(kout[i]=='E')
    				cout<<"<表达式>";
    			else if(kout[i]=='I')
    				cout<<"<项>";
    			else if(kout[i]=='T')
    				cout<<"<因子>";
    			else
    				cout<<kout[i];
    		}
    		cout<<endl;
    	}
    }

    void print()//输出函数,输出推导过程
    {
    	int kk,knum,kpos1,kpos2;
    	if(kflag==0)//主推导
    		shift();//输出
    	else//括号推导
    	{
    		knum=0;//第几个括号
    		for(kk=kout.length()-1;kk>=0;kk--)
    		{
    			if(kout[kk]==')')
    			{
    				knum++;//遇到')加1
    				if(knum==kflag)//当遇到第kflag个)时
    				{
    					kpos2=kk;//记录右括号位置
    					while(kout[kk]!='(')
    						kk--;
    					kpos1=kk;//记录左括号位置
    				}
    			}
    		}
    		kout.replace(kpos1+1,kpos2-kpos1-1,out);//将括号中字符替换为当前的括号推导
    		shift();//输出
    	}
    }

    int main()
    {
    	string P[3][3];
    	P[0][0]="E";
    	P[0][1]="I";
    	P[0][2]="E+I";
    	P[1][0]="I";
    	P[1][1]="T";
    	P[1][2]="I*T";
    	P[2][0]="T";
    	P[2][1]="(E)";
    	P[2][2]="i";

    	cout<<endl<<"文法如下:"<<endl;//输出文法
    	for(int i=0;i<3;i++)
    	{
    		cout<<"	"<<P[i][0]<<" → "<<P[i][1]<<"|"<<P[i][2]<<"	|	";
    		for(int j=0;j<3;j++)
    		{
    			for(int m=0;m<P[i][j].length();m++)
    			if(P[i][j][m]=='E')
    				cout<<"<表达式>";
    			else if(P[i][j][m]=='I')
    				cout<<"<项>";
    			else if(P[i][j][m]=='T')
    				cout<<"<因子>";
    			else
    				cout<<P[i][j][m];
    			if(j==0)
    				cout<<" →";
    			if(j==1)
    				cout<<" | ";
    		}
    		cout<<endl;
    	}

    	stack<string> S; //保存括号中的句子,栈顶到栈底依次保存的是E中从右向左每对括号中的内容
    	int n[20];//括号内串长度,下标默认为第几个括号
    	int pos[20];//记录表达式中所有(位置
    	string E;//保存输入的文法
    	kout="E";//输出总的推导过程
    	string choose="1";
    	while(choose=="1") //选择是否继续的循环
    	{
    		cout<<endl<<"★说明:无法推导括号嵌套“(())”或“(()())”的句子,只能推导多个单级括号“()()()”的句子!"<<endl;
    		int error=1;
    		while(error==1)//输入句子,简单判错
    		{
    			cout<<"请输入该文法的句子:"<<endl<<">>";
    			cin>>E;
    			for(int i=0;i<E.length();i++)
    			{
    				if(E[i]!='i' && E[i]!='+' && E[i]!='*' && E[i]!='(' && E[i]!=')')
    				{
    					cout<<"输入有误请重新输入!表达式应只含有“i,+,*,(,)”等字符!"<<endl;
    					error=1;
    					break;
    				}
    				else
    					error=0;
    			}
    		}

    		cout<<endl<<"句子  "<<E<<"  的推导过程如下:"<<endl;
    		cout<<endl<<"	E";
    		for(int j=0;j<30-1;j++)
    				cout<<" ";
    		cout<<"  |	<表达式>"<<endl;//输出开始符
    	K:
    		int k=0;//访问pos数组
    		int add=0;//括号外加号个数
    		out="E";//保存括号中内容推导过程
    		for(int i=0;i<E.length();i++)
    		{
    			if(E[i]=='(')//未考虑括号嵌套情况
    			{
    				if(k!=0)//保存(的相对位置
    				{
    					pos[k]=i;//一对()看成一个位置
    					for(int j=0;j<k;j++)
    						pos[k]-=n[j];
    					pos[k]-=k;
    				}
    				else
    					pos[k]=i;
    				k++;//查找下一个(
    				i++;//跳过(
    				n[k-1]=0;//计算括号内字符长度
    				while(E[i]!=')')//跳过括号中的字符,并计算长度
    				{
    					n[k-1]++;
    					i++;
    				}
    				S.push(E.substr(i-n[k-1],n[k-1]));//括号中符号串内容入栈,如果括号很多栈底为靠左括号内容
    			}
    			if(E[i]=='+') add++;//计数+个数
    		}
    		for(int i=0;i<add;i++)
    		{
    			out.append("+I");//几个+就输出几个+I
    			print();
    		}
    		if(add==0)//无+时
    		{
    			out="I";
    			print();
    		}
    		else//有+时
    		{
    			out[0]='I';//E用I替换
    			print();
    		}

    		int mul;//两个+之间的括号之外乘号个数
    		int pos1=E.length()-1,pos2; //两个+的位置
    		int Ipos;//I的位置
    		char str[2]={'*','T'};//“*T”
    		str[2]='\0';
    		int i=E.length()-1;
    		int m=out.length()-1;
    		while(i!=-1)//倒序遍历输入的句子E
    		{
    			for(;i>=0;i--)//未考虑括号嵌套情况
    			{
    				if(E[i]==')')//遇到)
    				{
    					i--;//跳过)
    					while(E[i]!='(')//跳过括号中的字符
    						i--;
    				}
    				if(E[i]=='+') break;
    			}
    			pos2=pos1;//记录区间(以+为分界点)末端位置 ,是上一个区间的开始位置
    			pos1=i;//记录区间开始位置
    			if(i!=-1)//保证循环能够结束
    				i--;
    			mul=0;
    			for(int j=pos1+1;j<pos2;j++)//扫描此区间
    			{
    				if(E[j]=='(')//遇到(
    				{
    					j++;//跳过(
    					while(E[j]!=')')//跳过括号中的字符
    						j++;
    				}
    				if(E[j]=='*') mul++; //记录*个数
    			}
    			for(;m>=0;m--)//倒序遍历out字符
    			{
    				if(out[m]=='I')
    				{
    					Ipos=m;//记录I的位置
    					m=m-1;//下次从下一个位置开始遍历
    					break;
    				}
    			}
    			if(mul!=0)//有乘号
    			{
    				for(int j=0;j<mul;j++)
    				{
    					out=out.insert(Ipos+1,str);//I位置之后插入"*T"
    					print();
    				}
    			}
    	    }

    	    for(i=out.length()-1;i>=0;i--)//倒序遍历out字符串
    	    {
    	    	for(int j=0;j<k;j++)//查询pos数组,即括号(位置
    	    	{
    	    		if(i==pos[j])//(位置与i相等时
    	    		{
    	    			if(out[i]=='I')//推导路线为I->T->(E)
    	    			{
    	    				out[i]='T';
    	    				print();
    					}
    					if(out[i]=='T')
    					{
    						out.replace(i,1,"(E)");
    						print();
    					}
    				}
    			}
    		}

       		for(i=out.length()-1;i>=0;i--)//倒序遍历out
    	    {
    	    	if(out[i]=='I')//将I,T推导至i
    	    	{
    	    		out[i]='T';
    	    		print();
    			}
    	    	if(out[i]=='T')
    	    	{
    	    		out[i]='i';
    	    		print();
    			}
    		}
    		if(kflag==0)//如果是主推导
    			kout=out;//保存总输出
    		if(!S.empty())//当S栈不空,说明句子中有括号
    		{
    			E=S.top();//栈顶为最右侧括号
    			S.pop();//出栈
    			kflag++;//表示当前正在推导第几个括号中的内容
    			goto K;
    		}
    		else
    			cout<<endl<<"推导完毕!"<<endl<<endl;
    		cout<<"输入 1 继续推导其他句子,输入其他退出。"<<endl;
    		cin>>choose;
    		kflag=0;//kflag清零,继续推导其他句子
    	}
    	return 0;
    }

1.i

2.(i)

3.i*i

4.i*i+i

5.i+(i+i)

6.i+i*i

  • 10
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
1.1 编译器概述.flv 1.2 编译器结构.flv 1.3 编译器实例.flv 2.1 词法分析的任务.flv 2.2.1 词法分析器的手工构造1.flv 2.2.2 词法分析器的手工构造2.flv 2.2.3 词法分析器的手工构造3.flv 2.3.1 正则表达式1.flv 2.3.2 正则表达式2.flv 2.3.3 正则表达式3.flv 2.3.4 正则表达式4.flv 2.4.1 有限状态自动机1.flv 2.4.2 有限状态自动机2.flv 3.1.1 RE转换成NFA:Thompson算法1.flv 3.1.2 RE转换成NFA:Thompson算法2.flv 3.1.3 RE转换成NFA:Thompson算法3.flv 3.2.1 NFA转换成DFA:子集构造算法1.flv 3.2.2 NFA转换成DFA:子集构造算法2.flv 3.2.3 NFA转换成DFA:子集构造算法3.flv 3.2.4 NFA转换成DFA:子集构造算法4.flv 3.3.1 DFA的最小化:Hopcroft算法1.flv 3.3.2 DFA的最小化:Hopcroft算法2.flv 3.3.3 DFA的最小化:Hopcroft算法3.flv 3.4.1 从DFA生成分析算法1.flv 3.4.2 从DFA生成分析算法2.flv 3.4.3 从DFA生成分析算法3.flv 4.1.1 语法分析的任务1.flv 4.1.2 语法分析的任务2.flv 4.1.3 语法分析的任务3.flv 4.2.1 上下文无关文法推导1.flv 4.2.2 上下文无关文法推导2.flv 4.2.3 上下文无关文法推导3.flv 4.2.4 上下文无关文法推导4.flv 4.2.5 上下文无关文法推导5.flv 4.3.1 分析树和二义性文法1.flv 4.3.2 分析树和二义性文法2.flv 4.3.3 分析树和二义性文法3.flv 4.3.4 分析树和二义性文法4.flv 4.4.1 自顶向下分析1.flv 4.4.2 自顶向下分析2.flv 4.4.3 自顶向下分析3.flv 4.4.4 自顶向下分析4.flv 4.5.1 递归下降分析算法1.flv 4.5.2 递归下降分析算法2.flv 4.5.3 递归下降分析算法3.flv 4.5.4 递归下降分析算法4.flv 5.1.1 LL(1)分析算法1.flv 5.1.2 LL(1)分析算法2.flv 5.1.3 LL(1)分析算法3.flv 5.1.4 LL(1)分析算法4.flv 5.1.5 LL(1)分析算法5.flv 5.1.6 LL(1)分析算法6.flv 5.1.7 LL(1)分析算法7.flv 5.1.8 LL(1)分析算法8.flv 5.2 LL(1)分析的冲突处理.flv 5.3.1 LR(0)分析算法1.flv 5.3.2 LR(0)分析算法2.flv 5.3.3 LR(0)分析算法3.flv 5.3.4 LR(0)分析算法4.flv 5.4 SLR分析算法.flv 5.5 LR(1)分析算法.flv 5.6.1 LR(1)分析工具1.flv 5.6.2 LR(1)分析工具2.flv 5.6.3 LR(1)分析工具3.flv 6.1.1 语法制导翻译1.flv 6.1.2 语法制导翻译2.flv 6.1.3 语法制导翻译3.flv 6.2.1 语法制导翻译的实现原理1.flv 6.2.2 语法制导翻译的实现原理2.flv 6.3.1 抽象语法树1.flv 6.3.2 抽象语法树2.flv 6.3.3 抽象语法树3.flv 6.3.4 抽象语法树4.flv 6.4.1 抽象语法树的自动生成1.flv 6.4.2 抽象语法树的自动生成2.flv 7.1.1 语义分析的任务1.flv 7.1.2 语义分析的任务2.flv 7.1.3 语义分析的任务3.flv 7.2.1 语义规则及实现1.flv 7.2.2 语义规则及实现2.flv 7.2.3 语义规则及实现3.flv 7.2.4 语义规则及实现4.flv 7.3.1 符号表1.flv 7.3.2 符号表2.flv 7.3.3 符号表3.flv 7.4 语义分析中的其它问题.flv

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值