S_型文法到q_型文法再到LL(1)型文法演进笔记
S_型文法到q_型文法再到LL(1)型文法演进笔记
S_型文法(简单的确定性文法)
-
每个产生式的右部都以终结符开始
-
同一非终结符的各个候选式的首终结符都不同
针对第一条的理解是,只要右部都是终结符开始,那么对于串当前的读入字符,我们可以很容易的直接根据右部开头的终结符来进行判断匹配,而无需进行产生式的推导;针对第二条的理解是,因为首终结符都不一样,所以根据当前串读入的字符,我们只会匹配到一个确定的产生式。
举例,现有文法如下:
现有字符串,我们从左部第一个字符开始输入,因为字符能够被产生式1匹配,因此我们首先选择产生式;接下来输入第二个字符,于是我们从产生式1到3种进行匹配判断,找到左部非终结符为(找的原因是上一步我们已经匹配到了产生式1,产生式1的右部现在已经匹配上了字符,我们接下来需要推导,所以要找左部为非终结符的产生式),右部首终结符为的产生式,于是找到产生式2。以此类推,我们最后能够如下的推导:
个人理解觉得,针对S_型文法的特征是:我们总是能够根据读入的字符,直接匹配找到一个确定的产生式。
然而,该文法的局限性非常的大,最基本的一点就是该文法不包含的产生式。例如,现有下列文法:
对于字符串,首先还是产生式1没得说;接下来,我们需要寻找的是左部为,右部首终结符为的产生式,于是我们选择产生式3,此时产生式推到得到:
因为此时输入字符为,且当前推导的式子中还存在非终结符,于是我们继续应用上述规则进行匹配选择(即,我们结束第3轮,进入第4轮准备开始查找),然而我们无法找到形如的产生式,此时报错。
尽管最终报错了,但是当前的文法存在这样一个问题:因为产生式的存在,本该在第3轮中就该发现的再无匹配的问题,在第4轮的检查过程中才被发现。为什么说第3轮就该发现呢?我们回到第3轮的检查中,上面说“因为产生式2、3的首位非终结符不是,我们选择,但是选择这个产生式真的正确吗?事实上,当产生式右部是的时候,我们应该要考虑空串之后紧跟着的非终结符是什么,如果我们知道紧跟着的非终结符也和当前的输入符号不匹配的话,我们立刻就能知道选择了空串后的下一步必然是无法匹配的。这里后面紧跟的等价于紧跟的,那么通过产生式1我们知道后面紧跟的是,而能够推导出和,故在第3轮中,我们首先知道已经无法选择产生式2和3了,接下来我们判断产生式4是否满足,因为当前的输入依然无法和、匹配,所以在第3轮我们已经无法继续进行下去,就应该报错了。
而我们上面所说的“紧跟着的...”也就是接下来要引入的一个概念:集。
(后继符号集)
非终结符的后继符号集
可能在某个句型中紧跟在后边的终结符的集合,记为
有了这个东西之后,再回头看待产生式就变得很明朗了。
如果当前某非终结符的产生式右部的首字符与输入不匹配的时候,若存在,则我们检查是否存在于的后继符号集中,若在其中,则匹配继续,否则程序报错。
于是,对于上面的判断流程的第3步中,,而当前我们遇到的符号是,不在对应的集中,于是该步骤已经结束。
(产生式可选集)
我们综合上述的对于非产生式和产生式,可以定义产生式的可选集这一概念。
产生式的可选集是指可以选用该产生式进行推导时对应的输入符号的集合,记为。例如:
q_文法
每个产生式的右部或为或以终结符开始
具有相同左部的产生式有不可相交的可选集
我们称这样的文法为q_型文法。为什么引入q_型文法,因为前面的概念的引入,解决产生式的问题,而这一点可以很好的地对照在q_型文法的第一条定义。尽管它对于S_型文法限制有了略微的放宽,支持了的产生式,可是还是有一定的限制:q_文法不含右部以非终结符
打头的产生式。
在前文中,我们总是在限制产生式的右部,要么首字符是一个终结符,要么是空串,之所以这样是因为我们总是希望在进行输入的时候右部具有确定的信息(这里就是一个首终结字符)。然而,当右部打头的是非终结符的时候,我们就无从下手了(至少目前是),因为非终结符是不确定的(至少一下子无法知道),它最终能够推导成什么样子的串,似乎变数太多,要是我们事先有一个集合,已经存放好了产生式右部的串(无论它是,还是终结符,还是非终结符打头)的首非终结符,每次进行输入匹配的时候,看一下是不是在这里面,问题就迎刃而解了。于是,我们引入概念:串首终结符集。
串首终结符集
给定一个文法符号串,的串首终结符集被定义为:可以从推导出的所有串首终结符的集合。此外,如果,那么。即:
至于这个串首终结符集如何生成,我们在后文再议。
有了串首终结符集的定义后,我们再次回头看一下产生式的可选集。我们先给出定义:
我们首先解读这个定义,先看第一种情况,因为,即无法通过任意步骤推导得到空串,即产生式的左部是无法推导得到空串,那么该产生式的可选集是确实是合理的,试想,现在我有个输入字符c,因为我知道,那么我检查字符c是不是在中就可以做出判断报错还是继续,而无需担心因为左部可以产生空串进而还要考虑的后继符号集;
对于第二种情况,其实就是把空串的情况也考虑了:不仅仅要判断输入字符是不是在右部的串首终结符集中,还因为左部能够推导出空串,而判断是不是在左部的后记符号集中。当然这里排除串首终结符中的空串的含义也是显而易见的,正式因为,我们才要判断是否在中,这里自然要排除空串了。
LL(1)型文法
在上述理论基础上,我们现在将产生式的右部已经扩展到了任意形式了。于是,我们引入LL(1)型文法。它的定义如下:
文法G是LL(1)的,当且仅当G的任意两个具有相同左部的产生式满足如下的条件:
- 如果和均不能推导出,则。
- 和至多有一个能推导出。如果,则;如果,则。
对于情况1,对于和都无法推导出空集的时候,假设两者的串首终结符集的交集不为空,那么对于某个输入字符c,我到底该选择右部为的产生式还是的呢?很显然出现了二义性,所以我们有了交集为空集的条件;
对于情况2,首先解释为什么至多有一个能推到出。假设都能推导出空集,根据前面产生式的可选集对于右部推导有空集的情况,可选集会包含左部的后记符号集,在这里也就是说,和都有,当输入字符c属于的时候,我们应该选择哪一个产生式呢?于是又出现了二义性。所以我们限定只能至多一个能够推导出。再进一步,当其中一个右部(这里以为例)能够推导出,那么,而无法推导出,所以,为了保证不会出现二义性,我们需要,这里和的交集不为空是一个隐藏的条件,因为是情况1的情形;于是,我们还需要保证。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!