从零开始写个编译器吧 - 分析非终结符

算法内卷
• 阅读 4271

tao 语言的 Parser 的语法分析是不带回溯的,自顶向下的。文法选用 LL(1),这种文法虽然略显薄弱,但还尚可用。

回顾上一章提到的 LL(1) 的定义,可以得出如下结论。

在不考虑 ε 时,对于一个非终结符,它的每一个产生式都有一个FIRST 集与之对应。而这些 FIRST 集彼此不相交。

这个特征很有用,因为这个特征很容易得出以下结论。

对于某个非终结符的所有产生式而言,任取一个终结符,该终结符……

  • 要么不属于任何一个 FIRST 集;

  • 要么仅属于某一个FIRST集,从而找到唯一的一个产生式与之对应。

基于这个结论,Parser 对某个非终结符展开形式的判定就变得明了起来。将从 Tokenizer 处读取到的 Token(即非终结符)递归的与非终结符产生式的 FIRST集做比较,一旦找到一个 FIRST 包含该 Token,即挑选该 FIRST 集对应的产生式。

整个过程可一气呵成,不需要进行任何回溯。

但这么做之前,我们必须知道每一个非终结符的所有产生式的 FIRST 集。嗯,这是必要的,赶紧记在小本子上吧,在将来的章节中我们必须要写求 FIRST 集的程序。

好,假设我们已经求出所有非终结符产生式的 FIRST 集了,是不是就可以开始写 Parser 了呢?非也,之前的结论是建立在“不考虑 ε”的前提下。

所以,让我们来讨论允许 ε 的情况。

如果产生式中可能出现 ε,那么每一个产生式中的非终结符都有可能导出 ε 的嫌疑。但 LL(1) 严格的要求一个非终结符最多只能有一个产生式可以导出 ε。这意味着我们必须明确知道每一个非终结符能不能导出 ε。好在这并非难事,让我们观察,对于。

A → α | β | ε

而言,不用说 A 肯定能导出 ε,我都抓到现行了你还说没有?!不解释,它就可以导出 ε。

对于,

B → abide

可以肯定,不能导出 ε,因为产生式右边全是终结符,终结符是不可能再展开的,因此我眼睛没看到 ε,它就导不出 ε。

但是,这种情况就比较暧昧了,

C → αβγ

因为 α、β、γ 三个是式子,能不能导出 ε 真说不准。不过可以肯定的是,只要有一个式子不能导出 ε,那么 C 一定无法导出 ε。因为那个导不出 ε 的式子永远不会“消失掉”,它霸占的位置最终会变成一组终结符串,故 C 绝不可能为空。反过来,只有当所有的式子都能导出 ε 的时候,C 才能导出 ε。

于是,判断式子是否可以导出 ε 的条件呼之欲出。

  • 终结符一定不能导出 ε。

  • 如果存在产生式 A → ε,则非终结符 A 能导出 ε。

  • 如果 A 的一个产生式 A → αβγ... 右侧所有符号都可以导出 ε,则 A 可以导出 ε。

当某个非终结符可以导出 ε 时,Parser 在发现一个终结符的时候,在与其所有产生式的 FIRST 集比较过一次无果后,还可以与 FOLLOW 集比较。如果FOLLOW 集包含这个终结符,则表明该非终结符需要导出 ε。

至此,看来我们不但要事先求出每个非终结符的所有产生式的 FIRST 集,还要判断每一个非终结符是否能导出 ε。这样,我又要在笔记本上多记一条了。

嗯,到这里我已经连续讲了3章理论了,虽然我原本打算尽量少讲理论的,但是现在发现这些东西似乎避免不了。因为之后我要写的东西全部来自这里头。不过,下一章我还会继续讲理论,然后开始写 Parser。

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
皕杰报表(关于日期时间时分秒显示不出来)
在使用皕杰报表设计器时,数据据里面是日期型,但当你web预览时候,发现有日期时间类型的数据时分秒显示不出来,只有年月日能显示出来,时分秒显示为0:00:00。1.可以使用tochar解决,数据集用selecttochar(flowdate,"yyyyMMddHH:mm:ss")fromtablename2.也可以把数据库日期类型date改成timestamp
Wesley13 Wesley13
4年前
java实现23种设计模式之解释器模式
解释器模式(InterpreterPattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在SQL解析、符号处理引擎等。构建语法树,定义终结符与非终结符。应用实例:编译器、运算表达式计算。packagecom.ceshi22;
Wesley13 Wesley13
4年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
4年前
mysql中时间比较的实现
MySql中时间比较的实现unix\_timestamp()unix\_timestamp函数可以接受一个参数,也可以不使用参数。它的返回值是一个无符号的整数。不使用参数,它返回自1970年1月1日0时0分0秒到现在所经过的秒数,如果使用参数,参数的类型为时间类型或者时间类型的字符串表示,则是从1970010100:00:0
Wesley13 Wesley13
4年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
4年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Stella981 Stella981
4年前
JVM 字节码指令表
字节码助记符指令含义0x00nop什么都不做0x01aconst\_null将null推送至栈顶0x02iconst\_m1将int型1推送至栈顶0x03iconst\_0将int型0推送至栈顶0x04iconst\_1将int型1推送至栈顶0x05ic
算法内卷
算法内卷
Lv1
凭高远望,见家乡、只在白云深处。
文章
4
粉丝
0
获赞
0