C++实现基于LL(1)分析表编写语法分析器

本文档介绍了如何构建和调试一个LL(1)预测分析表,用于C++实现的语法分析器。通过一个具体的文法G[E],详细展示了计算FIRST集、FOLLOW集、SELECT集的过程,并给出了预测分析表。在编程实现时,遇到了因替换符号和遗漏产生式导致的错误,经过调试和修正,最终成功实现了语法分析器。文章强调了编程时的细心和调试的重要性。
摘要由CSDN通过智能技术生成

【问题描述】

给定以下文法G[E]:
E->TE′
E′->+TE′| ε
T->FT′
T′->*FT′| e
F->a | (E)
构建LL(1)预测分析表,并用C++编程实现LL(1)语法分析器

【输入形式】
一个句子

【输出形式】
若句子语法正确,输出"Syntax analysis is right"
若语法语法错误,输出"Error on syntax analysis"
注:输出不包括引号

【样例输入】
a+a*a

【样例输出】
Syntax analysis is right

【构建基于LL(1)文法的预测分析表】
先算出FIRST集和FOLLOW集
FIRST集和FOLLOW集
然后算出SELECT集
SELECT集
最后构建预测分析表
预测分析表

【程序思想】
用一维数组VTVN存储终结符集和非终结符集,用二维字符串数组map存储预测分析表,写一个find函数用于匹配每个终结符和非终结符所在的行和列,用于后面在分析表中找产生式。用分析栈 st来协助分析,栈底要先放入终结符‘#’和开始符‘E’,不然没有办法分析正确。为了简化代码,我们用X和Y分别替代E’和T’,^代替ε,空格代表没有产生式。初始化一个全局变量index用于指示当前分析到输入字符串的哪一位,对于输入字符串中的每一位,分为终结符非终结符两种情况,如果是终结符,判断是否是‘#’,如果是且栈顶也是 ‘#’,则该语句语法正确,如果栈顶和当前字符相同但不是‘#’,栈顶弹出,index右移,判断下一个字符。如果是非终结符,就去预测分析表map中寻找栈顶和当前字符的产生式,如果找不到则分析错误,如果找到了,栈顶弹出,然后将产生式右部逆序入栈,进行下一轮分析。最后,记得输入的语句末尾要加上‘#’。

【代码实现】

#include<iostream>
#include<stack>
#include<vector>

using namespace std;

string s;
int index = 0;

//为了简化代码,我们用X和Y分别替代E'和T',^代替ε
char VN[5] = { 'E','X','T','Y','F' };
char VT[6] = { 'a','+','*','(',')','#' };

string map[6][7] = {
	" ","a","+","*","(",")","#",
	"E","TX"," "," ","TX"," "," ",
	"X"," ","+TX"," "," ","^","^",
	"T","FY"," "," ","FY"," "," ",
	"Y"," ","^","*FY"," ","^","^",
	"F","a"," "," ","(E)"," "," "
};


int find(char c) {
	switch (c) {
	case 'E':
		return 1;
	case 'X':
		return 2;
	case 'T':
		return 3;
	case 'Y':
		return 4;
	case 'F':
		return 5;
	case 'a':
		return 1;
	case '+':
		return 2;
	case '*':
		return 3;
	case '(':
		return 4;
	case ')':
		return 5;
	case '#':
		return 6;
	default:
		return 0;
	}
}

void LL()
{
	stack<char> st;
	st.push('#');
	st.push('E');
	int i = 0;
	int temp;//如果找到了终结符且匹配正确则标记一下不用继续后续步骤直接开始下一轮
	while (true)
	{
		char c = st.top();
		temp = 0;
		//栈顶是终结符
		for (int it = 0; it < 6; it++)
		{
			if (VT[it] == c)
			{
				if (s[i] == '#' && c == '#')
				{
					cout << "Syntax analysis is right";
					return;
				}
				if (s[i] == c)
				{
					st.pop();
					i++;
					temp = 1;
					break;
				}
				else
				{
					cout << "Error on syntax analysis";
					return;
				}
			}
		}
		//栈顶是非终结符
		if (temp == 1)
			continue;
		int n1 = find(c);
		int n2 = find(s[i]);
		st.pop();
		if (map[n1][n2] == " ")
		{
			cout << "Error on syntax analysis";
			return;
		}
		int len = map[n1][n2].length();
		for (int k = len - 1; k >= 0; k--)
		{
			if (map[n1][n2][k] == '^')
				continue;
			st.push(map[n1][n2][k]);//逆序进栈
		}
	}
}


int main()
{
	cin >> s;
	int length = s.length();
	s[length] = '#';
	LL();
	return 0;
}

/*
a+a*a
*/

【遇到的问题】
(1) 由于粗心,在初始化map的时候,我输入时忘记了自己已经将E’替换成X,T’替换成Y,导致有的产生式写错,还有产生式前面的加号被我看漏了,后来调试程序才找到错误源头。
(2) 还是由于粗心,我在写终结符的情况的时候,使用for循环遍历终结符数组时,将后面第二部分非终结符的情况也写入了这个for循环后面,而且由于样例输入前半部分字符非常巧合地找到了对的产生式,导致我debug了很久(在纸上演练了好几遍分析栈的过程)才发现这个错误。

最后总结一下,这次的语法分析实验总的来说让我对计算机编译程序时的语法分析有了更深刻的了解,也提升了写LL(1)预测分析表的熟练度,收获很多。当然了,也再一次提醒自己,写代码要细心细心再细心一点,粗心的程序很容易浪费很多时间在调试的过程上。

  • 10
    点赞
  • 103
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值