【编译原理】自顶向下语法分析深度解析:从 LL(1) 文法判定、改写到预测分析表


1. 引言

在编译器前端的设计中,语法分析(Syntax Analysis)扮演着至关重要的角色。它的主要任务是根据词法分析器提供的记号(Tokens)流,识别出源程序的语法结构。

自顶向下语法分析(Top-Down Parsing)作为一种直观且应用广泛的分析方法,其核心思想是:从文法的开始符号出发,试图推导出与输入符号串相匹配的句子 。本文将结合编译原理的核心考点,深度剖析如何通过消除左递归、提取左公因子将文法改写为 LL(1) 文法 ,并详细讲解 FIRST 集、FOLLOW 集 的计算以及 预测分析表 的构建过程。


2. 自顶向下语法分析的核心理论回顾

在动手解题之前,我们需要牢记自顶向下语法分析的三个核心概念:

2.1 消除左递归与提取左公因子

自顶向下分析不能处理含有 左递归 的文法(会导致无限递归)。

  • 消除直接左递归:

    若文法产生式为 A→Aα∣βA \to A\alpha \mid \betaA→Aα∣β,其中 β\betaβ 不以 AAA 开头,可改写为:
    A→βA′A \to \beta A'A→βA′
    A′→αA′∣ϵA' \to \alpha A' \mid \epsilonA′→αA′∣ϵ

  • 提取左公因子:

    若产生式为 A→αβ1∣αβ2A \to \alpha\beta_1 \mid \alpha\beta_2A→αβ1∣αβ2,通过提取公因子 α\alphaα,可改写为:
    A→αA′A \to \alpha A'A→αA′
    A′→β1∣β2A' \to \beta_1 \mid \beta_2A′→β1∣β2

2.2 LL(1) 文法的判别条件

一个文法是 LL(1) 文法,当且仅当对于每个非终结符 AAA 的任意两个不同的产生式 A→α∣βA \to \alpha \mid \betaA→α∣β,满足以下条件:

  1. FIRST(α)∩FIRST(β)=∅FIRST(\alpha) \cap FIRST(\beta) = \emptysetFIRST(α)∩FIRST(β)=∅
  2. 若 β⇒∗ϵ\beta \Rightarrow^* \epsilonβ⇒∗ϵ,则 FIRST(α)∩FOLLOW(A)=∅FIRST(\alpha) \cap FOLLOW(A) = \emptysetFIRST(α)∩FOLLOW(A)=∅

3. 经典习题深度拆解与实战演练

下面我们通过 5 道经典的编译原理大题,完整走一遍自顶向下语法分析的全流程。


【题目 4-1】文法推导与非递归预测分析

已知文法 G[S]G[S]G[S]:

  • S→a∣∧∣(T)S \to a \mid \wedge \mid (T)S→a∣∧∣(T)
  • T→T,S∣ST \to T,S \mid ST→T,S∣S
① 给出串 (a,(a,a))(((a,a), ^, (a)),a) 的最左推导过程
  • 最左推导的核心: 每次总是替换推导树中最左边的非终结符。

推导1:(a,(a,a))

  1. S⇒(T)S \Rightarrow (T)S⇒(T)
  2. ⇒(T,S)\Rightarrow (T, S)⇒(T,S)
  3. ⇒(S,S)\Rightarrow (S, S)⇒(S,S)
  4. ⇒(a,S)\Rightarrow (a, S)⇒(a,S)
  5. ⇒(a,(T))\Rightarrow (a, (T))⇒(a,(T))
  6. ⇒(a,(T,S))\Rightarrow (a, (T, S))⇒(a,(T,S))
  7. ⇒(a,(S,S))\Rightarrow (a, (S, S))⇒(a,(S,S))
  8. ⇒(a,(a,S))\Rightarrow (a, (a, S))⇒(a,(a,S))
  9. ⇒(a,(a,a))\Rightarrow (a, (a, a))⇒(a,(a,a)) (推导成功)

推导2:(((a,a), ^, (a)),a)

  1. S⇒(T)S \Rightarrow (T)S⇒(T)
  2. ⇒(T,S)\Rightarrow (T, S)⇒(T,S)
  3. ⇒(S,S)\Rightarrow (S, S)⇒(S,S)
  4. ⇒((T),S)\Rightarrow ((T), S)⇒((T),S)
  5. ⇒((T,S),S)\Rightarrow ((T, S), S)⇒((T,S),S)
  6. ⇒((T,S,S),S)\Rightarrow ((T, S, S), S)⇒((T,S,S),S)
  7. ⇒((S,S,S),S)\Rightarrow ((S, S, S), S)⇒((S,S,S),S)
  8. ⇒(((T),S,S),S)\Rightarrow (((T), S, S), S)⇒(((T),S,S),S)
  9. ⇒(((T,S),S,S),S)\Rightarrow (((T, S), S, S), S)⇒(((T,S),S,S),S)
  10. ⇒(((S,S),S,S),S)\Rightarrow (((S, S), S, S), S)⇒(((S,S),S,S),S)
  11. ⇒(((a,S),S,S),S)\Rightarrow (((a, S), S, S), S)⇒(((a,S),S,S),S)
  12. ⇒(((a,a),S,S),S)\Rightarrow (((a, a), S, S), S)⇒(((a,a),S,S),S)
  13. ⇒(((a,a),∧,S),S)\Rightarrow (((a, a), \wedge, S), S)⇒(((a,a),∧,S),S)
  14. ⇒(((a,a),∧,(T)),S)\Rightarrow (((a, a), \wedge, (T)), S)⇒(((a,a),∧,(T)),S)
  15. ⇒(((a,a),∧,(S)),S)\Rightarrow (((a, a), \wedge, (S)), S)⇒(((a,a),∧,(S)),S)
  16. ⇒(((a,a),∧,(a)),S)\Rightarrow (((a, a), \wedge, (a)), S)⇒(((a,a),∧,(a)),S)
  17. ⇒(((a,a),∧,(a)),a)\Rightarrow (((a, a), \wedge, (a)), a)⇒(((a,a),∧,(a)),a) (推导成功)

② 对文法 GGG 进行改写(消除左递归),并判断改写后的文法是否为 LL(1)

原产生式中 T→T,S∣ST \to T,S \mid ST→T,S∣S 存在直接左递归,引入新非终结符 T′T'T′ 改写文法:

  • S→a∣∧∣(T)S \to a \mid \wedge \mid (T)S→a∣∧∣(T)
  • T→ST′T \to S T'T→ST′
  • T′→,ST′∣ϵT' \to ,ST' \mid \epsilonT′→,ST′∣ϵ

LL(1) 判断:

  • FIRST(a)∩FIRST(∧)∩FIRST((T))=∅FIRST(a) \cap FIRST(\wedge) \cap FIRST((T)) = \emptysetFIRST(a)∩FIRST(∧)∩FIRST((T))=∅
  • FIRST(,ST′)∩FOLLOW(T′)={,}∩{),#}=∅FIRST(,ST') \cap FOLLOW(T') = \{ , \} \cap \{ ), \# \} = \emptysetFIRST(,ST′)∩FOLLOW(T′)={,}∩{),#}=∅

因此改写后的文法是 LL(1) 文法


③ 构建预测分析表
非终结符 aaa ^ ((( ))) ,,, #\##
SSS S→aS \to aS→a S→∧S \to \wedgeS→∧ S→(T)S \to (T)S→(T)
TTT T→ST′T \to ST'T→ST′ T→ST′T \to ST'T→ST′ T→ST′T \to ST'T→ST′
T′T'T′ T′→ϵT' \to \epsilonT′→ϵ T′→,ST′T' \to ,ST'T′→,ST′ T′→ϵT' \to \epsilonT′→ϵ

④ 给出输入串 (a,a)# 的分析过程
步骤 符号栈 输入串 所用产生式 / 动作
1 #S\#S#S (a,a)#(a,a)\#(a,a)# S→(T)S \to (T)S→(T)
2 #)T(\#)T(#)T( (a,a)#(a,a)\#(a,a)# 匹配 (((
3 #)T\#)T#)T a,a)#a,a)\#a,a)# T→ST′T \to ST'T→ST′
4 #)T′S\#)T'S#)T′S a,a)#a,a)\#a,a)# S→aS \to aS→a
5 #)T′a\#)T'a#)T′a a,a)#a,a)\#a,a)# 匹配 aaa
6 #)T′\#)T'#)T′ ,a)#,a)\#,a)# T′→,ST′T' \to ,ST'T′→,ST′
7 #)T′S,\#)T'S,#)T′S, ,a)#,a)\#,a)# 匹配 ,,,
8 #)T′S\#)T'S#)T′S a)#a)\#a)# S→aS \to aS→a
9 #)T′a\#)T'a#)T′a a)#a)\#a)# 匹配 aaa
10 #)T′\#)T'#)T′ )#)\#)# T′→ϵT' \to \epsilonT′→ϵ
11 #)\#)#) )#)\#)# 匹配 )))
12 #\## #\## 接受(成功)

【题目 4-2】FIRST 集与 FOLLOW 集计算实战

已知文法 G[E]G[E]G[E] 如下:

  • E→TE′E \to TE'E→TE′
  • E′→+E∣ϵE' \to +E \mid \epsilonE′→+E∣ϵ
  • T→FT′T \to FT'T→FT′
  • T′→T∣ϵT' \to T \mid \epsilonT′→T∣ϵ
  • F→PF′F \to PF'F→PF′
  • F′→∗F′∣ϵF' \to *F' \mid \epsilonF′→∗F′∣ϵ
  • P→(E)∣a∣b∣∧P \to (E) \mid a \mid b \mid \wedgeP→(E)∣a∣b∣∧
① 计算该文法的 FIRST 集和 FOLLOW 集

为了彻底防止 KaTeX 渲染报错,集合中的特殊符号均采用标准 Markdown 格式表示:

非终结符 FIRST 集 FOLLOW 集
EEE { (, a, b, ^ } { ), # }
E′E'E′ { +, ϵ\epsilonϵ } { ), # }
TTT { (, a, b, ^ } { +, ), # }
T′T'T′ { (, a, b, ^, ϵ\epsilonϵ } { +, ), # }
FFF { (, a, b, ^ } { (, a, b, ^, +, ), # }
F′F'F′ { *, ϵ\epsilonϵ } { (, a, b, ^, +, ), # }
PPP { (, a, b, ^ } { *, (, a, b, ^, +, ), # }
② 构建预测分析表

根据 FIRST 集和 FOLLOW 集,可构建出如下预测分析表:

非终结符 +++ ∗*∗ ((( ))) aaa bbb ^ #\##
EEE E→TE′E \to TE'E→TE′ E→TE′E \to TE'E→TE′ E→TE′E \to TE'E→TE′ E→TE′E \to TE'E→TE′
E′E'E′ E′→+EE' \to +EE′→+E E′→ϵE' \to \epsilonE′→ϵ E′→ϵE' \to \epsilonE′→ϵ
TTT T→FT′T \to FT'T→FT′ T→FT′T \to FT'T→FT′ T→FT′T \to FT'T→FT′ T→FT′T \to FT'T→FT′
T′T'T′ T′→ϵT' \to \epsilonT′→ϵ T′→TT' \to TT′→T T′→ϵT' \to \epsilonT′→ϵ T′→TT' \to TT′→T T′→TT' \to TT′→T T′→TT' \to TT′→T T′→ϵT' \to \epsilonT′→ϵ
FFF F→PF′F \to PF'F→PF′ F→PF′F \to PF'F→PF′ F→PF′F \to PF'F→PF′ F→PF′F \to PF'F→PF′
F′F'F′ F′→ϵF' \to \epsilonF′→ϵ F′→∗F′F' \to *F'F′→∗F′ F′→ϵF' \to \epsilonF′→ϵ F′→ϵF' \to \epsilonF′→ϵ F′→ϵF' \to \epsilonF′→ϵ F′→ϵF' \to \epsilonF′→ϵ F′→ϵF' \to \epsilonF′→ϵ F′→ϵF' \to \epsilonF′→ϵ
PPP P→(E)P \to (E)P→(E) P→aP \to aP→a P→bP \to bP→b P→∧P \to \wedgeP→∧

【题目 4-3】复杂文法分析表的构建

已知文法 G[S]G[S]G[S]:

  • S→MH∣aS \to MH \mid aS→MH∣a
  • H→LSo∣ϵH \to LSo \mid \epsilonH→LSo∣ϵ
  • K→dML∣ϵK \to dML \mid \epsilonK→dML∣ϵ
  • L→eHfL \to eHfL→eHf
  • M→K∣bLMM \to K \mid bLMM→K∣bLM
① 判断是否为 LL(1) 文法

由于 FIRST(K)∩FIRST(bLM)={d,ϵ}∩{b}=∅FIRST(K) \cap FIRST(bLM) = \{ d, \epsilon \} \cap \{ b \} = \emptysetFIRST(K)∩FIRST(bLM)={d,ϵ}∩{b}=∅ 且 FIRST(K)∩FOLLOW(M)=∅FIRST(K) \cap FOLLOW(M) = \emptysetFIRST(K)∩FOLLOW(M)=∅,各候选式 FIRST 集两两不相交,因此满足 LL(1) 文法要求。

② 构建预测分析表
非终结符 aaa ddd bbb eee ooo fff #\##
SSS S→aS \to aS→a S→MHS \to MHS→MH S→MHS \to MHS→MH S→MHS \to MHS→MH S→MHS \to MHS→MH S→MHS \to MHS→MH
HHH H→LSoH \to LSoH→LSo H→ϵH \to \epsilonH→ϵ H→ϵH \to \epsilonH→ϵ
KKK K→dMLK \to dMLK→dML K→ϵK \to \epsilonK→ϵ K→ϵK \to \epsilonK→ϵ K→ϵK \to \epsilonK→ϵ
LLL L→eHfL \to eHfL→eHf
MMM M→KM \to KM→K M→bLMM \to bLMM→bLM M→ϵM \to \epsilonM→ϵ M→ϵM \to \epsilonM→ϵ M→ϵM \to \epsilonM→ϵ

【题目 4-5】程序语言结构文法的 LL(1) 改写

原题目文法:

  1. <程序> -> begin <语句表> end
  2. <语句表> -> <语句> ; <语句表> ; <语句>
  3. <语句> -> <无条件语句> | <条件语句>
  4. <无条件语句> -> a
  5. <条件语句> -> if b then <无条件语句> | if b then <无条件语句> else <语句>

为了避免被误识别为 HTML 标签,所有的非终结符加了反斜杠转义:

改写后的文法:
  • \<程序\> -> begin \<语句表\> end
  • \<语句表\> -> \<语句\> \<语句表'\>
  • \<语句表'\> -> ; \<语句\> \<语句表'\> | ε
  • \<语句\> -> \<无条件语句\> | \<条件语句\>
  • \<无条件语句\> -> a
  • \<条件语句\> -> if b then \<语句\> \<else部分\>
  • \<else部分\> -> else \<语句\> | ε

【题目 4-7】理论分析与文法改写

① 答疑题:对于一个文法,消除左递归、提取左公因子后是否一定是 LL(1) 文法?

答案:不一定。

消除左递归和提取左公因子是改写文法的常用技术,但不能保证文法一定是 LL(1) 文法。因为可能依然存在某个非终结符 AAA,其不同产生式的 FIRST 集交集不为空,或者当某产生式可推导出 ϵ\epsilonϵ 时,其 FIRST 集与 FOLLOW(A) 集仍存在交集(发生冲突)。

② 文法改写与判定:

(1) 已知文法:

  • A→baB∣ϵA \to baB \mid \epsilonA→baB∣ϵ
  • B→Abb∣aB \to Abb \mid aB→Abb∣a

改写过程:

消除隐式左递归,将 AAA 的候选式代入 BBB 中:

  • B→baBbb∣bb∣aB \to baBbb \mid bb \mid aB→baBbb∣bb∣a

对 BBB 提取左公因子:

  • B→bB′∣aB \to bB' \mid aB→bB′∣a
  • B′→aBbb∣bB' \to aBbb \mid bB′→aBbb∣b

经计算,改写后的文法没有 FIRST/FOLLOW 冲突,是 LL(1) 文法

(2) 已知文法:

  • A→aABe∣aA \to aABe \mid aA→aABe∣a
  • B→Bb∣dB \to Bb \mid dB→Bb∣d

改写过程:

首先消除 B→Bb∣dB \to Bb \mid dB→Bb∣d 的直接左递归:

  • B→dB′B \to dB'B→dB′
  • B′→bB′∣ϵB' \to bB' \mid \epsilonB′→bB′∣ϵ

对 A→aABe∣aA \to aABe \mid aA→aABe∣a 提取左公因子:

  • A→aA′A \to aA'A→aA′
  • A′→ABe∣ϵA' \to ABe \mid \epsilonA′→ABe∣ϵ

验证:

由于 FIRST(ABe)={a}FIRST(ABe) = \{ a \}FIRST(ABe)={a} 且 FOLLOW(A)FOLLOW(A)FOLLOW(A) 中包含 FIRST(Be)FIRST(Be)FIRST(Be)。由于两者的交集不为空(存在交集),故该文法不是 LL(1) 文法


4. 总结

在进行自顶向下语法分析时,核心考点通常落在 FIRST/FOLLOW 集计算LL(1) 预测分析表构建。在实际解题或考试中,可以总结出以下固定套路:

  1. 文法检查: 先看有没有显式或隐式的左递归,有则立刻消除。
  2. 提取公因子: 检查同一个非终结符的候选式是否具有相同前缀。
  3. 精准求集: 特别注意 FOLLOW 集计算中开始符号自带 ,以及 ϵ\epsilonϵ 产生式带来的递归传递。
  4. 填表判断: 如果分析表中任何一个单元格都不包含多重产生式,则该文法是 LL(1) 文法。
相关推荐
优化控制仿真模型2 小时前
2026年初中英语考纲词汇表(1600词)PDF电子版
经验分享·pdf
ErizJ3 小时前
Linux|学习笔记
linux·笔记·学习
草履虫君3 小时前
windows系统装机,小白win10装机教程wepe模式,包括系统盘怎么制作,bios怎么设置
windows·经验分享
小马哥crazymxm4 小时前
arXiv论文周选 (2026-W18)
论文阅读·科技·考研
wanghanjiett4 小时前
笔记:ESP32驱动SimpleFOC成功(基于Espressif-IDE)
笔记·esp32·foc
大邳草民4 小时前
Python 爬虫:从 HTTP 请求到接口分析
笔记·爬虫·python
南湖渔歌4 小时前
【成功实践版】workbuddy_把多张图片转成完整Markdown笔记
人工智能·笔记·workbuddy
想成为优秀工程师的爸爸5 小时前
车载以太网之要火系列 - 第33篇:郭大侠学UDS(10服务)- 桃花岛内规矩多,模式切换要会说
网络·笔记·网络协议·信息与通信·车载以太网
智者知已应修善业5 小时前
【51单片机从奇数始再转偶数逐一点亮并循环】2023-9-8
c++·经验分享·笔记·算法·51单片机