步骤一:定义一个结构体struct g{char left[2];char right[100];}用来存放用户输入的文法;一个结构体struct fxb{int left;char mind[2];char right[5];}用来存放用户输入的分析表;一个char类型数组str用来存放用户要分析的符号串;一个int类型数组zt用来存放分析字符串过程中状态的变化;一个char类型数组fh用来存放分析字符串过程中符号的规约或移进变化;整形变量ztp、fhp、strp、i和j分别表示指向状态栈的栈顶状态、指向符号栈的栈顶符号、输入符号串队列的头指针、文法的总条数和分析表的总长度;
步骤二:提示用户输入文法,并将用户输入的文法记录在结构体数组G中,如果用户输入的文法不符合要求,提示用户重新输入;
步骤三:提示用户输入分析表,并将用户输入的分析表记录在结构体数组FXB中,如果用户输入的分析表不符合要求,提示用户重新输入;
步骤四:提示用户输入符号串,并将用户输入的符号串记录在字符串数组str中,如果用户没有输入字符串,提示用户重新输入;
步骤五:定义三个整型变量flag、count和flagp分别代表当差分析表查到“acc”则置flag为一以便跳出最外层循环、一共进行了多少步动作、用来调整输出格式;
步骤六:用一个无限循环,在每次动作之前都打印一下当前的步骤、状态栈、符号栈、输入串;开始遍历分析表,用状态栈的栈顶状态和输入符号的最左边的字符去查分析表,能查找则进行处理;不能查到则提示用户出错;
步骤七:在步骤六中能查到进行下面三种情况处理:①查到的符号串的首字母是‘s’说明要进行移进,将‘s’后面的字符数字转换为数字(进行-‘0’处理),将其压入状态栈中;再将输入串的最左边字符取出压入符号串中;打印在分析表中查到的信息;②查到的符号串的首字母是‘r’说明要进行规约,将‘r’后面的字符数字转换为数字(进行-‘0’处理)并作为文法数组的下标,计算要规约文法的长度,将符号栈指针和状态栈指针都减去该长度(即表示符号和状态出栈);遍历分析表将规约后的非终结符与状态栈栈顶状态进行查表,查到的状态入栈同时非终结符入栈,若没有查到则提示用户出错;③查到的符号串的首字母是‘a’说明接受,并置flag为1以便退出最外层循环。
#include<stdio.h>
#include<string.h>
struct g{
char left[2];
char right[100];
};
struct fxb{
int left;
char mind[2];
char right[5];
};
int main()
{
struct g G[100];
struct fxb FXB[200];
char str[100];
int zt[200];
char fh[200];
zt[0] = 0;
fh[0] = '#';
int ztp = 0,fhp = 0,strp = 0;
printf("请输入文法,例如E (L)表示文法E->(L),# #表示结束!\n");
int i = 1,j = 0;//i表示文法的条数 ,j表示分析表的总长度
scanf("%s %s",&G[i].left,&G[i].right);
while(G[i].left[0] != '#'&&G[i].right[0] != '#')
{
i++;
scanf("%s %s",&G[i].left,&G[i].right);
}
if(i == 1)
{
printf("你没有输入文法,请重新输入!");
return 0;
}
// for(int K = 0;K < i;K++)//输出文法
// {
// printf("%s %s\n",G[K].left,G[K].right);
// }
printf("请输入分析表,例如0 ( s2表示用0和(进行查表查到下一步应该将状态2压入状态栈,以此类推,0 # #表示结束!\n");
scanf("%d %s %s",&FXB[j].left,&FXB[j].mind,&FXB[j].right);
while(FXB[j].left != 0||FXB[j].mind[0] != '#'||FXB[j].right[0] != '#')
{
j++;
scanf("%d %s %s",&FXB[j].left,&FXB[j].mind,&FXB[j].right);
}
if(j == 0)
{
printf("你没有输入分析表,请重新输入!");
return 0;
}
// for(int k = 0;k < j;k++)//输出分析表
// {
// printf("%d %s %s\n",FXB[k].left,FXB[k].mind,FXB[k].right);
// }
printf("请输入要分析的符号串:\n");
scanf("%s",&str);
if(str[strlen(str)-1] != '#')
{
str[strlen(str)] = '#';
str[strlen(str) + 1] = '\0';
}
if(strlen(str) == 1)
{
printf("你没有输入字符串,请重新输入!");
return 0;
}
int flag = 0,count = 0,flagp = 0;
printf("步骤 状态栈 符号栈 输入串 ACTION GOTO\n");
while(1)
{
flagp = 0;
printf("(%d) ",++count);
for(int k = 0;k<=ztp;k++)//输出状态栈
{
if(zt[k]>9)
{
printf("(%d)",zt[k]);
flagp++;
}
else
{
printf("%d",zt[k]);
}
}printf(" ");
if(flagp == 0)
printf(" ");
for(int k = 0;k<=fhp;k++)//输出符号栈
{
printf("%c",fh[k]);
} printf(" ");
for(int k = strp;k<strlen(str);k++)//输出输入串
{
printf("%c",str[k]);
} printf(" ");
int t;
for(t = 0;t<j;t++)
{
if(zt[ztp] == FXB[t].left&&str[strp] == FXB[t].mind[0])
{
if(FXB[t].right[0] == 's')//将状态压入栈中
{
zt[++ztp] = FXB[t].right[1] - '0';//状态入栈
fh[++fhp] = str[strp];//符号入栈
strp++;//符号出栈
printf("%s\n",FXB[t].right);
}
else if(FXB[t].right[0] == 'r')//进行规约
{
fhp = fhp - strlen(G[FXB[t].right[1] - '0'].right);
ztp = ztp - strlen(G[FXB[t].right[1] - '0'].right);
printf("%s ",FXB[t].right);
int tt;
for(tt = 0;tt<j;tt++)//将规约后的非终结符与状态栈栈顶状态进行查表
{
if(zt[ztp] == FXB[tt].left&&G[FXB[t].right[1] - '0'].left[0] == FXB[tt].mind[0])
{
// zt[++ztp] = FXB[tt].right[0] - '0';//状态入栈
int sum = 0,isum = 0,cj = 1;
for(int k = strlen(FXB[tt].right)-1;k>=0;k--)
{
for(int q = 0;q < isum;q++)
{
cj *= 10;
}
sum = sum + cj*(FXB[tt].right[k] - '0');
isum++;
}
zt[++ztp] = sum;//状态入栈
fh[++fhp] = G[FXB[t].right[1] - '0'].left[0];//符号入栈
printf("%s\n",FXB[tt].right);
break;
}
}
if(tt == j)
{
printf("规约后的非终结符%c与状态栈栈顶状态%d没有关系!",G[FXB[t].right[1] - '0'].left[0],zt[ztp]);
break;
}
}
else if(FXB[t].right[0] == 'a')
{
printf("接受");
flag = 1;
}
break;
}
}
if(t == j)
{
printf("出错:分析表中没有%d和%c的关系\n",zt[ztp],str[strp]);
break;
}
if(flag == 1)
break;
}
return 0;
}
测试样例
//样例一:
E (L)
E a
L L,E
L E
# #
0 ( s2
0 a s3
0 E 1
1 # acc
2 ( s2
2 a s3
2 E 5
2 L 4
3 ) r2
3 , r2
3 # r2
4 ) s6
4 , s7
5 ) r4
5 , r4
6 ) r1
6 , r1
6 # r1
7 ( s2
7 a s3
7 E 8
8 ) r3
8 , r3
0 # #
((a),(a))//样例二:
E E+T
E T
T T*F
T F
F (E)
F i
# #
0 i s5
0 ( s4
0 E 1
0 T 2
0 F 3
1 + s6
1 # acc
2 + r2
2 * s7
2 ) r2
2 # r2
3 + r4
3 * r4
3 ) r4
3 # r4
4 i s5
4 ( s4
4 E 8
4 T 2
4 F 3
5 + r6
5 * r6
5 ) r6
5 # r6
6 i s5
6 ( s4
6 T 9
6 F 3
7 i s5
7 ( s4
7 F 10
8 + s6
8 ) s11
9 + r1
9 * s7
9 ) r1
9 # r1
10 + r3
10 * r3
10 ) r3
10 # r3
11 + r5
11 * r5
11 ) r5
11 # r5
0 # #
i+i*i#
运行结果
样例一:
接受
出错
样例二:
接受
出错
总结:
- 一开始记录分析表的状态时用的是char数组,将其第一位的字符数字取出并转换为数字后压入状态栈中,这里考虑不周,没有考虑到状态可能是大于9的情况,导致正确的句子被分析出是错误的。改进:从数组的最后一位开始遍历,将其装换为一个数字后在压入状态栈中;
- 用户输入符号串时要检查用户是否在字符串后加入了‘#’,如果没有将‘#’加入用户输入的符号串中以便分析;
- 手动构造分析表要注意正确性。