【编译原理】C++实现算符优先分析(超详细系列+有源码+有注释+可运行)

目录

算符优先分析

1.实验目的

2.实验过程

2.1算符优先关系表的构造方法

2.2思路

3.实验代码:

4.实验结果


算符优先分析

1.实验目的

了解用算符优先法对表达进行语法分析的方法,掌握自顶向下的预测语法分析程序的手工构造方法。

实验要求:一个交互式面向对象的算符优先分析程序基本功能是:

(1)  输入文法规则

(2)  对文法进行转换

(3)  生成每个非终结符的FirstVT和LastVT

(4)  生成算符优先分析表

(5)  再输入文法符号串

(6)  生成移进规约步骤

2.实验过程

2.1算符优先关系表的构造方法

利用文法G中的每个非终结符PFIRSTVT集和LASTVT集,我们就能方便地构造文法G的算符优先关系表,其构造方法如下:

规则一:对形如P→…ab…P→…aQb…的产生式,有a=b
规则二:对形如P→…aR…的产生式,若有bFIRSTVT®,则a<b
规则三:对形如P→…Rb…的产生式,若有aLASTVT®,则a>b
规则四:对于语句括号#,有#=#,且若aFIRSTVT(S)bLASTVT(S),则有#<ab>#

2.2思路

首先定义一个char类型的二维数组来存发优先关系(<,>,=)使用之前进行初始化一下,然后遍历产生式,根据上面四个规则进行判断,对表赋值。

  1. 移进:将输入串的一个符号移进分析栈。
  2. 归约:发现栈顶呈可归约串,并用适当的相应符号去替换这个串。
  3. 接受:宣布最终分析成功,可看作是归约的一种特殊形式。
  4. 报错:发现栈顶内容与输入串相悖,调用出错处理程序进行诊察和校正,并对栈顶内容和输入符号进行调整

3.实验代码:

Base.h:

#pragma once
#ifndef BASE_H_
#define BASE_H_
#include<iostream>
#include<fstream>
using namespace std;
struct Proce { //用结构体数组来存放产生式
	char Left;//存放产生式的左部
	char Right[100];//存放产生式的右部
};
struct Myset {
	char VN;//用于存放FIRSTVT(P)和LASTVT(P)的非终结符
	char VT[100]; //用于存放FIRSTVT(P)和LASTVT(P)的终结符
};
class Base
{
public:
	int flag;
	struct Proce pro[100];      //产生式
	struct Myset firstvt[100];  //firstvt集合
	struct Myset lastvt[100];   //lastvt集合
public:
	Base() :flag(0) {
		memset(firstvt, 0, sizeof(firstvt)); //置空字符串
		memset(lastvt, 0, sizeof(lastvt));
		memset(pro, 0, sizeof(pro));
	}
	int is_VN(char ch); //判断是否是非终结符,默认大写字母为非终结符,其他为终结符
	void is_gram(Proce pro[], int length); //判断文法是否为算符文法
	void NoAdd(Myset VNT[], int length1); //去掉集合中重复部分
	void FIRSTVT(Proce pro[], Myset firstvt[], int length);//求各非终结符的FIRSTVT集合
	void LASTVT(Proce pro[], Myset lastvt[], int length);//求各非终结符的FIRSTVT集合
};
#endif

 Base.cpp:

#include "Base.h"
int Base::is_VN(char ch) //判断是否是非终结符,默认大写字母为非终结符,其他为终结符
{
	if (ch >= 'A' && ch <= 'Z')
		return 1;
	else
		return 0;
}
void Base::is_gram(Proce pro[], int length) //判断文法是否为算符文法
{
	for (int i = 0; i < length; i++)
	{
		for (int j = 0; j < strlen(pro[i].Right) - 1; j++)
			if (is_VN(pro[i].Right[j]) == 1 && is_VN(pro[i].Right[j + 1]) == 1)//两个非终结符在一起了;
			{
				flag = 1;
				break;
			}
	}
	if (flag == 1)
	{
		cout << "该文法不是算符文法!" << endl;
		return;
	}
	else
		cout << "该文法是算符文法!" << endl;
}
void Base::NoAdd(Myset VNT[], int length1) //去掉集合中重复部分
{
	char str1[20];//非终结符
	char str2[20][100];//终结符
	int length;
	for (int i = 0; i < length1; i++)
	{
		str1[i] = VNT[i].VN;
		strcpy(str2[i], VNT[i].VT);//把终结符复制给str2
	}
	for (int i = 0; i < length1; i++)
		memset(VNT[i].VT, 0, sizeof(VNT[i].VT));//清空
	for (int i = 0; i < length1; i++)
	{
		int t = 0;
		for (int j = 0; j < strlen(str2[i]); j++)
		{
			flag = 1;
			for (int k = 0; k < t; k++)
				if (VNT[i].VT[k] == str2[i][j])//重复了
					flag = 0;
			if (flag == 1)
				VNT[i].VT[t++] = str2[i][j];
		}
		length = strlen(VNT[i].VT);
	}
}
//求各非终结符的FIRSTVT集合
void Base::FIRSTVT(Proce pro[], Myset firstvt[], int length)
{
	int  m = 0;//非终结符个数,flag记录产生式个数
	int  j, k;
	while (flag < length)//length=9产生式总个数
	{
		j = 0;
		firstvt[m].VN = pro[flag].Left;
		while (firstvt[m].VN == pro[flag].Left)
		{
			if (is_VN(pro[flag].Right[0]) == 0) //P->a...则将a加入firstvt(P)中
				firstvt[m].VT[j++] = pro[flag].Right[0];
			else if (is_VN(pro[flag].Right[0]) == 1 && is_VN(pro[flag].Right[1]) == 0) //P->Qa...则将a加入firstvt(P)中
				firstvt[m].VT[j++] = pro[flag].Right[1];
			flag++;
		}
		m++;
	}
	for (int i = length - 1; i >= 0; i--) //P->Q...,则将Q中的终结符加入P中
		if (is_VN(pro[i].Right[0]) == 1 && pro[i].Left != pro[i].Right[0])
		{
			for (j = 0; j < m; j++)   //E->E跳出
				if (firstvt[j].VN == pro[i].Right[0])
					break;
			for (k = 0; k < m; k++)
				if (firstvt[k].VN == pro[i].Left)
					break;
			strcat(firstvt[k].VT, firstvt[j].VT);//firstvt[j].VT添加到firstvt[k].VT后面(strcat字符串连接函数)
		}
	NoAdd(firstvt, m);//去重
	for (int i = 1; i < m; i++) //集合输出
	{
		cout << "FIRSTVT(";
		cout << firstvt[i].VN << ")" << "=" << "{";
		cout << firstvt[i].VT[0];
		for (int j = 1; j < strlen(firstvt[i].VT); j++)
			cout << "," << firstvt[i].VT[j];
		cout << "}" << endl;
	}
}
void Base::LASTVT(Proce pro[], Myset lastvt[], int length)//求各非终结符的FIRSTVT集合
{
	int m = 0;//非终结符个数,flag记录产生式个数
	int  j, k, t;
	while (flag < length)
	{
		j = 0;
		lastvt[m].VN = pro[flag].Left;//把非终结符赋给VN
		while (lastvt[m].VN == pro[flag].Left)
		{
			t = strlen(pro[flag].Right) - 1;
			if (is_VN(pro[flag].Right[t]) == 0) //P->...a则将a加入lastvt(P)中
				lastvt[m].VT[j++] = pro[flag].Right[t];
			else if (is_VN(pro[flag].Right[t]) == 1 && is_VN(pro[flag].Right[t - 1]) == 0) //P->...aQ则将a加入lastvt(P)中
				lastvt[m].VT[j++] = pro[flag].Right[t - 1];
			flag++;
		}
		m++;
	}
	for (int i = length - 1; i >= 0; i--) //P->...Q,则将Q中的终结符加入P中
	{
		t = strlen(pro[flag].Right) - 1;
		if (is_VN(pro[i].Right[t]) == 1 && pro[i].Left != pro[i].Right[t])
		{
			for (j = 0; j < m; j++)
				if (lastvt[j].VN == pro[i].Right[t])
					break;
			for (k = 0; k < m; k++)
				if (lastvt[k].VN == pro[i].Left)
					break;
			strcat(lastvt[k].VT, lastvt[j].VT);
		}
	}
	NoAdd(lastvt, m);
	for (int i = 0; i < m; i++) //集合输出
	{
		cout << "LASTVT(";
		cout << lastvt[i].VN << ")" << "=" << "{";
		cout << lastvt[i].VT[0];
		for (j = 1; j < strlen(lastvt[i].VT); j++)
			cout << "," << lastvt[i].VT[j];
		cout << "}" << endl;
	}
}

TableStack.h:

#pragma once
#ifndef TABLESTACK_H_
#define TABLESTACK_H_
#include"Base.h"
class TableStack :public Base
{
	char table[50][50]; //存放优先表
	int step;           //序号
	char S[100];          //符号栈
public:
	TableStack() :step(1)
	{
		memset(table, 0, sizeof(table));//置空字符串
		memset(S, 0, sizeof(S));
	}
	void Table(Proce pro[], Myset firstvt[], Myset lastvt[], int length); //优先关系表 //返回非终结符个数
	char GetIndex(char a, char b); //找到a,b对应的关系
	void control(char* str);
	void out();
};
#endif

 TableStack.cpp:

#include "TableStack.h"
#include<string>
void TableStack::Table(Proce pro[], Myset firstvt[], Myset lastvt[], int length) //优先关系表 
{
	char str[50]; //存放终结符
	int i, k, i1, i2;
	int t = 0;
	memset(str, 0, sizeof(str));//初始化
	for (i = 0; i < length; i++) //遍历所有的产生式
	{
		flag = 1;
		for (k = 0; k < strlen(pro[i].Right); k++)//找终结符
			if (is_VN(pro[i].Right[k]) == 0)
			{
				for (i1 = 0; i1 < t; i1++)
					if (pro[i].Right[k] == str[i1])//已经存在了flag = 0;
						flag = 0;
				if (flag == 1)
					str[t++] = pro[i].Right[k];
			}
	}
	for (i = 0; i < strlen(str); i++)//与习惯保持一致,将#置于最后一个
	{
		if (str[i] == '#')
			swap(str[i], str[strlen(str) - 1]);//#和最后一个交换,把#放最后一个
	}
	for (i = 1; i <= strlen(str); i++)
	{
		table[0][i] = str[i - 1]; //第一列的终结符
		table[i][0] = str[i - 1]; //第一行的终结符 
	}
	for (int i = 0; i < length; i++)//遍历所有的产生式
	{
		int length1 = strlen(pro[i].Right);
		for (int j = 0; j < length1 - 1; j++)//遍历产生式的右部
		{
			if (is_VN(pro[i].Right[j]) == 0 && is_VN(pro[i].Right[j + 1]) == 0)//P->...ab...  
			{
				for (i1 = 0; i1 <= strlen(str); i1++)//遍历终结符
					for (i2 = 0; i2 <= strlen(str); i2++)
						if (table[0][i1] == pro[i].Right[j] && table[i2][0] == pro[i].Right[j + 1])//a=b
						{
							if (table[i1][i2] != 0) //刚开始表里面的值都为0
							{
								cout << "该文法不是算符优先文法!" << endl;
								return;
							}
							else//赋值=
								table[i1][i2] = '=';
						}
			}
			if (j < length1 - 2 && is_VN(pro[i].Right[j]) == 0 && is_VN(pro[i].Right[j + 2]) == 0 && is_VN(pro[i].Right[j + 1]) == 1)//P->...aQb... #E#,(E)
			{
				for (i1 = 0; i1 <= strlen(str); i1++)//遍历终结符
					for (i2 = 0; i2 <= strlen(str); i2++)
						if (table[0][i1] == pro[i].Right[j] && table[i2][0] == pro[i].Right[j + 2])//a=b
						{
							if (table[i1][i2] != 0)
							{
								cout << "该文法不是算符优先文法!" << endl;
								return;
							}
							else
							{
								table[i1][i2] = '=';
							}

						}
			}
			if (is_VN(pro[i].Right[j]) == 0 && is_VN(pro[i].Right[j + 1]) == 1)//P->...aQ...且Q=>b...或Q=>Rb...
			{                                                                  //P->X1 X2...Xi Xi+1.....Xn
				for (i1 = 0; table[0][i1] != pro[i].Right[j]; i1++);//a不等于b
				for (k = 0; firstvt[k].VN != pro[i].Right[j + 1]; k++);//非终结符P,Q不相等
				for (i2 = 0; i2 <= strlen(str); i2++)//遍历终结符
					for (t = 0; t < strlen(firstvt[k].VT); t++)//遍历firstvt集合
						if (table[i2][0] == firstvt[k].VT[t])       //firstvt(Xi+1)中的每个a  Xi<a
						{
							if (table[i1][i2] != 0)
							{
								cout << "该文法不是算符优先文法!" << endl;
								return;
							}
							else
								table[i1][i2] = '<';
						}
			}
			if (is_VN(pro[i].Right[j]) == 1 && is_VN(pro[i].Right[j + 1]) == 0)//P->...Qb...且Q=>..a或Q=>..aR
			{                                                                  //P->X1 X2...Xi Xi+1.....Xn
				for (t = 0; lastvt[t].VN != pro[i].Right[j]; t++);//非终结符P,Q不相等
				for (int k = 0; k < strlen(lastvt[t].VT); k++)//遍历lastvt集合
					for (i1 = 0; i1 <= strlen(str); i1++)//遍历终结符
						for (i2 = 0; i2 <= strlen(str); i2++)
							if (table[0][i1] == lastvt[t].VT[k] && table[i2][0] == pro[i].Right[j + 1])
							{                                                                  //lastvt(Xi)中的每个a >b
								if (table[i1][i2] != 0)
								{
									cout << "该文法不是算符优先文法!" << endl;
									return;
								}
								else
									table[i1][i2] = '>';
							}
			}
		}
	}
	for (int i = 0; i <= strlen(str); i++)
	{
		for (int j = 0; j <= strlen(str); j++)
			cout << table[i][j] << "    ";
		cout << endl;
	}
	cout << "---------------------------------------" << endl;
}
char TableStack::GetIndex(char a, char b) //找到a,b对应的关系
{
	int i, j;
	for (i = 0; table[0][i] != a; i++);
	for (j = 0; table[j][0] != b; j++);
	return table[i][j];
}
void TableStack::control(char* str)
{
	char a;   //输入串里面的每个字符
	int flag = 0;
	char Q;
	int j, k;
	cout << "步骤" << "\t\t符号栈" << "\t\t输入串" << "\t\t动作" << endl;
	a = str[0];//输入串的第一个字符赋给a
	k = 1;     //栈顶指针   栈S的深度
	S[k] = '#'; //栈里面压#
	while (a != '#')
	{
		a = str[flag++]; //把下一个输入字符读入a中                     //N1a1N2a2...aj-1Nj
		if (is_VN(S[k]) == 0)//j指向栈顶的终结符
			j = k;
		else                //栈顶是非终结符 j指向它下面的终结符
			j = k - 1;                            //j指向栈的最上面的终结符
		while (GetIndex(S[j], a) == '>')//外面a的优先级低于栈顶的 就规约
		{
			do { //自栈顶向栈底方向找出最左子串S[i]<S[i+1]…S[j]>a
				Q = S[j];
				if (is_VN(S[j - 1]) == 0) //j从最左素短语末逐步移向首
					j = j - 1;
				else
					j = j - 2;
			} while (S[j] == Q); //S[j]<Q时表明找到了最左素短语的首部
			cout << step++ << "\t\t" << S + 1 << "\t\t" << str + flag - 1 << "\t\t归约" << endl;
			for (int i = j + 2; i <= k; i++)
				S[i] = 0;
			k = j + 1;
			S[k] = 'N';   //栈顶的这些符号与某一个候选对应相等,就规约到N上
		}
		if (GetIndex(S[j], a) == '<' || GetIndex(S[j], a) == '=')//外面a的优先级高于或者等于栈顶的 就移进
		{
			cout << step++ << "\t\t" << S + 1 << "\t\t" << str + flag - 1;
			if (a != '#')
			{
				cout << "\t\t移进" << endl;
			}
			k = k + 1; //移进来的a就变成了栈顶终结符
			S[k] = a;
		}
		else
		{
			cout << "抱歉,输入的句子有误" << endl;
			return;
		}
	}
	cout << "\t\t接受" << endl << "恭喜您,分析成功" << endl;
	cout << "---------------------------------------------------" << endl;
}
void TableStack::out()
{
	char str3[100] = { 0 };//用于存放一个产生式子
	char str2[100];//用于存放待检测的字符串
	char filename[10];//文件名
	int length = 0; //记录产生式个数
	cout << "请输入文件名:";
	cin >> filename;
	ifstream fin(filename);
	if (!fin)
	{
		cout << "Cannot open the file.\n"; //未找到对应的文件名的文件
		exit(1);
	}
	while (fin)
	{
		fin.getline(str3, 100); //读出一个产生式
		cout << str3 << endl;
		pro[length].Left = str3[0];//产生式的左部
		strcpy(pro[length].Right, str3 + 3);
		length++;
	}
	length -= 1;
	is_gram(pro, length);
	cout << "各非终结符的FIRSTVT集合如下:" << endl;
	FIRSTVT(pro, firstvt, length);
	cout << "各非终结符的LASTVT集合如下:" << endl;
	LASTVT(pro, lastvt, length);
	cout << "-------------构造分析表如下------------" << endl;
	Table(pro, firstvt, lastvt, length);
	cout << "---------------------分析表过程---------------------" << endl;
	cout << "请任意输入一个输入串(以#号键结束):" << endl;
	cin >> str2;
	control(str2);
}

main:

#include"TableStack.h"
int main()
{
	TableStack s;
	s.out();
	system("pause");
	return 0;
}

4.实验结果

  • 14
    点赞
  • 110
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
算符优先分析C++ 编译原理 运行环境:Visual Studio 2005 #include "SStack.h" #include <iostream> #include <string> using namespace std; class Functor { private : char ** table; string ** production; string prog;//待分析字符串 int p;//字符指针 int num;//终结符个数 int num1;//产生式个数 SStack <char> stack; public: Functor(int m,int n,char ** T,string **prod,string pr) { num=m; num1=n; table=T; production=prod; prog=pr; p=0; stack.push('$'); } void traversal() { while(p<(prog.length())) { stack.display(); cout<<prog.substr(p)<<" "; char ch; if(Getnum(stack.gettop())) { ch=stack.gettop(); } else { ch=stack.getsecond(); } switch(compare(ch,prog[p])) { case 1: case 2:stack.push(prog[p]);p++;cout<<"移入"<<endl;break; case 3:reduct();cout<<"归约"<<endl;break; } } cout<<"分析成功!"<<endl; } int Getnum(char ch) { for(int i=1;i<num;i++) { if(ch==table[i][0]) { return i; } } return 0; } int compare(char col,char row) { int c=Getnum(col); int r=Getnum(row); switch( table[c][r]) { case '>': return 3;break; case '<': return 2;break; case '=': return 1;break; default:cout<<endl<<"输入串有误,程序将终止!"<<endl;system("pause");exit(0);break; } } void reduct() { //待定 string token=""; int temp; string str=""; if(!Getnum(stack.gettop())) { token+=stack.gettop(); stack.pop(); } char ch=stack.gettop(); str+=ch; temp=Haven(str); if(temp!=-1) { token+=production[temp][0]; } else { token+=ch; } stack.pop(); bool Nover=true; while(Nover) { if(Getnum(stack.gettop())) { if(compare(stack.gettop(),ch)==2) { Nover=false; } else { ch=stack.gettop(); str=""; str+=ch; temp=Haven(str); if(temp!=-1) { token+=production[temp][0]; } else { token+=ch; } stack.pop(); } } else { token+=stack.gettop(); stack.pop(); } } string token2=""; //cout<<token<<" "; for(int i=token.length()-1;i>=0;i--) { token2+=token[i]; } //cout<<token2<<endl; if(Haven(token2)!= -1) { stack.push(production[Haven(token2)][0][0]); } else { cout<<"输入串有误!分析终止!"<<endl; system("pause"); exit(0); } } int Haven(string temp) { for(int i=0;i<num1;i++) { int j=1; while(production[i][j]!="") { if(temp==production[i][j]) { return i; } j++; } } return -1; } public: ~Functor(void) { } };
算符优先分析文法是一种工具,在编译的过程中,隶属于语法分析环节,却又与中间代码的生成息息相关,编译可以分为五个阶段:词法分析、语法分析、语义分析(中间代码的生成)、代码优化、目标代码生成。语法分析是指:在词法分析基础上,将单词符号串转化为语法单位(语法范畴)(短语、子句、句子、程序段、程序),并确定整个输入串是否构成语法上正确的程序。也就是说语法分析是检验输入串的语法是否正确,注意这里的语法正确,只是简单地符合自己定义的规范,而不能检测出运行时错误,比如"X/0",空指针错误,对象未初始化等错误。在这一个实验中,我将通过算符优先分析文法这一个工具,在语法分析的时候,顺便进行语义分析,也就是识别出语法单位,同时简要的将识别出的中间代码进行计算(目标代码的生成+运行),得到相应的结果,来检验自己设计的正确性。可以说题目虽然叫做算符优先分析文法,其实却是一个贯穿了“词法分析+语法分析+语义分析+中间代码优化+目标代码生成+运行”全过程的一个极具概括性的程序。如果能将这个程序得心应手的完成出来,我相信诸位对编译原理的掌握也算是炉火纯青了。时隔将近两年再来整理自己以前写过的实验报告,还是挺有感慨的,对一件东西感兴趣,原来影响还会如此深远,还记得自己当时连续六个小时全神贯注写出的实验报告,现在看看竟然写了五六十页,核心内容也有三四十页,不觉的感慨当年充满热情的时代慢慢的竟走出许久
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小树ぅ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值