【编译原理实验/c++】实验三 算符优先分析算法的设计与实现

一、 实验目的

根据算符优先分析法,对表达式进行语法分析,使其能够判断一个表达式是否正确。通过算符优先分析方法的实现,加深对自下而上语法分析方法的理解。

二、 实验要求

1、输入文法。可以是如下算术表达式的文法(你可以根据需要适当改变):        

E→E+T|E-T|T
T→T*F|T/F|F
F→(E)|i

   这里说明一下,我没有按这个格式来,而是一个一个输入,具体请参见最后的测试数据

2、对给定表达式进行分析,输出表达式正确与否的判断。

程序输入/输出示例:

输入:1+2;
输出:正确

输入:(1+2)/3+4-(5+6/7);
输出:正确

输入:((1-2)/3+4
输出:错误

输入:1+2-3+(*4/5)
输出:错误

三、实验步骤

1、参考数据结构

char *VN=0,*VT=0;//非终结符和终结符数组
char firstvt[N][N],lastvt[N][N],table[N][N];

typedef struct   //符号对(P,a)
{
  char Vn;
  char Vt;
} VN_VT;

typedef struct  //栈
{
    VN_VT *top;
    VN_VT *bollow;
    int  size;
}stack;

2、根据文法求FIRSTVT集和LASTVT集

给定一个上下文无关文法,根据算法设计一个程序,求文法中每个非终结符的FirstVT 集和LastVT 集。

算符描述如下:

/*求 FirstVT 集的算法*/

PROCEDURE insert(P,a);
IF not F[P,a] then  
begin
        F[P,a] = true; //(P,a)进栈 
end; 
Procedure FirstVT;
Begin
for 对每个非终结符 P和终结符 a do
   F[P,a] = false
for 对每个形如 P    a…或 P→Qa…的产生式 do
Insert(P,a)
while stack  非空
begin
栈顶项出栈,记为(Q,a)
for  对每条形如 P→Q…的产生式 do
         insert(P,a)
end;
end.

同理,可构造计算LASTVT的算法。

3、构造算符优先分析表

依据文法和求出的相应FirstVT和 LastVT 集生成算符优先分析表。

算法描述如下:

for  每个形如 P->X1X2…Xn的产生式  do
  for i =1 to n-1 do
  begin
        if Xi和Xi+1都是终结符 then 
           Xi   =   Xi+1
        if i<= n-2, Xi和Xi+2 是终结符, 但Xi+1 为非终结符 then
           Xi  = Xi+2
        if Xi为终结符, Xi+1为非终结符 then  
             for FirstVT 中的每个元素 a do
                  Xi  <  a ;
        if Xi为非终结符, Xi+1为终结符 then
             for LastVT 中的每个元素 a do
                  a  >  Xi+1 ;
  end

4、构造总控程序

   算法描述如下:  

 stack S;
   k = 1;  //符号栈S的使用深度
   S[k] = ‘#’
   REPEAT
       把下一个输入符号读进a中;
       If S[k]
 VT   then  j = k  else  j = k-1;
       While S[j] > a  do
           Begin
           Repeat
               Q = S[j];
               if  S[j-1]
 VT  then  j = j-1  else   j = j-2
           until  S[j] < Q;
           把S[j+1]…S[k]归约为某个N,并输出归约为哪个符号;
           K = j+1;
           S[k] = N;
           end of while
       if S[j] < a  or  S[j] = a  then
           begin  k = k+1; S[k] = a      end
        else  error //调用出错诊察程序
    until a = ‘#’

四、代码实现(使用C++实现)

源码

#include<iostream>
#include<cstring>
#include<string>
#include<stack>

using namespace std;

typedef struct{
    string formula;//产生式
}grammarElement;
grammarElement  gramOldSet[50];
int n=0;

string terSymbol;//终结符号
string non_ter;//非终结符号
string firstvt[50],lastvt[50];

char table[50][50];

typedef struct   //符号对(P,a)
{
	char Vn;
	char Vt;
} VN_VT;

void FirstVTscan(char &non);
void LastVTscan(char &non);
void toTable();
void Control();

void leftsu(string &str,int &x,int &y);
bool tui(char &A,char &B);
int get_ter_ps(string &str);
string add(string s1,string s2);
void addF(char non_t,char ter);
void addL(char non_t,char ter);

int main(){
	cout << "non_ter: ";
    cin >> non_ter;
	non_ter+= "X";

    cout << "ter: ";
    cin >> terSymbol;
	terSymbol+= "#";
    cout<<"num: ";
    cin>> n;
    for(int i=1;i<=n;i++){
        cin>>gramOldSet[i].formula;
    }
	gramOldSet[0].formula ="X->##";
	gramOldSet[0].formula.insert(4, 1, gramOldSet[1].formula[0]);

	// FirstVT();
	// LastVT();
	for(int i=0;i<non_ter.size();i++){
		FirstVTscan(non_ter[i]);
	}
	for(int i=0;i<non_ter.size();i++){
        cout<<firstvt[i]<<endl;
    }

	cout<<endl;

	for(int i=0;i<non_ter.size();i++){
		LastVTscan(non_ter[i]);
	}

	for(int i=0;i<non_ter.size();i++){
        cout<<lastvt[i]<<endl;
    }
	cout<<endl;

	toTable();
    for(int i=0;i<terSymbol.size();i++){
        for(int j=0;j<terSymbol.size();j++){
            cout.width(4);
            cout<<table[i][j];
			if(table[i][j]==0) cout<<" ";
        }
        cout<<endl;
    }

	Control();

    return 0;
}

void Control(){
	bool flag=true;
	string an="#",inputs;
	cin>>inputs;
	inputs.push_back('#');
	int p=0;	//p是栈顶终结符的位置
	int k=0;

	while(flag){
		cout.width(5);
		cout<<left<<k;
		k++;

		p=get_ter_ps(an);
		//查表,看关系是<=>
		char c = table[terSymbol.find(an[p])][terSymbol.find(inputs[0])];
		if(c=='=' && inputs[0] == '#'){
			flag=false;
		}
		//移进
		else if (c=='<'||c=='='){
			an.push_back(inputs[0]);
			inputs.erase(0,1);
			//输出结果
			cout.width(12);
			cout<<left<<an;
			cout.width(12);
			cout<<right<<inputs<<endl;
			// cout<<"   移进"<<endl;
		}
		else if (c=='>'){
			int x=0,y=0;
			leftsu(an,x,y);

			// if(x==y){
				for(int i=1;i<=n;i++){
					if(gramOldSet[i].formula.find(an[x],3) != -1){
						int size = gramOldSet[i].formula.size()-3;
						int start = x-(gramOldSet[i].formula.find(an[x],3)-3);	//归约的第一个字符开始的下标

						bool is_this=true;	//是否为当前文法
						if(size==1){
							an.pop_back();
							an.push_back(gramOldSet[i].formula[0]);
							break;
						}
						else if (size>1){
							//看其他非终结符是否能推出an中的非终结符
							for(int j=0;j<size;j++){
								if(non_ter.find(an[start+j]) == -1 ){
									continue;
								}
								else if ( tui(gramOldSet[i].formula[3+j], an[start+j]) ){
									
								}else{
									is_this = false;
									break;
								}
							}
							//
							if(is_this){
								//归约过程
								for(int j=0;j<size;j++){
									an.pop_back();
								}
								an.push_back(gramOldSet[i].formula[0]);
								break;
							}

						}

						
					}
				}
			// }
			//输出结果
			cout.width(12);
			cout<<left<<an;
			cout.width(12);
			cout<<right<<inputs<<endl;
			// cout<<"   归约"<<endl;
		}
		else if (c==0){
			flag = false;
			cout<<"error!"<<endl;
		}
		
	}
}

void FirstVTscan(char &non){
	for(int i=0;i<non_ter.size();i++){
		for(int j=0;j<=n;j++){
			if(gramOldSet[j].formula[0]==non){
				//P->a...
				if( terSymbol.find(gramOldSet[j].formula[3])!=-1 ){
					addF(gramOldSet[j].formula[0],gramOldSet[j].formula[3]);
				}
				//P->Q
				else if ( gramOldSet[j].formula.size()>4 ){
					//P->Qa...
					if ( non_ter.find(gramOldSet[j].formula[3])!=-1 && terSymbol.find(gramOldSet[j].formula[4])!=-1 ){
						addF(gramOldSet[j].formula[0],gramOldSet[j].formula[4]);
					}
				}
				//P->Q...
				if( non_ter.find(gramOldSet[j].formula[3])!=-1 && gramOldSet[j].formula[0]!=gramOldSet[j].formula[3] ){
					char c=gramOldSet[j].formula[3];
					FirstVTscan(c);
					firstvt[non_ter.find(non)] = add(firstvt[non_ter.find(non)], firstvt[non_ter.find(gramOldSet[j].formula[3])]);
				}
			}
		}
	}
	return ;
}

void LastVTscan(char &non){
	//为什么加了&之后就不报错了ToT
	//https://blog.csdn.net/mofushaohua_ln/article/details/77934368
	for(int i=0;i<non_ter.size();i++){
		for(int j=0;j<=n;j++){
			int it = gramOldSet[j].formula.size()-1;

			if( gramOldSet[j].formula[0]==non ){
				//p->..a
				if(terSymbol.find( gramOldSet[j].formula[it] ) != -1){
					addL(non,gramOldSet[j].formula[it]);
				}
				//P->..aQ
				else if(gramOldSet[j].formula.size()>4){
					if(non_ter.find(gramOldSet[j].formula[it])!=-1 && terSymbol.find(gramOldSet[j].formula[it-1]) !=-1 ){
						addL(non, gramOldSet[j].formula[it-1]);
					}
				}

				//P->...Q
				if(non_ter.find(gramOldSet[j].formula[it])!=-1 && gramOldSet[j].formula[0]!=gramOldSet[j].formula[it] ){
					char c= gramOldSet[j].formula[it];
					LastVTscan(c);
					lastvt[non_ter.find(non)] = add(lastvt[non_ter.find(non)], lastvt[non_ter.find(gramOldSet[j].formula[it])]);
				}
			}
		}
	}
	return ;
}

void toTable(){
	for(int i=0;i<=n;i++){
		int p=3,q=gramOldSet[i].formula.size()-1;
		for(int j=p;j<=q-1;j++){
			int p0=terSymbol.find(gramOldSet[i].formula[j]),p1=terSymbol.find(gramOldSet[i].formula[j+1]);
			int q0=non_ter.find(gramOldSet[i].formula[j]),q1=non_ter.find(gramOldSet[i].formula[j+1]);
			if(j<=q-2){
				//...aQb...
				if(p0!=-1 && q1!=-1 && terSymbol.find(gramOldSet[i].formula[j+2])!=-1 ){
					table[p0][terSymbol.find(gramOldSet[i].formula[j+2])] = '=';
				}
			}
			//...ab..
			if(p0!=-1&& p1!=-1){
				table[p0][p1] = '=';
			}
			//...aQ...
			else if (p0!=-1 && q1!=-1){
				for(int k=0;k<firstvt[q1].size(); k++){
					table[p0][terSymbol.find(firstvt[q1][k])] = '<';
				}
			}
			//...Qa...
			else if(q0!=-1&& p1!=-1){
				for(int k=0;k<lastvt[q0].size(); k++){
					table[terSymbol.find(lastvt[q0][k])][p1] = '>';
				}
			}

		}
	}
}



//寻找最左素短语
void leftsu(string &str,int &x,int &y){
	int z=1;
	for(int i=1; i<str.size(); i++){
		if(terSymbol.find(str[i])!= -1){
			x=i; break;
		}
	}

	y=x;
	for(z=x+1 ; z<str.size(); z++){
		if(terSymbol.find(str[z]) != -1){
			if(table[terSymbol.find(str[y])][terSymbol.find(str[z])] == '<'){
				x=z;
				y=z;
			}
			else if (table[terSymbol.find(str[y])][terSymbol.find(str[z])] == '='){
				y=z;
			}
			else if (table[terSymbol.find(str[y])][terSymbol.find(str[z])] == '>'){
				break;
			}
			
		}
	}

}

//A是否能推出B
bool tui(char &A,char &B){
	if(A==B) return true;
	for(int i=1;i<=n;i++){
		if(gramOldSet[i].formula[0] == A && gramOldSet[i].formula.size() == 4){
			if( gramOldSet[i].formula[3]==B){
				return true;
			}
			else if( non_ter.find(gramOldSet[i].formula[3])!=-1){
				return tui(gramOldSet[i].formula[3],B);
			}
		}
			
	}

	return false;
		

}

//寻找栈中第一个终结符
int get_ter_ps(string &str){
	for(int i=str.size()-1; i>=0; i--){
		if(terSymbol.find(str[i])!= -1) return i;
	}
	return -1;
}

//将s1与s2合并,返回s2
string add(string s1,string s2){
	for(int i=0;i<s1.size();i++){
		if(s2.find(s1[i])!=-1) continue;
		else {
			s2.push_back(s1[i]);
		}
	}
	return s2;
}

//向firstvt集中添加
void addF(char non_t,char ter){
	int p=non_ter.find(non_t);
	if(firstvt[p].find(ter)==-1){
		firstvt[p].push_back(ter);
	}
} 

//向lastvt集中添加
void addL(char non_t,char ter){
	int p=non_ter.find(non_t);
	if(lastvt[p].find(ter)==-1){
		lastvt[p].push_back(ter);
	}
}

测试数据:

ETFP
+*!()i
8
E->E+T
E->T
T->T*F
T->F
F->P!F
F->P
P->(E)
P->i
(i+i)*i

  • 10
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
算符优先分析表是一种将文法转化为语法分析表的方法,它用于确定每个运算符在表达式中的优先级和结合性。以下是用C++构造算符优先分析表的步骤: 1. 定义运算符的优先级和结合性。通常,优先级越高的运算符越优先进行计算,而结合性指定在没有括号的情况下运算符的结合顺序(是从左到右还是从右到左)。 2. 构建运算符优先关系表。这个表可以使用二维数组来实现,其中行和列分别代表运算符的优先级。根据结合性,表中的每个元素可以存储以下三种值之一:"<"、">"或"=",分别表示左侧运算符的优先级低于、高于或等于右侧运算符的优先级。 3. 对输入表达式进行算符优先分析。这可以使用堆栈来实现。首先,将一个“#”符号和表达式的第一个符号压入堆栈。然后,根据运算符优先关系表中的值,对堆栈中的符号进行比较,并将需要计算的子表达式压入堆栈。最终,堆栈中只剩下一个符号,即整个表达式的值。 下面是一个简单的C++代码示例,用于构造算符优先关系表和执行算符优先分析: ```c++ #include <iostream> #include <stack> #include <vector> #include <map> using namespace std; int main() { // 定义运算符的优先级和结合性 map<char, int> priority = {{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}}; map<char, char> associativity = {{'+', 'L'}, {'-', 'L'}, {'*', 'L'}, {'/', 'L'}}; // 构建运算符优先关系表 vector<vector<char>> table(4, vector<char>(4, ' ')); for (auto iter1 : priority) { for (auto iter2 : priority) { if (iter1.second > iter2.second) { table[iter1.first - '+'][iter2.first - '+'] = '>'; } else if (iter1.second < iter2.second) { table[iter1.first - '+'][iter2.first - '+'] = '<'; } else { table[iter1.first - '+'][iter2.first - '+'] = '='; } } } // 对输入表达式进行算符优先分析 string expression; stack<char> operators; cin >> expression; operators.push('#'); for (auto c : expression) { if (c >= '0' && c <= '9') { cout << c; } else { while (operators.top() != '#' && table[operators.top() - '+'][c - '+'] == '>') { cout << operators.top(); operators.pop(); } if (associativity[c] == 'L' && table[operators.top() - '+'][c - '+'] == '=') { cout << operators.top(); operators.pop(); } operators.push(c); } } while (operators.top() != '#') { cout << operators.top(); operators.pop(); } return 0; } ``` 在此示例中,我们首先定义了四种运算符(加、减、乘、除)的优先级和结合性。然后,我们使用map和vector构建了运算符优先关系表。最后,我们使用堆栈和运算符优先关系表对输入表达式进行算符优先分析,并输出结果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值