简述
课本第四章的递归下降和表驱动都是自顶向下的,即通过推导得到能够完全匹配输入串的推导式。
第五章的优先分析法和第六章的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;
}