编译器-FIRST集合(补充:左递归)
上一篇中实现的First函数没有考虑左递归,在这对此说明和实现
1.立即左递归
A -> Ab|a
1.两步或两步以上产生的左递归
A -> Bc|a
B -> Ab|d
前面的实现中,递归用first函数
public Set<Terminal> first(List<Symbol> tokens) {
first(token)
}
public Set<Terminal> first(Symbol token) {
first(tokens)
}
如果产生式存在左递归就会出现循环调用,得不到结果
那么需要做下面处理
1. 需要记录当前外层有没有处理这个符号,如果有则跳过
2. A -> Bc|a B->Ad | ε
1)first(A)
2)first(Bc) 并 first(a)
3)first(B) frist(c) 并 first(a)
4)first(Ad) frist(ε) first(c) 并 first(a)
5)first(A) first(d) frist(ε) first(c) 并 first(a)
可以看第5步又需要first(A),因为外层正在处理,所以直接返回,但是返回前需要判断A是否为空,
如果不为空,则直接返回,如果为空,则继续first(d)
看两组产生式的结果
A -> Bc|a B->Ad| d | ε A的first集合是 a, d, c, B的first集合 a d c
A -> Bc|a B->Ad | d A的first集合是 a, d, B的first集合 a d
可以看出 A -> Bc 对 frist(A)集合,如果A, B可以推出 ε 那么集合就多了c,否则 A->Bc 不影响 first(A)的结果
所以需要加过一个函数专门用来判断A是否可以推导出ε
/**
* tokens是否可以推导出ε
* @param tokens
* @param expands
* @return
*/
public boolean hasEpsilon(List<Symbol> tokens, Set<Symbol> expands) {
for (Symbol token : tokens) {
if (expands.contains(token)) {
return false;
}
if (Terminal.epsilon.equals(token)) {
return true;
}
if (token instanceof Terminal) {// 终结符直接返回
return false;
} expands.add(token);
boolean hasEpsilon = false;
List<Production> productionList = getProduction((Nonterminal) token);
for (Production production : productionList) {
if (hasEpsilon(production.getBody(), expands)) {
hasEpsilon = true;
break;
}
}
if (!hasEpsilon) {
return false;
}
expands.remove(token);
}
return true;
}
最终First集合函数修改如下
/**
* P140 FIRST(X)集合
* @param tokens 正在计算的串
* @param expands 用于记录正在计算的串是由哪个非终结符展开得到的
* @return
*/
public Set<Terminal> first(List<Symbol> tokens, Set<Symbol> expands) { // 是否全部包含ε
boolean allContainsEpsilon = true;
Set<Terminal> result = new HashSet<>();
for (Symbol token : tokens) { Set<Symbol> epsilonExpands = new HashSet<>();
boolean hasEpsilon = hasEpsilon(Arrays.asList(token), epsilonExpands); Set<Terminal> firstSet = first(token, expands);
if (firstSet == null) {
if (hasEpsilon) {
continue;
} else {
return result;
}
} else {
for (Terminal terminal : firstSet) {
if (terminal.equals(Terminal.of("ε"))) {
continue;
}
result.add(terminal);
} if (!hasEpsilon) {
allContainsEpsilon = false;
break;
}
}
} // 如果对于所有的j=1,2,...,k,ε在FIRST(Yj)中,那么将ε加入到FIRST(X)
if (allContainsEpsilon) {
result.add(Terminal.of("ε"));
} return result;
}
编译器-FIRST集合(补充:左递归)的更多相关文章
- 常用对象API、附加:集合补充
基本数据类型对象包装类: 为了方便操作基本数据类型值,将其封装成了对象,在对象中定义了属性和行为丰富了该数据的操作. 用于描述该对象的类就称为基本数据类型对象包装类. byte——Byte short ...
- Python 之父的解析器系列之五:左递归 PEG 语法
原题 | Left-recursive PEG grammars 作者 | Guido van Rossum(Python之父) 译者 | 豌豆花下猫("Python猫"公众号作者 ...
- python学习笔记:第14天 内置函数补充和递归
一.匿名函数 匿名函数主要是为了解决一些简单需求而设计的一种函数,匿名函数的语法为: lambda 形参: 返回值 先来看一个例子: # 计算n的n次方 In[2]: lst = lambda n: ...
- python 内置函数补充 or 递归 or 二分法
一.内置函数的补充 repr() 显示出字符串的官方表示形式 chr() print(chr(20013)) # 把数字编码转换成字符串 ord() print(ord('中')) # 20013 把 ...
- JavaSE 集合补充点(JDK1.9对集合添加的优化)
通常,我们在代码中创建一个集合(例如,List 或 Set ),并直接用一些元素填充它. 实例化集合,几个 add方法调用,使得代码重复. public class Demo01 { public s ...
- Java 集合补充
集合大致可以分List,Set,Queue,Map四种体系. 集合和数组不一样,数组元素可以是基本类型的值,也可以是对象(的引用变量),集合里只能保存对象(的引用变量). 访问:如果访问List集合中 ...
- 消除左递归c语言文法
<程序> -〉 <外部声明> | <函数定义><外部声明> -〉<头文件> | <变量> | <结构体> <头 ...
- C程序语法(无左递归)
<程序> -〉 <外部声明> | <函数定义><外部声明> -〉<头文件> | <变量> | <结构体> <头 ...
- java集合-补充HashMapJDK1.8
在Java 8 之前,HashMap和其他基于map的类都是通过链地址法解决冲突,它们使用单向链表来存储相同索引值的元素.在最坏的情况下,这种方式会将HashMap的get方法的性能从O(1)降低到O ...
- C语言简易文法(无左递归)
<程序> -〉 <外部声明> | <函数定义><外部声明> -〉<头文件> | <变量> | <结构体> <头 ...
随机推荐
- TypeScript – 冷知识
当 generic return 遇上 parameter 报错了.原因是 querySelector 默认返回类型是抽象的 Element. 而 method 参数要求的是具体的 InputElem ...
- t-io 学习笔记(一)
基础介绍理解篇 序:本文也是在t-io官网学习的基础上写的理解学习笔记:1.什么是t-io? t-io是基于JVM的网络编程框架,和netty属同类,所以netty能做的t-io都能做,考虑到 ...
- JavaScript习题之判断题
1. JavaScript是Java语言的脚本形式.( ) 2. JavaScript中的方法名不区分大小写.( ) 3. JavaScript语句结束时的分号可以省略.( ) 4. 通过外链式引入J ...
- AD域下,没有登录服务器处理登录请求
原因: IP地址配置有问题 或者 DNS : 解决办法: 重新设置 IP地址 和 DNS : 此案例中, 切换到 test 账户(域管理员)后发现 , 未配置 IP地址 和 DNS :
- cpu proc sys文件系统下的含义
proc文件系统(/proc/sys/kernel/) sched_child_runs_first /proc/sys/kernel/sched_child_runs_first是Linux内核中的 ...
- vue项目中的package.json的private选项的作用
{ "name": "项目名称", "description": "描述", "version": ...
- 安装gd库出错
最近手痒,就安装了一个centos 6.5玩玩. 由于新的centos的当前版本已经达到7.1. 而且最糟的是在本人不小心的时候在安装软件的时候, 确定里里面的更新系统命令,导致版本变为了6.9. 然 ...
- Python自带difflib模块
官方文档:https://docs.python.org/3/library/difflib.html difflib模块的作用是比对文本之间的差异,且支持输出可读性比较强的HTML文档,与Linux ...
- Go语言单元测试的执行
Go 语言推荐测试文件和源代码文件放在同一目录下,测试文件以 _test.go 结尾.比如,当前 package 有 calc.go 一个文件,我们想测试 calc.go 中的 Add 和 Mul 函 ...
- (系列十一)Vue3框架中路由守卫及请求拦截(实现前后端交互)
说明 该文章是属于OverallAuth2.0系列文章,每周更新一篇该系列文章(从0到1完成系统开发). 该系统文章,我会尽量说的非常详细,做到不管新手.老手都能看懂. 说明:OverallAuth2 ...