1. 程式人生 > >【FLA學習筆記】7:基於Opterator Precedence(算符優先)的LL(1)語法分析

【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;
}

執行結果

這裡寫圖片描述

這裡寫圖片描述