已知文法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,用于保存括号中待推导的表达式。 4. 不足之处 |
#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