【编译原理学习笔记】7:基于Opterator Precedence(算符优先)的LL(1)语法分析

简述

课本第四章的递归下降和表驱动都是自顶向下的,即通过推导得到能够完全匹配输入串的推导式。

第五章的优先分析法和第六章的LR分析是自底向上的(规约得到开始符)。

第五章的简单优先分析法是规范规约(左规约),而算符优先分析法不是规范规约,并且只考虑终结符之间的优先关系。

OPG文法

算符优先分析法,需要文法是一个OPG算符优先文法。GPG文法首先是一个OG文法。

G为OG文法当且仅当G中无形如A->…BC…的产生式,B和C属于VN。

性质1:OG文法的任何句型无相邻非终结符。

性质2:若A属于VN和b属于VT组成的Ab或者bA出现在OG文法的句型中,则该句型中任何含b的短语必含有A。即b必须和A一起规约,不过A不必一定和b一起规约(如bAcA)。

当一个OG文法,不含epsilon,而且对任一终结符对(a,b)至多只有<·,·>,=·=之一成立,才是OPG算符优先文法。

算符优先级表构造过程

使用两个集合:

FIRSTVT(B)={b|B=+=>b...或B=+=>Cb...}
LASTVT(B)={a|B=+=>a...或B=+=>...aC}

具体的用法后面题里做。

题目与分析

题目:

设有文法G[S]:S→SaF | F  F→FbP | P   P→c | d
(1) 构造G[S]的算符优先关系表
(2) 分别给出cadbdac# 和 dbcabc# 的分析过程

判<·关系(看A->…aB…有a<·all in FIRSTVT(B)):

FIRSTVT(S)={a,b,c,d}		=>null
FIRSTVT(F)={b,c,d}		=>a<·{b,c,d}
FIRSTVT(P)={c,d}		=>b<·{c,d}

判·>关系(看A->…Ba…有all in LASTVT(B)·>a):

LASTVT(S)={a,b,c,d}		=>{a,b,c,d}>a
LASTVT(F)={b,c,d}		=>{b,c,d}·>b
LASTVT(P)={c,d}		=>null

得到算符优先关系表:
这里写图片描述
依此可以得到两个输入串的分析过程:
这里写图片描述

这里写图片描述

程序实现

#include<iostream>
#include<string>
#include<list>
#include<set>
#include<cstdio>
using namespace std;
//终结符(关注的符号) 
char Vt[4]={'a','b','c','d'};
set<char> st_Vt(Vt,Vt+4);
//可以出现的规约形式
string okRdc[4]={"RaR","RbR","c","d"};
#define LENOKRDC 4
//set<string> st_okRdc(okRdc,okRdc+4);
//原始串 
char chr[100],c;//c用作当前输入符 
//原始串长度 
int lenStr;
//记录至今为止仍然没有出错
bool psr=true; 
//当前在第几步
int numNow=0; 
//模拟栈S 
list<char> ls_S;
//模拟栈输入串剩余部分
list<char> ls_lgcy;
//临时char数组
char ext[10]; 

//删除条件 
bool allDel(const char& c){
	return '|'==c;
}

//比较,小于时为-1,大于时为+1,否则0 
int cmp(char x,char y){
	if('#'==x && '#'==y)
		return 0;
	if('#'==x)
		return -1;
	if('#'==y)
		return 1;
	if('a'==y)//刷掉一列 
		return 1;
	if('a'==x)//刷掉一行 
		return -1;
	if('b'==y)//刷掉一列 
		return  1;
	if('b'==x)//刷掉一行 
		return -1;
	return 3;//灰色出错区域  
}

//算符优先语法分析,返回是否接受  
bool parse(){
	//[接受]状态判定 
	if('#'==c){
		//判定栈S里只剩头'#'终结符 
		char ch;
		for(list<char>::iterator it=ls_S.begin();it!=ls_S.end();it++)
			if(*it<'A' || *it>'Z')
				ch=*it;
		if('#'==ch)//接受 
		{
			cout<<"接受\n";
			return true;		
		}
	}
	//找并记录比较的位置迭代器,计算比较值
	list<char>::iterator it_cmper;
	for(--(it_cmper=ls_S.end());it_cmper!=ls_S.begin();it_cmper--){
		if(*it_cmper<'A' || *it_cmper>'Z')
			break;
	}
	int cmprd=cmp(*it_cmper,c);
	//<·和=·时移进 
	if(0>=cmprd){
		//c压入栈S 
		ls_S.push_back(c);
		//替换c 
		c=ls_lgcy.front();
		//遗留栈去掉一个(给了c) 
		ls_lgcy.pop_front();
		cout<<"移进";
	}
	//·>时规约 
	else if(1==cmprd){
		cout<<"规约:";
		//找到规约开始位置 
		list<char>::iterator it_end,it_nxt;//最后一个游标,上一个游标 
		for(it_end=it_cmper;it_end!=ls_S.begin();it_end--){
			 //确保是终结符 
			 if(*it_end<'A' || *it_end>'Z'){
			 	//找其前的上一个终结符游标
				for(--(it_nxt=it_end);it_nxt!=ls_S.begin();it_nxt--){
					if(*it_nxt<'A' || *it_nxt>'Z')
						break;
				}
				//成功找到了前一个非终结符,而且是小于关系 
				if(it_nxt!=it_end && -1==cmp(*it_nxt,*it_end)){
					//要规约的部分检查,先转储到string 
					list<char>::iterator it_rd=it_nxt;
					it_rd++;
					int i_ext=0;
					for(;it_rd!=it_end;it_rd++){
//						cout<<*it_rd<<" ";
						ext[i_ext++]=*it_rd;
						//在这里顺便把它变成'|',以在后面删去 
						*it_rd='|';//和后面规约时很耦合 
					}
//					cout<<*it_rd;
					ext[i_ext++]=*it_rd;
					//考虑它后面的一位要不要跟着规约
					if(++it_rd!=ls_S.end() && *it_rd>='A' && *it_rd<='Z'){
						it_rd--;//先回来 
						*it_rd='|';//把这里变成一会要删掉的 
						it_rd++;//再过去 
						ext[i_ext++]=*it_rd;//把其后的那个记录下来 
					}else{
						it_rd--;//如果不要,多此一举,回来 
					}
					//在这里顺便把它变成'R',后面真的规约时候就不用规约了
					*it_rd='R';//和后面规约时很耦合 
					ext[i_ext]='\0';
					string s(ext);
					bool canRdc=false;//记录是否能成功规约 
					//在string数组里匹配 
					for(int i=0;i<LENOKRDC;i++){
						if(s==okRdc[i]){
							canRdc=true;
							break;
						}
					}
					//存在该形式的规约,前面已经耦合了变换 
					if(canRdc){
						//按条件删去
						//ls_S.remove_if(it_rd,it_end,[](char c)->bool{return true;});
						ls_S.remove_if(allDel);//这个才是对的 
						*it_end='R';//规约成R,算符优先分析不必考虑非终结符符号 
						cout<<"允许";
					}
					else{
						psr=false;
						cout<<"不存在该形式的规约";
						cout<<"\n错误的规约形式:"<<s<<endl;
					}	
					//FIXME
					break;
				}
			 }
		}
	}
	//出错 
	else{
		psr=false;
		cout<<"出错";
	}
	return false;
}


int main(){
	cout<<"请输入要推导的句子:\n\t";
	for(lenStr=0;(c=getchar())!='\n';lenStr++){
		if(0==st_Vt.count(c)){
			cout<<"必须输入合法的终结符序列!"<<endl;
			return 0;
		}
		ls_lgcy.push_back(c);
	}
	ls_lgcy.push_back('#');
	//表头
	cout<<"   步骤   |"<<"     栈S     |"<<" 当前输入符 |"<<" 输入串剩余部分 |"<<"  动作"<<endl; 
	//初始化 
	ls_S.push_back('#');
	c=ls_lgcy.front();
	ls_lgcy.pop_front();
	//循环 
	while(true==psr){
		printf("%6d    | ",++numNow);
		for(list<char>::iterator it=ls_S.begin();it!=ls_S.end();it++)
            cout<<*it;
        for(int i=ls_S.size();i<12;i++)
        	cout<<" ";
        cout<<"|      "<<c<<"     |";
		for(int i=ls_lgcy.size();i<15;i++)
			cout<<" ";
		for(list<char>::iterator it=ls_lgcy.begin();it!=ls_lgcy.end();it++)
            cout<<*it;
        cout<<" |  ";
        //解析,当[接受]时退出 
        if(true==parse())
        	break;
        cout<<endl;
	}
	return 0;
}

运行结果

这里写图片描述

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值