LR語法分析自動生成程序實驗文檔
<!--[if !supportLists]-->1. <!--[endif]-->在程序中表示文法
<!--[if !supportLists]-->1.1 <!--[endif]-->文法的輸入和讀取
爲了程序讀取的方便,非/終結符相互間以空格分開。
例如
應該輸入:
E -> E + T
T -> T * F | T
F -> ( E ) | id
E -> T
而不是輸入:
E->E+T|T
……
文法先保存到文件中然後程序讀取文件。
<!--[if !supportLists]-->1.2 <!--[endif]-->文法的拓展
爲了在LR分析時能夠指示分析器正確停止並接受輸入,一般在所有輸入文法前加上一個新的產生式,以上面文法爲例,我們要保存的文法應該是如此:
<!--[if !supportEmptyParas]--> <!--[endif]-->
E’ -> E + T
E -> T * F | T
……
<!--[if !supportLists]-->1.3 <!--[endif]-->文法的保存格式
設計一個類Grammar來對文法進行各種處理。
首先把文件中的文法保存到一個序偶表中,以上面的文法爲例子,我們保存的格式類似於下面的表
非終結符 | 產生試右部 |
E | E + T |
T | |
T | T * F |
T | |
F | ( E ) |
id |
<!--[if !supportEmptyParas]--> <!--[endif]-->
也就是說,每一個項是一個2元組,記錄了終結符,和產生式右部。其中非終結符可以用字符串(string)類型表示,產生式右部可用字符串數組( vector<string > )表示。而在保存的同時又可記錄下文法的所有非終結符(因爲文法的產生式左部會出現所有的非終結符),然後再對已經記錄的文法的產生式右部再掃描一遍,記錄下所有的終結符。
在本程序中,我雖然記錄了原始的符號串,但是在具體過程處理時使用的是符號串對應的下標來進行的,因此再對原始形式的文法再掃描一遍,生成對應的以下標形式保存的文法。同時我對終結符號和非終結符號的保存位於不同的數組中,於是下標就會產生衝突,我採用方式是建立一個下標數據結構 Index 來保存下標
struct Index
{
int index; // [非終結符或者終結符的下標]
bool teminal; // [爲真表示該下標保存的是終結符]
bool is_teminal(); //[下標爲終結符則返回true]
}
現在往類Grammar 中加入數據成員爲:
vector< pair< string/*非終結符號*/,vector<string >/*產生式右部*/ > > m_s_grammar ; // [記錄以字符串形式保存的文法]
vector< vector<vector<Index > > > m_idx_grammar;
// [記錄以下標保存的文法]
MyCollection m_s_nonteminals; // [記錄原始的非終結符號串]
MyCollection m_s_terminals; // [記錄原始的終結符號串]
現在已經可以對文法進行保存了,接下來要對文法進行相關處理了。
<!--[if !supportLists]-->2. <!--[endif]-->LR(0)項目規範集的生成
<!--[if !supportLists]-->2.1 <!--[endif]-->LR(0)項目
如果有產生式
E -> A | B
則其對應的所有項目爲
E -> 。A
E –> A 。
E -> 。BZ
E -> B 。
其實就是多了一個插在符號之間的點。
項目的數據結構爲
struct Item
{
int index_of_nont; // [該項目所在的非終結符位置]
int index_of_pro; // [該項目對應該非終結符的產生式位置]
int index_of_dot; // [記錄點的位置]
}
<!--[if !supportEmptyParas]--> <!--[endif]-->
爲了方便(不考慮內存空間的因素),我把所有的項目保存到一個數組中。
<!--[if !supportEmptyParas]--> <!--[endif]-->
Item_0 {0, 0, 0 }數據項 |
Item_1 {0, 0, 1 } |
Item_2 {0, 0, 2 } |
…… |
Item_i {0, m , n } |
…… |
其中數據類型爲
vector<Item > m_items; // [記錄所有的項目]
<!--[if !supportLists]-->2.2 <!--[endif]--> 項目集合及其相關運算
一個項目集合包含了多個項目,也等價於後面要建立的自動機的一個狀態。
本程序中對項目集合中存儲的項目,我採取的也是下標形式存儲。因此項目集合的數據結構爲:
struct Item_c
{
vector<int > index_c;
int operator[](int index);
int size();
bool operator == ();
Item_c& operator = (const Item_c& item);
void push_back(int idx);
void clear();
}
<!--[if !supportEmptyParas]--> <!--[endif]-->
之所以爲項目集合新建一個類來封裝一個數組,是爲了能夠支持相等的語義。因爲在之後要建立自動機時會對狀態進行等價判斷,而狀態的類型估計我會直接這樣表示:
<!--[if !supportEmptyParas]--> <!--[endif]-->
typedef Item_c TypeState; // [一般進行別名定義時,我喜歡在類型前加上Type]
<!--[if !supportEmptyParas]--> <!--[endif]-->
下面我定義一下項目集合的閉包函數, 在這之前我要定義一個輔助函數:
// [返回項目的類型,如果不是規約項目,還返回點的下一位置的符號下標信息]
enum ItemType
{
IT_R = 0; // [規約]
IT_S , // [移進]
IT_A , // [接受]
IT_IVALID // [非法]
}
ItemType get_info_of_next_index(const Item& item, Index& out_index)
{
// [如果點的位置在最後,則表示規約項目]
if(item.index_of_dot>= m_idx_grammar[item.index_of_nont][item.index_of_pro].size() )
{
// [判斷是否爲接受項目]
if(m_idx_grammar[item.index_of_nont][item.index_of_pro][ item.index_of_dot - 1] == this->get_pre_idx_start()
/*獲得開始符號下標,輸入時的而不是擴展後的*/)
{
return IT_A;
}
return IT_R;
}
// [暫時不做下標越界判斷]
out_index =
this->m_idx_grammar[item.index_of_nont][item.index_of_pro][item.index_of_dot];
return IT_S;
}
// [獲得非終結符的項目下標集,條件是項目的點在最前面]
vector<int>& get_itemc_of_nont_dot_at_begin(const Index& index,vector<int>& out_itemc)
{
/*
* [首先獲得該非終結符的項目開始位置]
* */
Item tmp_item(index.index,0,0);
vector<Item>::const_iterator it = find( m_items.begin(),m_items.end(),tmp_item );
if( it != m_items.end() )
{
unsigned int idx_pos = unsigned int(it - m_items.begin());
<!--[if !supportEmptyParas]--> <!--[endif]-->
out_itemc.push_back(idx_pos);
for(unsigned int i=1 ; i<m_idx_grammar[index.index].size(); i++)
{
out_itemc.push_back(idx_pos + m_idx_grammar[index.index][i-1].size()+1);
idx_pos += (int)m_idx_grammar[index.index][i].size()+1;
}
}
return out_itemc;
}
// [獲得項目的閉包]
Item_c& closure(Item_c& itemc)
{
for( int j = 0; j< itemc.size(); j++ )
{
Item item = m_items[itemc[j]];
Index nxt_index;
if( IT_S == get_info_of_next_index(item,nxt_index) )
{
// [如果是非終結符]
if( !nxt_index.is_teminal())
{
vector<int> tmp_itemc;
get_itemc_of_nont_dot_at_begin(nxt_index, tmp_itemc);
for_each(tmp_itemc.begin(),tmp_itemc.end(), item_c.add_item );
}
}
}
}
// [GOTO 運算]
// [首先我要定義個個項目集合表來保存所有的項目集合,然後再對索引進行計算]
vector<Item_c > m_item_c_list;
Item_c& goto(const Item_c& in_item,
const string& label,
const Item_c& out_item)
{
for(unsigned int i=0; i<in_itemc.size(); i++)
{
Index nxt_idx;
if(IT_S== this->get_info_of_next_index(m_items[in_itemc[i]],nxt_idx) )
{
if(nxt_idx == in_idx)
{
/*
* [根據保存的結構可知道,下一個項目的位置是當前項目的位置加一]
* */
out_item.add_item(in_itemc[i] + 1);
}
}
}
/*
* [接着對該項目集合進行閉包運算]
* */
return this->closure(out_item);;
}
<!--[if !supportEmptyParas]--> <!--[endif]-->
接下來可以生成項目集規範族了,生成的方法是:
<!--[if !supportLists]-->1) <!--[endif]-->獲得項目集合item_c中點的位置的下一個符號的下標集合index_collection。
<!--[if !supportLists]-->2) <!--[endif]-->然後對下標集合的每一個下標求goto(item_c, index_collection[i] )
<!--[if !supportLists]-->3) <!--[endif]-->記錄集合之間的狀態轉換信息( DFA )
/*—————————————————————————————————/
* Desc: 生成項目集規範族
* Parm
/—————————————————————————————————*/
void Grammar::gen_itemc()
{
Item_c itemc;
/*
* [首先生成第一個項目集,包含第一個項目]
* */
itemc.add_item(0);
this->closure(itemc);
<!--[if !supportEmptyParas]--> <!--[endif]-->
/*
* [加入項目集表中]
* */
this->m_item_c_list.Add(itemc);
<!--[if !supportEmptyParas]--> <!--[endif]-->
for(unsigned int i=0; i<this->m_item_c_list.size(); i++)
{
const Item_c& cur_itemc = this->m_item_c_list.GetElement(i);
<!--[if !supportEmptyParas]--> <!--[endif]-->
IndexCollection nxt_infos;
this->get_infos_of_next_index(cur_itemc,nxt_infos);
<!--[if !supportEmptyParas]--> <!--[endif]-->
for(unsigned int j=0; j<nxt_infos.size(); j++)
{
const Index& index = nxt_infos.GetElement(j);
int next_pos_of_itmc = this->go_to(i, index);
/*
* [記錄狀態轉換關係]
* */
this->m_dfa.set_next( i, index, next_pos_of_itmc);
}
}
debug_out("項目集規範族");
debug_out(m_item_c_list);
}
<!--[if !supportEmptyParas]--> <!--[endif]-->
<!--[if !supportLists]-->3. <!--[endif]-->First,Follow 集的生成
由於之前我已經完成了 LL1語法器的生成。這裏僅是借鑑了之前的代碼。下面是類的聲明和部分實現
/************************************************************************/
/* Author : 陳正
/* Time : 2005/06/23
/* Desc : 計算文法的 First 集
/************************************************************************/
<!--[if !supportEmptyParas]--> <!--[endif]-->
#include "TypeDefine.h"
#include "Grammar.h"
<!--[if !supportEmptyParas]--> <!--[endif]-->
#include <hash_map>
#include <deque>
using namespace std;
class First
{
public:
typedef Grammar::TypeProductionItem TypeProductionItem;
typedef Grammar::TypeProduction TypeProduction ;
typedef Grammar::TypeProductionList TypeProductionList;
<!--[if !supportEmptyParas]--> <!--[endif]-->
typedef CustomCollection<Index > TypeFirstCollection;
typedef deque<TypeFirstCollection > TypeFirstListCollection;
public:
First(const Grammar& grammar);
~First(void);
<!--[if !supportEmptyParas]--> <!--[endif]-->
/*
* [計算規則的First集]
* */
void Calculate_First();
<!--[if !supportEmptyParas]--> <!--[endif]-->
/*
* [返回First集]
* */
const TypeFirstCollection& GetFirst(const Index& index)const;
TypeFirstCollection& GetFirst(const TypeProductionItem&
grammar,TypeFirstCollection& outC)const;
TypeFirstCollection& GetFirst(const TypeProductionItem::const_iterator begin, const TypeProductionItem::const_iterator end,
TypeFirstCollection& outC)const;
/* —— 2005/06/24 ——
* [返回字符串形式]
* */
string ToString();
protected:
/*
* [計算First]
* */
TypeFirstCollection& Calculate_First(const TypeProductionItem& pro_itm, TypeFirstCollection& firstC);
TypeFirstCollection& Calculate_First(TypeProductionItem::const_iterator begin,
TypeProductionItem::const_iterator end,
TypeFirstCollection& firstC);
TypeFirstCollection& Calculate_First(const TypeProduction& production, TypeFirstCollection& firstC);
TypeFirstCollection& Calculate_First(const Index& index, TypeFirstCollection& firstC);
TypeFirstCollection& Calculate_First(
TypeProductionItem::const_iterator begin,
TypeProductionItem::const_iterator end,
TypeFirstCollection& firstC)const;
protected:
const Grammar& m_grammar;
map<Index,bool> m_has_calculated;
TypeFirstListCollection m_first_of_nonterminal;
TypeFirstListCollection m_first_of_terminal;
};
<!--[if !supportEmptyParas]--> <!--[endif]-->
<!--[if !supportEmptyParas]--> <!--[endif]-->
/************************************************************************/
/* Author : 陳正
/* Time : 2005/06/23
/* Desc : 計算 follow 集
/************************************************************************/
#include "Grammar.h"
#include "First.h"
#include "CustomCollection.h"
<!--[if !supportEmptyParas]--> <!--[endif]-->
#include <deque>
using namespace std;
<!--[if !supportEmptyParas]--> <!--[endif]-->
class Follow
{
public:
typedef Grammar::TypeProductionItem TypeProductionItem;
typedef Grammar::TypeProduction TypeProduction ;
typedef Grammar::TypeProductionList TypeProductionList;
<!--[if !supportEmptyParas]--> <!--[endif]-->
typedef CustomCollection<Index > TypeFollowCollection;
typedef deque<TypeFollowCollection > TypeFollowListCollection;
public:
Follow(const First& _first,const Grammar &_grammar);
~Follow(void);
<!--[if !supportEmptyParas]--> <!--[endif]-->
public:
/*
* [計算規則的Follow集]
* */
void Calculate_Follow();
<!--[if !supportEmptyParas]--> <!--[endif]-->
/*
* [返回某個非終結符的 Follow 集]
* */
const TypeFollowCollection& GetFollow(int vn_index)const;
<!--[if !supportEmptyParas]--> <!--[endif]-->
/* —— 2005/06/24 ——
* [字符串顯示]
* */
string ToString()const;
protected:
/*
* [根據課本第2個規則計算Follow集]
* */
TypeFollowCollection& Calculate_Follow_2nd_rules(const Index& vn_index,TypeFollowCollection& outC);
/*
* [根據第3個規則計算,注意必須先根據第2個規則計算後才能調用此函數]
* */
TypeFollowCollection& Calculate_Follow_3rd_rules(const Index& vn_index,TypeFollowCollection& outC);
protected:
const First& m_first;
const Grammar& m_grammar;
TypeFollowListCollection m_follow_of_nonterminal;
deque<CustomCollection<Index> > m_helper; // [在根據第三個條件求Follow集時輔助]
bool m_has_calculated;
};
<!--[if !supportLists]-->4. <!--[endif]-->SLR分析表的生成
#pragma once
<!--[if !supportEmptyParas]--> <!--[endif]-->
/************************************************************************/
/* Author : 陳正
/* Time : 2005/06/24
/* Desc : SLR 分析表
/************************************************************************/
<!--[if !supportEmptyParas]--> <!--[endif]-->
#include "Grammar.h"
#include "TypeDefine.h"
#include "First.h"
#include "Follow.h"
#include <deque>
#include <vector>
using namespace std;
<!--[if !supportEmptyParas]--> <!--[endif]-->
class SLR_Table
{
public:
typedef deque<map<string, Action > > TypeAction;
typedef deque<map<string, int > > TypeGoto;
public:
SLR_Table(const Grammar& m_grammar,const First& first,const Follow& follow);
~SLR_Table(void);
<!--[if !supportEmptyParas]--> <!--[endif]-->
/* —— 2005/06/24 ——
* [生成SLR 分析表]
* */
bool gen_table(string& error_str);
<!--[if !supportEmptyParas]--> <!--[endif]-->
/* —— 2005/06/24 ——
* [返回GOTO值]
* */
bool get_goto(int ,const Index& index, int& );
<!--[if !supportEmptyParas]--> <!--[endif]-->
/* —— 2005/06/24 ——
* [返回動作表]
* */
bool get_action(int cur,const Index& index, Action& out_action)const;
<!--[if !supportEmptyParas]--> <!--[endif]-->
/* —— 2005/06/24 ——
* [設置動作表]
* */
bool add_to_action_s(int cur,const Index& index,int nxt);
bool add_to_action_r(int index_of_itemc,int index_of_item,const Index& index);
bool add_to_action_a(int cur);
<!--[if !supportEmptyParas]--> <!--[endif]-->
/* —— 2005/06/24 ——
* [設置goto表]
* */
void add_to_goto(int index_of_itemc,const Index& index, int nxt);
<!--[if !supportEmptyParas]--> <!--[endif]-->
/* —— 2005/06/24 ——
* [字符串顯示]
* */
string ToString();
<!--[if !supportEmptyParas]--> <!--[endif]-->
private:
const Grammar& m_grammar;
const First& m_first;
const Follow& m_follow;
TypeAction m_action_table;
TypeGoto m_goto_table;
};
<!--[if !supportEmptyParas]--> <!--[endif]-->
……