算符優先分析程序&對着註釋理解代碼

一 實驗目的和要求

1.理解自下而上分析算法的構造思想。
2.理解算符文法和算符優先文法的概念。
3.掌握 FIRSTVT 集、LASTVT 集和算符優先關係表的構造方法。
4.理解素短語和最左素短語的概念,並掌握其尋求方法。
5.理解算符優先分析算法,能夠使用某種高級語言實現一個算符優先分析程序。

二 實驗內容

編寫一個算符優先分析程序,能實現以下功能:
1.輸入文法,判斷是否爲算符文法;
2.構造並輸出該文法的每個非終結符的 FIRSTVT 集和 LASTVT 集;
3.構造並輸出算符優先分析表,判斷是否爲算符優先文法,若不是提示無法進行分析;
4.任意輸入一個輸入串,可得到成功的分析或錯誤的提示,輸出其分析過程或打印語法 分析樹。

三 實驗過程

產生式爲:
E→E+T|T
T→T*F|F
F-> P^ F|P
P→(E)|i

3.1什麼是算符文法?

如果一個文法G中的任何產生式的右部候選項都不含兩個連續的非終結符,即不含形如 P->…QR…

3.2FIRSTVT和LASTVT

3.2.1構造FIRSTVT

1,FIRSTVT(P)
對算符文法G的每個非終結符P

定義:FIRSTVT(P)={a|P=>a…,或P=>Qa…,a屬於VT,Q 屬於VN}

2,集合FIRSTVT(P)的構造方法

規則一:若有產生式P→a…或 P→Qa…,則a∈FIRSTVT(P);
規則二:若a∈FIRSTVT(Q)且有產生式P→Q…,則a∈FIRSTVT(P) ;
規則三:反覆使用以上兩條規則,直到FIRSTVT(P)不再增大爲止。

3.2.3構造LASTVT

1,LASTVT(P)
對算符文法G的每個非終結符P

定義:LASTVT(P)={a|P=>…a,或P=>…aQ,a含於VT,Q 含於VN}

2,集合LASTVT(P)的構造方法

規則一:若有產生式P→…a或 P→…aQ,則a∈LASTVT(P);
規則二:若a∈LASTVT(Q)且有產生式 P→…Q,則a∈LASTVT(P) ;
規則三:反覆使用以上兩條規則,直到LASTVT( P)不再增大爲止。

3.2.4思路

這個實驗和上個實驗LL(1)分析,差不多,思路方法都一樣。 首先爲了提高效率,將產生式進行化簡,爲了不考慮產生式中的"- 和>"符號 化簡如下。
E->E+T
E->T
T->T*F
T->F
F->P^F
F->P
P->(E)
P->i
對於判斷是不是算符文法,只要遍歷一下產生式 然後if判斷一下。對於FIRSTVT集,對每一條產生式使用以上規則,再結合if判斷,LASTVT集和FIRSTVT集求法一樣。
在這裏插入圖片描述

3.3優先分析表

1,算符優先關係表的構造方法
利用文法G中的每個非終結符P的FIRSTVT集和LASTVT集,我們就能方便地構造文法G的算符優先關係表,其構造方法如下:

規則一:對形如P→…ab…或P→…aQb…的產生式,有a=b;
規則二:對形如P→…aR…的產生式,若有b∈FIRSTVT®,則a<b;
規則三:對形如P→…Rb…的產生式,若有a∈LASTVT®,則a>b;
規則四:對於語句括號#,有#=#,且若a∈FIRSTVT(S)和b∈LASTVT(S),則有#<a且b>#。

2,思路
首先定義一個char類型的二維數組來存發優先關係(<,>,=)使用之前進行初始化一下,然後遍歷產生式,根據上面四個規則進行判斷,對錶賦值。
在這裏插入圖片描述
在以上算法描述中,爲了能夠計算#與其它終結符之間的關係,一般在文法的產生式中添加一個新的產生式Z ->#E#

3.4分析過程

一個算符優先文法G的任何句型#N1a1N2a2 … NmamNm+1#的最左素短語是滿足如下條件的最左子串:Njaj … NiaiNi+1 :(其中, ai是終結符, Ni是可有可無的非終結符)
aj-1<aj
aj=aj+1,aj+1=aj+2 ,…,ai-1=ai
ai>ai+1
在這裏插入圖片描述
實際分析時,爲了便於識別符號串,一般首先將“#”壓入分析棧,當分析成功時,分 析棧中只剩下文法的開始符號和“#”。這裏,將“#”作爲輸入串的結束符,並非文法中的 符號。
在這裏插入圖片描述

①移進:將輸入串的一個符號移進分析棧。
②歸約:發現棧頂呈“可歸約串”,並用適當的相應符號去替換這個串。
③接受:宣佈最終分析成功,可看作是歸約的一種特殊形式。
④報錯:發現棧頂內容與輸入串相悖,調用出錯處理程序進行診察和校正,並對棧頂內容和輸入符號進行調整

在這裏插入圖片描述
最後結果
在這裏插入圖片描述
思路: 棧頂的終結符或者次棧頂的終結符和輸入字符a的優先級比較,a的優先級低於棧頂的優先級,就規約。a的優先級高於或者等於棧頂的就移進.

四 使用代碼

1,Base.h文件

#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

2,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;
	}
}

3,TableStack.h文件

#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

4,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);
}

5,main.cpp文件

//編譯器VS2013
#include"TableStack.h"
int main()
{
	TableStack s;
	s.out();
	system("pause");
	return 0;
}

五 實驗結果

代碼總共406行
在這裏插入圖片描述

六 總結

文法存放用,每一條文法都以->爲界限,可以分爲左半部分和右半部分,左半部分就一個非終結符,所以用結構體來存放。每個文法它是由終結符和非終結符組成,所以用結構體來存放終結符與非終結符。最主要的是分析表的構造,算符優先算符的實現。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章