1. 程式人生 > >編譯原理:LL(1)文法 語法分析器(預測分析表法)

編譯原理: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學習,希望可以給大家帶來幫助。