S_型文法到q_型文法再到LL(1)型文法演进笔记
title: S_型文法到q_型文法再到LL(1)型文法演进笔记
date: 2020-08-23
S_型文法到q_型文法再到LL(1)型文法演进笔记
S_型文法(简单的确定性文法)
每个产生式的右部都以终结符开始
同一非终结符的各个候选式的首终结符都不同
针对第一条的理解是,只要右部都是终结符开始,那么对于串当前的读入字符,我们可以很容易的直接根据右部开头的终结符来进行判断匹配,而无需进行产生式的推导;针对第二条的理解是,因为首终结符都不一样,所以根据当前串读入的字符,我们只会匹配到一个确定的产生式。
举例,现有文法如下:
&1. \quad S \rightarrow aBC\\
&2. \quad B \rightarrow b\\
&3. \quad C \rightarrow c\\
\end{aligned}
\]
现有字符串\(abc\),我们从左部第一个字符开始输入,因为字符\(a\)能够被产生式1匹配,因此我们首先选择产生式\(1. \quad S \rightarrow aBC\);接下来输入第二个字符\(b\),于是我们从产生式1到3种进行匹配判断,找到左部非终结符为\(B\)(找\(B\)的原因是上一步我们已经匹配到了产生式1,产生式1的右部现在已经匹配上了\(a\)字符,我们接下来需要推导\(B\),所以要找左部为非终结符\(B\)的产生式),右部首终结符为\(b\)的产生式,于是找到产生式2。以此类推,我们最后能够如下的推导:
\]
个人理解觉得,针对S_型文法的特征是:我们总是能够根据读入的字符,直接匹配找到一个确定的产生式。
然而,该文法的局限性非常的大,最基本的一点就是该文法不包含\(\varepsilon\)的产生式。例如,现有下列文法:
&1. \quad S \rightarrow aBC \\
&2. \quad B \rightarrow bC \\
&3. \quad B \rightarrow dB \\
&4. \quad B \rightarrow \varepsilon \\
&5. \quad C \rightarrow c \\
&6. \quad C \rightarrow a \\
&7. \quad D \rightarrow e \\
\end{aligned}
\]
对于字符串\(ade\),首先还是产生式1没得说;接下来,我们需要寻找的是左部为\(B\),右部首终结符为\(d\)的产生式,于是我们选择产生式3,此时产生式推到得到:
& 1. \Rightarrow a,选择产生式1,得到 aBC \\
& 2. \Rightarrow d,选择产生式3,得到 adBC \\
& 3. \Rightarrow e,因为产生式2、3的首位非终结符不是e,这里选择\varepsilon,即选择产生式4,得到 adC \\
\end{aligned}
\]
因为此时输入字符为\(e\),且当前推导的式子中还存在非终结符\(C\),于是我们继续应用上述规则进行匹配选择(即,我们结束第3轮,进入第4轮准备开始查找),然而我们无法找到形如\(C \rightarrow e...\)的产生式,此时报错。
尽管最终报错了,但是当前的文法存在这样一个问题:因为\(\varepsilon\)产生式的存在,本该在第3轮中就该发现的再无匹配的问题,在第4轮的检查过程中才被发现。为什么说第3轮就该发现呢?我们回到第3轮的检查中,上面说“因为产生式2、3的首位非终结符不是\(e\),我们选择\(B \rightarrow \varepsilon\),但是选择这个产生式真的正确吗?事实上,当产生式右部是\(\varepsilon\)的时候,我们应该要考虑空串之后紧跟着的非终结符是什么,如果我们知道紧跟着的非终结符也和当前的输入符号不匹配的话,我们立刻就能知道选择了空串后的下一步必然是无法匹配的。这里\(\varepsilon\)后面紧跟的等价于\(B\)紧跟的,那么通过产生式1我们知道\(B\)后面紧跟的是\(C\),而\(C\)能够推导出\(c\)和\(a\),故在第3轮中,我们首先知道已经无法选择产生式2和3了,接下来我们判断产生式4是否满足,因为当前的输入\(e\)依然无法和\(c\)、\(a\)匹配,所以在第3轮我们已经无法继续进行下去,就应该报错了。
而我们上面所说的“紧跟着的...”也就是接下来要引入的一个概念:\(FOLLOW\)集。
\(FOLLOW 集\)(后继符号集)
非终结符\(A\)的后继符号集
可能在某个句型中紧跟在\(A\)后边的终结符\(a\)的集合,记为\(FOLLOW(A)\)
\]
有了\(FOLLOW集\)这个东西之后,再回头看待\(\varepsilon\)产生式就变得很明朗了。
如果当前某非终结符\(A\)的产生式右部的首字符与输入\(a\)不匹配的时候,若存在\(A \rightarrow \varepsilon\),则我们检查\(a\)是否存在于\(A\)的后继符号集\(FOLLOW(A)\)中,若在其中,则匹配继续,否则程序报错。
于是,对于上面的判断流程的第3步中,\(FOLLOW(B) = \{a, c\}\),而当前我们遇到的符号是\(e\),不在对应的集中,于是该步骤已经结束。
\(SELECT 集\)(产生式可选集)
我们综合上述的对于非\(\varepsilon\)产生式和\(\varepsilon\)产生式,可以定义产生式的可选集这一概念。
产生式\(A \rightarrow \beta\)的可选集是指可以选用该产生式进行推导时对应的输入符号的集合,记为\(SELECT(A \rightarrow \beta)\)。例如:
\(SELECT(A \rightarrow \alpha\beta) = \{\alpha\}\)
\(SELECT(A \rightarrow \varepsilon) = FOLLOW(A)\)
q_文法
每个产生式的右部或为\(\varepsilon\)或以终结符开始
具有相同左部的产生式有不可相交的可选集
我们称这样的文法为q_型文法。为什么引入q_型文法,因为前面的\(FOLLOW集\)概念的引入,解决\(\varepsilon\)产生式的问题,而这一点可以很好的地对照在q_型文法的第一条定义。尽管它对于S_型文法限制有了略微的放宽,支持了\(\varepsilon\)的产生式,可是还是有一定的限制:q_文法不含右部以非终结符打头的产生式。
在前文中,我们总是在限制产生式的右部,要么首字符是一个终结符,要么是\(\varepsilon\)空串,之所以这样是因为我们总是希望在进行输入的时候右部具有确定的信息(这里就是一个首终结字符)。然而,当右部打头的是非终结符的时候,我们就无从下手了(至少目前是),因为非终结符是不确定的(至少一下子无法知道),它最终能够推导成什么样子的串,似乎变数太多,要是我们事先有一个集合,已经存放好了产生式右部的串(无论它是\(\varepsilon\),还是终结符,还是非终结符打头)的首非终结符,每次进行输入匹配的时候,看一下是不是在这里面,问题就迎刃而解了。于是,我们引入概念:串首终结符集。
串首终结符集
给定一个文法符号串\(\alpha\),\(\alpha\)的串首终结符集\(FIRST(\alpha)\)被定义为:可以从\(\alpha\)推导出的所有串首终结符的集合。此外,如果\(\alpha \Rightarrow^* \varepsilon\),那么\(\varepsilon \in FIRST(\alpha)\)。即:
& 对于 \forall \alpha \in (V_T, V_N)^+,FIRST(\alpha) = \{ a | a\beta, \quad a \in V_T, \quad \beta \in (V_T \cup V_N)^* \} \\
& 如果\alpha \Rightarrow^* \varepsilon,那么\varepsilon \in FIRST(\alpha) \\
\end{aligned}
\]
至于这个串首终结符集如何生成,我们在后文再议。
有了串首终结符集的定义后,我们再次回头看一下产生式\(A \rightarrow \alpha\)的可选集\(SELECT(A \rightarrow \alpha)\)。我们先给出定义:
SELECT(A \rightarrow \alpha) = \begin{cases}
FIRST(\alpha), & \text{if} \quad \varepsilon \notin FIRST(\alpha) \\
(FIRST(\alpha) - \{\varepsilon\}) \cup FOLLOW(A), & \text{if} \quad \varepsilon \in FIRST(\alpha)
\end{cases}
\end{equation*}
\]
我们首先解读这个定义,先看第一种情况,因为\(\varepsilon \notin FIRST(\alpha)\),即\(\alpha\)无法通过任意步骤推导得到空串,即产生式\(A \rightarrow \alpha\)的左部\(A\)是无法推导得到空串,那么该产生式的可选集是\(FIRST(\alpha)\)确实是合理的,试想,现在我有个输入字符c,因为我知道\(\varepsilon \notin FIRST(\alpha)\),那么我检查字符c是不是在\(FIRST(\alpha)\)中就可以做出判断报错还是继续,而无需担心因为左部\(A\)可以产生空串进而还要考虑\(A\)的后继符号集;
对于第二种情况,其实就是把空串的情况也考虑了:不仅仅要判断输入字符是不是在右部的串首终结符集中,还因为左部\(A\)能够推导出空串,而判断是不是在左部的后记符号集中。当然这里排除串首终结符中的空串的含义也是显而易见的,正式因为\(A \rightarrow \alpha \Rightarrow^* \varepsilon\),我们才要判断是否在\(FOLLOW(A)\)中,这里自然要排除空串了。
LL(1)型文法
在上述理论基础上,我们现在将产生式的右部已经扩展到了任意形式了。于是,我们引入LL(1)型文法。它的定义如下:
文法G是LL(1)的,当且仅当G的任意两个具有相同左部的产生式\(A \rightarrow \alpha|\beta\)满足如下的条件:
- 如果\(\alpha\)和\(\beta\)均不能推导出\(\varepsilon\),则\(FIRST(\alpha) \cap FIRST(\beta) = \emptyset\)。
 - \(\alpha\)和\(\beta\)至多有一个能推导出\(\varepsilon\)。如果\(\beta \Rightarrow^* \varepsilon\),则\(FIRST(\alpha) \cap FOLLOW(A) = \emptyset\);如果\(\alpha \Rightarrow^* \varepsilon\),则\(FIRST(\beta) \cap FOLLOW(A) = \emptyset\)。
 
对于情况1,对于\(\alpha\)和\(\beta\)都无法推导出空集的时候,假设两者的串首终结符集的交集不为空,那么对于某个输入字符c,我到底该选择右部为\(\alpha\)的产生式还是\(\beta\)的呢?很显然出现了二义性,所以我们有了交集为空集的条件;
对于情况2,首先解释为什么至多有一个能推到出\(\varepsilon\)。假设都能推导出空集,根据前面产生式的可选集对于右部推导有空集的情况,可选集会包含左部的后记符号集,在这里也就是说,\(SELECT(A \rightarrow \alpha)\)和\(SELECT(A \rightarrow \beta)\)都有\(FOLLOW(A)\),当输入字符c属于\(FOLLOW(A)\)的时候,我们应该选择哪一个产生式呢?于是又出现了二义性。所以我们限定只能至多一个能够推导出\(\varepsilon\)。再进一步,当其中一个右部(这里以\(\beta\)为例)能够推导出\(\varepsilon\),那么\(SELECT(A \rightarrow \beta) = \{FIRST(\beta) - \{\varepsilon\}\} \cup FOLLOW(A)\),而\(\alpha\)无法推导出\(\varepsilon\),所以\(SELECT(A \rightarrow \alpha) = FIRST(\alpha)\),为了保证不会出现二义性,我们需要\(SELECT(A \rightarrow \beta) \cap SELECT(A \rightarrow \alpha) = \emptyset\),这里\(FIRST(\alpha)\)和\(FIRST(\beta)\)的交集不为空是一个隐藏的条件,因为是情况1的情形;于是,我们还需要保证\(FIRST(\alpha) \cap FOLLOW(A) = \emptyset\)。
S_型文法到q_型文法再到LL(1)型文法演进笔记的更多相关文章
- 再探快速傅里叶变换(FFT)学习笔记(其三)(循环卷积的Bluestein算法+分治FFT+FFT的优化+任意模数NTT)
		
再探快速傅里叶变换(FFT)学习笔记(其三)(循环卷积的Bluestein算法+分治FFT+FFT的优化+任意模数NTT) 目录 再探快速傅里叶变换(FFT)学习笔记(其三)(循环卷积的Blueste ...
 - 再起航,我的学习笔记之JavaScript设计模式02
		
我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 我们 ...
 - 再起航,我的学习笔记之JavaScript设计模式01
		
我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 在通 ...
 - 再起航,我的学习笔记之JavaScript设计模式03
		
我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 上一 ...
 - 再起航,我的学习笔记之JavaScript设计模式04
		
我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 上回 ...
 - 再起航,我的学习笔记之JavaScript设计模式05(简单工程模式)
		
我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 前几 ...
 - 再起航,我的学习笔记之JavaScript设计模式06(工厂方法模式)
		
上一次已经给大家介绍了简单工厂模式,相信大家对创建型设计模式有了初步的了解,本次我将给大家介绍的是工厂方法模式. 工厂方法模式 工厂方法模式(Factory Method):通过对产品类的抽象使其创建 ...
 - 再起航,我的学习笔记之JavaScript设计模式14(桥接模式)
		
桥接模式 桥接模式(Bridge): 在系统沿着多个维度变化的同时,又不增加其复杂度并已达到解耦 从定义上看桥接模式的定义十分难以理解,那么我们来通过示例来演示什么是桥接模式. 现在我们需要做一个导航 ...
 - 再起航,我的学习笔记之JavaScript设计模式18(观察者模式)
		
观察者模式 观察者模式(Observer): 又被称为发布-订阅者模式或消息机制,定义了一种依赖关系,解决了主体对象与观察者之间功能的耦合. 创建一个观察者对象 首先我们创建一个闭包对象,让其在页面加 ...
 
随机推荐
- 微信小程序学习笔记二 列表渲染 + 条件渲染
			
1. 列表渲染 1.1 wx:for 在组件上使用wx:for控制属性绑定一个数组, 即可使用数组中各项的数据重复渲染该组件 默认数组的当前项的下标变量名默认为 index, 数组当前项的变量名默认为 ...
 - Linux centos 安装 maven 3.5.4
			
一.maven下载 1.官方下载 打开网址:http://maven.apache.org/download.cgi 下拉滚动条,找到标记处并点击 选择自己想要的版本,我这里选择的是 3.5.4,然后 ...
 - linux安装mysql80
			
打开网址:https://dev.mysql.com/downloads/repo/yum/,选择对应li 安装mysql源 yum -y localinstall mysql80-community ...
 - flink的watermark机制你学会了吗?
			
大家好,今天我们来聊一聊flink的Watermark机制. 这也是flink系列的的第一篇文章,如果对flink.大数据感兴趣的小伙伴,记得点个关注呀. 背景  flink作为先进的流水计算引擎, ...
 - linux centos7 获取开机时间
			
2021-08-03 1. who 命令 who 命令显示关于当前在本地系统上的所有用户信息:登录名,线路,时间,备注 # 列出当前登录本系统的用户 who # 列出本系统的开机/重启时间 who - ...
 - MySQL数据库迁移之data目录
			
其实迁移数据库,一般用sql文件就行,把A服务器数据库的表结构和数据等等导出,然后导入到B服务器数据库, 但是这次数据文件过大,大约有40个G,使用命令行导入,效果不是很好,经常在执行过程中报错.卡死 ...
 - Nginx版本平滑升级方案
			
背景:由于负载均衡测试服务器中nginx版本过低,存在安全漏洞,查询相关修复漏洞资料,需要采取nginx版本升级形式对漏洞进行修复. Nginx平滑升级方案 1.案例采用版本介绍 旧版本 nginx- ...
 - 关于electron-vue打包后静态视频文件无法正常加载的问题解决方法
			
最近在使用electron-builder构建vue项目的时候发现在生产模式下视频可以正常加载并显示,但是一旦打包到开发环境下,视频就读取不出来了,控制台也并没有报错 一开始博主以为是路径问题,在将路 ...
 - C# Dapper基本三层架构使用 (四、Web UI层)
			
三层架构的好处,一套代码无论WinForm还是Web都可以通用,只写前台逻辑就可以了,现在展示Web调用三层的示例 首先在项目中创建一个Web MVC5项目,目前项目目录如下 在Web项目Web.co ...
 - 除PerfDog之外,还有什么性能测试工具。
			
除PerfDog之外,还有什么性能测试工具. 高通的Snapdragon Profiler 下载地址:https://developer.qualcomm.com/software/snapdrago ...