【编译原理】语法分析LL(1)分析法的FIRST和FOLLOW集
2016-07-06 16:20
417 查看
近来复习编译原理,语法分析中的自上而下LL(1)分析法,需要构造求出一个文法的FIRST和FOLLOW集,然后构造分析表,利用分析表+一个栈来做自上而下的语法分析(递归下降/预测分析),可是这个FIRST集合FOLLOW集看得我头大。。。
教课书上的规则如下,用我理解的语言描述的:
老师和同学能很敏锐地求出来,而我只能按照规则,像程序一样一条条执行。于是我把这个过程写成了程序,如下:
数据元素的定义:
主函数的流程很简单,从文件读入指定格式的文法,然后依次求文法的FIRST集、FOLLOW集
put_follow_follow
运行结果(请忽略集合最后一个元素后的逗号。。。):
注:
1. 语法分析的每个终结符号实际上代表一个单词,是从词法分析器获取的,这里为了简化问题所以只用了一个char型表示;而每个非终结符号则是一个语法单元,这里同样用char型表示了;
2. 感觉我的实现稍显复杂,C++的集合操作不太会用(没有找到原生的类似.addAll这样的方法,所以是自己用迭代器一个个加的),考完试用其他语言实现一个更简洁的。
3. 这样的算法用程序实现并不复杂,但是它规则比较多,且退出的条件是“集合不再增大”,手算起来一轮一轮的容易乱。祝我期末好运吧。
教课书上的规则如下,用我理解的语言描述的:
任意符号α的FIRST集求法: 1. α为终结符,则把它自身加入FIRSRT(α) 2. α为非终结符,则: (1)若存在产生式α->a...,则把a加入FIRST(α),其中a可以为ε (2)若存在一串非终结符Y1,Y2, ..., Yk-1,且它们的FIRST集都含空串,且有产生式α->Y1Y2...Yk...,那么把FIRST(Yk)-{ε}加入FIRST(α)。如果k-1抵达产生式末尾,那么把ε加入FIRST(α) 注意(2)要连续进行,通俗地描述就是:沿途的Yi都能推出空串,则把这一路遇到的Yi的FIRST集都加进来,直到遇到第一个不能推出空串的Yk为止。 重复1,2步骤直至每个FIRST集都不再增大为止。
任意非终结符A的FOLLOW集求法: 1. A为开始符号,则把#加入FOLLOW(A) 2. 对于产生式A-->αBβ: (1)把FIRST(β)-{ε}加到FOLLOW(B) (2)若β为ε或者ε属于FIRST(β),则把FOLLOW(A)加到FOLLOW(B) 重复1,2步骤直至每个FOLLOW集都不再增大为止。
老师和同学能很敏锐地求出来,而我只能按照规则,像程序一样一条条执行。于是我把这个过程写成了程序,如下:
数据元素的定义:
const int MAX_N = 20;//产生式体的最大长度 const char nullStr = '$';//空串的字面值 typedef int Type;//符号类型 const Type NON = -1;//非法类型 const Type T = 0;//终结符 const Type N = 1;//非终结符 const Type NUL = 2;//空串 struct Production//产生式 { char head; char* body; Production(){} Production(char h, char b[]){ head = h; body = (char*)malloc(strlen(b)*sizeof(char)); strcpy(body, b); } bool operator<(const Production& p)const{//内部const则外部也为const if(head == p.head) return body[0] < p.body[0];//注意此处只适用于LL(1)文法,即同一VN各候选的首符不能有相同的,否则这里的小于符号还要向前多看几个字符,就不是LL(1)文法了 return head < p.head; } void print() const{//要加const printf("%c -- > %s\n", head, body); } }; //以下几个集合可以再封装为一个大结构体--文法 set<Production> P;//产生式集 set<char> VN, VT;//非终结符号集,终结符号集 char S;//开始符号 map<char, set<char> > FIRST;//FIRST集 map<char, set<char> > FOLLOW;//FOLLOW集 set<char>::iterator first;//全局共享的迭代器,其实觉得应该用局部变量 set<char>::iterator follow; set<char>::iterator vn; set<char>::iterator vt; set<Production>::iterator p; Type get_type(char alpha){//判读符号类型 if(alpha == '$') return NUL;//空串 else if(VT.find(alpha) != VT.end()) return T;//终结符 else if(VN.find(alpha) != VN.end()) return N;//非终结符 else return NON;//非法字符 }
主函数的流程很简单,从文件读入指定格式的文法,然后依次求文法的FIRST集、FOLLOW集
void put_follow_follow(char B, char A){//把FOLLOW[A]加到FOLLOW[B] follow = FOLLOW[A].begin(); for(; follow != FOLLOW[A].end(); follow++){ FOLLOW[B].insert(*follow); } }
put_follow_follow
运行结果(请忽略集合最后一个元素后的逗号。。。):
注:
1. 语法分析的每个终结符号实际上代表一个单词,是从词法分析器获取的,这里为了简化问题所以只用了一个char型表示;而每个非终结符号则是一个语法单元,这里同样用char型表示了;
2. 感觉我的实现稍显复杂,C++的集合操作不太会用(没有找到原生的类似.addAll这样的方法,所以是自己用迭代器一个个加的),考完试用其他语言实现一个更简洁的。
3. 这样的算法用程序实现并不复杂,但是它规则比较多,且退出的条件是“集合不再增大”,手算起来一轮一轮的容易乱。祝我期末好运吧。
相关文章推荐
- 是什么原因导致了技术团队对需求理解的不到位?
- 维护没有源代码,float改成double
- 初始化小结
- 准确率与召回率
- 深入解析Radix Sort基数排序算法思想及C语言实现示例
- [leetcode] 371. Sum of Two Integers
- 排座位并查集
- C语言变长数组 struct中char data[0]的用法
- Java socket编程
- linux常用端口
- 博客新地址上线
- Some current MySQL Architecture writings
- 正益移动王国春:布局在是与不是之间
- BAT三家互联网公司哪家更注重用户体验?
- loading 遮罩层
- 图表部件设置X轴的旋转显示
- Android学习之七牛(二)——初始化设置和上传文件基本用法
- MongoDB启动和停止
- java8中新的Date和Time详解
- IOS上传图片到服务器