编译技术第六次上机作业

算术表达式的扩充

 

1. 实验目的

充分理解语义分析的方法及相关语义计算的执行时机。

掌握LR分析表的设计方法和语义加工程序的扩充。

2. 实验要求

参照算术表达式LR分析表的设计方法,设计扩充后的算术表达式LR分析表,并对原语义加工程序进行修改,加入新添加的内容。写一段程序,打印出计算结果E。

3. 实验内容

假设有以下文法:

L®En

E®E+T

E®E-T

E®T

T®T*F

T®T/F

T®F

F®(E)

F®id

设该文法进行自下而上计算时,打印出四则运算的计算结果。

E、T、F这些非终结符需要综合属性。以L属性的翻译方案为基础,将下表的语义规则嵌套在语法分析的过程中,即实现语法制导的翻译过程。

产  生  式 

语  义  规  则 

L®En

{print(E.val)}

E®E1+T

{ E.val=E1.val+T.val}

E®E1-T

{ E.val=E1.val-T.val}

E®T

{ E.val= T.val}

T®T1*F

{ T.val=T1.val*F.val}

T®T1/F

{ T.val=T1.val/F.val}

T®F

{ T.val= F.val}

F®(E)

{ F.val=E.val}

F®id

{ F.val=id.lexval}

2.以词法分析和语法分析部分的上机结果为基础,添加语义分析部分。即以LR文法为基础。当进行产生式归约时执行对应的语义动作。

3.输入:

5+3+8*2

输出:24

输入10-6/2

输出:7

4. 若输入有误,如:3++2

则应提示:重新输入!

5. 由于输入串是具体的数值,因此应调用相应的词法分析的功能。

        一样的,只要画出来LR分析表了,代码逻辑头脑里就清晰了。如果第五次上机顺利做出来了的话,第六次也不会有很大问题,我也是根据第五次上机的成果写出来的。第五次上机的代码可以去看我的上一篇博客。

随手画的LR分析表:

        说一下我的思路,其实第六次上机和第五次上机差别不大,就是要求能够按照语义将具体的算数式子算出来,所以怎么让代码运行过程中能够携带具体的数值,并且能够进行运算,是本题的核心。

        因为我们平时画注释树都是按照形如“E.val=8”,“id.lexval=3”来承载数值,所以在程序中我也用非终结符或者id后面加数值的方式(形如E4,F9,id2)来模拟该过程.

#include<iostream>
#include<map>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
vector<string> zhan;
void dis(vector<string> &v);
static string getT(string s1,string s2){//LR分析表 
	int count=0;
	while((count<s2.length())&&(s2.at(count)<'0'||s2.at(count)>'9')){//找到数字开始的下标 
					count++;
		}
	s2=s2.substr(0,count);// 将数字去除 
	map<string,map<string,string>> m;
	map<string,string>n0={{"id","s5"},{"(","s4"},{"E","1"},{"T","2"},{"F","3"}},
	n1={{"+","s6"},{"#","acc"},{"-","s7"}},
	n2={{"*","s8"},{"/","s9"},{"+","r4"},{"-","r4"},{")","r4"},{"#","r4"}},
	n3={{"+","r7"},{"-","r7"},{"*","r7"},{"/","r7"},{"#","r7"},{")","r7"}} ,
	n4={{"(","s4"},{"id","s5"},{"E","10"},{"T","2"},{"F","3"}},
	n5={{"+","r9"},{"-","r9"},{"*","r9"},{"/","r9"},{"#","r9"},{")","r9"}},
	n6={{"(","s4"},{"id","s5"},{"T","11"},{"F","3"}},
	n7={{"(","s4"},{"id","s5"},{"F","3"},{"T","12"}},
	n8={{"(","s4"},{"id","s5"},{"F","13"}},
	n9={{"(","s4"},{"id","s5"},{"F","14"}},
	n10={{"+","s6"},{"-","s7"},{")","s15"}},
	n11={{"/","s9"},{"*","s8"},{"+","r2"},{"-","r2"},{")","r2"},{"#","r2"}},
	n12={{"/","s9"},{"*","s8"},{"+","r3"},{"-","r3"},{")","r3"},{"#","r3"},},
	n13={{"+","r5"},{"-","r5"},{"*","r5"},{"/","r5"},{")","r5"},{"#","r5"}},
	n14={{"+","r6"},{"-","r6"},{"*","r6"},{"/","r6"},{")","r6"},{"#","r6"}}
	;
	m["0"]=n0;
	m["1"]=n1;
	m["2"]=n2;
	m["3"]=n3;
	m["4"]=n4;
	m["5"]=n5;
	m["6"]=n6;
	m["7"]=n7;
	m["8"]=n8;
	m["9"]=n9;
	m["10"]=n10;
	m["11"]=n11;
	m["12"]=n12;
	m["13"]=n13;
	m["14"]=n14;
	string s=m[s1][s2];
	if(s.length()==0){
		cout<<"请重新输入!"<<endl;//因为题目上要求这样输出,但是其实弹栈纠错也能运行出正确结果 
		exit(1);//所以加了这两行代码,但是保留原来弹栈的代码 
		cout<<"error"<<endl;
		return "error";
	}else
	return m[s1][s2];
}
static vector<string> getL(string num){//扩充后的文法 
	map<string,vector<string>> m;
	vector<string>v2={"E","E","+","T"};
	vector<string>v3={"E","E","-","T"};
	vector<string>v4={"E","T"};
	vector<string>v5={"T","T","*","F"};
	vector<string>v6={"T","T","/","F"};
	vector<string>v7={"T","F"};
	vector<string>v8={"F","(","E",")"};
	vector<string>v9={"F","id"};
	m["2"]=v2;
	m["3"]=v3;
	m["4"]=v4;
	m["5"]=v5;
	m["6"]=v6;
	m["7"]=v7;
	m["8"]=v8;
	m["9"]=v9;
	return m[num];
}

vector<string> push(string a){
	vector<string> v;
	string s="";
	v.push_back("#");
	for(int i=a.length()-1;i>=0;i--){//注意是从字符串的左往右提取,因为栈先进后出 
		char w=a.at(i);
		s+=w;
		if(s==" "||s=="#"){
			s="";
			continue;	
		}
		else if(s=="*"||s=="("||s==")"||s=="+"||s=="-"||s=="/"){
			v.push_back(s);
		}else if(s>="0"&&s<="9"){ 
			while((i-1)>=0&&a.at(i-1)>='0'&&a.at(i-1)<='9'){
					s+=a.at(i-1);
					i--;
			}		
			reverse(s.begin(),s.end());//将数字翻转过来,比如我们输入的是10,但是经过 
			v.push_back("id"+s);//这里函数的提取之后变成01,所以将他翻转得到原来的样子 
		}
		s="";
}
	return v;	
}
string jisuan(string op,vector<string> v){//该函数的功能是将字符串数字运算输出字符串数字 
	int num=0;
	if(op=="+"){
		num=stoi(v[0])+stoi(v[1]);
	}else 
	if(op=="*"){
		num=stoi(v[0])*stoi(v[1]);
	}else
	if(op=="/"){
		num=stoi(v[1])/stoi(v[0]);
	} else
	if(op=="-"){
		num=stoi(v[1])-stoi(v[0]);
	}
	return to_string(num); 
}
int main(){
	int count=0; 
	vector<string>v=push("5+3+8*2#");
	zhan.push_back("#");
	zhan.push_back("0");
	while(count<20){
		count++;
		string get=getT(zhan.back(),v.back());//查LR分析表 
		string state="";
		string op="";//装运算符号 
		vector<string> cc; //装用来运算的数字 
		state+=get.at(0);
		string num=get.substr(1);//将查表得到的状态,比如"s3","s4","r6"等拆分开 
		if(state=="s")
		{
			zhan.push_back(v.back());
			zhan.push_back(num);
			v.pop_back();
		}else if(state=="r"){
			vector<string>list=getL(num);
			for(int i=1;i<list.size();i++) {
				if(i==2){
					op=list[i];//去中间的符号是运算符 
				}
			}
			while(list.size()>1){
				zhan.pop_back();//抛掉数字 
				int count=0;
				string str=zhan.back();
				while((count<str.length())&&(str.at(count)<'0'||str.at(count)>'9')){//找到数字开始的下标 ,只有非终结符才可以携带数字 
					count++;
				}
				if(str.substr(count).length()>0){//如果是终结符的话,截取的长度为0,不近栈 
					cc.push_back(str.substr(count));
				}
				count=0;
				zhan.pop_back();
				list.pop_back(); 
			}
			string val;
			if(op.length()>0) {
			val=jisuan(op,cc);
		}else{
			val=cc.back();
		} 
			string pre=zhan.back();
			zhan.push_back(list.back()+val);
			string go=getT(pre,zhan.back());
			zhan.push_back(go) ;
		}
		else if(state=="a"){
			zhan.pop_back();
			int count=0;
			string str=zhan.back();
			while((count<str.length())&&(str.at(count)<'0'||str.at(count)>'9')){//找到数字开始的下标 ,只有非终结符才可以携带数字 
				count++;
			}
			cout<<"结果为:"<<str.substr(count)<<endl;
			break;
		}else if(state=="e"){
			zhan.pop_back();
			zhan.pop_back();
		}
		state="";
		
	}
}

        上面代码总共不到200行,较同类代码,稍显小巧。题目中的三个例子都测试了,没问题,但是测试过程中也发现了LR表的细小错误,都予以了更正,如果大家用了其他的例子跑不通,大概是LR分析表哪里抄错,欢迎留言!

        另外,有些小伙伴想要弄懂程序运行的细节,可以运行下面的注释版代码,运行后可以清晰地打印出各种输入输出,便于学习。

      

#include<iostream>
#include<map>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
vector<string> zhan;
void dis(vector<string> &v);
static string getT(string s1,string s2){
	int count=0;
	while((count<s2.length())&&(s2.at(count)<'0'||s2.at(count)>'9')){//找到数字开始的下标 
					count++;
		}
	s2=s2.substr(0,count); 	
	cout<<"使用"<<s1<<"和"<<s2<<"查表"<<endl;
	map<string,map<string,string>> m;
	map<string,string>n0={{"id","s5"},{"(","s4"},{"E","1"},{"T","2"},{"F","3"}},
	n1={{"+","s6"},{"#","acc"},{"-","s7"}},
	n2={{"*","s8"},{"/","s9"},{"+","r4"},{"-","r4"},{")","r4"},{"#","r4"}},
	n3={{"+","r7"},{"-","r7"},{"*","r7"},{"/","r7"},{"#","r7"},{")","r7"}} ,
	n4={{"(","s4"},{"id","s5"},{"E","10"},{"T","2"},{"F","3"}},
	n5={{"+","r9"},{"-","r9"},{"*","r9"},{"/","r9"},{"#","r9"},{")","r9"}},
	n6={{"(","s4"},{"id","s5"},{"T","11"},{"F","3"}},
	n7={{"(","s4"},{"id","s5"},{"F","3"},{"T","12"}},
	n8={{"(","s4"},{"id","s5"},{"F","13"}},
	n9={{"(","s4"},{"id","s5"},{"F","14"}},
	n10={{"+","s6"},{"-","s7"},{")","s15"}},
	n11={{"/","s9"},{"*","s8"},{"+","r2"},{"-","r2"},{")","r2"},{"#","r2"}},
	n12={{"/","s9"},{"*","s8"},{"+","r3"},{"-","r3"},{")","r3"},{"#","r3"},},
	n13={{"+","r5"},{"-","r5"},{"*","r5"},{"/","r5"},{")","r5"},{"#","r5"}},
	n14={{"+","r6"},{"-","r6"},{"*","r6"},{"/","r6"},{")","r6"},{"#","r6"}}
	;
	m["0"]=n0;
	m["1"]=n1;
	m["2"]=n2;
	m["3"]=n3;
	m["4"]=n4;
	m["5"]=n5;
	m["6"]=n6;
	m["7"]=n7;
	m["8"]=n8;
	m["9"]=n9;
	m["10"]=n10;
	m["11"]=n11;
	m["12"]=n12;
	m["13"]=n13;
	m["14"]=n14;
	string s=m[s1][s2];
	if(s.length()==0){
		cout<<"error"<<endl;
		return "error";
	}else
	return m[s1][s2];
}
static vector<string> getL(string num){
	map<string,vector<string>> m;
	vector<string>v2={"E","E","+","T"};
	vector<string>v3={"E","E","-","T"};
	vector<string>v4={"E","T"};
	vector<string>v5={"T","T","*","F"};
	vector<string>v6={"T","T","/","F"};
	vector<string>v7={"T","F"};
	vector<string>v8={"F","(","E",")"};
	vector<string>v9={"F","id"};
	m["2"]=v2;
	m["3"]=v3;
	m["4"]=v4;
	m["5"]=v5;
	m["6"]=v6;
	m["7"]=v7;
	m["8"]=v8;
	m["9"]=v9;
	return m[num];
}

vector<string> push(string a){
	cout<<"将输入转换成栈"<<endl; 
	vector<string> v;
	string s="";
	v.push_back("#");
	for(int i=a.length()-1;i>=0;i--){
		char w=a.at(i);
		s+=w;
		if(s==" "||s=="#"){
			s="";
			continue;	
		}
		else if(s=="*"||s=="("||s==")"||s=="+"||s=="-"||s=="/"){
			cout<<s<<"是符号"<<endl;
			cout<<"装入"<<s<<endl;
			v.push_back(s);
		}else if(s>="0"&&s<="9"){
			cout<<s<<"是数字"<<endl; 
			while((i-1)>=0&&a.at(i-1)>='0'&&a.at(i-1)<='9'){
					cout<<a.at(i-1)<<"也是数字"<<endl;
					s+=a.at(i-1);
					i--;
			}		
			reverse(s.begin(),s.end());
			cout<<"装入"<<"id"+s<<endl;
			v.push_back("id"+s);
		}
		s="";
	}
	dis(v);
	return v;	
}
void dis(vector<string> &v){
	cout<<"此时:"<<endl;
	for(int i=0;i<v.size();i++){
		cout<<v[i]<<" ";
	}
	cout<<endl;
}
string jisuan(string op,vector<string> v){
	cout<<"进入计算过程"<<endl; 
	int num=0;
	if(op=="+"){
		cout<<"是加号"<<endl;
		num=stoi(v[0])+stoi(v[1]);
	}else 
	if(op=="*"){
		cout<<"是乘号"<<endl;
		num=stoi(v[0])*stoi(v[1]);
	}else
	if(op=="/"){
		cout<<"是除号"<<endl;
		num=stoi(v[1])/stoi(v[0]);
	} else
	if(op=="-"){
		cout<<"是减号"<<endl;
		num=stoi(v[1])-stoi(v[0]);
	}
	return to_string(num); 
}
int main(){
	int count=0; 
	vector<string>v=push("10-6/2#");
	zhan.push_back("#");
	zhan.push_back("0");
	while(count<20){
		count++;
		cout<<"使用两个key:"<<zhan.back()<<"和"<<v.back()<<endl;
		string get=getT(zhan.back(),v.back());
		cout<<"查表得到"<<get<<endl;
		string state="";
		string op="";
		vector<string> cc; 
		state+=get.at(0);
		string num=get.substr(1);
		cout<<"分解为"<<state<<"和"<<num<<endl;
		if(state=="s")
		{
			cout<<"移进"<<endl; 
			zhan.push_back(v.back());
			zhan.push_back(num);
			if(v.back().at(0)>='0'&&v.back().at(0)<='9'){
			cout<<"装入"<<"id"+v.back()<<"和"<<num<<endl;
		}else{
			cout<<"装入"<<v.back()<<"和"<<num<<endl;
		}
			v.pop_back();
			dis(zhan);
		}else if(state=="r"){
			vector<string>list=getL(num);
			cout<<"按照"<<list[0]<<"->";
			for(int i=1;i<list.size();i++) {
				cout<<list[i];
				if(i==2){
					op=list[i];
				}
			}
			cout<<"进行归约"<<endl;
			while(list.size()>1){
				zhan.pop_back();//抛掉数字 
				cout<<"约去"<<zhan.back()<<"和"<<list.back()<<endl;
				int count=0;
				string str=zhan.back();
				while((count<str.length())&&(str.at(count)<'0'||str.at(count)>'9')){//找到数字开始的下标 ,只有非终结符才可以携带数字 
					count++;
				}
				cout<<"针对"<<str<<",并且count="<<count<<endl;
				if(str.substr(count).length()>0){
					cout<<"装入用于计算的数字:"<<str.substr(count)<<endl;
					cc.push_back(str.substr(count));
				}
				count=0;
				zhan.pop_back();
				list.pop_back(); 
			}
			string val;
			if(op.length()>0) {
			cout<<"拆得的运算符为"<<op<<endl;
			val=jisuan(op,cc);
			cout<<"计算得到的数:"<<val<<endl;
		}else{
			val=cc.back();
		} 
			cout<<"装入"<<list.back()+val<<endl;
			string pre=zhan.back();
			zhan.push_back(list.back()+val);
			string go=getT(pre,zhan.back());
			cout<<"再次查表得到goto表项,并装入"<<go<<endl;
			zhan.push_back(go) ;
			dis(zhan);
		}
		else if(state=="a"){
			dis(zhan);
			zhan.pop_back();
			int count=0;
			string str=zhan.back();
			while((count<str.length())&&(str.at(count)<'0'||str.at(count)>'9')){//找到数字开始的下标 ,只有非终结符才可以携带数字 
				count++;
			}
			cout<<"结果为:"<<str.substr(count)<<endl;
			cout<<"接受"<<endl;
			break;
		}else if(state=="e"){
			zhan.pop_back();
			zhan.pop_back();
			dis(zhan);
		}
		state="";
		
	}
}

        其实我真的不建议大家真的去弄懂上面代码的所有细节,因为即使代码是我写的,我写第六次上机的时候隔了两周回去看第五次上机的代码头脑也乱T_T,所以直接按照你画的LR分析表和分析器写代码就行了。上面的记录LR表和文法的方式以及如何在代码运行过程中承载和计算数值的方法可以提供一个思路。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值