原文链接:
https://blog.csdn.net/gj_007/article/details/79587673
本文对了原贴代码做了如下改变和改进:
1.文法变为如下:
2.当分析字符串为空时,若栈内不为空仍可继续判断栈内字符是否可为空从而接受该字符串
3.出错信息显示,哪个字符无法匹配
4.注释命名更加清晰
注:预测分析表手动填写,非程序计算得来,这样很局限但是简单
代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<dos.h>
char analyse[10]; //分析栈
char remain[10]; //剩余串
char vt[10] = { '+','-','~','*','/','(',')','i','#'};//终结符
char vn[10] = { 'E','G','T','S','F' }; //非终结符
typedef struct shiZi //产生式
{
char begin; //左边字符
char array[5]; //右边字符串
int length; //右边字符个数
}shiZi;
shiZi e, t, g2, g3, g1, s2, s3, s1, f2, f1; //10个产生式
shiZi table[10][10]; //定义预测分析表
void printStack(); //输出分析栈
void printRemain(); //输出剩余串
int j = 0, b = 0, top = 0;
int l; //l为输入串长度
int main(){
int m, n, k = 0, flag = 0, finish = 0;
char ch, x;
shiZi now; //目前使用的产生式
e.begin = 'E';
strcpy(e.array, "TG");
e.length = 2;
g1.begin = 'G';
strcpy(g1.array, "+TG");
g1.length = 3;
g2.begin = 'G';
strcpy(g2.array, "-TG");
g2.length = 3;
g3.begin = 'G';
g3.array[0] = '~';
g3.length = 1;
t.begin = 'T';
strcpy(t.array, "FS");
t.length = 2;
s1.begin = 'S';
strcpy(s1.array, "*FS");
s1.length = 3;
s2.begin = 'S';
strcpy(s2.array, "/FS");
s2.length = 3;
s3.begin = 'S';
s3.array[0] = '~';
s3.length = 1;
f1.begin = 'F';
strcpy(f1.array, "(E)");
f1.length = 3;
f2.begin = 'F';
f2.array[0] = 'i';
f2.length = 1;
//初始化分析表
for (m = 0; m <= 4; m++)
for (n = 0; n <= 8; n++)
table[m][n].begin = 'n';
//手动填写分析表
table[0][5] = e; table[0][7] = e;
table[1][0] = g1; table[1][1] = g2; table[1][2] = table[1][6] = table[1][8] = g3;
table[2][5] = t; table[2][7] = t;
table[3][0] = table[3][1] = table[3][2] = table[3][8] = s3; table[3][4] = s2; table[3][3] = s1;
table[4][5] = f1; table[4][7] = f2;
printf("请输入要分析的字符串(以'#'结尾):");
//读入字符串
do
{
scanf("%c", &ch);
if ((ch != 'i') && (ch != '+') && (ch != '*') && (ch != '(') && (ch != ')') && (ch != '/') && (ch != '#'))
{
printf("输入串中有非法字符\n");
exit(1);
}
remain[j] = ch;
j++; //字符串长度
}while (ch != '#');
l = j;
ch = remain[0]; //当前字符
analyse[top] = '#';
analyse[++top] = 'E'; //文法开始符号'E'进栈
printf("步骤\t\t分析栈\t\t剩余字符\t\t所用产生式\n");
do{
x = analyse[top--];//当前栈顶字符
printf("%d", k++);
printf("\t\t");
for (j = 0; j <= 8; j++)
if (x == vt[j]) //如果是终结符
{
if (x == '#' && ch != '#'){ //没有输入字符但栈内仍有字符 ,则继续判断栈内字符是否可为空
flag = 0;
break;
}
else{
flag = 1; //进入终结符的判断
break;
}
}
if (flag == 1)
{
if (x == '#' && ch == '#')
finish = 1;//结束
if (x == ch){
printStack();
printRemain();
if(x == '#' && ch == '#'){ //特殊情况,栈内、字符串全为'#'
printf("结束\n\n");
printf("合法字符串!");
}
else{ //其他情况:字符匹配
printf("%c匹配\n", ch);
ch = remain[++b]; //next字符
flag = 0;
}
}
else{
printStack();
printRemain();
printf("%c无法匹配\n\n", ch);/*输出出错终结符*/
printf("非法字符串!");
exit(1);
}
}
else{ //非终结符
for (j = 0; j <= 4; j++)
if (x == vn[j])
{
m = j; //分析表中匹配某一非终结符,行
break;
}
for (j = 0; j <= 8; j++)
if (ch == vt[j])
{
n = j; //分析表中匹配某一字符,列
break;
}
now = table[m][n];
if (now.begin != 'n') //有产生式
{
printStack();
printRemain();
printf("%c->", now.begin); //输出所用的产生式
for (j = 0; j<now.length; j++)
printf("%c", now.array[j]);
printf("\n");
for (j = (now.length - 1); j >= 0; j--) //产生式逆序入栈
analyse[++top] = now.array[j];
if (analyse[top] == '~') //为空则不进栈
top--; //指向下一个字符
}
else{
printStack();
printRemain();
printf("%c无法匹配\n", x); //输出无法匹配的非终结符
printf("非法字符串!");
exit(1);
}
}
}while (finish == 0);
}
void printStack()
{
int a;
for(a=0; a <= top+1; a++)
printf("%c", analyse[a]); //输出栈内元素
printf("\t\t");
}
void printRemain()
{
int j;
for (j = 0; j<b; j++)
printf(" "); //对齐
for (j = b; j <= l; j++)
printf("%c", remain[j]); //输出剩余字符串
printf("\t\t\t");
}
运行示例: