編譯原理:LL(1)文法 語法分析器(預測分析表法)
設計要求:對於任意輸入的一個LL(1)文法,構造其預測分析表,並對指定輸入串分析其是否為該文法的句子。
思路:首先實現集合FIRST(X)構造演算法和集合FOLLOW(A)構造演算法,再根據FIRST和FOLLOW集合構造出預測分析表,並對指定的句子打印出分析棧的分析過程,判斷是否為該文法的句子。
指定文法:
//文法
E->TK
K->+TK
K->$
T->FM
M->*FM
M->$
F->i
F->(E)
對於輸入串i+i*i# ,這裡我們先給出實驗結果截圖:
這個實驗花了我一天的時間,主要難點在於first集和follow集中的遞迴判斷部分,其他的只要按照下面的判斷流程走就可以了。
(1)求FIRST集的演算法思想
如果產生式右部第一個字元為終結符,則將其計入左部first集
如果產生式右部第一個字元為非終結符
->求該非終結符的first集
->將該非終結符的非$first集計入左部的first集
->若存在$,則將指向產生式的指標右移
->若不存在$,則停止遍歷該產生式,進入下一個產生式
->若已經到達產生式的最右部的非終結符,則將$加入左部的first集
處理陣列中重複的first集中的終結符
(2)求FOLLOW集的演算法思想
對於文法G中每個非終結符A構造FOLLOW(A)的辦法是,連續使用下面的規則,直到每個FOLLOW不在增大為止.
(1) 對於文法的開始符號S,置#於FOLLOW(S)中;
(2) 若A->aBb是一個產生式,則把FIRST(b)\{ε}加至FOLLOW(B)中;
(3) 若A->aB是一個產生式,或A->aBb是一個產生式而b=>ε(即ε∈FIRST(b))則把FOLLOW(A)加至FOLLOW(B)中
(3)生成預測分析表的演算法思想
構造分析表M的演算法是:
(1) 對文法G的每個產生式A->a執行第二步和第三步;
(2) 對每個終結符a∈FIRST(a),把A->a加至M[A,a]中;
(3) 若ε∈FIRST(a),則把任何b∈FOLLOW(A)把A->a加至M[A,b]中;
(4) 把所有無定義的M[A,a]標上出錯標誌.
(4)對符號串的分析過程
預測分析程式的總控程式在任何時候都是按STACK棧頂符號X和當前的輸入符號行事的,對於任何(X,a),總控程式
每次都執行下述三種可能的動作之一;
(1) 若X=a=”#”,則宣佈分析成功,停止分析過程.
(2) 若X=a≠”#”,則把X從STACK棧頂逐出,讓a指向下一個輸入符號.
(3) 若X是一個非終結符,則檢視分析表M,若M[A,a]中存放著關於X的一個產生式,那麼,首先把X逐出STACK棧頂,然後
把產生式的右部符號串按反序一一推進STACK棧(若右部符號為ε,則意味著不推什麼東西進棧).在把產生式的右部
符號推進棧的同時應做這個產生式相應得語義動作,若M[A,a]中存放著”出錯標誌”,則調用出錯診察程式ERROR.
在我的程式中,Base類為基類,負責求出FIRST和FOLLOW集;TableStack繼承Base類,求出預測分析表和顯示符號串的分析過程。
Base.h
struct node
{
char left;
string right;
};
class Base
{
protected:
int T;
node analy_str[100]; //輸入文法解析
set<char> first_set[100];//first集
set<char> follow_set[100];//follow集
vector<char> ter_copy; //去$終結符
vector<char> ter_colt;//終結符
vector<char> non_colt;//非終結符
public:
Base() :T(0){}
bool isNotSymbol(char c);
int get_index(char target);//獲得在終結符集合中的下標
int get_nindex(char target);//獲得在非終結符集合中的下標
void get_first(char target); //得到first集合
void get_follow(char target);//得到follow集合
void inputAndSolve(); //處理得到first和follow
void display(); //顯示
};
TableStack.h
class TableStack :public Base
{
protected:
vector<char> to_any; //分析棧
vector<char> left_any;//剩餘輸入串
int tableMap[100][100];//預測表
public:
TableStack(){ memset(tableMap, -1, sizeof(tableMap)); }
void get_table(); //得到預測表
void analyExp(string s); //分析棧的處理
void print_out();//輸出
void getAns(); //結合處理
};
接下來只列出FIRST集中的核心遞迴過程,有詳細註釋:
void Base::get_first(char target)
{
int tag = 0;
int flag = 0;
for (int i = 0; i<T; i++)
{
if (analy_str[i].left == target)//匹配產生式左部
{
if (!isNotSymbol(analy_str[i].right[0]))//對於終結符,直接加入first
{
first_set[get_index(target)].insert(analy_str[i].right[0]);
}
else
{
for (int j = 0; j<analy_str[i].right.length(); j++)
{
if (!isNotSymbol(analy_str[i].right[j]))//終結符結束
{
first_set[get_index(target)].insert(analy_str[i].right[j]);
break;
}
get_first(analy_str[i].right[j]);//遞迴
// cout<<"curr :"<<analy_str[i].right[j];
set<char>::iterator it;
for (it = first_set[get_index(analy_str[i].right[j])].begin(); it != first_set[get_index(analy_str[i].right[j])].end(); it++)
{
if (*it == '$')
flag = 1;
else
first_set[get_index(target)].insert(*it);//將FIRST(Y)中的非$就加入FIRST(X)
}
if (flag == 0)
break;
else
{
tag += flag;
flag = 0;
}
}
if (tag == analy_str[i].right.length())//所有右部first(Y)都有$,將$加入FIRST(X)中
first_set[get_index(target)].insert('$');
}
}
}
}
這個語法分析的全部工程程式碼在我的 上,歡迎大家star學習,希望可以給大家帶來幫助。