SLR(1)语法分析程序的实现

步骤一:定义一个结构体struct g{char left[2];char right[100];}用来存放用户输入的文法;一个结构体struct fxb{int left;char mind[2];char right[5];}用来存放用户输入的分析表;一个char类型数组str用来存放用户要分析的符号串;一个int类型数组zt用来存放分析字符串过程中状态的变化;一个char类型数组fh用来存放分析字符串过程中符号的规约或移进变化;整形变量ztpfhpstrpij分别表示指向状态栈的栈顶状态、指向符号栈的栈顶符号、输入符号串队列的头指针、文法的总条数和分析表的总长度;

步骤二:提示用户输入文法,并将用户输入的文法记录在结构体数组G中,如果用户输入的文法不符合要求,提示用户重新输入;

步骤三:提示用户输入分析表,并将用户输入的分析表记录在结构体数组FXB中,如果用户输入的分析表不符合要求,提示用户重新输入;

步骤四:提示用户输入符号串,并将用户输入的符号串记录在字符串数组str中,如果用户没有输入字符串,提示用户重新输入;

步骤五:定义三个整型变量flagcountflagp分别代表当差分析表查到“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#

运行结果

样例一:

接受

出错

样例二:

接受

出错

总结:

  1. 一开始记录分析表的状态时用的是char数组,将其第一位的字符数字取出并转换为数字后压入状态栈中,这里考虑不周,没有考虑到状态可能是大于9的情况,导致正确的句子被分析出是错误的。改进:从数组的最后一位开始遍历,将其装换为一个数字后在压入状态栈中;
  2. 用户输入符号串时要检查用户是否在字符串后加入了‘#’,如果没有将‘#’加入用户输入的符号串中以便分析;
  3. 手动构造分析表要注意正确性。
  • 24
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SLR(1)语法分析器是一种自下而上的语法分析器,它使用了LR(0)项目集家族和FIRST/FOLLOW集合,进行语法分析SLR(1)语法分析器的原理如下: 1. 构造LR(0)项目集家族:首先,需要根据文法G的所有产生式构造出LR(0)项目集家族,包括起始符号S的增广文法G'的所有产生式。每个项目集Ii由LR(0)项目组成,其中每个项目都有形如A→α·β的形式,表示在某个产生式的某个位置上,将会选择beta作为下一步的推导。 2. 计算FIRST集:对于文法G的每个非终结符号A,计算FIRST(A),即终结符号集合{a|A=>*aβ},其中=>*表示A能够推导出β。 3. 计算FOLLOW集:对于每个非终结符号A,计算FOLLOW(A),即终结符号集合{a|S=>*αAaβ},其中α和β是文法符号串。 4. 构造LR(0)自动机:基于LR(0)项目集家族和FIRST/FOLLOW集合,构造出LR(0)自动机。其中,每个状态代表一个LR(0)项目集,每个转换代表一个文法符号。 5. 构造SLR(1)表:在LR(0)自动机的基础上,通过计算每个状态的ACTION和GOTO函数,得到SLR(1)分析表。ACTION函数用于处理终结符号,GOTO函数用于处理非终结符号。ACTION[i,a]表示在状态i和读入符号a的情况下,执行的动作,可以是移进、规约或者接受。GOTO[i,A]表示在状态i和读入非终结符号A的情况下,转移到的状态。 6. 进行语法分析:使用SLR(1)分析表,根据输入符号串进行语法分析,具体流程为:从状态0开始,每次读取一个输入符号,根据分析表执行移进或者规约操作,直到接受或者出现错误。 SLR(1)语法分析器的优点是简单易懂,缺点是不能处理所有文法,只能处理SLR(1)文法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值