【编译原理】核心考点:语法制导翻译(SDD)与自底向上分析硬核图解与方法总结
前言
在计算机基础学科的学习中,编译原理往往被称为“神仙课程”,其抽象的推导和繁杂的属性计算很容易让人迷失。好记性不如烂笔头,本文整理了我近期的专业课手写笔记,重点针对语法制导翻译(SDD/SDT)中的属性计算求值顺序,以及**自底向上的语法分析(移进-规约)**进行了硬核拆解,并总结了几套非常实用的解题“套路”。希望这些沉淀下来的方法论,能帮助大家在啃这些硬骨头时少走弯路。
一、 语法制导翻译与属性计算(SDD)
在语法制导定义(SDD)中,核心难点在于理清属性之间的依赖关系,并找出正确的计算拓扑排序。
1. 核心概念回顾
- 继承属性(
inh):结点的属性值取决于其父结点或兄弟结点。(自上而下/自左向右流动) - 综合属性(
syn):结点的属性值取决于其子结点。(自底向上流动)
2. 基础文法实例
假设我们有如下处理乘法运算的文法:
T→FT′T \rightarrow F T'T→FT′
T′→∗FT1′T' \rightarrow * F T_1'T′→∗FT1′
T′→εT' \rightarrow \varepsilonT′→ε
F→digitF \rightarrow \text{digit}F→digit
3. 🌟 高效解题:拓扑排序五步法
面对复杂的属性依赖,不要慌,按照以下五个步骤进行工程化拆解,能够确保求值顺序绝对正确:
- ① 语法分析树:首先根据输入串,画出完整的抽象语法树。
- ② 标继承/综合属性:严格根据文法的语义规则,依次在树的对应结点上标注
inh或syn。 - ③ 自底向上画流式箭头:找出属性间的依赖关系,画出依赖箭头(箭尾指向箭头,代表计算的先后顺序)。
- ④ 分析排序:拆解局部的依赖链条。例如在当前实例中,可以拆解出:
- 1→31 \rightarrow 31→3
- 2→42 \rightarrow 42→4
- 3→53 \rightarrow 53→5
- 4,5→64, 5 \rightarrow 64,5→6
- 6→76 \rightarrow 76→7
- 7→87 \rightarrow 87→8
- 8→98 \rightarrow 98→9
- ⑤ 合并同流箭头排序!将所有零散的链条进行合并,输出最终满足拓扑排序的计算序列。
- 最终排序结果:
1 3 5 2 4 6 7 8 9
二、 自底向上语法分析与规约技巧
在自底向上的语法分析(如 LR 分析)中,面对长句型,如何快速找到正确的规约路径是一大难点。
1. 文法与输出动作
考虑如下带有打印动作的文法:
S→SaA {print "0"}S \rightarrow S a A \ \{ \text{print } "0" \}S→SaA{print"0"}
S→Λ {print "1"}S \rightarrow \Lambda \ \{ \text{print } "1" \}S→Λ{print"1"}
A→AbB {print "2"}A \rightarrow A b B \ \{ \text{print } "2" \}A→AbB{print"2"}
A→B {print "3"}A \rightarrow B \ \{ \text{print } "3" \}A→B{print"3"}
B→cSd {print "4"}B \rightarrow c S d \ \{ \text{print } "4" \}B→cSd{print"4"}
B→e {print "5"}B \rightarrow e \ \{ \text{print } "5" \}B→e{print"5"}
2. 🌟 方法总结:特征逆向规约法
面对目标句型,我的破题思路(盲点突破)总结如下:
对文法分析:看右式。
观察文法发现,终结符bbb、ddd和非终结符BBB是独特的。
再看目标句型:AacAbcBaAdbedA a c A b c \mathbf{B a A d} b e \mathbf{d}AacAbcBaAdbed
可见 “bebebe” 需有AAA,进行A→AbBA \rightarrow A b BA→AbB。
结合前望字符,B→cSdB \rightarrow c S dB→cSd有唯一机会实现。
再有cBaAd⇐cSdc B a A d \Leftarrow c S dcBaAd⇐cSd,至此初步解析完成。
核心逻辑:不要盲目移进,要寻找特征鲜明的字符,利用最右推导的逆过程(规约),锁定唯一的规约切入点。
3. 完整规约过程推导
目标句型:A a c A b c B a A d b e d
(注:⇐\Leftarrow⇐表示规约,右侧数字为该步触发的打印动作)
- ⇐SacAbcB‾aAdbed\Leftarrow S a c A b c \underline{B} a A d b e d⇐SacAbcBaAdbed(1)
- ⇐SacAbcA‾aAdbed\Leftarrow S a c A b c \underline{A} a A d b e d⇐SacAbcAaAdbed(3)
- ⇐SacAbcSaA‾dbed\Leftarrow S a c A b c \underline{S a A} d b e d⇐SacAbcSaAdbed(1)
- ⇐SacAbcSd‾bed\Leftarrow S a c A b c \underline{S d} b e d⇐SacAbcSdbed(0)
- ⇐SacAbB‾bed\Leftarrow S a c \underline{A b B} b e d⇐SacAbBbed(4)
- ⇐SacA‾bed\Leftarrow S a c \underline{A} b e d⇐SacAbed(2)
- ⇐SacAbB‾d\Leftarrow S a c A b \underline{B} d⇐SacAbBd(5)
- ⇐SacAd‾\Leftarrow S a c \underline{A d}⇐SacAd(2)
- ⇐SacSd‾\Leftarrow S a \underline{c S d}⇐SacSd(1)
- ⇐SaB‾\Leftarrow S a \underline{B}⇐SaB(4)
- ⇐SaA‾\Leftarrow S a \underline{A}⇐SaA(3)
- ⇐S‾\Leftarrow \underline{S}⇐S(0)
最终输出动作序列:1 3 1 0 4 2 5 2 1 4 3 0
三、 复杂属性依赖树与 Accepted 判定
在处理更复杂的输入串时,属性流动会形成庞大的网络。
- 全局视角:根结点通常为起始符号SSS,最终的判定目标是达到accepted状态。
- 流水线拆解:在画出完整的红线依赖流和
num计数(如num=1,in=3,syn=3num=1, in=3, syn=3num=1,in=3,syn=3等)后,不要试图一口气得出结果。应当将求值步骤按阶段进行归类划分。 - 阶段沉淀:例如一棵 16 步求值的树,可以拆分为以下流水线阶段:
1, 2, 3(底层属性收集)4, 56, 78, 910~15(中高层综合与继承交织)16(最终判定 accepted)
结语
编译原理虽然底层且枯燥,但它是理解计算机系统运行逻辑的关键拼图。通过画图、找规律、总结标准化步骤,完全可以把这些晦涩的理论变成得分利器。这篇笔记作为我知识体系构建的一部分,分享出来希望能帮到同样在死磕计算机基础的同学们。欢迎在评论区交流你们的解题心得!
