[cp] SLR 分析表的生成以及分析程序 (1)
2005-07-03 14:28
309 查看
由于之前写的发上来后出现了错误的格式,以及一些符号替换,而重写后再发老是超时,因此分成两个小的 :)SLR语法分析自动生成程序实验文档
E’ -> E + TE -> T * F | T……
也就是说,每一个项是一个2元组,记录了终结符,和产生式右部。其中非终结符可以用字符串(string)类型表示,产生式右部可用字符串数组( vector<string > )表示。而在保存的同时又可记录下文法的所有非终结符(因为文法的产生式左部会出现所有的非终结符),然后再对已经记录的文法的产生式右部再扫描一遍,记录下所有的终结符。在本程序中,我虽然记录了原始的符号串,但是在具体过程处理时使用的是符号串对应的下标来进行的,因此再对原始形式的文法再扫描一遍,生成对应的以下标形式保存的文法。同时我对终结符号和非终结符号的保存位于不同的数组中,于是下标就会产生冲突,我采用方式是建立一个下标数据结构 Index 来保存下标
struct Index{ int index; // [非终结符或者终结符的下标]
bool teminal; // [为真表示该下标保存的是终结符]
bool is_nonteminal(); // [返回! terminal]
}
现在往类Grammar 中加入数据成员为:
vector< pair< string,vector<string > > > m_str_grammar ; // [记录以字符串形式保存的文法]
vector< vector<vector<Index > > > m_idx_grammar; // [记录以下标保存的文法]
MyCollection m_str_nonteminals; // [记录原始的非终结符号串]
MyCollection m_str_terminals; // [记录原始的终结符号串]
接下来要对文法进行相关处理了。
int index_of_pro; // [该项目对应该非终结符的产生式位置]
int index_of_dot; // [记录点的位置]
}
基于方便(不考虑优化存储),我把所有的项目保存到一个数组中。
其中数据类型为 vector<Item > m_items; // [记录所有的项目]
* [项目集合]
* */
struct Item_c{ int operator[](int index)const; size_t size()const; void clear(); void push_back(int idx); bool operator == (const Item_c& item)const; Item_c& operator = (const Item_c& item); operator string()const;private: vector<int > index_c; hash_set<int > unique_checker; // [用于快速的消重]};
之所以为项目集合新建一个类来封装一个数组,是为了能够支持相等的语义。因为在之后要建立自动机时会对状态进行等价判断,而状态的类型估计我会直接这样表示:
下面我定义一下项目集合的闭包函数, 在此之前我先定义一个辅助函数:
// [返回项目的类型,如果不是规约项目,还返回点的下一位置的符号下标信息]
enum ItemType{ IT_R = 0; // [规约]
IT_S , // [移进]
IT_A , // [接受]
IT_INVALID // [非法]
}/*————————————————————————————————————————————/
* Desc: 返回项目的类型,如果不是规约项目,返回紧跟着点的符号下标信息
* Parm
/————————————————————————————————————————————*/
ItemType Grammar::get_info_of_next_index(const Item& item, Index& out_index)const{ /*
* [如果点的位置在最后,则表示规约项目]
* */
if(item.index_of_dot >= (int)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;}
/*————————————————————————————————————————————/
* Desc: 若项目A->a。Bb 且B->f 是一个产生式 则返回的项目集中包含[B->。f]
* Parm:
/————————————————————————————————————————————*/
vector<int>& Grammar::get_itemc_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());
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;}/*————————————————————————————————————————————/
* Desc: 获得项目的闭包
* Parm
/————————————————————————————————————————————*/
Item_c& Grammar::closure(Item_c& itemc){ for( unsigned 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_terminal()) { vector<int> tmp_itemc; get_itemc_of_nont_dot_at_begin(nxt_index, tmp_itemc); for(unsigned int x = 0; x<tmp_itemc.size(); x++) itemc.push_back(tmp_itemc[x]); } } } return itemc;}
/*————————————————————————————————————————————/
* Desc: go_to 运算
* Parm -in_item_idx 当前的项目集下标
-in_idx 符号下标
* Ret 当前项目集 遇到 符号 到达的项目集的下标
/————————————————————————————————————————————*/
int Grammar::go_to(const int& in_item_idx, const Index& in_idx){ const Item_c& in_itemc = this->m_item_c_list.GetElement(in_item_idx); 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.push_back(in_itemc[i] + 1); } } } /*
* [对该项目集合进行闭包运算]
* */
this->closure(out_item); /*
* [判断该项目集合是否已存在,并记录对应的下标 goto 关系]
* */
int pos_of_new_itemc = this->m_item_c_list.indexOf(out_item); if(pos_of_new_itemc == this->m_item_c_list.nPos) { this->m_item_c_list.Add(out_item); pos_of_new_itemc = (int)this->m_item_c_list.size() - 1; } return pos_of_new_itemc;}
接下来可以生成项目集规范族了,生成的方法是:1) 获得项目集合item_c中点的位置的下一个符号的下标集合index_collection。2) 然后对下标集合的每一个下标求goto(item_c, index_collection[i] )3) 记录集合之间的状态转换信息( DFA )
/*—————————————————————————————————/
* Desc: 生成项目集规范族
* Parm
/—————————————————————————————————*/
void Grammar::gen_itemc()
{
Item_c itemc;
/*
* [首先生成第一个项目集,包含第一个项目]
* */
itemc.push_back(0);
this->closure(itemc);
/*
* [加入项目集表中]
* */
this->m_item_c_list.Add(itemc);
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);
IndexCollection nxt_infos;
this->get_infos_of_next_index(cur_itemc,nxt_infos);
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);
}
1. 在程序中表示文法
1.1 文法的输入和读取
为了程序读取的方便,非/终结符相互间以空格分开。例如应该输入:E -> E + T T -> T * F | TF -> ( E ) | idE -> T而不是输入:E->E+T|T…… 文法先保存到文件中然后程序读取文件。1.2 文法的拓展
为了在LR分析时能够指示分析器正确停止并接受输入,一般在所有输入文法前加上一个新的产生式,以上面文法为例,我们要保存的文法应该是如此:E’ -> E + TE -> T * F | T……
1.3 文法的保存格式
设计一个类Grammar来对文法进行各种处理。首先把文件中的文法保存到一个序偶表中,以上面的文法为例子,我们保存的格式类似于下面的表非终结符 | 产生试右部 |
E | E + T |
T | |
T | T * F |
T | |
F | ( E ) |
id |
struct Index{ int index; // [非终结符或者终结符的下标]
bool teminal; // [为真表示该下标保存的是终结符]
bool is_nonteminal(); // [返回! terminal]
}
现在往类Grammar 中加入数据成员为:
vector< pair< string,vector<string > > > m_str_grammar ; // [记录以字符串形式保存的文法]
vector< vector<vector<Index > > > m_idx_grammar; // [记录以下标保存的文法]
MyCollection m_str_nonteminals; // [记录原始的非终结符号串]
MyCollection m_str_terminals; // [记录原始的终结符号串]
接下来要对文法进行相关处理了。
2. LR(0)项目规范集的生成
2.1 LR(0)项目
如果有产生式E -> A | B则其对应的所有项目为E -> 。AE –> A 。E -> 。BZE -> B 。其实就是多了一个插在符号之间的点。项目的数据结构为struct Item{ int index_of_nont; // [该项目所在的非终结符位置]int index_of_pro; // [该项目对应该非终结符的产生式位置]
int index_of_dot; // [记录点的位置]
}
基于方便(不考虑优化存储),我把所有的项目保存到一个数组中。
Item_0 {0, 0, 0 }数据项 |
Item_1 {0, 0, 1 } |
Item_2 {0, 0, 2 } |
…… |
Item_i {0, m , n } |
…… |
2.2 项目集合及其相关运算
一个项目集合包含了多个项目,也等价于后面要建立的自动机的一个状态。本程序中对项目集合中存储的项目,我采取的也是下标形式存储。因此项目集合的数据结构为:/** [项目集合]
* */
struct Item_c{ int operator[](int index)const; size_t size()const; void clear(); void push_back(int idx); bool operator == (const Item_c& item)const; Item_c& operator = (const Item_c& item); operator string()const;private: vector<int > index_c; hash_set<int > unique_checker; // [用于快速的消重]};
之所以为项目集合新建一个类来封装一个数组,是为了能够支持相等的语义。因为在之后要建立自动机时会对状态进行等价判断,而状态的类型估计我会直接这样表示:
下面我定义一下项目集合的闭包函数, 在此之前我先定义一个辅助函数:
// [返回项目的类型,如果不是规约项目,还返回点的下一位置的符号下标信息]
enum ItemType{ IT_R = 0; // [规约]
IT_S , // [移进]
IT_A , // [接受]
IT_INVALID // [非法]
}/*————————————————————————————————————————————/
* Desc: 返回项目的类型,如果不是规约项目,返回紧跟着点的符号下标信息
* Parm
/————————————————————————————————————————————*/
ItemType Grammar::get_info_of_next_index(const Item& item, Index& out_index)const{ /*
* [如果点的位置在最后,则表示规约项目]
* */
if(item.index_of_dot >= (int)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;}
/*————————————————————————————————————————————/
* Desc: 若项目A->a。Bb 且B->f 是一个产生式 则返回的项目集中包含[B->。f]
* Parm:
/————————————————————————————————————————————*/
vector<int>& Grammar::get_itemc_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());
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;}/*————————————————————————————————————————————/
* Desc: 获得项目的闭包
* Parm
/————————————————————————————————————————————*/
Item_c& Grammar::closure(Item_c& itemc){ for( unsigned 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_terminal()) { vector<int> tmp_itemc; get_itemc_of_nont_dot_at_begin(nxt_index, tmp_itemc); for(unsigned int x = 0; x<tmp_itemc.size(); x++) itemc.push_back(tmp_itemc[x]); } } } return itemc;}
/*————————————————————————————————————————————/
* Desc: go_to 运算
* Parm -in_item_idx 当前的项目集下标
-in_idx 符号下标
* Ret 当前项目集 遇到 符号 到达的项目集的下标
/————————————————————————————————————————————*/
int Grammar::go_to(const int& in_item_idx, const Index& in_idx){ const Item_c& in_itemc = this->m_item_c_list.GetElement(in_item_idx); 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.push_back(in_itemc[i] + 1); } } } /*
* [对该项目集合进行闭包运算]
* */
this->closure(out_item); /*
* [判断该项目集合是否已存在,并记录对应的下标 goto 关系]
* */
int pos_of_new_itemc = this->m_item_c_list.indexOf(out_item); if(pos_of_new_itemc == this->m_item_c_list.nPos) { this->m_item_c_list.Add(out_item); pos_of_new_itemc = (int)this->m_item_c_list.size() - 1; } return pos_of_new_itemc;}
接下来可以生成项目集规范族了,生成的方法是:1) 获得项目集合item_c中点的位置的下一个符号的下标集合index_collection。2) 然后对下标集合的每一个下标求goto(item_c, index_collection[i] )3) 记录集合之间的状态转换信息( DFA )
/*—————————————————————————————————/
* Desc: 生成项目集规范族
* Parm
/—————————————————————————————————*/
void Grammar::gen_itemc()
{
Item_c itemc;
/*
* [首先生成第一个项目集,包含第一个项目]
* */
itemc.push_back(0);
this->closure(itemc);
/*
* [加入项目集表中]
* */
this->m_item_c_list.Add(itemc);
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);
IndexCollection nxt_infos;
this->get_infos_of_next_index(cur_itemc,nxt_infos);
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);
}
相关文章推荐
- [cp] SLR分析表的生成 以及分析程序 (2)
- [cp] SLR分析表的生成 以及分析程序(3)
- 我的SLR(1)分析表生成程序【Python】
- 我的SLR(1)分析表生成程序【Python】
- [cp] SLR分析表的生成
- 程序运行 栈帧分析 以及 修改栈帧中数据以及函数地址
- 【JavaWeb开发(二)】第一个jsp程序以及web.xml分析
- 使用dbghelp生成dump文件以及事后调试分析
- 豆瓣电视剧评论的爬取以及进行情感分析+生成词云
- 【Storm】storm安装、配置、使用以及Storm单词计数程序的实例分析
- 分析DFM文件生成程序界面
- C/C++程序编译运行生成过程分析
- 在线制作一键生成微信小程序实现原理之需求分析
- C/C++程序从编译到最终生成可执行文件的过程分析
- 使用dbghelp生成dump文件以及事后调试分析
- Tga图片格式分析以及程序实现
- 利用ANTLR生成C++描述的分析程序.doc
- C/C++程序编译运行生成过程分析
- Eclipse中的快捷键快速生成常用代码(例如无参、带参构造,set、get方法),以及Java中重要的内存分析(栈、堆、方法区、常量池)
- 在linux(suse)下利用ANTLR生成C++描述的分析程序