【编译原理】03语法分析

1,语法分析的若干问题

1.1 语法分析器的作用        

        编译器前端的重要组成部分:        

        (1) 根据词法分析器提供的记号流,为语法正确的输入构造分析树(或语法树)。

         (2) 检查输入中的语法(可能包括词法)错误,并调用出错处理器进行适当处理。

语法分析器:

1、通用的语法分析方法

         Cocke-Younger-Kasami算法和Earley算法

2、自上而下

        对任何输入串,试图用一切可能的办法,从文法开始符号(根结点)出发,自上而下地为输入串建立一棵语法树;或者说为输入串寻找一个最左推导。

3、自下而上

       自上而下的一般方法: 消除左递归 提取左因子 递归下降分析 预测分析器

语法分析器在编译器中的位置与作用

1.2  语法错误的处理原则

1.2.1.源程序中可能出现的错误

语法错误

        词法错误:非法字符或关键字、标识符拼写错误等

        语法错误:语法结构出错,如少分号、{/}不配对等  

语义错误

        静态语义错误涉及的是编译时可检查出来的错误,如类型不一致、参数不匹配等;

        动态语义错误一般是指程序运行时的逻辑错误,如无穷递归、变量为零时作除数等。        

        大多数错误的诊断和恢复集中在语法分析阶段,一个原因是大多数错误是语法错误,另一个原因是语法分析方法的准确性,它们能以非常有效的方法诊断语法错误。在编译的时候,想要准确地诊断语义或逻辑错误有时是很困难的。

1.2.2.语法错误处理的目标

① 清楚而准确地报告错误的出现,地点正确、不漏报、不错报也不多报;      

② 迅速地从每个错误中恢复过来,以便分析继续进行;      

③ 对语法正确源程序的分析速度不应降低太多。

1.2.3.语法错误的基本恢复策略

        希望编译器的语法分析方式不是遇到一个语法错误就停止,就需要采取某种恢复策略,使得分析在遇到错误时还能够继续进行。

        紧急方式恢复:这是最简单的方法,适用于大多数分析方法。

        短语级恢复:建立在产生式(CFG)的基础上,以短语为基本分析单元,同时也便于进行语法制导翻译,恢复得比紧急方式要精确,因此被认为是一种较为理想的恢复方式。

        出错产生式

        全局纠正

例题:

        下述两条是有语法错误的语句,其中第一条赋值句结束时忘记加分号:x = a + b             y = c + d; 

        紧急方式:  x = a + b + d; -- 丢弃b之后的若干记号,直到遇到同步记号+      

        短语级恢复:x = a + b;     -- 加入分号,使之成为一个赋值句             

2,上下文无关文法(CFG)

2.1  CFG的定义与表示    

2.1.1 定义

         定义3.1  上下文无关文法是一个四元组G =(N,T,P,S):      

        ① N是非终结符的有限集合(Nonterminals);      

        ② T是终结符的有限集合(Terminals),且N∩T=Φ;      

        ③P是产生式的有限集合(Productions),每个产生式形如: A→α,其中A∈N,被称为产生式的左部,α∈(N∪T)*,被称为产生式的右部,若α=ε,则称A→ε为空产生式(也可以记为A →);      

        ④ S是非终结符,被称为文法的开始符号(Start symbol)。 

2.2.2 例题:

        定义简单算术表达式的上下文无关文法G3.1=(N,T,P,S)如下所示。    

 N={E}   T={+,*,(,),–,id}    S=E    

 P:      

E → E + E        (1)        

E → E * E         (2)        

E → (E)            (3)          

E → –E            (4)          

E → id              (5) 

2.2.3 表示 

        1.由产生式集表示CFG        

        文法可以由其产生式集P代替,而不写四元组。CFG的产生式表示也被称为巴克斯范式(BNF),值得注意的是,规范的BNF中,“→”用“::=”表示。

         2.产生式的一般读法:

         一般情况下,可以将产生式中的记号“→”读作“定义为”或者“可导出”。

        3.终结符与非终结符书写上的区分

         本教材默认采用区分大小写的方法来区分终结符与非终结符,若不作特殊说明,一般用大写英文字母A、B、C表示非终结符,小写英文字母a、b、c表示终结符,小写希腊字母α、β、δ表示任意的文法符号序列,即大小写混合的英文字母串。

        4.产生式的缩写形式 

           2.2.2的例题 G3.1可以重写为如下形式:E → E + E   |  E * E  |  (E)     | –E  | id            

2.2  CFG产生语言的基本方法 —— 推导

2.2.1 推导定义以及性质

        通过推导可以产生CFG所描述的语言。

        推导就是从文法的开始符号S开始,反复使用产生式,将产生式左部的非终结符替换为右部的文法符号序列,直到得到一个终结符序列 

2.2.2 例题:

文法G(E):

E → E + E           (1)

E→ E * E                 (2)          

E → (E)              (3)          

E → –E              (4)        

E → id                     (5)  

 根据文法G使用最左推导得出终结符序列  ”–(id+id)” 。

2.2.3 上下文无关语言(CFL),最左推导

定义3.3  

        由CFG  G所产生的语言L(G)被定义为:            

             

        L(G)称为上下文无关语言(CFL),ω称为句子。若Sα,α∈(N∪T)*,则称α为G的一个句型。 

定义3.4  

        在推导过程中,若每次直接推导均替换句型中最左边的非终结符,则称为最左推导,由最左推导产生的句型被称为左句型。    

        

        类似地,可以定义最右推导与右句型,最右推导也被称为规范推导。  

2.3  推导、分析树与语法树    

定义3.5  对CFG G的句型,分析树被定义为具有下述性质的一棵树。    

        ① 根由开始符号所标记;    

        ② 每个叶子由一个终结符、非终结符或ε标记;    

        ③ 每个内部结点由一个非终结符标记;    

        ④ 若A→X1X2...Xn是一个产生式,则X1,X2,...,Xn是标记为A的内部结点从左到右所有孩子的标记。若A→ε,则标记为A的结点可以仅有一个标记为ε的孩子。 

     分析树与文法和语言存在下述关系:      

        ① 每一直接推导(或者每个产生式),对应一棵仅有父子关系的子树,即产生式左部非终结符“长出”右部的孩子;      

        ② 分析树的叶子从左到右,构成G的一个句型。若叶子仅由终结符标记,则构成一个句子。 

     分析树与推导的关系:    

         ① 分析树是推导的图形表示,但不能显示推导的顺序;    

         ② 每棵分析树都可以对应惟一的最左推导或最右推导;但每个句子(产生式)不一定只有一棵分析树,也就是说每个句子不一定只有一个最左推导或者最右推导。 例:句子id + id * id      

例题:

  用文法G采用最左推导产生句子id+id*id. 

一个句子两棵分析树 (a) 先进行+运算;(b) 先进行*运算;(c) +左结合;(d) +右结合

2.4  二义性与二义性的消除

2.4.1  二义性(Ambiguity)

        定义3.7  若文法G对同一个句子产生不止一棵分析树,则称G是二义的。 

        ① 一个句型有多于一棵分析树,仅与文法和句型有关,与采用的推导方法无关;      

        ②  造成二义性的原因,是文法中缺少对文法符号优先级和结合性的规定。        

        在任何一个程序设计语言中,如果出现了二义性,则表示同一段程序在确定的、相同的环境下反复执行,会得到不同的结果,而这种情况在程序设计中是不允许的。也就是说,任何一个程序设计语言不应该有二义性。 

2.4.2 为文法符号规定优先级和结合性    

        改写文法可以解决二义性问题,但不是唯一的解决方法。比较上述讨论过的二义文法和非二义文法,发现二义文法至少有两个优点:      

        ① 比非二义文法容易理解;      

        ② 分析效率高(分析树低,直接推导步骤少)。 

3,语言与文法简介

        到目前为止,我们在两个层面上讨论了程序设计语言的结构:从词法分析的层面上看,语言是由字母组成的记号的集合;从语法分析的层面上看,语言是由记号组成的句子的集合。记号可以用正规式描述,句子可以用CFG描述。        

        程序设计语言的结构均可以用文法来描述。文法无论对程序设计语言的设计还是对编译器的编写,至少在以下三个方面起着重要作用:

        ① 文法给出了精确的、易于理解的语言结构的说明;      

        ② 以文法为基础的语言,便于加入新的或修改、删除旧的语言结构;      

        ③ 有些类别的文法,可以自动生成高效的分析器。        

3.1.为什么用正规式而不用CFG描述程序设计语言的词法  

          根据推论3.1,CFG既描述程序设计语言的语法又描述词法,而基于下述几个原因,往往采用正规式而不是CFG描述词法:      

        ① 词法规则简单,用正规式描述已足够;      

        ② 正规式的表示比CFG更直观、简洁、易于理解;      

        ③ 有限自动机的构造比下推自动机简单,且分析效率高;    

        ④ 区分词法和语法,为编译器前端的模块划分提供方便。 

3.2 正规式和CFG的适用条件

        一般情况下,正规式适合描述线性结构,如标识符、关键字、注释等。而CFG适合描述具有嵌套(层次)性质的非线性结构,如不同结构的句子if-then-else、begin-end等。

3.3  形式语言与自动机简介      

        乔姆斯基(Chomsky)把文法分为四种类型: 0型、1型、2型和3型。文法之间的差异在于对产生式施加不同的限制。      

        定义3.8  若文法G=(N,T,P,S)的每个产生式α→β中,均有α∈(N∪T)*,且至少含有一个非终结符,β∈(N∪T)*,则称G为0型文法。          

        对0型文法施加以下第i条限制,即可得到i型文法。

        1.G的任何产生式α→β(S→ε除外) 均满足|α|≤|β| (|x|表示x中文法符号的个数);      

        2.G的任何产生式形如A→β,其中A∈N,β∈(N∪T)*;      

        3.G的任何产生式形如A→a 或者A→aB(或者A→Ba),其中A,B∈N,a∈T。          

        0型文法也被称为短语文法,任何0型语言都是递归可枚举的,反之,递归可枚举集也必定是一个0型语言。

       1型文法就是上下文有关文法,这种文法意味着对非终结符的替换必须考虑上下文,并且一般不允许换成ε串。例如,若αAβ→αγβ是1型文法的产生式,α和β不全为空,则非终结符A只有在左边是α,右边是β这样的上下文中才可能替换成γ。      

        2型文法就是上下文无关文法,非终结符的替换无需考虑上下文。      

        3型文法等价于正规式,因而也被称为正规文法或线性文法。      

        四种类型的文法和它们所描述的语言,以及识别对应语言的自动机分别列举在表中。

4, 自上而下分析的一般方法        

4.1 基本思想 

        自上而下分析的基本思想是:对于任何一个输入序列,从文法的开始符号开始,进行最左推导,直到得到一个合法句子或者发现一个非法结构。在推导的过程中试图用一切可能的方法,自上而下、从左到右为输入序列建立分析树。整个分析是一种试探的过程,是反复使用不同产生式谋求与输入序列匹配的过程。

4.2 例题

        已知如下文法G(S),给出输入序列ω=cad,自上而下试探建立分析树的过程。

        G(S):  S → cAd            A → ab | a

试探性质的自上而下分析 (a) 匹配失败,回溯;(b) 匹配成功,接收

        (1) 若存在形如A → αβ1|αβ2的产生式,即A产生式中有多于一个候选项的前缀相同(称为公共左因子,或简称左因子),则可能会造成虚假匹配,使得在分析过程中可能需要进行大量回溯,从而造成分析效率低、语义动作难以恢复以及出错位置的报告不确切等。    

        (2) 若存在形如A → Aα的产生式,则分析过程中一旦采用了该产生式去替换,就会陷入死循环而使分析无法进行下去。产生式的这种形式被称为左递归。        

        为了避免上述弱点,需要对文法进行重写。消除左递归,以避免陷入死循环;提取左因子,以避免回溯。 

4.3  消除左递归

        分类:直接左递归 间接左递归 

   规则1     直接左递归的产生式A→Aα|β (β不以A开头), A→βA'  A'→αA'|ε

   规则2     直接左递归的产生式 A→ Aα1 | Aα2 | ... | Aαm |β1 |β2 | ...|βn(所有βi均不以A直接开头或间接开头), A→β1A'|β2A'|...|βnA'  A'→α1A'|α2A'|...αmA'|ε

并不是所有的文法都能消除左递归!!! 

4.4  提取左因子    

         对于有左因子的文法,推导过程中会出现无法确定用A产生式的哪个候选项替换A的情况,这时,可以重写A产生式来推迟这种决定,直到看见足够的输入,能正确决定所需选择为止。这一过程类似于有限自动机的确定化,被称为提取左因子。    

 算法3.3  提取文法的左因子      

输入 : 文法G      

输出:  等价的无左因子文法G' 

方法  :为每个产生式A,找出其候选项中最长公共前缀α,重排A产生式如下,其中γ是不以α为前缀的其他候选项。      

        A→ αβ1|αβ2| ...|αβn|γ    

并用下述产生式替代之。      

        A→ αA'|γ    

        A'→ β1|β2| ...|βn

重复此过程,直到所有A产生式的候选项中均不再有公共前缀。

练习:

1、文法G(A):A→aABj | a                      B→Bb | d

2、文法G(M):M→MaH | H                       H→b(M) | (M) | b

3、文法G(S):S→AdD | ε                      A→aAd | ε               D→DdA | b | ε 

5,FIRST集和FOLLOW集 

5.1 FIRST集 

  算法3.5  计算X的FIRST集合  

输入  文法符号X  

输出  X的FIRST集合  

方法  应用下述规则:    

        ① 若X是终结符,则FIRST(X)={X};  

         ② 若X 是非终结符且有X→ε,则加入ε到FIRST(X);    

        ③ 若X 是非终结符且有X→Y1Y2...Yk,并令Y0=ε,Yk+1=ε,则对所有j(0≤j≤k),若a∈FIRST(Yj+1)且ε∈FIRST(Yj),加入a到FIRST(X)。 

5.2 FOLLOW 

    算法3.6  计算所有非终结符的FOLLOW集合    

输入  文法G    

输出  G中所有非终结符的FOLLOW集合    

方法  应用下述规则:    

① 加入#到FOLLOW(S),其中,S是开始符号,#是输入结束标记;    

② 若有产生式A→αBβ,则除ε外,FIRST(β)的全体加入到FOLLOW(B);        

③ 若有产生式A→αB或A→αBβ而ε∈FIRST(β),则FOLLOW(A)的全体加入FOLLOW(B)。 

6,预测分析表

    算法3.7  构造预测分析表    

输入  文法G    

输出  分析表M    

方法  应用下述规则:    

① 对文法的每个产生式A→α,执行②和③;    

② 对FIRST(α)的每个终结符a,加入α到M[A,a];    

③ 若ε在FIRST(α)中,则对FOLLOW(A)的每个终结符b(包括#),加入α到M[A,b];        

④ M中其他没有定义的条目均是error。

     预测分析表是一个二维数组M[A, a] 所有的非终结符构成分析表的行下标; 所有的终结符构成分析表的列下标,其中包括特殊的结束标志#。 M[A, a]中的内容表示当前栈顶为非终结符A且当前输入为终结符a时,分析器要进行的动作。 

3.4.5.3  LL(1)文法      

        定义3.12  一个文法G被称为是LL(1)文法,当且仅当为它构造的预测分析表中不含多重定义的条目。由此分析表所组成的分析器被称为LL(1)分析器,它所分析的语言被称为LL(1)语言。第一个L表示从左到右扫描输入序列,第二个L表示产生最左推导,1表示在确定分析器的每一步动作时向前看一个终结符。 

     推论3.2  一个文法G是LL(1)的,当且仅当G的任何两个产生式A→α|β,满足下面条件:       ① 对任何终结符a,α和β不能同时推导出以a开始的文法符号序列;      

② α和β最多有一个可以推导出ε;      

③ 若β       ε,则α不能推导出以在FOLLOW(A)中的终结符开始的任何文法符号序列。 

         推论3.2实际上等价于当且仅当G的任何两个产生式A→α|β满足上述三个条件时,为文法G构造的预测分析表中不含多重定义的条目。反之,若不满足三个条件之一,则分析表中有多重定义条目。         若条件①不满足,即存在终结符a,α和β同时推导出以a开始的文法符号序列,则根据算法3.7规则②,M[A,a]中就会有多重定义条目A→α和A→β;

            若条件②不满足,即α和β均可推出ε,则根据算法3.7规则③,任何属于FOLLOW(A)的终结符b(包括#),M[A,b]中就会有多重定义条目A→α和A→β;     若条件③不满足,即存在终结符b,它既在FOLLOW(A)中,又在FIRST(α)中,则     算法3.7规则②把条目A→α加入到M[A,b]中,而规则③又把条目A→β加入到M[A,b]中。

LL(1)文法的弱点:  (1) 文法比较难写。因为按照人们习惯写出的文法,往往含有左递归和左因子。虽然有成熟的算法可以消除左递归和提取左因子,但改写之后的文法很难读也很难使用。同时对比分析树可以看出,改写后文法构造的分析树不直观且推导步骤增加。 (2) LL(1)文法适应范围有限,对于有些语言,往往写不出它的LL(1)文法。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/562765.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何在一台服务器上同时运行搭载JDK 8, JDK 17, 和 JDK 21的项目:终极指南

🐯 如何在一台服务器上同时运行搭载JDK 8, JDK 17, 和 JDK 21的项目:终极指南 🚀 摘要 在企业开发环境中,常常需要在同一台服务器上运行使用不同Java开发工具包(JDK)版本的多个项目。本文详细介绍如何在L…

Linux——进程基本概念中篇

Linux——进程基本概念中篇 文章目录 Linux——进程基本概念中篇一、通过系统调用创建进程——fork1.1 fork的理解1.2 fork的返回值 二、进程状态2.1 运行状态2.2 睡眠状态和休眠状态2.3 停止状态和死亡状态2.4 僵尸进程2.5 孤儿进程2.6 前台和后台进程 三、进程优先级3.1 查看…

linux普通用户执行sudo

一般普通用户想要具有root的权利,那么只有把普通用户切换到root用户 但是我们发现在普通用户的命令前面sudo 然后系统会让你输入普通用户自己的密码 但是这样并不能实现,因为如果随便一个普通用户都只要在前面sudo就能执行root的权限命令,那么…

在一台笔记本电脑上试用Ubuntu22.04

在一台笔记本电脑上试用Ubuntu22.04。 本来想看以下该操作系统能否识别笔记本电脑上的硬盘,于是下载试一下。选了一个国内镜像网站下载。下载速度很快。下载以后用软件win image 将下载的iso文件写到U盘上,用的是usb2.0的U盘,该操作用时11分…

利用Opencv4.9为图像添加边框

返回:OpenCV系列文章目录(持续更新中......) 上一篇利用OpenCV4.9制作自己的线性滤波器! 下一篇 :OpenCV系列文章目录(持续更新中......) 目标 在本教程中,您将学习如何: 使用 OpenCV 函数 …

在使用 sqlite 时遇到的奇怪问题的正解

在使用 sqlite 时遇到的奇怪问题, 用 DB Browser for SQLite (一款 sqlite 可视化工具)操作 sqlite 数据库 ,一个名叫 testDB 的数据库(位置在C:\sqlite\testDB.db),在里面建了一个表&#xff0…

【Linux】在centos快速搭建K8S1.18集群

使用 kubeadm 创建集群帮助文档 如果您需要以下几点,该工具是很好的选择:kubeadm 一种简单的方法,让你尝试 Kubernetes,可能是第一次。现有用户自动设置群集并测试其应用程序的一种方式。其他生态系统和/或安装程序工具中的构建…

Qt 窗口的坐标体系

Qt 窗口的坐标体系 Qt 中使用的坐标系也是平面直角坐标系,但是呢又和我们平时在数学中使用的坐标系是不同的,我们在数学中使用的坐标系是 x 向右增长, y 向上增长。 但是在 Qt 中使用的是 x 轴不变,y 轴是向下增长的。 所以Qt 中…

【Python】自定义修改pip下载模块默认的安装路径

因为电脑下载了Anaconda提供的默认Python 3.9 以及后期下载的python3.10所以在Pychram进行项目开发时,发现一些库怎么导入都导入不了,手动install也是失败,后期在cmd里面发现python以及pip配置有点儿混乱,导致执行命令时&#xff…

十大排序算法详解-上篇:比较排序算法【python 动态图解】

作者介绍:10年大厂数据\经营分析经验,现任大厂数据部门负责人。 会一些的技术:数据分析、算法、SQL、大数据相关、python 欢迎加入社区:码上找工作 作者专栏每日更新: LeetCode解锁1000题: 打怪升级之旅 python数据分析…

TCP/IP协议—MQTT

TCP/IP协议—MQTT MQTT协议MQTT协议特点MQTT通信流程MQTT协议概念 MQTT报文固定报头可变报头有效载荷 MQTT协议 消息队列遥测传输(Message Queuing Telemetry Transport,MQTT)是一个基于客户端-服务器的消息发布/订阅传输协议。它的设计思想…

C语言指针进阶:各类型指针变量详解

目录 1. 字符指针变量2. 数组指针变量2.1 什么是数组指针变量2.2 数组指针变量的初始化 3. 二维数组传参的本质4. 函数指针变量4.1 函数指针变量的创建4.2 函数指针变量的使用4.3 代码分析4.3.1 typedef 关键字 5. 函数指针数组6. 转移表 正文开始。 1. 字符指针变量 我们可以…

基于SpringBoot + Vue实现的时装购物管理系统设计与实现+毕业论文+开题报告+答辩PPT

介绍 系统包含用户、管理员两个角色 管理员:首页、个人中心、用户管理、商品分类管理、颜色管理、商品信息管理、商品评价管理、系统管理、订单管理 用户:首页、个人中心、商品评价管理、我的收藏管理、订单管理 前台首页:首页、商品信息、商品资讯、个人中心、后台…

vue 的生命周期--图解

生命周期函数中的this指向是vm 或 组件实例对象。 常用的生命周期钩子: mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。 关于销毁Vue实例 销毁后借助Vu…

Spring-datasource事务管理-手动请求事务回滚

什么场景下会触发? 在 Spring 中,调用 setRollbackOnly() 方法会将当前事务标记为 rollback-only,表示事务只能回滚,不能提交。这种情况通常发生在以下情景中: 业务逻辑判断:在方法中根据某些业务逻辑的判…

[GFCTF 2021]wordy

用ida查看可知存在大量jmp跳转语令,编写脚本将其改为空指令

MyBatis Dynamic SQL基本使用

MyBatis Dynamic SQL基本使用 一、概念二、特性Hamcrest是什么 三、MyBatis Dynamic SQL 快速入门3.1 环境准备3.2 定义表和列3.3 创建 MyBatis3 映射器3.4 使用 MyBatis3 执行 SQL 四、数据库对象表示4.1 表或视图表示4.2 表别名4.3 列表示 五、Where 子句支持5.1 简单的 wher…

磁盘损坏无法读取:原因、恢复方案与防范之道

在数字化信息爆炸的时代,磁盘作为数据存储的重要载体,承载着无数重要的文件和资料。然而,当磁盘突然损坏,无法读取数据时,我们往往会陷入困境,焦虑不已。面对这种情况,我们该如何应对&#xff1…

晶圆制造之MPW(多项目晶圆)简介

01、MPW是什么? 在半导体行业中,MPW 是 "Multi Project Wafer" 的缩写,中文意思是多项目晶圆。MPW 的主要思想是将使用相同工艺的多个集成电路设计放在同一晶圆片上进行流片(即制造)。这种方法允许多个设计共…