编译原理-5-LL(1)语法分析器
LL(1)语法分析器
1. 什么是LL(1)语法分析器
- 自顶向下的、递归下降的、预测分析的、适用于LL(1)文法的LL(1)语法分析器
- 自顶向下构建语法分析树
- 根节点是文法的起始符号SSS
- 每个中间节点表示对某个非终结符应用于某个产生式进行推导
- 叶节点是词法单元流$w$$
- 仅包含终结符号与特殊的文件结束符$$$
- 递归下降算法的实现框架
- 为每个非终结符写一个递归函数:但是工作量会比较大
- 内部按需调用其它非终结符对应的递归函数
1.1. 语法树生成例子
S→FS→(S+F)F→a\begin{array}{l} S \rightarrow F \\ S \rightarrow (S+F) \\ F \rightarrow a \\ \end{array} S→FS→(S+F)F→a
演示递归下降过程(指针移动):针对结构w=((a+a)+a)w=((a+a)+a)w=((a+a)+a)
- 首先进入S的递归函数,我们选择一条产生式(假设已经选择好),我们选择的是S→(S+F)S \rightarrow (S + F)S→(S+F)
- 然后我们发现还是非终结符,我们仍然选择S→(S+F)S \rightarrow (S + F)S→(S+F)
- 然后我们发现还是非终结符,我们选择S→FS \rightarrow FS→F,
- 然后我们发现是终结符,检查F→aF \rightarrow aF→a得到的结果是否和当前指针指向的位置的值,匹配则继续,否则报错。
- 之后省略,注意右括号被匹配意味相应部分递归结束。
以上的过程在本质上是DFS的过程。
- 每次都选择语法分析树最左边的非终结符进行展开,所以得到最左推导
- Q:同样是展开非终结符S,为什么前两次选择了S→(S+F)S \rightarrow (S + F)S→(S+F), 而第三次选择了S→FS \rightarrow FS→F?
- A:因为只有选择S→(S+F)S \rightarrow (S + F)S→(S+F)才能生成一个左括号开头的式子,这个是由文法决定的,也就是因为它们面对的当前词法单元不同
1.2. 使用预测分析表确定产生式
- 指明了每个非终结符在面对不同的词法单元或文件结束符时,该选择哪个产生式(按编号进行索引)或者报错
- 预测分析表如下图所示
- 例子:根据当前值选择使用不同的产生式
(
选择2号产生式a且S
则选择1号产生式a且F
则选择3号产生式- 上表中的空白标记记为报错。
- 问题:如何构造上面的表格?
1.3. Definition (LL(1) 文法)
- 如果文法G的预测分析表是无冲突的, 则G是LL(1)文法。
- 无冲突: 每个单元格里只有一个生成式(编号),不然就会出现选择问题。
- 对于当前选择的非终结符,仅根据输入中当前的词法单元即可确定需要使用哪条产生式
- 递归下降的、预测分析实现方法
1.4. 实现LL(1)算法的具体代码
1.4.1. 解析S()S()S()
1: procedure S()
2: if token = '(' then
3: MATCH('(')
4: S()
5: MATCH('+')
6: F()
7: MATCH(')')
8: else if token ='a' then
9: F()
10: else
11: ERROR(token, {'(', 'a'})
1.4.2. 解析F()F()F()
1: procedure F()
2: if token = 'a' then
3: MATCH('a')
4: else
5: ERROR(token, {'a'})
1.4.3. 解析MATCH()MATCH()MATCH()
1: procedure MATCH(t)
2: if token = t then
3: token <- NEXT-TOKEN
4: else
5: ERROR(token, t)
2. 如何计算给定文法G的预测分析表?
下文则会重点解释如何计算给定文法G的预测分析表
2.1. Definition(First(α)First(\alpha)First(α)集合)
对于任意的(产生式的右部) α∈(N∪T)∗\alpha \in (N \cup T)^*α∈(N∪T)∗
FIRST(α)=t∈T∪ϵ∣α⇒∗tβ∧α⇒∗ϵFIRST(\alpha) = {t \in T \cup {\epsilon} | \alpha \xRightarrow{*} t\beta \wedge \alpha \xRightarrow{*}\epsilon} FIRST(α)=t∈T∪ϵ∣α∗tβ∧α∗ϵ
- First(α)First(\alpha)First(α) 是可从α\alphaα推导得到的句型的首终结符号的集合
- TTT是终结符集合,ttt可能是终结符,也可能是ϵ\epsilonϵ
- 考虑非终结符A的所有产生式A→α1,A→α2,...,A→αmA \rightarrow \alpha_1,A \rightarrow \alpha_2, ... ,A \rightarrow \alpha_mA→α1,A→α2,...,A→αm,如果它们对应的First(αi)First(\alpha_i)First(αi) 集合互不相交,则只需查看当前输入词法单元, 即可确定选择哪个产生式(或报错),如果相交,则不是LL(1)文法。
2.2. Definition(Follow(A)Follow(A)Follow(A)集合)
对于任意的(产生式的左部) 非终结符A∈NA \in NA∈N
Follow(A)={t∈T∪{$}∣∃w.S⇒∗w=βAtγ}Follow(A) = \{t \in T \cup \{\$\} | \exist w.S \xRightarrow{*} w = \beta A t \gamma\} Follow(A)={t∈T∪{$}∣∃w.S∗w=βAtγ}
- Follow(A)Follow(A)Follow(A)是可能在某些句型中紧跟在A右边的终结符的集合
- ttt可以是终结符和文件终止符。
- 考虑产生式:A→aA \rightarrow aA→a,如果从α\alphaα可能推导出空串(α⇒∗ϵ\alpha \xRightarrow{*} \epsilonα∗ϵ),则只有当当前词法单元t∈Follow(A)t \in Follow(A)t∈Follow(A), 才可以选择该产生式:因为AAA可能推导没了,然后就是ttt了,我们期望ttt能够匹配当前位置上的对应符号。
2.3. 计算FirstFirstFirst集合
2.3.1. 先计算每个符号X的First(X)First(X)First(X)集合
- 如果Y1...Yi−1Y_1...Y_{i-1}Y1...Yi−1都可以推导成为空串,那么YiY_iYi的首终结符应该也是X的首终结符。
- 不断应用上面的规则, 直到每个First(X)First(X)First(X)都不再变化(闭包!!!)尤其注意递归。
2.3.2. 再计算每个符号串α\alphaα的First(α)First(\alpha)First(α)集合
α=XβFirst(α)={First(X)ϵ∈L(X)Fitst(X)∪First(β)ϵ∉L(X)\begin{array}{l} \alpha = X \beta \\ First(\alpha) = \begin{cases} First(X) & \epsilon \in L(X) \\ Fitst(X)\cup First(\beta) & \epsilon \notin L(X) \\ \end{cases} \end{array} α=XβFirst(α)={First(X)Fitst(X)∪First(β)ϵ∈L(X)ϵ∈/L(X)
2.3.3. 求解First(α)First(\alpha)First(α)的例子
(1)X→Y(2)X→a(3)Y→ϵ(4)Y→c(5)Z→d(6)Z→XYZ\begin{array}{l} (1) & X \rightarrow Y \\ (2) & X \rightarrow a \\ (3) & Y \rightarrow \epsilon \\ (4) & Y \rightarrow c \\ (5) & Z \rightarrow d \\ (6) & Z \rightarrow XYZ \\ \end{array} (1)(2)(3)(4)(5)(6)X→YX→aY→ϵY→cZ→dZ→XYZ
FIRST(X)={a,c,ϵ}FIRST(Y)={c,ϵ}FIRST(Z)={a,c,d}FIRST(XYZ)=FIRST(X)∪FISRT(YZ)=FIRST(X)∪FIRST(Y)∪FIRST(Z)={a,c,d}\begin{array}{l} FIRST(X) = \{a,c,\epsilon\} \\ FIRST(Y) = \{c, \epsilon\} \\ FIRST(Z) = \{a, c, d\} \\ FIRST(XYZ) = FIRST(X) \cup FISRT(YZ) \\ = FIRST(X) \cup FIRST(Y) \cup FIRST(Z) = \{a, c, d\}\\ \end{array} FIRST(X)={a,c,ϵ}FIRST(Y)={c,ϵ}FIRST(Z)={a,c,d}FIRST(XYZ)=FIRST(X)∪FISRT(YZ)=FIRST(X)∪FIRST(Y)∪FIRST(Z)={a,c,d}
注意不要忘记和ϵ\epsilonϵ相关的操作。
2.4. 为每个非终结符X计算Follow(X)Follow(X)Follow(X)集合
- 不断应用上面的规则, 直到每个Follow(X) 都不再变化(闭包!!!)
- Follow例子:注意闭包,可以无限扩充
(1)X→Y(2)X→a(3)Y→ϵ(4)Y→c(5)Z→d(6)Z→XYZ\begin{array}{l} (1) & X \rightarrow Y \\ (2) & X \rightarrow a \\ (3) & Y \rightarrow \epsilon \\ (4) & Y \rightarrow c \\ (5) & Z \rightarrow d \\ (6) & Z \rightarrow XYZ \\ \end{array} (1)(2)(3)(4)(5)(6)X→YX→aY→ϵY→cZ→dZ→XYZ
FOLLOW(X)={a,c,d,$}FOLLOW(Y)={a,c,d,$}FOLLOW(Z)=∅\begin{array}{l} FOLLOW(X) = \{a, c, d, \$\} \\ FOLLOW(Y) = \{a, c, d, \$\} \\ FOLLOW(Z) = \emptyset \\ \end{array} FOLLOW(X)={a,c,d,$}FOLLOW(Y)={a,c,d,$}FOLLOW(Z)=∅
2.5. 如何根据FirstFirstFirst与FollowFollowFollow集合计算给定文法G的预测分析表?
按照以下规则, 在表格[A, t] 中填入生成式A→α(编号)A \rightarrow \alpha(编号)A→α(编号)
t∈First(α)(1)α⇒∗ϵ∧t∈Follow(A)(2)\begin{array}{l} t \in First(\alpha) & (1)\\ \alpha \xRightarrow{*} \epsilon \wedge t \in Follow(A) & (2)\\ \end{array} t∈First(α)α∗ϵ∧t∈Follow(A)(1)(2)
对于每一个生成式只要满足上面一条规则即可(或关系),首先,在下单元格中可以填写A→αA \rightarrow \alphaA→α,则可以推导出α⇒∗ϵ∧t∈Follow(A)\alpha \xRightarrow{*} \epsilon \wedge t \in Follow(A)α∗ϵ∧t∈Follow(A)(必要条件),但是由于是LL(1)文法的唯一性,那么必要条件等价于充分条件,也就是这是一个充要条件。
ttt | |
---|---|
AAA | A→αA \rightarrow \alphaA→α |
2.6. Definition (LL(1) 文法)
如果文法G的预测分析表是无冲突的, 则G是LL(1)文法。
(1)X→Y(2)X→a(3)Y→ϵ(4)Y→c(5)Z→d(6)Z→XYZ\begin{array}{l} (1) & X \rightarrow Y \\ (2) & X \rightarrow a \\ (3) & Y \rightarrow \epsilon \\ (4) & Y \rightarrow c \\ (5) & Z \rightarrow d \\ (6) & Z \rightarrow XYZ \\ \end{array} (1)(2)(3)(4)(5)(6)X→YX→aY→ϵY→cZ→dZ→XYZ
FIRST(X)={a,c,ϵ}FIRST(Y)={c,ϵ}FIRST(Z)={a,c,d}FIRST(XYZ)=FIRST(X)∪FISRT(YZ)=FIRST(X)∪FIRST(Y)∪FIRST(Z)={a,c,d}\begin{array}{l} FIRST(X) = \{a,c,\epsilon\} \\ FIRST(Y) = \{c, \epsilon\} \\ FIRST(Z) = \{a, c, d\} \\ FIRST(XYZ) = FIRST(X) \cup FISRT(YZ) \\ = FIRST(X) \cup FIRST(Y) \cup FIRST(Z) = \{a, c, d\}\\ \end{array} FIRST(X)={a,c,ϵ}FIRST(Y)={c,ϵ}FIRST(Z)={a,c,d}FIRST(XYZ)=FIRST(X)∪FISRT(YZ)=FIRST(X)∪FIRST(Y)∪FIRST(Z)={a,c,d}
FOLLOW(X)={a,c,d,$}FOLLOW(Y)={a,c,d,$}FOLLOW(Z)=∅\begin{array}{l} FOLLOW(X) = \{a, c, d, \$\} \\ FOLLOW(Y) = \{a, c, d, \$\} \\ FOLLOW(Z) = \emptyset \\ \end{array} FOLLOW(X)={a,c,d,$}FOLLOW(Y)={a,c,d,$}FOLLOW(Z)=∅
3. LL(1) 语法分析器
- L:从左向右(left-to-right) 扫描输入
- L:构建最左(leftmost) 推导
- 1:只需向前看一个输入符号便可确定使用哪条产生式
3.1. 非递归的预测分析方法
非递归算法效率会高一些
3.2. 改造文法成为LL(1)文法
- 改造它
- 消除左递归
- 提取左公因子
- 有左递归,有左公因子,则必然不是LL(1)文法
- 没有左递归,没有左公因子,则未必是LL(1)文法
3.2.1. 左递归
E→E+T∣E−T∣TT→T∗F∣T/F∣FF→(E)∣id∣num\begin{array}{l} E \rightarrow E + T | E - T | T \\ T \rightarrow T * F | T / F | F \\ F \rightarrow (E) | id | num \\ \end{array} E→E+T∣E−T∣TT→T∗F∣T/F∣FF→(E)∣id∣num
- E 在不消耗任何词法单元的情况下, 直接递归调用E, 造成死循环,E左侧没有非终结符,不能消耗符号。
- 存在FIRST(E+T)∩FIRST(T)≠∅FIRST(E + T) \cap FIRST(T) \neq \emptysetFIRST(E+T)∩FIRST(T)=∅,不是LL(1)文法
- 消除左递归
3.2.2. 消除左递归
- 左递归:
E→E+T∣TE \rightarrow E + T | T E→E+T∣T
消除左递归(至少会消耗一个+)
E→TE′E′→+TE′∣ϵ\begin{array}{l} E \rightarrow TE' \\ E' \rightarrow +TE' | \epsilon \\ \end{array} E→TE′E′→+TE′∣ϵ将左递归转为右递归,注: 右递归对应右结合; 需要在后续阶段进行额外处理,至少语法分析可以通过。
3.2.3. 左递归例子
A→Aα1∣Aα2∣...Aαm∣β1∣β2∣...βn\begin{array}{l} A \rightarrow A\alpha_1 | A\alpha_2 |...A\alpha_m|\beta_1|\beta_2|...\beta_n \\ \end{array} A→Aα1∣Aα2∣...Aαm∣β1∣β2∣...βn
- βi\beta_iβi都不以A开发
A→β1A′∣β2A′∣...∣βnA′A′→α1A′∣α2A′∣...∣αmA′∣ϵ\begin{array}{l} A \rightarrow \beta_1A' | \beta_2A' | ... | \beta_nA' \\ A' \rightarrow \alpha_1A'|\alpha_2A'|...|\alpha_mA'|\epsilon \\ \end{array} A→β1A′∣β2A′∣...∣βnA′A′→α1A′∣α2A′∣...∣αmA′∣ϵ
3.2.4. 左递归例子II
E→E+T∣TT→T∗F∣FF→(E)∣id\begin{array}{l} E \rightarrow E + T|T \\ T \rightarrow T * F|F \\ F \rightarrow (E) | id \\ \end{array} E→E+T∣TT→T∗F∣FF→(E)∣id
- 消除左递归
E→TE′E′→+TE′∣ϵT→FT′T′→∗FT′∣ϵF→(E)∣id∣num\begin{array}{l} E \rightarrow TE' \\ E' \rightarrow +TE' | \epsilon \\ T \rightarrow FT' \\ T' \rightarrow *FT' | \epsilon \\ F \rightarrow (E)|id|num \\ \end{array} E→TE′E′→+TE′∣ϵT→FT′T′→∗FT′∣ϵF→(E)∣id∣num
3.2.5. 非直接左递归
S→Aa∣bA→Ac∣Sb∣ϵS⇒Aa⇒Sda\begin{array}{l} S \rightarrow Aa|b \\ A \rightarrow Ac|Sb|\epsilon\\ S \Rightarrow Aa \Rightarrow Sda \\ \end{array} S→Aa∣bA→Ac∣Sb∣ϵS⇒Aa⇒Sda
3.2.6. 消除左递归算法
Ak→Alα⇒l>kA_k \rightarrow A_l\alpha \Rightarrow l > k Ak→Alα⇒l>k
- 不会出现左递归,也不会出现间接左递归:因为不会回到自己
- 例子:
S→Aa∣bA→Ac∣Sb∣ϵ\begin{array}{l} S \rightarrow Aa|b \\ A \rightarrow Ac|Sb|\epsilon \\ \end{array} S→Aa∣bA→Ac∣Sb∣ϵ
考虑1号非终结符S,替换掉A中的S
A→Ac∣Aad∣bd∣ϵA \rightarrow Ac|Aad|bd|\epsilon A→Ac∣Aad∣bd∣ϵ
考虑2号非终结符A,使用A’进行替换,首先找不含A的生成A’,然后交换包含A的位置
S→Aa∣bA→bdA′∣A′A′→cA′∣adA′∣ϵ\begin{array}{l} S \rightarrow Aa|b \\ A \rightarrow bdA'|A' \\ A' \rightarrow cA'|adA'|\epsilon \\ \end{array} S→Aa∣bA→bdA′∣A′A′→cA′∣adA′∣ϵ
3.2.7. 消除左递归的例子
注意"("等部分首终结符,最好选择一个顺序:比如从下往上算。
E→TE′E′→+TE′∣ϵT→FT′T′→∗FT′∣ϵF→(E)∣id∣num\begin{array}{l} E \rightarrow TE' \\ E' \rightarrow +TE' | \epsilon \\ T \rightarrow FT' \\ T' \rightarrow *FT' | \epsilon \\ F \rightarrow (E)|id|num \\ \end{array} E→TE′E′→+TE′∣ϵT→FT′T′→∗FT′∣ϵF→(E)∣id∣num
First从左侧开始找First(F)={(,id}First(T′)={∗,ϵ}First(T)={(,id}First(E)={(,id}First(E′)={+,ϵ}Follow从右侧开始找Follow(E)={),$}Follow(E′)={),$}Follow(T)={+,),$}Follow(T′)={+,),$}Follow(F)={+,∗,),$}\begin{array}{l} First从左侧开始找 \\ First(F) = \{(,id\} \\ First(T') = \{*,\epsilon\} \\ First(T) = \{(,id\} \\ First(E) = \{(,id\} \\ First(E') = \{+ , \epsilon\} \\ Follow从右侧开始找 \\ Follow(E) = \{), \$\} \\ Follow(E') = \{), \$\} \\ Follow(T) = \{+, ), \$\} \\ Follow(T') = \{+, ), \$\} \\ Follow(F) = \{+, ∗, ), \$\} \\ \end{array} First从左侧开始找First(F)={(,id}First(T′)={∗,ϵ}First(T)={(,id}First(E)={(,id}First(E′)={+,ϵ}Follow从右侧开始找Follow(E)={),$}Follow(E′)={),$}Follow(T)={+,),$}Follow(T′)={+,),$}Follow(F)={+,∗,),$}
3.2.8. 提取左公因子
包含左公因子
S→iEtS∣iEtSeS∣aE→b\begin{array}{l} S \rightarrow iEtS|iEtSeS|a \\ E \rightarrow b \end{array} S→iEtS∣iEtSeS∣aE→b
提取左公因子
S→iEtSS′∣aS′→eS∣ϵE→b\begin{array}{l} S \rightarrow iEtSS'|a \\ S' \rightarrow eS|\epsilon \\ E \rightarrow b \\ \end{array} S→iEtSS′∣aS′→eS∣ϵE→b
解决二义性(人为解决): 选择S′→eSS' \rightarrow eSS′→eS, 将else与前面最近的then关联起来
3.2.9. $$$符号的必要性
编译原理-5-LL(1)语法分析器相关推荐
- 编译原理课程设计:语法分析器
配套代码: 编译原理课程设计:语法分析器-Python文档类资源-CSDN下载所使用的开发环境:Windows10.python(PyCharm)环境注意:expression更多下载资源.学习资料请 ...
- 编译原理:LL(1)语法分析器的实现(内含代码详细注释)
自顶向下语法分析器的设计与实现 目录 一.说明 二.程序功能及运行截图 功能 运行截图 三.算法逻辑和程序流程图 定义的主要变量或存储结构 (1)消除直接左递归 (2)求FIRST集合 (3)求FO ...
- c语言语法分析源程序,深入浅出编译原理-5-一个简单语法分析器的C语言实现
引言 前面已经介绍了编译器的预处理,词法分析,词法分析器的实现,也在其中说到了语法分析的任务和过程. 语法分析的输入是词法单元序列,然后根据语言的文法表示(展开式),利用有限状态机理论,生成抽象语法树 ...
- 从0开始的python学习:编译原理实验4:语法分析器1--预测分析器构造
这里刚开始试用python的函数功能,可能把局部变量和全局变量给写乱了,后期交之前还想再优化的时候发现越改越乱,太真实的hhh 实验四:语法分析器1–预测分析器构造 实验目的: 通过编写一个预测分析器 ...
- 编译原理实验-递归下降语法分析器的构建
实验目的: 针对给定的上下文无关文法,编制一个递归下降分析程序. 分析: 递归下降语法分析的前提是保证LL(1)文法 递归下降的思路就是暴力dfs.对每个程序直接不管三七二十一搜进去,只要能搜到就继续 ...
- 编译原理之词法与语法(2)
书接上回(要看前面的点这里) 一.高级语言介绍 高级语言指定的是不同于机器语言与汇编语言需要经过编译才能被执行的语言,如c,c++,java,py等(我想大家都知道).对于他们是强制性.应用性.算法 ...
- 【编译原理】高级语言及其语法描述
文章目录 高级语宫及其语法描述 (一)程序语言的定义 (二)高级语言的一般特性 1.高级语言的分类 2.数据类型与操作 3.语句与控制结构 (三)程序语言的语法描述 1.几个重要概念 2.上下文无关文 ...
- 【编译原理笔记10】语法制导翻译:在递归预测过程中进行翻译,L属性定义的自底向上翻译
本次笔记内容: 5-7 在递归预测过程中进行翻译 5-8 L属性定义的自底向上翻译 本节课幻灯片,见于我的 GitHub 仓库:第10讲 语法制导翻译_3 文章目录 在递归的预测分析过程中进行翻译 算 ...
- 编译原理(十)语法制导翻译
语法制导定义 Syntax-Driect Definition SDD 语法制导的翻译方案Syntax-driected translation scheme SDT,可以看作SDD的具体实现方案,S ...
- 【编译原理笔记09】语法制导翻译:语法制导翻译方案,在非递归的预测分析过程中进行翻译
本次笔记内容: 5-5 语法制导翻译方案 5-6 在非递归的预测分析过程中进行翻译 本节课幻灯片,见于我的 GitHub 仓库:第9讲 语法制导翻译_2 文章目录 语法制导翻译方案 语法制导翻译方案 ...
最新文章
- 杨彪 | 一次线上游戏卡死的解决历程(文末赠书福利)
- 3D物体识别的如果检验
- 递归--基于回溯和递归的八皇后问题解法
- 机器学习kaggle竞赛实战-泰坦尼克号
- [Java学习资料] [成长之路]
- river mongodb mysql_mongodb与mysql的应用场景?
- SpringBoot2.1.5 (32)--- SpringBoot整合 Freemaker 模板引擎
- mac idea命令精简使用版常用指令
- iOS SDK:预览和打开文档
- C# 判断文件/文件夹是否存在;
- Java高并发编程实战2,原子性、可见性、有序性,傻傻分不清
- 微信H5活动抽奖单页面模板源码
- 微信小程序数据库一次查询多个条件的方法
- 新人如何快速融入团队
- Gradle 2.0 用户指南翻译——第十九章. Gradle 守护进程
- android 视频添加音乐,手机怎么给视频加音乐,安卓手机软件怎么给视频添加音乐比较方便...
- 表单验证设计的用户体验基本原则
- JNLP文件的打开方法
- OEM版Win7激活原理
- element-ui table中span-method(行合并)方法使用
热门文章
- [英语歌曲]夜之祈祷: Night Prayer
- 西亚历史上的十个超级大国
- python知识笔记_python笔记(1)--基础知识
- 电商网站学习demo(一)--首页
- lidar360 5.4软件安装
- jquery中each用法
- Nordic蓝牙系列芯片对比 及nRF52832详细介绍
- python -day06初始面向对象
- Linux cpuidle framework(3)_ARM64 generic CPU idle driver
- 我用【c++】写出了会说话的学生考勤系统