【FLA学习笔记】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;
}

运行结果

这里写图片描述

这里写图片描述

猜你喜欢

转载自blog.csdn.net/shu15121856/article/details/80248025