语法分析]
Posted zy691357966
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了语法分析]相关的知识,希望对你有一定的参考价值。
[编译原理读书笔记][第4章 语法分析]
标签(空格分隔): 未分类
首先介绍概念,然后介绍适合手工实现的技术,最后介绍用于自动化的工具的算法,然后因为经常包含语法错误,讨论如何扩展语法分析方法,以便从常见错误中恢复.
- 文法给出了一个程序设计语言的精确易懂的语法规约
- 对于某些类型的文法,我们可以自动的构造出高效的语法分析器
4.1 引论
4.1.1 语法分析器的作用
语法分析器的作用是构造出语法分析树
- 实际上,并不需要显式地构造,如之前几章看见,使用动作,可以跟语义分析和中间代码生成结合成一个模块.
处理文法的语法分析器大体上可以分为三种类型的:通用的,自顶向下的,自底向上的
- 通用的:
Cocke-Younger-Kasami
算法,Earley
算法,效率很低. - LL 手工
- LR 自动
- 通用的:
4.1.2 代表性的文法
主要关注表达式,因为运算符的优先级和结合性,使得表达式的处理更具挑战性.
(4.1)
- 属于LR文法,适用于自底向上的语法分析技术.
- 经过修改能处理更多优先级
- 不能用自顶向下,因为是左递归的.
下面给出无左递归版本. (4.2)
在给出一个具有二义性的文法,将来用于说明语法分析过程处理二义性的技术
4.1.3 语法错误的处理
本节将考虑语法错误的本质,以及错误恢复的一些策略,
- 其中两种策略被称为恐慌模式,短语层次恢复,
- 将和特定的语法分析方法讨论
程序有不同层次的错误
- 词法错误:标识符,关键词拼写错误,没有在字符串文本加入正确的引号.
- 语法错误: 分号放错地方,括号不匹配.
- 语义错误: 运算符和类型的不匹配
- 逻辑错误: 比如要使用
==
地方,使用=
语法分析方法的精确性让我们能高效检测语法错误,有些语法分析方法,比如LL
和LR
方法,都能第一时间发现错误.
- 编译的时候,语义错误和逻辑错误很难被发现.
语法分析器的目标简单,但是实现很麻烦.
- 清晰精确的报告出现的错误
- 尽快从错误中恢复,检查后面的错误.
- 尽可能少的减少检测正确程序的开销.
4.1.4 错误的恢复策略
- 比较普遍的想法是检测到错误就退出.
- 比较高级的想法是从错误中恢复,以检查后面更多的错误.
恐慌模式的恢复
- 使用这个方法,语法分析器一旦发现错误就不断丢弃输入中的符号,一次丢弃一个,直到找到同步词法单元集合中的某个元素.
同步词法单元
通常是界限符,比如分号和.
- 编译器的设计者,必须挑选适当的界限符.
- 恐慌模式会跳过大量输入.
- 但是他很简单,且不会陷入无限循环.
短语层次的恢复
- 当发现一个错误的时候,语法分析器可以在余下的输入上进行局部性纠正
- 常用的纠正方法包括将一个逗号替换为分号
- 删除一个多余的分号,或者插入一个遗漏的分号.
- 小心使用替换,以免无限循环
错误产生式
- 文法加入特殊的产生式
全局纠正
- 通过某种算法,寻找一个最合适的纠正
- 开销很大,只具有理论价值
4.2 上下文无关文法
4.2.1 上下文无关文法的正式定义
- 一些关于终结符号,非终结符号的定义就不写了.重在意会.
- 产生式的
->
有时也可用::=
来代替
4.2.2 符号表示的约定
本书对文法的约定
- 没什么好做笔记的..
4.2.3 推导
将产生式看做重写规则,就可以从推导的角度精确描述构造语法分析树的方法.
从开始符号出发,每个重写步骤把一个非终结符号替换为他的产生式的体.
- 这个推导思想对应于自顶向下构造语法分析树的过程.
自底向上语法分析和一种被称为最右推导的推导类型相关.
- 在这种推导过程,每一步重写的都是最右边的非终结符号.
推导的定义
句型(sentential form)
:- 如果
S=>a
,其中S
是文法G
的开始符号,我们说a
是G
的一个句型. - 一个句型可能既有终结符号,也有非终结符号,或者空串.
- 如果
句子(sentence)
- 文法
G
的一个句子是只包含终结符号的句型
- 文法
一个文法生成的语言是他所有句子的集合
- 一个终结串
w
在G
生成的语言L(G)
中,当且仅当w
是G
的一个句子.
- 一个终结串
由上下文无关文法生成的语言叫做上下文无关语言(context-free language)
- 如果两个文法G,GG生成同样的语言,则说两个文法是等价的
左推导,右推导
最右推导有时也叫规范推导
4.2.4 语法分析树和推导
语法分析树是推导的图形表示,他过滤掉了推导过程对非终结符号应用产生式的顺序
一颗语法分析树的叶子节点可以是终结符号或者非终结符号(还没分析完)
- 从左到右排列这些符号可以得到一个句型
- 它称为这个树的
结果(yield)
或边缘(frontier)
推导和语法树,是多对一的关系
4.2.5 二义性
- 二义性:如果一个文法可以为某个句子生成多颗语法分析树,那么它就是
二义性(ambiguous)
.
4.2.6 验证文法生成的语言
- 证明文法
G
生成语言L
可以分为两个部分
- 证明
G
生成的每个串都在L
中. - 证明
L
中每个串都能由G
生成.
- 证明
4.2.7 上下文无关法和正则表达式
我们要说明有点很重要的
文法是比正则表达式具有更强的表达能力.
- 正则表达式能表达的文法都能表达,反则不行
- 正则表达式能表达的文法都能表达,反则不行
也可以机械的根据NFA来构造出一个文法.
有穷自动机无法计数
- 不能表示形如语言
L=a^nb^n n>=1
的语言 - 证明见书,反证法.
- 也就是说有穷自动机无法记录读到b之前a的个数.
- 类似的,一个文法可以对两个个体计数,无法对三个个体计数.
- 在4.3.5节考虑非上下文无关的语言构造时考虑.
- 不能表示形如语言
4.3 设计文法
文法能够描述程序设计语言大部分(但不是全部)语法.
- 比如标识符先声明后使用,不能通过文法来实现.
- 编译器后续还需要检查.
本节主要讨论词法分析器和语法分析器之间分配工作
然后考虑如何使文法如何更加适合语法分析的转换方法.
- 消除二义性
- 消除左递归和提取左公因子
- 使得文法更加适用自顶向下的语法分析.
最后在考虑不能用文法分析的程序语言
4.3.1 词法分析和语法分析
为什么使用正则表达式来定义一个语言的词法语法?
- 一个语言的词法规则通常很简单,不需要词法那么强大的表示方法.
- 正则表达式提供了更加简洁易于理解的表示词法单元的方法.
根据正则表达式自动构造的词法分析器效率优于任意文法自动构造.
正则表达式适合描述诸如标识符,常量,关键词,空白这样的语言构造
- 文法适合描述嵌套结构
- 这些嵌套结构不能使用正则表达式
4.3.2 消除二义性
文法4-14
根据这个文法下面的复合语句具有如下的语法树
if E1 then S1 else if E2 then S2 else S3
此文法的二义性表述
if E1 then if E2 then S1 else S2
在实际的程序语言中都会选择第一种语法树
- 采取的是一种就近原则来避免二义性.
从理论上讲,可以直接写个表达式来表现上面的就近原则,但是一般不这样用.
改写文法,使得
then
和else
之间不能 存在if then
这种形式.- 使得else必定就近匹配.
4.3.3 左递归的消除
左递归(left recursive):如果非终结符号存在一个这样的推导,那么就是左递归
自顶向上语法分析方法不能处理左递归的文法.
处理立即左递归
-立即左递归:一步推导得到的左递归叫做立即左递归
对于替换为非左递归的产生式
处理非立即左递归
形如下图的表达式S
,是左递归,但不是立即左递归
- 可以使用如下的算法4.19系统的消除左递归
- 只要文法不存在
环(A=>A)
或ε
产生式形如(A->ε)
- 只要文法不存在
算法4.19
例子
4.3.4 提取公因子
完整算法
4.3.5 非上下文无关语言的构造
在常见的程序设计语言,可以找到少量不能仅用文法描述的语法构造.这里我们介绍两种
例4.25
这个例子抽象的表示了检查标识符在程序中先声明后使用的问题
这个语言用形如
wcw
的串组成- 第一个
w
表示对标识符的声明,c
代表程序片段,第二个w
表示对这个标识符的使用.
- 第一个
这个抽象语言是
- 这个语言是非上下文无关的,证明这点超过了本书内容.
- 也证明了因此像
C
或Java
这样的语言不是上下文无关的.
- 都必须要先声明后使用,支持任意长度的标识符.
处于这个原因,C或者Java的文法不区分由不同字符串组成的标识符,所有标识符在文法都表示像
id
一样的词法单元,在有这种语法的编译器中,标识符是否先声明后使用在语义分析阶段完成
例4.26
这个例子的非上文无关语言抽象的表示了参数个数检查的问题.
- 它检查一个函数声明的形参个数是否等于该函数的某次使用的实际参数个数.
这个语言由形如
- a^n,b^m 表示两个分别有n和m个参数的函数声明的形式参数列表
- c^n,d^m 表面调用这两个函数的实际参数列别
- 参数个数检查也是在语义分析阶段完成
4.4 自顶向下的语法分析
自顶向下语法分析可以被看作是为输入串构造语法分析树的问题,它从语法分析树的根节点开始,按照先根次序创建这颗语法分析树的各个结点.
自顶向下语法分析也可以看作寻找输入串的最左推导的过程
- 在自顶向下语法中,最重要的是每一步选择什么产生式.
- 本节首先给出被称为递归下降语法分析的自顶向下语法分析的通用形式.
- 这种方法可能需要回溯,以找到要应用的正确
A
产生式.
- 这种方法可能需要回溯,以找到要应用的正确
- 对于有些文法,我们可以构造出向前看
k
个输入符号的预测分析器,这一类文法有时也被称为LL(k)
文法类.
- 4.4.3节将讨论
LL(1)
文法. - 4.4.2先介绍预备知识,计算
FIRST
和FOLLOW
集合的方法.
- 根据一个文法的
FIRST
和FOLLOW
集合,我们将构造出预测分析表 - 这个集合同样适用于自底向下语法分析.
- 根据一个文法的
- 4.4.3节将讨论
- 4.4.4 介绍一个非递归的语法分析算法(模拟一个栈而已)
- 4.4.5 讨论自顶向下语法分析过程中的错误恢复问题.
4.4.1 递归下降的语法分析
说到底,就是一个裸算法而已,很简单的递归而已.
这种算法的改进形式在练习4.4.9:动态规划算法或者
Earley
方法一个有左递归的文法会使递归下降文法进入无限循环.
4.4.2 FIRST 和 FOLLOW
自顶向下和自底向上语法分析器的构造可以使用和文法G相关的两个函数 FIRST
和FOLLOW
来实现.
- 在自顶向下中,
FIRST
和FOLLOW
使得我们可以根据下一个输入来选择哪个产生式. - 在恐慌模式下,由
FOLLOW
产生的词法单元可以作为同步词法单元.
FIRST
如何计算
FOLLOW
FOLLOW
定义:对于非终结符号A
,FOLLOW(A)
被定义为可能在某些句型中紧跟在A右边的终结符号的集合.
如何计算
例子
4.4.3 LL(1) 文法
对于称为 LL(1)
文法, 我们可以构造出预测分析器.
- 第一个
L
表示从左向右扫描输入,第二个L
表示产生最左推导,而1
则表示在每一步只需要一个向前看符号来决定语法分析动作.
预测分析器的转换图
转换图有助于预测分析器可视化
要构造一个文法的转换图,首先要消除左递归,提取左公因式
然后对于每个非终结符A
- 创建一个初始状态和一个结束状态
- 对于每个产生式
A->X1X2X3...XN
,创建一个从初始状态到结束状态的路径.
预测分析器的转换图和词法分析器的转换图不同.
- 分析器的转换图对每个非终结符号都有一个图
- 边的标号可以是词法单元,也可以是非终结符号.
- 非终结符号
A
的转换图表示对A的过程的一次调用
转换图可以化简
- 在2.5.4节,我们将详细讲解,使用尾递归消除和过程体替换
LL(1)文法的约束条件
- 不能有二义性和左递归
为什么LL(1)文法能够构造语法分析器
- 有关控制流的各个语言构造带有不同的关键词,它们通常都满足LL(1)的约束.
预测分析表 M[A,a]
- 接下来的算法吧
FIRST
和FOLLOW
集合的信息放到一个预测分析表M[A,a]
中
A
是一个非终结符号,a
是一个终结符号或者特殊符号$
.
算法4.31
不能用LL1文法表示的语言
4.4.4 非递归的预测分析
例子,处理id+id*id
4.4.5 预测分析中的错误恢复
当预测分析中,遇到M[A,a]
为error
,预测分析器就检测到了语法错误.
恐慌模式
以后考虑吧,有点暂时难理解错误恢复.
4.5 自底向上的语法分析
本节将介绍被称为
移入-规约
语法分析的自底向上语法分析的通用框架.4.6 4.7 讨论
LR
文法类,它是最大的,可以构造相应移入-规约
语法分析器的文法.- 虽然手工构造一个LR语法分析的工作量非常大,但根据自动生成工具可以使人们轻松构建.
以上是关于语法分析]的主要内容,如果未能解决你的问题,请参考以下文章