实验四 LR(0)分析方法的设计与实现 (8学时)

实验四 LR(0)分析方法的设计与实现(8学时)

一、实验目的
通过LR分析方法的实现,加深对自下而上语法分析方法及语法分析程序自动生成过程的理解。
二、实验要求
输入上下文无关文法,对给定的输入串,给出其LR分析过程及正确与否的判断。
三、实验步骤
1.参考数据结构

typedef struct{/*文法*/
	char head;//产生式左部符号
	char b[20];//用于存放产生式
	int point;
	int lg;//产生式的长度
}regular;                        
typedef struct{
	int l;       //文法数量
	int m;       //vn 数量
	int n;       //vt 数量
	regular re[20];
	nfinal vn[20];//非终结符
	final vt[20];//终结符
}condition;
condition cd[20];//项目
regular first[20];//产生式
  1. 使用闭包函数(CLOSURE)和转换函数(GO(I,X))构造文法G’的LR(0)的项目集规范族。
    计算LR(0)项目集规范族C={I0,I1,…,In}
    算法描述如下:
    Procedure itemsets(G’);
    Begin C = { CLOSURE ({S’ →.S})}
    Repeat
    For C 中每一项目集I和每一文法符号X
    Do if GO(I,X) 非空且不属于C
    Then 把 GO(I,X) 放入C中
    Until C 不再增大
    End;
    定义转换函数如下:
    GO(I,X)= CLOSURE(J)
    其中:I为包含某一项目集的状态,X为一文法符号,J={A→aX.b|A→a.X b∈I}。
  2. 构造LR(0)分析表
    对于LR(0)文法,我们可以直接从它的项目集规范族C和活前缀识别自动机的状态转换函数GO构造出LR分析表。
    算法描述如下:
    begin
    if A→α•aβ Ik and GO(Ik,a) = Ij (aVT) then
    置ACTION[k,a] = sj;
    If A→α• Ik then
    对任意终结符a(包括#)置ACTION[k,a] = rj;
    If S’ →S• Ik then
    置ACTION[k,#] = acc;
    If GO(Ik,A) = Ij (AVN) then
    置 GOTO(k,A) = j;
    else ERROR //出错
  3. LR分析算法描述
    对给定的输入串,给出其分析过程及正确与否的判断
    将S0移进状态栈,#移进符号栈,S为状态栈栈顶状态
    begin
    a=getsym() //读入第一个符号给a
    while(ACTION[S,a]!=acc)
    If ACTION[S,a]=si then
    PUSH i,a(分别进栈);输出进栈信息
    a=getsym();//读入下一个符号给a
    else if ACTION[S,a] = rj (第j条产生式为A→β) then
    输出归约信息
    将状态栈和符号栈分别弹出|β|项; push(A);
    将GOTO[S’,A]移进状态栈(S’为当前栈顶状态);
    else error;
    输出分析结果,接受或出错
    End
    5、对给定输入串输出其准确与否的分析过程。
    四、实验报告要求
    1.写出编程思路、源代码(或流程图);
#include

using namespace std;

typedef struct{
    string formula;//原始文法产生式
}grammarElement;

typedef struct{
	string regular;//项目文法产生式
	int loca;//来自文法的第j个产生式
}condition;

grammarElement  gramOldSet[100];//原始文法的产生式集
grammarElement  gramSet[100];//初始项目文法集
condition cd[200];//原始项目
set<string> I[20];//I是文法G的任一项目集,无序
set<set<string> > C;//LR(0)项目集规范族

int sum;
int gm_n,cd_n=0, I_n = 1;//文法产生式的个数,项目的个数,项目集的序号
string terSymbol;//终结符号
string non_ter;//非终结符号
string allSymbol;//所有符号
string M[200][200];//LR(0)分析表
string str_cin;//输入串
stack <int> Cd;    ///状态栈
stack <char> S;   ///符号栈

int atoi(string s){//字符串转换为数字
	int num = 0;
	for(int i = 0;i < s.length();i++){
		num *=10;
		num += (s[i] - 48);
	}
	return num;
}

string Print_Stack(stack <int> ss){//打印状态栈
    stack <int> sta1;
    sta1 = ss;
    string str_temp;//暂存符号栈
    while(!sta1.empty()){
        str_temp.insert(0,to_string(sta1.top()));
        sta1.pop();
    }
    return str_temp;
}

string PrintStack(stack <char> ss){//打印符号栈
    stack <char> sta1;
    sta1 = ss;
    string str_temp;//暂存符号栈
    while(!sta1.empty()){
        str_temp.insert(0,1,sta1.top());
        sta1.pop();
    }
    return str_temp;
}

string PrintStrCin(string s,int num){//打印输入串
    string str;
    for(int i = num; i<s.length();i++){
        str += s[i];
    }
    return str;
}

void initSet(grammarElement s[],int n1){//原始文法的初始化
    s[0].formula = "S->";//扩广文法S->#E#
    s[0].formula.insert(3,1,s[1].formula[0]);
    non_ter.insert(0,1,'S');//文法符号是开始符号S
    gm_n++;
    for(int i = n1; i >= 1; i--){
        string temp = s[i].formula;//暂时保存需要处理的产生式
        for(int j = s[i].formula.length() - 1; j >= 0; j-- ){
            if(s[i].formula[j]  == '|'){//确定带有 | 的产生式
				int pos = i+1;
				string str0 = temp.erase(3,j-2);//加上新的产生式
				temp = s[i].formula.erase(j);//去除 |
				for(int k = gm_n; k >= pos;k--){
					s[k] = s[k-1];
				}
				s[pos].formula = str0; 
				gm_n++;
            }
        }
    }
	cout<<"\n消除 | 的产生式:"<<endl;
	for(int i = 0;i < gm_n; i++){
        cout<<s[i].formula<<endl; 
		gramSet[i] = s[i];//项目初始集
		gramSet[i].formula.insert(3,1,'.');
		int temp = cd_n;
		string str = s[i].formula; 
		for(int j = 3; j <= str.length(); j++){
			cd[cd_n].regular = str;
			cd[cd_n].loca = i;
			cd[cd_n++].regular.insert(j,1,'.');
		}
    }
	I[0].insert(gramSet[0].formula);//I0有扩广文法
    cout<<endl;	
	cout<<"这个文法的项目有:"<<endl;
	for(int i = 0; i < cd_n; i++)
		cout<<cd[i].regular<<endl;
	cout<<endl;	
}

set<string> CLOSURE(set <string> I){//构造I的闭包CLOSURE(I)
	int oldSize;
	do{
		oldSize = I.size();
		for(auto it:I){//使用C++新特性循环遍历I,如果需要改变i的值,还需要在前面加上&
			string str0;
			str0 = it;
			for(int i = 3;i < str0.length();i++){
				if((str0[i] == '.' )&&(non_ter.find(str0[i+1]) != -1)){
					for(int j =  0; j < gm_n ;j++){
						if(gramSet[j].formula[0] == str0[i+1])
							I.insert(gramSet[j].formula);
					}
				}
			}			
		}
	}while(I.size() != oldSize);
	return I;
}

set<string> GO(set <string> I,char X){//转换函数
	set <string> J;
	int point;//点的位置
	string str0;
	string str1;
	str0.append(1,X);
	str0 += ".";
	for(auto it:I){
		str1 = it;
		point = str1.find(".");
		if(str1.rfind(X) == (point + 1)){
			str1.replace(point,2,str0);
			J.insert(str1);
			J = CLOSURE(J);
		}else{
			continue;
		}
	}
	return J;
}

void ITEMSETS(){//构造文法G’的LR(0)的项目集规范族
	I[0] = CLOSURE(I[0]);
	C.insert(CLOSURE(I[0]));
	int oldSize;
	do{
		oldSize = C.size();
		for(auto it:C){
			set <string> temp0 = it;
			for(int i = 0; i < allSymbol.length();i++){
				set <string> temp1 = GO(temp0,allSymbol[i]);
				if(!temp1.empty() && (C.count(temp1) == 0)){//GO(I,X)非空且不属于C
					I[I_n++] = temp1;
					C.insert(temp1);
				} 
			}
		}
	}while(C.size() != oldSize);
}

void dealTable(){//构造LR(0)分析表
    terSymbol += '#';
	non_ter.erase(non_ter.begin() + non_ter.find('S'));
	allSymbol = terSymbol + non_ter;
    int cnt = 0;//计数项目的个数
	string st;//保存数字转成字符串
	// memset(M,NULL,sizeof(M));
	do{
		string s = cd[cnt].regular;
		for(int k = 0; k < I_n;k++){
			if(I[k].count(s) == 1){//项目属于Ik
				if((s.find('.') == s.length()-1)){
					if(s[0] == 'S'){//若项目S’→S?属于Ik,则置ACTION[k,#]为"接受",简记为"acc"
						M[k][allSymbol.find('#')] = "acc"; 
					}else{//若项目A→α?属于Ik,那么,对任何终结符a(或结束符#),
						for(int m = 0; m < terSymbol.length(); m++){//置ACTION[k,a]为"用产生式A→α进行归约"
							st = to_string(cd[cnt].loca);//简记为"rj"(假定产生式A→α是文法G的第j个产生式)
							M[k][m] = "r" + st;
						}
					}
				}else if(terSymbol.find(s[s.find('.')+1]) != -1){//若项目A→α?β属于Ik且GO(Ik,a)=Ij,
					for(int j = 0; j < I_n;j++){//a为终结符,则置ACTION[k,a]为"把(j,a)移进栈",
						if(GO(I[k],s[s.find('.')+1]) == I[j]){//简记为"sj"
							st = to_string(j);
							M[k][terSymbol.find(s[s.find('.')+1])] = "s" + st;
						}
					}
				}
			}
			else{//若GO(Ik,A)=Ij,A为非终结符,,则置GOTO[k,A]=j
				for(int i = terSymbol.length(); i < allSymbol.length();i++)
					for(int j = 0; j < I_n;j++){
						if(GO(I[k],allSymbol[i]) == I[j]){
							st = to_string(j);
							M[k][i] = st;
						}
					}
			}
		}
		cnt++;
	}while(cnt < cd_n);
    cout<<setw(8+allSymbol.length()*6)<<"LR(0)分析表"<<endl;
    cout<<setw(8)<<"状态"<<setw(6*terSymbol.length())<<"       ACTION      "<<setw(6*non_ter.length())<<"   GOTO   "<<endl;
	cout<<setw(8)<<"    ";
	for(int i = 0; i < allSymbol.length(); i++){//表头
		if(i < terSymbol.length()){
			cout<<setw(6)<<allSymbol[i];
		}else{
			cout<<setw(6)<<allSymbol[i];
		}
	}
	cout<<endl;
    for(int i = 0;i < I_n;i++){
		cout<<setw(8)<<i;
        for(int j = 0;j < allSymbol.length();j++)
            cout<<setw(6)<<M[i][j];
        cout<<endl;
    }
	cout<<endl;
}

void allContronl(){//总控程序,LR分析算法描述
    cout<<"待分析的输入串为(‘#’是输入串的结束符):";
    cin>>str_cin;//输入串
    cout<<"LR(0)分析器的工作过程:"<<endl;
    char a;//当前的输入符a
	bool flag = true;
    int j,pos = 0, counter = 0;//pos输入串的指示坐标,counter步骤计数器
    cout<<setw(8)<<std::left<<"步骤"<<setw(12)<<std::left<<"状态栈"<<setw(12)<<std::left<<"符号栈";
    cout<<setw(12)<<std::right<<"输入串"<<"\t"<<setw(12)<<std::left<<"动作"<<endl;
    Cd.push(0);//状态栈置初值
	S.push('#');//符号栈置初值
    cout<<setw(8)<<std::left<<"0"<<setw(12)<<std::left<<Print_Stack(Cd)<<setw(12)<<std::left<<PrintStack(S);
    cout<<setw(12)<<std::right<<PrintStrCin(str_cin,pos)<<"\t"<<setw(12)<<std::left<<"预备"<<endl;
    a = str_cin[pos];//读入第一个符号给a,将S0移进状态栈,#移进符号栈,S为状态栈栈顶状态
	string action;//保存动作
	int x = Cd.top();//状态栈顶
	int y = allSymbol.find(a);
	do{
		if(M[x][y].find('s') != -1){//ACTION(S,a) = si.
			action = "把(,)移进栈";action.insert(action.find(','),1,M[x][y][1]);action.insert(action.find(',')+1,1,a);
			Cd.push(atoi(M[x][y].substr(1)));S.push(a);//PUSH  i,a(分别进栈)
			cout<<setw(8)<<std::left<<++counter<<setw(12)<<std::left<<Print_Stack(Cd)<<setw(12)<<std::left<<PrintStack(S);
    		cout<<setw(12)<<std::right<<PrintStrCin(str_cin,++pos)<<"\t"<<setw(12)<<std::left<<action<<endl;
			a = str_cin[pos];//读入下一个符号给a
		}else if(M[x][y].find('r') != -1){
			int j = atoi(M[x][y].substr(1)); 
			string str0 = gramOldSet[j].formula;
			action = "用产生式"+gramOldSet[j].formula+"进行归约"; 
			str0.erase(0,3);
			for(int i = 0; i < str0.length(); i++){//将状态栈和符号栈分别弹出对应的项
				Cd.pop();
				S.pop();
			}
			S.push(gramOldSet[j].formula[0]);//符号栈压入归约符号
			x = Cd.top();
			y = allSymbol.find(gramOldSet[j].formula[0]);
			Cd.push(atoi(M[x][y].substr(0)));
			cout<<setw(8)<<std::left<<++counter<<setw(12)<<std::left<<Print_Stack(Cd)<<setw(12)<<std::left<<PrintStack(S);
    		cout<<setw(12)<<std::right<<PrintStrCin(str_cin,pos)<<"\t"<<setw(12)<<std::left<<action<<endl;
		}else{
			cout<<"错误"<<endl;
			flag = false;
			break;
		}
		x = Cd.top();//状态栈顶
		y = allSymbol.find(a);
	}while(M[x][y] != "acc");
	if(flag){
		cout<<setw(8)<<std::left<<++counter<<setw(12)<<std::left<<Print_Stack(Cd)<<setw(12)<<std::left<<PrintStack(S);
    	cout<<setw(12)<<std::right<<PrintStrCin(str_cin,pos)<<"\t"<<setw(12)<<std::left<<"acc"<<endl;
		cout<<"\nCongratulation!"<<endl;
	}
}

int main(){
	FILE *stream;
    freopen_s(&stream, "Compiler/project4/test.txt", "r", stdin);
    freopen_s(&stream, "Compiler/project4/result.txt", "w", stdout);
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cout<<"非终结符号如下:  ";
    cin>>non_ter;
	cout<<non_ter<<endl;
    cout<<"终结符号如下:  ";
    cin>>terSymbol;
	cout<<terSymbol<<endl;
    allSymbol = terSymbol + non_ter;
    cout<<"文法产生式的个数:  ";
    cin>>gm_n;
	cout<<gm_n<<endl;
    cout<<endl;
    for(int i = 1;i <= gm_n;i++){
        cout<<"请输入第"<<i<<"产生式:"<<endl;
        cin>>gramOldSet[i].formula;
		cout<<gramOldSet[i].formula<<endl;
    }
    initSet(gramOldSet,gm_n);  
	ITEMSETS();
	cout<<"文法的LR(0)项目集规范族:"<<endl;
	for(int i = 0; i < I_n;i++){
		cout<<"I"<<i<<setw(6)<<std::left<<": ";
		for(auto it:I[i])
			cout<<setw(12)<<std::left<<it;
		cout<<endl;
	}
	dealTable();
	allContronl();
    return 0;
}

2.写出上机调试时发现的问题,以及解决的过程;
问题:定义项目集规范族时进行集合的集合定义,如果要考虑集合的排序规则不能直接定义成unordered_set,须定义成有排序规则的集合,只有这样才能符合C++集合的去重规则。
最后可在数据结构定义相应的去重和排序规则,或者直接定义成set。
问题:在进行分析时符号栈时如何进行转换是矩阵的数字对应
将符号栈定义成整型,自行编写字符串转整型进入符号栈。
3.写出你所使用的测试数据及结果;
测试数据:(注意文件路径)

EAB
abcd
3
E->aA|bB
A->cA|d
B->cB|d
bccd#

实验四 LR(0)分析方法的设计与实现 (8学时)_第1张图片
结果:
实验四 LR(0)分析方法的设计与实现 (8学时)_第2张图片
实验四 LR(0)分析方法的设计与实现 (8学时)_第3张图片
实验四 LR(0)分析方法的设计与实现 (8学时)_第4张图片
4.谈谈你的体会。
LR(0)文法规范族的每一个项目即不含冲突项目,因此,按上法构造的分析表的每个入口都是唯一的(即不含多重定义)。四种项目的类型:“归约项目”、“移进项目”、“待约项目”和“接受项目”,这几种项目类型不同的冲突会一定程度上使LR(0)失效。(读者也可尝试自己定义set的排序依据,这样效率相对高些)(感兴趣的读者也可自行加错误识别打印出对应分析报告表的位置

另附YACC自动实现:(感兴趣可自行学习查阅资料)
手把手教程-lex与yacc/flex与bison入门(一)(使用windows环境)
1.写出编程思路、源代码(或流程图);
/test.y/

%{
#include 
#include 
#include 
int yylex(void);
void yyerror(char *);
%}

%token NUM ADD SUB MUL DIV VAR ERROR CR 

%%
       line_list: line
                | line_list line
                ;
				
	       line : expression CR  {printf("YES\n");}
		   
      expression: term 
                | expression ADD term
				| expression SUB term
                ;

            term: single
				| term MUL single
				| term DIV single
				;
				
		  single: NUM
				| VAR
				;
%%
void yyerror(char *str){
    fprintf(stderr,"error:%s\n",str);
}

int yywrap(){
    return 1;
}
int main()
{
    yyparse();
}

/*test.l*/
%{
#include"y.tab.h"
void yyerror(char *);
%}
LETTER [a-zA-Z]
DIGIT [0-9]
OINTEGER [1-9]{DIGIT}*
INTEGER ("+"|"-")+{OINTEGER}
DECIMAL {INTEGER}(.{DIGIT}*)
FLOAT ({INTEGER}+[.](DIGIT)*)+[Ee]({INTEGER})
MARK ({LETTER}|_)({LETTER}|_|{DIGIT})* 
NUM ({DIGIT}|{OINTEGER}|{INTEGER}|{DECIMAL}|{FLOAT})
ERROR{NUM}+{MARK}
SGPS \/\/.*
DBPS \/\*(.|\n)*\*\/
SPACE (" ")

%%
{NUM}		return NUM;
"+"			return ADD;
"-" 		return SUB;
"*" 		return MUL;
"/" 		return DIV;
{MARK}		return VAR;
{ERROR} 	return ERROR;
\n 			return CR;
({SGPS}|{DBPS}|{SPACE}) /*ignore note*/
[\t]+		/*ignore whitespace*/
%%

2.写出上机调试时发现的问题,以及解决的过程;
问题:YACC中bios与flex共同使用时相应的文件如何排版,flex文件不改进行联合编译时会直接报错提示。需要将相应的flex文件删去下面的主函数,只定义规则。
3.写出你所使用的测试数据及结果
实验四 LR(0)分析方法的设计与实现 (8学时)_第5张图片

你可能感兴趣的:(c语言,开发语言)