编译器-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语言简易文法(无左递归)
<程序> -〉 <外部声明> | <函数定义><外部声明> -〉<头文件> | <变量> | <结构体> <头 ...
随机推荐
- Angular Material 18+ 高级教程 – CDK Drag and Drop
前言 CDK Drag and Drop 和 CDK Scrolling 都是在 Angular Material v7 中推出的. 它们有一个巧妙的共同点,那就是与 Material Design ...
- ASP.NET Core Library – Hangfire
前言 以前写过 Hangfire 的学习笔记, 但写的很乱. 这篇做个整理. 介绍 Hangfire 是用来做 server task 的, 比如: background job, delay job ...
- [rCore学习笔记 026]第三章作业
写在前面 本随笔是非常菜的菜鸡写的.如有问题请及时提出. 可以联系:1160712160@qq.com GitHhub:https://github.com/WindDevil (目前啥也没有 编程题 ...
- linux、unix软链接注意事项
前言 在使用linux过程中,经常使用到软链接(类似windows快捷方式): 创建软链接之后,删除时不注意就会出现到问题 先说结论 删除软链接,确实是使用rm进行删除:但是有个小细节必须要特别注意! ...
- 【译】通过新的 WinUI 工作负荷和模板改进,深入原生 Windows 开发
在 Build 2024 上,WinUI 团队宣布将重新关注 WinUI,将其作为我们推荐的原生 Windows 应用开发的首要应用开发框架之一.为了使其尽可能无缝和轻松地进入编码,我们创建了一个新的 ...
- springboot整合shiro框架详解
在ShiroRealm 中 对所有 引入的service 加上注解 @Lazy ,防止 事务回滚失败.具体原因看该文章 新增整合swagger2,因为之前整合了shiro,所以再访问swagger的时 ...
- iOS中异常处理机制使用小结
在iOS开发中经常会由于数组越界,添加数据为空,通信或者文件错误,内存溢出导致程序终端运行而引入异常处理机制.常用的处理方式是try catch机制.不过有几个专业术语需要解释,异常句柄.异常处理域断 ...
- 使用svn保存markdown文档
使用svn管理md文档时,使用谷歌直接访问出现中文乱码,解析格式错误的问题,可以通过安装谷歌浏览器的插件解决. 一.解决格式解析错误的问题 安装插件 Markdown Preview Plus ,安装 ...
- RPC框架JMH测试-chatgpt自动生成
本文将介绍如何使用Java的JMH测试框架来测试RPC框架的性能.我们选择了Apache Dubbo作为目标RPC框架,Dubbo是一种高效的远程调用框架,它支持多种传输协议和序列化协议,并且具有很好 ...
- 7000元才有的高性能显卡配置,ToDesk云电脑只要不到1块!
高性能显卡不仅仅是游戏玩家的刚需,也是设计师.工程师和剪辑师等专业人士的必备电脑配置.对于追求极致图形处理能力的用户来说,7000元的显卡预算并不罕见.然而,这样的花费对于大多数个人用户和小型企业来说 ...