自上而下的语法分析

自上而下的语法分析

自上而下 指 从文法的开始符号开始,逐步向下构建出整棵语法树。这样的分析过程会遇到以下问题:

  • 文法左递归:导致分析过程无限循环

  • 回溯问题:在进入错误分支后,需要恢复进入分支前的现场,导致程序设计复杂

构造不会陷入死循环的、不需要回溯的语法分析算法

1. 消除左递归

文法可以消除左递归的条件

  • 不含以 ϵ 为右部的产生式

  • 不含回路,即不存在 PP

1.1 直接左递归

PPα|β

该产生式表示 以一个beta开头,以若干个alpha结尾的串 。即 βα\*

需要设计出不含左递归的文法:左递归变右递归。

PβP

PαP|ϵ

容易知道,这两种文法是等价的。

CAUTION 左递归的实质是语法树不断增长而输入串的匹配没有任何推进,左递归改为右递归后,虽然语法树还可能一直增长,但是输入串的匹配指针在一直前进。

推广 更复杂产生式的直接左递归消除

含有左递归的文法 PPα1|Pα2|...|Pαm|β1|β2|...|βn

如此改为右递归:

Pβ1P|β2P|...|βnP

Pα1P|α2P|...|αmP|ϵ

1.2 间接左递归

SQc|c

QRb|b

RSa|a

若从 S 开始推导,会出现 SQcRbcSabc ,出现循环,为间接左递归。

为了消除这种依赖关系,进行如下操作:

QRb|bSab|ab|b

SSabc|abc|bc|c

这样出现了直接左递归,可用之前提到的方法进行消除,即:

SabcS|bcS|cS

SabcS|ϵ

由于文法的轮换对称性,对其他非终结符进行操作得到的文法是等价的。

1.3 消除左递归的通用算法

sort(G) # 把文法G的所有非终结符按照一种顺序排列 P_1, P_2, ..., P_N
for i in range(1, N + 1): # 把P_i的规则修改为 P_i -> a...|P_{i+1}...|P_{i+2}...|...
                          # 即只能包含P_i之后的非终结符

    for j in range(1, i):
        把形如 P_i -> P_j\gamma 的产生式中的P_j替换为它的定义式(消除P_j)
    消除关于P_i的直接左递归
simplify(G) # 化简文法G,删除无法到达的文法

由于在外层循环执行到 i 时, Pj 均已经只包含 Pj 之前的非终结符,所以该算法可以正确执行,消除 Pi 的左递归。

2. 消除回溯

为了消除回溯,必须保证选派的候选项可以 100% 成功匹配输入串,这就要求我们合理构造 First 集合和 Follow 集合。

2.1 First集合

假设当前需要匹配的输入串的 token 为 a ,那么文法的规则中只能有至多一个以 a 开头,或者可以推导出以 a 开头的串,由此定义候选项的 First 集合:

First(α)={αa...,aT}

特别地,若 alpha 可以推导出 ϵ ,则 ϵFirst(α)

在满足 文法的规则中只能有至多一个以 a 开头 这一条件的情况下,若 aFirst(αi) ,则一定会选派 αi 去完成对 a 的匹配。

但是并非所有的文法都满足上述条件,即 First(αi)First(αj)= ,有时可以通过提取左公共因子的方式使其满足这一条件。

2.2 Follow集合

上述的 First 集合没有考虑使用 ϵ 进行匹配的情况。

a 可以被 ϵ 匹配,则 a 一定可以紧跟在当前正在匹配的非终结符 A 的后面出现,即存在一条规则,满足 P...Aa... 。由此定义非终结符的 Follow 集合:

Follow(A)={S...Aa...,aT}

特别地,若 S 可推导出 ...A ,则 ϵFollow(A)

2.3 LL(1)文法

  • 文法不含左递归

  • 文法中的每个非终结符的产生式的所有候选项的 First 集合两两不相交

  • 对于文法中的每个非终结符,若它的某个候选项的 First 集合中存在 ϵ ,则所有候选项的 First 集合不能和该非终结符的 Follow 集合相交

上述第三条规则保证了不会出现 a 可以同时匹配某个非空候选项和 ϵ 的情况。

LL(1)文法分析过程

  1. aFirst(αi) ,则指派 αi 完成匹配

  2. a 不属于任何一个候选项的 First 集合,则:

若某个候选项的 First 集合中有 ϵ ,且 aFollow(A) ,则用 ϵ 匹配。

否则, a 的出现为语法错误。

2.4 First集合的构造

对于每个 XVTVN ,连续使用下面的规则,直到每个候选项的 First 集合均不再发生变化:

  1. XVT ,则 First(X)=X

  2. XVN ,且有产生式 Xa ,则将 a 加入 First(X) 中。特别地,若 Xϵ 也是一条产生式,则也将 ϵ 加入 First(X) 中。

  3. XY... 是一条产生式,且 YVN ,则将 First(Y) 中所有非 ϵ 元素加入 First(X) 中。

  4. XY1Y2...Yi1Yi...Yk 是一个产生式,Y1,Y2,...,Yi1VN ,且 Y1Yi1First 集合中均含有 ϵ ,则将 First(Yi) 中的非 ϵ 元素加入 First(X) 。特别地,若 Y1YkFirst 集合中均含有 ϵ ,则将 ϵ 加入 First(X)

虽然一个产生式 XY1Y2...Yk 的前几个非终结符可能含有 ϵ ,但是这些 ϵ 的后面还可能会有别的串,所以在第 4 步中,只有所有非终结符均含有 ϵ 时,才将 ϵ 加入 First(X)

构造任意符号串的First集合

对文法 G 的任何符号串 α=X1X2...Xn 构造 First(α) 的算法如下:

  1. First(α)=First(X1)

  2. 若对于任意 1ji1 ,均有 ϵFirst(Xj) ,则置 First(α)=First(α)(First(Xi) \ {ϵ}) 。特别地,若 X1XnFirst 集合均含有 ϵ ,则置 First(α)=First(α){ϵ}

2.5 Follow 集合的构造

假设空串用 # 表示,对于文法 G 的每个非终结符 A 构造 Follow 集合的办法是,连续使用下面的规则,直到所有非终结符的 Follow 集合都不再发生变化:

  1. 对于文法的开始符号 S ,将 # 加入 Follow(S)

  2. AαBβ 是一个产生式,则将 First(β) \ {ϵ} 中的元素加入 Follow(B)

  3. AαB 是一个产生式,则将 Follow(A) 中的元素加入 Follow(B) 中。

  4. AαBβ 是一个产生式,且 βϵ ,则将 Follow(A) 中的元素加入 Follow(B) 中。

3 递归下降分析器

按照上述方法进行分析即可,程序实现较为简单。

posted @   Franky0705  阅读(146)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示