【FLA學習筆記】7:基於Opterator Precedence(算符優先)的LL(1)語法分析
阿新 • • 發佈:2019-02-14
簡述
課本第四章的遞迴下降和表驅動都是自頂向下的,即通過推導得到能夠完全匹配輸入串的推導式。
第五章的優先分析法和第六章的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;
}