自己动手写编译器:First 集合,Follow 集合和 Select 集合

在上一节内容,我们手动设计了解析跳转表,表的行对应当前解析堆栈上的非终结符,列对应当前读取的终结符,于是对应的表格数字表示当前应该采取哪个推导表达式。本节我们看看如何自动化构建解析跳转表。首先我们引入一个概念叫 First 集合,我们先看一组表达式:

statement -> LEFT_BRACKET expression RIGHT_BRACKET | expression SEMICOLON

expression -> LEFT_PARENT expression RIGHT_PARENT | term MINUS expression | EPSILON

term -> NUMBER | IDENTIFIER

我们看看如果从非终结符expression 开始,它可以通过推导“直达”的终结符有哪些? 首先根据表达式 expression -> LEFT_PARENT expression RIGHT_PARENT 可以看到它能直达非终结符 LEFT_PARENT, 然后通过表达式:
expression -> term -> NUMBER|IDENTIFIER, 可以看到它也能“直达” NUMBER, IDENTIFIER。 此外通过 expression -> EPSILON 得出它也能直达终结符 EPSILON。

注意 expression 不能“直达” RIGHT_PARENT, 因为要抵达 RIGHT_PARENT ,它需要先经过非终结符 expression, 于是这个路径必须先经过 LEFT_PARENT,
NUMBER, INDENTIFIER 中的一个,因此 RIGHT_PARENT 无法“直达”。

由此我们将一个给定的非终结符能直达的终结符的集合称作它的 First 集合,也就是 First(expression)={LEFT_PARENT, NUMBER, IDENTIFIER, EPSILON}. 我们看看计算 First 集合的步骤

1, 如果 A 是一个终结符,那么 Fisrt(A) = {A}

2, 如果存在表达式 s -> A a , 其中 s 是非终结符, a 可能是一个或多个终结符和非终结符,例如表达式 expression -> LEFT_PARENT expression RIGHT_PARENT, 那么 expression 就对应 s, LEFT_PARENT 就对应 A, expression RIGHT_PARENT 就
对应 a, 在 s -> A a 这种情况下, A 属于集合 First(s)。

3, 对于表达式 s -> b a,其中 s, b 对应一个非终结符, a 可以是一个或多个终结符和非终结符的集合,那么 First(b)是 First(a)的一个子集。

4,对于表达式 s -> a b c 其中 s 是一个非终结符,a 是一个非终结符,并且 a 可以推导出 EPSILON,b 可以是一个终结符或者非终结符,那么 First(a) 并上 First(b)是 First(a)的子集。
例如表达式 statement -> expression SEMICOLON,statement 对应 s, expression 对应a, SEMICOLON 对应 c,于是 First(expression)并上 First(SEMICOLON) 是 First(statement)的子集。 因为 First(expression)={LEFT_PARENT, NUMBER,
IDENTIFIER, EPSILON}, First(SEMICOLON)={SEMICOLON}, 同时 expression->EPSILON,也就是 expression 能推导到EPSILON,所以两个集合并起来也就是{LEFT_PARENT, NUMBER, IDENTIFIER, EPSILON, SEMICOLON}是 First(statement)的一个
子集。需要注意的是,如果a对应三个非终结符的集合x,y,z,并且他们都能推导到 EPSILON, 那么 First(s)就会包含 First(x), First(y), First(z)。

我们看个具体例子:

stmt -> expr SEMICOLON

expr -> term expr_prime | EPSILON

expr_prime -> PLUS term expr_prime | EPSILON

term -> factor term_prime

term_prime -> STAR factor term_prime | EPSILON

factor -> LEFT_PAREN expr RIGHT_PAREN | NUMBER

首先有 First(factor)={LEFT_PAREN, NUMBER}, 由于 factor 出现在 term->factor term_prime, 因此 First(factor)是 First(term)的子集, 同理 First(term)也是 First(expr)的子集,同理 First(expr)是 First(stmt)的子集。

从表达式中可以观察到 First(factor)={LEFT_PAREN, NUMBER}, term的推导中直接跟着 factor,所以 First(term)=First(factor) = {LEFT_PAREN, NUMBER}. expr 的推导中直接跟着 term,同时它又可以直接推导到 EPSILON,

因此First(expr) = {LEFT_PAREN, NUMBER, EPSILON}, expr_prime 在推导中后面只能直接跟着 PLUS, 所以 First(expr_prime)={PLUS}, stmt 在推导中跟着 expr,注意到 expr能推导到 EPSILON,因此 expr 后面的 SEMICOLON 也属于First(stmt),
因此有First(stmt)={LEFT_PAREN,NUMBER,EPSILON,SEMICOLON}, 对应 term_prime 来说,它在推导中直接抵达 STAR,因此有 First(term_prime)={STAR}。

除了 First 集合,我们还需要了解另一种集合叫 Follow 集合。 所谓 Follow 集合就是给定某个非终结符,我们把所以在推导表达式中能直接跟着该符号的终结符找出来形成一个集合。我们看具体例子:

1,compound_stmt -> LEFT_BRACKET stmt_list RIGHT_BRACKET,

2,stmt_list -> stmt_list stmt

3,stmt -> expr sEMICOLON

从第一个表达式看到 RIGHT_BRACKET 跟在 stmt_list 后面,因此它属于集合 Follow(stmt_list)。 下面我们看一个推导过程:

compund_stmt -> LEFT_BRACKET stmt_list RIGHT_BRACKET, 使用表达式 2 替换其中的 stmt_list 就有:compund_stmt -> LEFT_BRACKET stmt_list stmt RIGHT_BRACKET

于是乎 RIGHT_BRACKET 也能在表达式中跟在 stmt 后面因此它也属于集合 Follow(stmt)。 如果使用表达式 3 去替换此时的 stmt 就有:

compound_stmt -> LEFT_BRACKET stmt_list expr SEMICOLON RIGHT_BRACKET,这意味着 SEIMICOLON, RIGHT_BRACKET 属于 Follow(expr)。

我们看看如何计算前面表达式中非终结符的 Follow 集合。 首先从表达式 stmt -> expr SEMICOLON, factor -> LEFT_PAREN expr RIGHT_PAREN 可以看到 SEMICOLON, RIGHT_PAREN 都直接跟在 expr 后面,
因此有 Follow(expr)={RIGHT_PAREN,SEMICOLON} , 从表达式 expr_prime -> PLUS term expr_prime 可以看出,所有出现在 expr_prime 能直达的终结符也必然跟在 term 的后面,因此 First(expr_prime)属于 Follow(term)。
根据表达式 term_prime -> STAR factor term_prime ,任何能出现在 term_prime 能直达终结符也必然跟在 factor 后面,因此 STAR 也属于 Follow(term_prime),于是经过一轮分析我们就有:

Follow(stmt) ={},
Follow(expr) = {SEMI, RIGHT_PAREN}
Follow(expr_prime) = {}
Follow(term) = {PLUS}
Follow(term_prime)={}
Follow(factor)={STAR}

其实一轮分析还不够,我们需要返回运用前面的分析过程直到没有任何非终结符对应的 Follow 集合发生变化为止。 综合来说寻找 Follow 集合的步骤如下:

1,如果 s 是推导表达式中的起始符号,也就是第一个表达式箭头左边的符号,那么 EOF(end of input)这个符号先加入 Follow(s)。

2,对于表达式 s -> … a b … ,其中 a 是非终结符,b 是终结符或非终结符,那么 First(b)属于 Follow(a)。

3,对于表达式 s -> … a b c … ,其中 a 是非终结符,b 是可以推导为 EPSILON 的非终结符,那么 Follow(a)就包含 First(b)和 First©。

4,对于表达式 s -> … a,其中 a 是最右边的非终结符,那么 Follow(s)是 Follow(a)的子集。

5,对于表达式 s -> … a b1 b2 … bn,其中 b1, b2…bn 对应可以推导到 EPSILON 的非终结符,那么 Follow(s)是 Follow(a)的子集。

在前面我们构造的解析跳转表中,最顶部一行对应所有终结符,最左边一列对应非终结符,然后表中的格子对应表达式编号,我们先从解析堆栈拿到当前要解析的非终结符,然后从输入中读入终结符,接着从跳转表中查询要使用的推导表达式。我们看一个具体例子,假设有如下表达式:

1, terminal -> PERKIN_ELEMER pk

2, terminal -> ADM3 adm

3, terminal -> dec_term

4, dec_term -> VT_52

5, dec_term -> VT_100

然后它对应如下解析跳转表:

[外链图片转存中…(img-K7olmHw2-1715056118298)]

根据以上信息我们得出每个表达式对应的 Select 集合如下:

Select(1) = {PERKIN_ELMER}

Select(2) = {ADM3}

Select(3) = {VT_52, VT_100}

Select(4) = {VT52}

Select(5) = {VT_100}

其中 Select(3)表示当当前输入的终结符是 VT_52, VT_100 ,解析堆栈顶部的非终结符是表达式 3 箭头左边的非终结符 terminal 时,选择表达式 3. 这里跟我们前面一节不同的是,集合针对的是表达式的编号,而不是表达式的非终结符。对于 LL(1)语法来说,
如果多个表达式箭头左边的非终结符一样,那么表达式对应的 Select 集合必须不同,要不然语法解析就无法进行,因为给定同一个非终结符,那么对应当前输入的终结符,它就可以有多个表达式可以选择,于是语法解析就不知道该选哪一个好。

我们看看生成 Select 集合的基本步骤:
1,如果一个表达式它右边所有的非终结符都可以推导出 EPSILON 或者它右边就是 EPSILON,那么我们称该表达式为 Nullable。

2,对非 nullable 的表达式,假设它有如下形式 s -> a1 a2 …an b … ,其中 s 是非终结符,a1…an 是一系列可以推导到 EPSILON 的非终结符集,b 是一个终结符或者是不能推导到 EPSILON 的非终结符,假设该表达式的编号为n,
那么 Select(n) = {First(a1), …, First(an), First(b)}, 如果 a1,…an,中包含不能推导到 EPSILON 的非终结符,那么 Select(n)={First(b)}

3, 对于 nullable 的表达式 s -> a1, a2, … an,也就是 a1, a2…an 能推导到 EPSILON,假设其编号为 n,那么 Select(n)={First(a1), First(a2),…,First(an), Follow(s)}

下面我们给出自动化创建解析跳转表的步骤:

1, 将跳转表 parse_table的每个格子设置为 error,

2, 遍历每个表达式,假设当前表达式编号为 n, lhs 为表达式箭头左边的非终结符, token 为 Select(n)中的终结符,那么有parse_table[lhs][token] = n

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

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

相关文章

ssh远程免密登录

ssh远程连接分为五个阶段 版本号协商阶段密钥和算法协商阶段认证阶段会话请求阶段交互会话阶段 而上图的SessionKey即是在阶段2:密钥和算法协商阶段,服务器端和客户端利用DH交换(Diffie-Hellman Exchange)算法、主机密钥对等参数…

零基础自学网络安全/Web安全(超详细入门到进阶)学完即可就业(含学习笔记)

一、为什么选择网络安全? 这几年随着我国《国家网络空间安全战略》《网络安全法》《网络安全等级保护2.0》等一系列政策/法规/标准的持续落地,网络安全行业地位、薪资随之水涨船高。 未来3-5年,是安全行业的黄金发展期,提前踏入…

【前端】HTML实现个人简历信息填写页面

文章目录 前言一、综合案例:个人简历信息填写页面 前言 这篇博客仅仅是对HTML的基本结构进行了一些说明,关于HTML的更多讲解以及CSS、Javascript部分的讲解可以关注一下下面的专栏,会持续更新的。 链接: Web前端学习专栏 下面我对…

OpenNJet 应用引擎:在 NGINX 基础上的云原生增强

目录 一、初识OpenNJet二、系统架构三、动手实践1.CentOS 编译环境配置1.1配置yum源:1.2.yum安装软件包1.3.创建符号连接 2.编译代码编译 OpenNJet执行 make 四、基本使用说明1.目录结构概述:2.常用命令: 五、部署 Web 应用程序配置文件修改启动 NJet 六、总结 一、…

设计宝典与速查手册,设计师必备资料合集

一、资料描述 本套设计资料,大小194.34M,共有13个文件。 二、资料目录 01-《商业设计宝典》.pdf 02-《色彩速查宝典》.pdf 03-《配色宝典》.pdf 04-《解读色彩情感密码》.pdf 05-《行业色彩应用宝典》.pdf 06-《构图宝典》.pdf 07-《创意宝典》…

PXE 批量安装部署

目录 一、PEX批量部署优点 二、PXE:预启动执行环境 三、搭建PXE远程服务器 要想全自动安装 接下来请看步骤: 一、PEX批量部署优点 规模化:同时装配多台服务器自动化:安装系统 配置各种服务远程实现:不需要光盘&…

勾股定理 口诀

def t_o(a):t int(a/2)b t*t-1c t*t1f (a*ab*bc*c)print(f,ou,a,b,c,a*ab*b,c*c)def t_j(a):t a*abint(t/2)c t-bf (a*ab*bc*c)print(f,j-,a,b,c,f,a*ab*b,c*c)for i in range(2,100,2):t_o(i)t_j(i1) 奇数平方写连续 偶数半方加减一

“A”分考试经验分享:云计算HCIE考试请注意这几点...

大家好,我是誉天云计算HCIE的王同学,于4月2日"A"分通过了云计算3.0 HCIE的认证考试。 首先感谢誉天教育对我的辅导,感谢苗苗老师和石老师对我的帮助,通过这次考试让我对华为云计算有了一定的了解。接下来我就与大家分享…

力扣刷题--数组--第一天

一、数组 数组特点: 连续内存空间存储得数据元素类型一致数组可以通过下标索引查找数据元素,可以删除、替换、添加元素等 1.1 二分查找 使用二分查找需满足得条件: 数组是有序的;数组中没有重复元素;查找的target…

PHP+B/S架构 不良事件管理系统源码 医院不良事件报告系统源码,开发技术vue2+element+laravel8

PHPB/S架构 不良事件管理系统源码 医院不良事件报告系统源码,开发技术vue2elementlaravel8 技术架构:前后端分离,仓储模式,BS架构, 开发技术:PHPvscodevue2elementlaravel8mysql5.7,专业团队研…

对C语言符号的一些冷门知识运用的剖析和总结

符号 目录* 符号 注释 - 奇怪的注释 - C风格的注释无法嵌套 - 一些特殊的注释 - 注释的规则建议 反斜杠’’ - 反斜杠有续行的作用,但要注意续行后不能添加空格 * 回车也能起到换行的作用,那续行符的意义在哪? - 反斜杠的转义功能 单引号…

参数服务器

参数服务器在ROS中主要用于实现不同节点之间的数据共享。参数服务器相当于是独立于所有节点的一个公共容器,可以将数据存储在该容器中,被不同的节点调用,当然不同的节点也可以往其中存储数据。 参数服务器,一般适用于存在数据共享…

mysql 离线安装

package download mysql https://dev.mysql.com/downloads/mysql/ libaio http://mirror.centos.org/centos/7/os/x86_64/Packages/libaio-0.3.109-13.el7.x86_64.rpm 根据自己服务器选择下载对应的安装包及依赖 删除本机自带mysql相关 # 首先排查服务器自身是否有安装对应m…

Java | Leetcode Java题解之第71题简化路径

题目&#xff1a; 题解&#xff1a; class Solution {public String simplifyPath(String path) {String[] names path.split("/");Deque<String> stack new ArrayDeque<String>();for (String name : names) {if ("..".equals(name)) {if …

企业节能降耗系统,助力企业节能降耗

随着社会的发展和能源消耗的增加&#xff0c;节能降耗已经成为企业可持续发展的重要课题。为了更有效地监测和管理能源消耗&#xff0c;越来越多的企业开始使用能耗在线监测系统。作为一种节能降耗的有力手段&#xff0c;能耗在线监测系统在企业中得到广泛应用。 能耗在线监测…

春秋云镜 CVE-2022-4230

靶标介绍&#xff1a; WP Statistics WordPress 插件13.2.9之前的版本不会转义参数&#xff0c;这可能允许经过身份验证的用户执行 SQL 注入攻击。默认情况下&#xff0c;具有管理选项功能 (admin) 的用户可以使用受影响的功能&#xff0c;但是该插件有一个设置允许低权限用户…

去中心化金融与Web3:科技驱动的金融革命

随着区块链技术的发展和普及&#xff0c;去中心化金融&#xff08;DeFi&#xff09;作为其重要应用之一&#xff0c;正在成为金融领域的一场革命。结合Web3技术&#xff0c;去中心化金融正在以前所未有的方式重新定义着金融服务和产品的交付方式&#xff0c;推动着金融领域的创…

亚信科技精彩亮相2024中国移动算力网络大会,数智创新共筑“新质生产力”

4月28至29日&#xff0c;江苏省人民政府指导、中国移动通信集团有限公司主办的2024中国移动算力网络大会在苏州举办。大会以“算力网络点亮AI时代”为主题&#xff0c;旨在凝聚生态伙伴合力&#xff0c;共同探索算力网络、云计算等数智能力空间&#xff0c;共促我国算网产业和数…

贡献思维,CF1644E. Expand the Path

目录 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 Problem - 1644E - Codeforces 二、解题报告 1、思路分析 很容易想明白被…

大数据基础工程技术团队4篇论文入选ICLR,ICDE,WWW

近日&#xff0c;由阿里云计算平台大数据基础工程技术团队主导的四篇时间序列相关论文分别被国际顶会ICLR2024、ICDE2024和WWW2024接收。 论文成果是阿里云与华东师范大学、浙江大学、南京大学等高校共同研发&#xff0c;涉及时间序列与智能运维结合的多个应用场景。包括基于P…
最新文章