编译原理笔记16:自下而上语法分析(3)构造 DFA、DFA 对下一步分析的指导(有效项目)

目录

    • 由 NFA 用子集法构造 DFA
    • 由 LR(0) 项目直接构造识别活前缀的 DFA
      • 构造 DFA
        • 求拓广文法 G'
        • CLOSURE & GO
          • 例:
        • 构造 DFA
    • DFA 指导下一步分析
      • 有效项目

看了前面的内容,我们已经了解到:分析表和驱动器算法,是 LR 分析器的核心。

在分析的过程中,语法分析器总是根据栈顶的状态、当前剩余输入的第一个终结符查询分析表,以确定改变格局的动作并执行,实现对栈和剩余输入的内容的修改,从一个格局转移到另一个格局,如此往复直至分析完毕(或报错)。

下面我们就来研究一下如何从文法构造 DFA —— 这是构造 LR(0) 、SLR(1) 分析表的第一步。

由 NFA 用子集法构造 DFA

前一篇博客中讲解了该如何构造 NFA,有了 NFA 就可以使用子集法构造 DFA 了。

构造方法与词法分析的 NFA 转 DFA 类似,此处不再重复,接下来说一些该 DFA 的特点。

在这里插入图片描述

DFA 中的每个状态都是原 NFA 的状态集(因为 DFA 是 NFA 使用子集法构造的)。而又因 NFA 的每个状态都对应一个项目,所以 DFA 中的每个状态又是一个项目集。

DFA 的初态由原 NFA 初态求 ε-闭包得到,DFA 中的所有状态都是其终态。

(类比子集法构造词法分析 DFA——DFA 的终态是包含原 NFA 终态的那些状态,而原来的那个 NFA 本身就已经所有状态均为终态了,也就是说从初态出发到达任何一个状态的路径所标记的连接都是该 DFA 可识别的活前缀。同理,原来的句柄识别态到这里依然是适用的,就是图中的 1、8、10、6、7、11、9 )

该识别活前缀的 NFA 和原来的词法分析 NFA 有一个重大差别:词法分析 NFA 状态号就只是一个编号,而此处的状态编号却是和项目相对应的——每个编号就对应着一个项目,而且状态的转移关系也是基于这些项目建立起来的。

那么。。其实也可以直接从项目构造 DFA

由 LR(0) 项目直接构造识别活前缀的 DFA

LR(0) 项目集规范族:构成识别一个文法活前缀的 DFA 的项目集(状态)的全体,称为这个文法的 LR(0) 项目集规范族。这个规范族提供了建立一类 LR(0) 和 SLR 分析器的基础。

实际上,这个【规范族】就是我们要构造的识别活前缀的 DFA 的状态的全体——上面那个 DFA 中所有的矩形状态加起来就是了。主要是因为 DFA 的每个状态都是一个 LR(0) 项目集,所以就想起来这么个骚名字叫做【LR(0) 项目集规范族】

(如果题目让计算一个LR(0) 项目集规范族,那其实就是要计算识别活前缀的 DFA。有了 DFA 就可以构造 LR(0) 分析表和 SLR(1) 分析表了)

我们先给项目做做分类、起起名字,后面要用到:

  • 圆点在最右端的项目(如 A → α. ,A 不是拓广文法的开始符号)称为”规约项目“,在出现规约项目的状态就可以进行规约动作;
  • 文法开始符号 S’ 的规约项目称为”接受项目“(比如 S’ → α. );
  • 形如 A → α.aβ 的项目称为”移进项目“,a 为终结符。在该状态下,如果下一个输入是终结符 a 则可以进行移进动作;
  • 形如 A → α.Bβ 的项目称为”待约项目“,B 为非终结符。”待约项目“的含义是【等待把 B 规约出来】:若想按照该产生式进行规约,就得先想办法把 B 搞出来,看到 B 之后才能进行后面的规约动作;(B 是非终结符,可是输入序列中么的非终结符啊。。因此,想要用这个产生式进行规约那需要凑齐该产生式的右部——先把输入序列中由 B 推导出来的符号序列规约成 B 就可以了)

构造 DFA

求拓广文法 G’

拓广文法: G’ = G ∪ { S’ → S }

其实就是先引入一个新的非终结符 S’ 作为拓广文法的新的开始符号,并引入新的产生式 S’ → S

CLOSURE & GO

  1. CLOSURE( I ):从项目集 I 不经过任何文法符号到达的项目全体(和词法分析中的 ε-闭包(I) 的含义相同)
  2. GO( I, X ):所有从 I 经文法符号 X 能到达的项目全体(只需要到达就好了,不止一步也可以。因此要先计算直接到达的,再在能够直接到达的基础上求闭包。因此显然,GO 和词法分析 NFA 的 smove 的功能不对等:GO 含有闭包计算)。X 只是一个文法符号,终结符、非终结符均可。
例:

对于下面的文法 G,若 I={ S → .E }

S' → E
E → aA | bB
A → cA | d
B → cB | d
  • 求 CLOSURE(I)

    CLOSURE(I) = { S’ → .E, E → .aA, E → .bB }

    首先,S’ → .E 是项目集自身元素,自然不需要经过任何文法符号即可到达。后两个项目则是因为他们均可由初态通过 ε 边即可到达,同样满足【从项目集 I 不经过任何文法符号到达的项目】这一要求。(此处可以脑补一下 NFA 图)

  • 求 GO(I, a)

    GO(I, a) = { E→a.A, A→.cA, A→.d }

    首先, E→a.A 是直接而从 I 中的项目出发经过 a 能够到达的项目。后面两个项目则可由这个 E→a.A 通过 ε 边可到达。这也符合“所有从 I 经文法符号 a 【能】到达的项目全体”这一定义。

构造 DFA

构造下面的文法 G’ 的 DFA

E' → E
E → E-T | T
T → T*F | F
F → -F | id

首先求 CLOSURE( {E’ → .E} ),并将其作为整个 DFA 的初态,即:

I0 = CLOSURE( {E’ → .E} ) = { E’ → .E, E → .E-T, E → .T, T → .T*F, T → .F, F → .-F, F → .id }

将 I0 作为初态,其实也就是像下图左边这样,画一个矩形大方框再把 I0 里面的这些个项目依次写进去。。这整个一个方框就是我们的 DFA 的初态了。接下来就可以从该初态出发,逐步构造 DFA 。这个逐步构造的过程,无非也就是【挨个算一下从初态的各个项目起,经过哪些文法符号可以得到哪些新项目】,新项目自然也就可以形成新的状态,通过转移用的文法符号与原来的状态相连。

在这里插入图片描述

使用同样的规则重复操作(摊大饼),最后能得到如下图所示的 DFA

在这里插入图片描述

该 DFA 就能够用来识别活前缀了。与词法分析 DFA 不同的是,该 DFA 的任何一个状态都是终态。这也就意味着,从初态开始到任一个状态所形成的路径上面的标记连接起来,都是一个 DFA 能够识别的活前缀。

比如,对于状态3(即 I3),其能够识别的活前缀有 F 和 E-F;状态6能够识别的活前缀只有 E-

现在,我们再回头看一下移进规约的过程就会发现一些有意思的东西

在这里插入图片描述

确实,每个时刻,栈中的内容都和分析表、自动机有着关联。我们可以尝试去读栈中任一时刻的元素(从栈底到栈顶),这些状态号、文法符号相间的序列均能在上图左边的 DFA 中和一条路径相匹配。

DFA 指导下一步分析

  1. DFA 每个状态识别的活前缀不同;
  2. 对语法结构正确的输入序列进行分析的任一时刻,若此时分析器正处于 DFA 的某状态 i ,则状态 i 识别的活前缀出现在栈中,且状态 i 正位于栈顶。语法分析器正是根据此刻状态 i 中包含的 LR(0) 项目来指导下一步分析的;
  3. 状态 i 中出现的 LR(0) 项目对状态 i 能够识别的活前缀有效。

好的,DFA 看起来是个好东西,那么我们现在假设栈顶是 5 状态,此时 DFA 怎么指导下一步的分析呢?

有效项目

若存在最右推导:S’ =*> αAω => αβ1β2ω ,则称项目 A → β12 对活前缀 αβ1 有效。

项目 A → β12 对活前缀 αβ1 有效,作用是说明:在当前活前缀为 αβ1 的情况下,当前状态中的 A → β12 这个项目可以指导下一步的分析动作为: αAω => αβ1β2ω 。

我觉得吧:已经读到并且已经入栈的东西,全都是活前缀。活前缀呢,就是仍然可以活动的前缀——根据其后添加的符号不同,这个活前缀可能会变成某个句柄——也就是说,活前缀能够在后面加入某些新的符号之后成为某个产生式的右部,也就可以被用这个产生式规约掉。

对于符号串 αβ1β2ω ,看起来应该是可以使用 A → β1β2 对其进行规约。读写头在读输入序列的时候是一个一个符号去读,读到了要移进的也是一个接着一个压栈。当栈中是 αβ1 (即当前活前缀是 αβ1 )时,如果我们已经知道有这么个项目: A → β12 ,那就说明如果下一个读到了 β2 ,栈顶就能够形成该产生式的句柄,我们也就可以利用这个项目的下一步项目 A → β1β2. 进行规约了。

这其实就体现了 DFA 对分析的指导作用。

在这里插入图片描述

可知:同一项目集中的所有项目,对此项目集的所有活前缀均有效。即,项目集中的每个项目均有同等权利指导下一步动作。

有效项目的意义:

  1. 项目到目前为止的分析均是正确的;
  2. 可以指导下一步的分析:
    1. A → α.aβ(可移进项):若当前剩余输入为终结符 a ,则移进 a;
    2. B → β. (可规约项):按产生式 B → β 进行规约。

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

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

相关文章

基于Java+Swing+Mysql实现图书管理系统V2.0

基于JavaSwingMysql实现图书管理系统V2.0 一、系统介绍二、功能展示1.项目内容2.项目骨架3.数据库表4.主界面5.添加6、修改7、查询8、删除 四、其它1.其他系统实现五.获取源码 一、系统介绍 本系统主要有对图书信息的增删改查操作功能。 项目类型:Java SE项目&…

linux 下查看 USB 设备

文章目录 前言目录内容详解usb11-0:1.01-1.1:1.0 结构图设备信息bDeviceClassversionbusnum & devnumdevbMaxPoweridVendor & idProductproductmanufacturerbcdDevicespeedueventbmAttributesdrivers_autoprobe 前言 在 sysfs 文件系统下,查看 USB 设备&am…

Android 9-SystemUI:(1)启动流程

具体分析(以下代码示例,讲解,都是通过,Android9代码来举例) SystemUI,其实是可以看作是一个系统级的服务,也就是SystemUIService, SystemUI的服务启动,要从SystemServer.run()方法入手 main 方法里启动了…

企业级微服务架构实战项目--xx优选3-mq+nacos+es实现上下架

一 nacosmqes实现上下架 1.1 架构图 1.2 工程结构 1.3 核心代码流程 1.3.1 请求product模块 2.修改数据库,推送rabbitmq中 1.3.2 rabbitmq的工具类 1.3.3 search模块中rabbit客户端订阅信息 1.监听器监听信息 2.调用相应的上下架方法 2.1 调用product模块&…

Flink 学习三 Flink 流 process function API

Flink 学习三 Flink 流&process function API 1.Flink 多流操作 1.1.split 分流 (deprecated) 把一个数据流根据数据分成多个数据流 1.2 版本后移除 1.2.分流操作 (使用侧流输出) public class _02_SplitStream {public static void main(String[] args) throws Excep…

uniapp设置滚动条滚动到指定位置

场景:左侧菜单栏,每次切换时,需要右侧商品展示区保持滚动条及页面在最顶部 1.利用scroll-view 中scroll-top属性实现 1.1设置scrollToTop属性为0 data() {return {// 保证每次切换,滚动条位置都在最顶部scrollToTop: 0,}; } 1.…

ansible的剧本(playbook)

一、playbooks 概述以及实例操作 1、playbooks 的组成 playbooks 本身由以下各部分组成 (1)Tasks:任务,即通过 task 调用 ansible 的模板将多个操作组织在一个 playbook 中运行 (2)Variables&#xff1…

iOS 单元测试之常用框架 OCMock 详解 | 京东云技术团队

一、单元测试 1.1 单元测试的必要性 测试驱动开发并不是一个很新鲜的概念了。在日常开发中,很多时候需要测试,但是这种输出是必须在点击一系列按钮之后才能在屏幕上显示出来的东西。测试的时候,往往是用模拟器一次一次的从头开始启动 app&a…

团体程序设计天梯赛-练习集L2篇④

🚀欢迎来到本文🚀 🍉个人简介:Hello大家好呀,我是陈童学,一个与你一样正在慢慢前行的普通人。 🏀个人主页:陈童学哦CSDN 💡所属专栏:PTA 🎁希望各…

模糊聚类在负荷实测建模中的应用(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

VSCode使用CodeWhisperer(AI编程)

安装AWS Toolkit插件,点击侧边插件搜索并安装 2.点击aws ->CodeWhisperer ->Start 3.在下拉菜单中点击Use a personal email to sign up and sign in with AWS Builder ID 4.点Copy Code and Proceed,这会自动复制一些东西 5. 收到提示打开外部网…

【跑实验05】利用CLIP中的图像编码器,如何遍历文件夹中的图像,将图像文件改为28*28的尺寸,然后输出到excel中的每一列,最后一列全都标记为0

文章目录 一、初步实现二、警告信息的解决三、Excel的限制四、尝试解决 一、初步实现 要遍历文件夹中的图像并将其尺寸调整为28x28,并将结果输出到Excel中,可以按照以下步骤进行操作: 首先,确保您已经安装了Pandas库&#xff0c…

“插入排序:小数据量排序的王者“

文章目录 🔍什么是插入排序?🔑插入排序的优缺点🚀实现插入排序 🔍什么是插入排序? 插入排序是一种简单的排序算法,它的基本思想是:将待排序的元素,从第二个元素开始&…

Adobe Creative Cloud 摄影计划 - 当图像与想象力相遇。 PS+LRc套餐 国际版 1年订阅/398

这里重点介绍国际版摄影计划套餐详情: 国际版包括:Photoshop、Lightroom Classic、Photoshop Express、Lightroom Mobile、Lightroom、云服务。中国版包括:Photoshop、Lightroom Classic、Photoshop Express、Lightroom Mobile 桌面应用程序…

力扣高频SQL50题(基础版)——第十天

力扣高频SQL50题(基础版)——第十天 1 只出现过一次的最大数字 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出1 1.1.3 示例输入输出2 1.2 示例sql语句 # 查不到时的结果自然就为Null SELECT MAX(t.num) num FROM (SELECT numFROM MyNumbersGROUP By numHAVING count…

2023考研一战上岸 电子科技大学 860软件工程 经验分享

目录 1. 前言:考研,心态最重要! 2. 初试各科复习经验 (1) 数学一 (2) 英语一 (3) 专业课 (4) 政治 (5) 四门课时间划分 3. 复试流程和备考建议 (1) 复试流程 (2) 备考建议 4. 结语 首先,先简要做一个自我介绍&#xff…

【开源与项目实战:开源实战】82 | 开源实战三(中):剖析Google Guava中用到的几种设计模式

上一节课,我们通过 Google Guava 这样一个优秀的开源类库,讲解了如何在业务开发中,发现跟业务无关、可以复用的通用功能模块,并将它们从业务代码中抽离出来,设计开发成独立的类库、框架或功能组件。 今天,…

网络安全学术顶会——CCS '22 议题清单、摘要与总结(上)

注意:本文由GPT4与Claude联合生成。 按语:ChatGPT在计算机领域的翻译质量还是欠缺一些,翻译出来的中文有的不够自然,经常完全按照英文的表达方式来,导致中文特别长,很绕。GPT4的翻译效果相对ChatGPT效果要好…

内网安全:内网渗透.(拿到内网主机最高权限 vulntarget 靶场 A)

内网安全:内网渗透.(拿到内网主机最高权限) 内网穿透又被称为NAT穿透,内网端口映射外网,在处于使用了NAT设备的私有TCP/IP网络中的主机之间建立连接的问题。通过映射端口,让外网的电脑找到处于内网的电脑。…

TensorFlow2进行CIFAR-10数据集动物识别,保存模型并且进行外部下载图片测试

首先,你已经安装好anaconda3、创建好环境、下载好TensorFlow2模块并且下载好jupyter了,那么我们就直接打开jupyter开始进行CIFAR10数据集的训练。 第一步:下载CIFAR10数据集 下载网址:http://www.cs.toronto.edu/~kriz/cifar-10…