【编译原理】山东大学编译原理复习提纲

前言

本提纲涵盖所有考点,包含多套真题,复习绝对高效,持续更新,点赞+留邮箱获取pdf版本。
实验地址为:https://github.com/roomdestroyer/PL0Compiler,python编写,写了非常详细的README和代码注释,帮助你搞懂这个实验。 (觉得有用的话点个star哦~)

把本文涉及到的知识点全部搞清楚,应付考试可游刃有余。后续同学们考完试,可以把回忆版私发给我,我在此文中持续更新。

山东大学编译原理复习提纲

一、简答与计算

1.1 必考

1. 编译过程

  • 画图表示编译过程的各阶段,并简要说明各阶段的功能:
img
  • 词法分析器:输入源程序,进行词法分析,输出单词符号;
  • 语法分析器:根据文法构建分析表,对单词符号进行语法分析,检查程序是否符合语法规则;
  • 语义分析与中间代码生成器:按照文法翻译规则对语法分析器归约出的语法单位进行语义分析,并把它们翻译成一定形式的中间代码;
  • 优化器:对中间代码进行优化处理;
  • 目标代码生成器:把中间代码翻译成目标代码。

2. 消除左递归

image-20220616162952878 image-20220616163330803

如果从开始符号依次推导出 A1 A2 A3 ,则按其逆序排序时得到的产生式最少。

3. 消除回溯

image-20220620164318886

通过反复提取左公因子可以消除回溯。

4. 提左公因子

image-20220616164716208

5. 后缀表达式

中缀转后缀算法:

初始化符号栈,顺序读取中缀表达式,遇到 ( 则将其入栈,遇到 ) 则依次弹出栈顶元素到结果串中,直到遇到 ( ,将其出栈但不附加到结果串;

遇到 +-,弹出栈顶的所有操作符,附加到结果串中,然后自己进栈;

遇到 */,先让栈顶的 */ 出栈,附加到结果串中,然后自己进栈;

遇到数字,直接附加到结果串中;

最后若符号栈非空,将符号栈栈顶元素依次附加到结果串中。

// 中缀转后缀
vector<string> interToPost(vector<string> vec)
{
	vector<string> res;
	stack<string> op_stack;
	for (int i = 0; i < vec.size(); i++) {
		if (vec[i] == "(")
			op_stack.push(vec[i]);
		else if (vec[i] == ")") {
			while (op_stack.top() != "(") {
				res.push_back(op_stack.top());
				op_stack.pop();
			}
			// 左括号出栈
			op_stack.pop(); 
		}
		// 遇到+-,先让栈中的+-*/出栈
		else if (vec[i] == "+" || vec[i] == "-") {
			while (!op_stack.empty() && op_stack.top() != "(") {
				res.push_back(op_stack.top());
				op_stack.pop();
			}
			op_stack.push(vec[i]);
		}
		// 遇到*/,先让栈中的*/出栈
		else if (vec[i] == "*" || vec[i] == "/") {
			while (!op_stack.empty() && (op_stack.top() == "*" || op_stack.top() == "/")) {
				res.push_back(op_stack.top());
				op_stack.pop();
			}
			op_stack.push(vec[i]);
		}
		else
			res.push_back(vec[i]);
	}
	while (!op_stack.empty()) {
		res.push_back(op_stack.top());
		op_stack.pop();
	}
	return res;
}

计算后缀表达式:

初始化数据栈,顺序读取后缀表达式,遇到操作符 op 则将栈次顶元素 op 栈顶元素,弹出操作数并压入运算结果;

遇到数据,则直接压入栈顶;

最终栈顶元素就是结果。

// 计算后缀表达式
double solvePostPrefix(vector<string> post_prefix) {
	stack<double> dataStack;
	double num1, num2, result;
	for (auto x : post_prefix) {
		if (x == "+" || x == "-" || x == "*" || x == "/") {
			num1 = dataStack.top();
			dataStack.pop();
			num2 = dataStack.top();
			dataStack.pop();
			if (x == "+")
				result = num2 + num1;
			if (x == "-")
				result = num2 - num1;
			if (x == "*")
				result = num2 * num1;
			if (x == "/")
				result = num2 / num1;
			dataStack.push(result);
		}
		else {
			double num = strToDouble(x);
			dataStack.push(num);
		}
	}
	return dataStack.top();
}

1.2 选考

1. 编译、翻译和解释

  • 翻译程序:把一种语言程序(源语言)转换成另一种语言程序(目标语言);
  • 编译程序:源语言为高级语言,目标语言为低级语言的翻译程序;
  • 解释程序:以源语言作为输入,但不产生目标程序,而是边翻译边执行源程序本身。

2. 中间代码

是一种含义明确、便于处理的记号系统,通常独立于具体的硬件但与指令形式有某种程度的接近,或者能够比较容易的转换为机器指令,常见的形式有:

  • 三元式;
  • 间接三元式;
  • 四元式;
  • 逆波兰式;
  • 树形表示。

3. 目标代码

把中间代码变换为特定机器上的低级语言代码,产生出足以发挥硬件效率的目标代码是一件非常不容易的事情。常见的形式有:

  • 汇编指令代码:需要汇编才能执行;

  • 绝对指令代码:可以立即执行;

  • 可重定位指令代码:运行前必须借助于一个链接装配程序,把各个目标模块(包括系统提供的库模块)连接在一起,确定程序装入内存中的起始地址,使之成为一个可以运行的绝对指令代码程序。

4. 属性文法

  • S-属性文法:只含有综合属性的文法;
  • L-属性文法:含有综合属性或继承属性,且改继承属性仅依赖于:
    • 产生式体该符号左边的那些属性;
    • 产生式头的继承属性;

5. T 型图

img

6. 高级语言的分类

  • 强制式语言:面向底层和语句,每个语句的执行引起若干存储单元中值的改变;

  • 应用式语言:更注重程序所表示的功能,每条语句都封装了复杂的功能;

  • 基于规则的语言:检查一定的条件,当它满足值,则执行适当的动作;

  • 面向对象的语言:主要特征是封装性、继承性、多态性。

7. 闭包

用 Σ* 表示 Σ 上所有符号串的全体,空字 ε 也在其中,称为 Σ 的闭包。

  • Σ = {a, b},则 Σ* = {ε, a, b, aa, ab, bb, aaa, …}。

8. 上下文无关文法

image-20220615185419507

9. 构造文法

image-20220615192409498 image-20220616111325547

10. 二义文法

如果一个文法的某个句子对应两棵不同的语法树,即其最左( 最右)推导不唯一,称该文法为二义文法。对程序设计语言来说,常常希望它的文法是无二义的,因为我们希望对它每个语句的分析是唯一的。

  • 消除文法二义性的关键步骤:

    • 划分优先级和结合性;
    • 优先级高一级的运算,增加一层产生式;
    • 递归非终结符在左边,运算具有左结合性,否则具有右结合性。
  • 例子:改写二义文法

    E -> E + E 
    E -> E * E
    E -> (E) | -E | id
    
    • 优先级从低到高: [+][*][( ), -, id]

    • 结合性:

      左结合:[+, *]
      右结合:[-]
      无结合:[id]

    • 非终结符与运算:

      E:+ (E产生式,左递归)
      T:* (T产生式,左递归)
      F:-,( ),id (F产生式,右递归)

    E → E + T | T
    T → T * F | F
    F → (E) | -E | id
    

11. 构造正规式

image-20220615203242446

12. DFA

image-20220615203548406

  • 设计 DFA 例题

    image-20220615204549610

    image-20220615204803833

    image-20220615204844463

13. NFA

  • 构造 NFA 例题

    image-20220615215948007

14. 句柄

句型: 从文法开始符号推导出来的任一文法符号串;

句子: 从文法开始符号推导出来的任一终结符号串;

短语: 对文法 G[S],如果有 S =>* αAδA =>+ β,则称 β 是句型 αAδ 相对于非终结符号 A 的短语。

短语是语法树中所有子树的边缘,是相对于子树根节点的短语。

例如:

image-20220616224920733

短语是 S(T)Sd(T)bSd(T)db(Sd(T)db)

直接短语: 对文法 G[S],如果有 S =>* αAδA => β,则称 β 是句型 αAδ 相对于非终结符号 A 的短语。

直接断语是所有二层子树的边缘,在上图中只有三个二层子树。

例如:上图的直接短语是 S(T)b

句柄: 最左直接短语;

句柄是最左二层子树的边缘。

例如:上图的句柄是 S

素短语: 至少包含一个终结符的短语,并且除它自身之外不再包含其他素短语;

例如:上图的素短语是 (T)b

最左素短语: 句型最左边的素短语,是算符优先分析法的规约对象;

例如:上图的最左素短语是 (T)

例子:

image-20220616225710296

image-20220616225718632

活前缀: 是句型的一个前缀,不含句柄以后的任何符号;

例子:

image-20220616235229778

15. 规范规约

规范规约是最右推导的逆过程,因此也称为最左规约。

image-20220616231023848

16. 根据 FA 写正规式

是根据正规式构造 FA 的逆过程。

17. 符号表

用于登记源程序的各类信息,如变量名、常量名、过程名等,以及编译各阶段的进展状况。当扫描器识别出一个标识符后,把该名字填入符号表,在语义分析阶段回填类型,在目标代码生成阶段回填地址。

符号表的作用和地位:(重点)

  • 收集符号属性;
  • 语义合法性检查的依据,如重复变量定义;
  • 作为目标代码生成阶段地址分配的依据。

符号表的主要属性:

  • 符号名:变量、过程、类的名称;
  • 符号数据类型:整型、实型、布尔型;
  • 符号声明类别:staticconst 等;
  • 符号存储方式:堆区存储还是栈区存储等;
  • 符号作用域:全局变量与局部变量;

符号表的组织方式:

  • 构造多个符号表,具有相同属性种类的符号组织在一起;
  • 把所有符号项都组织在一张大的符号表中;
  • 折中了上述两种方案,根据符号属性的相似程度分类成若干张表;
  • 使用对象组织,需要编译器的支持,但非常方便管理;

符号表项的排列:

  • 数组:线性组织;
  • 链表:Hash 表,跳表;
  • 树形:二叉树,平衡二叉树。

18. 运行时空间组织

运行时存储器的划分

  • 代码区:编译生成的目标代码;
  • 静态区:编译时就可以完全确定的数据;
  • 栈区:栈式内存分配;
  • 堆区:堆式内存分配;

存储分配策略:

  • 静态分配策略: 在编译时对所有数据对象分配固定的存储单元,且在运行时始终保持不变;
  • 栈式动态分配策略: 在运行时把存储器作为一个栈进行管理,运行时,每当调用一个过程,它所需要的存储空间就动态地分配于栈顶,一旦退出,它所占空间就予以释放;
  • 堆式动态分配策略: 在运行时把存储器组织成堆结构,以便用户关于存储空间的申请与归还(回收),凡申请者从堆中分给一块,凡释放者退回给堆;

活动记录

为了管理过程在一次执行中所需要的信息,使用一个连续的存储块,这样的一个连续存储块称为活动记录, 一般包括:

  • 局部变量:源代码中在过程体中声明的变量;
  • 临时单元:为了满足表达式计算要求而不得不预留的临时变量;
  • 内情向量:内情向量是静态数组的特征信息,用来描述数组属性信息的一些常量,包括数组类型、维数、各维的上下界及数组首地址;
  • 返回地址:调用位置的地址;
  • 动态链:SP 指向当前过程的动态链地址(也是帧起始地址),它又指向调用该过程的上一过程的帧起始地址,用于过程结束后回收分配的帧;它和函数的嵌套定义关系无关,只与调用顺序有关;
  • 静态链:指向当前过程的直接父过程的帧起始地址,用于访问非局部数据,它只与函数嵌套定义关系有关。

image-20220618015919118

19. 优化手段

  • 源代码级别:选择适当的算法,例如快排优于插排;
  • 语义动作级别:生成高效的中间代码,例如在词法分析阶段加入错误检查;
  • 中间代码级别:安排专门的优化阶段,例如 DAG 优化;
    • 局部优化:例如 DAG 优化;
    • 循环优化:包含代码外提、强度削弱、删除归纳变量、复写传播等;
  • 目标代码级别:考虑如何有效地利用寄存器,例如窥孔优化。

20. 待用/活跃信息

当翻译 A = B op C 时:

  • 待用信息:变量在哪些中间代码中还会被引用
  • 活跃信息:ABC 是否还会在基本块内被引用

image-20220618185515068

21. LL(1)分析

对于一个不含回溯和左递归的文法,LL(1) 方法从左到右扫描输入串,维护一个状态栈和一个符号栈,每一步只向右查看一个符号,根据状态栈顶、符号栈顶和分析表的内容来确定下一步的动作,最终分析出整个句子。

22. LR(1)分析

LR(1) 分析适合大多数上下文无关文法,它从左到右扫描符号串,能记住移进和规约出的整个符号串,即 “记住历史”,还可以根据所用的产生式推测未来可能碰到的输入符号,即 “展望未来”,根据 “历史”、“展望” 和分析表的内容来确定下一步的动作,最终分析出整个句子。

23. display 表

为提高访问非局部变量的速度,引入指针数组指向本过程的所有外层,成为嵌套层次显示表,display 表是一个栈,自顶向下依次指向当前层、直接外层、直接外层的直接外层,直到最外层。

24. 语法制导翻译法

对单词符号串进行语法分析,构造语法分析树,然后根据需要遍历语法树并在语法树的各结点处按语义规则进行计算。这种有源程序的语法结构驱动的处理办法就是语法制导翻译法。

二、综合题

2.1 词法分析

给定正规式: ①构造NFA ②确定化 ③最小化

例题

① + ②

image-20220615220445134

image-20220615221745695

image-20220615222334279

2.2 LL(1) 分析

给出文法:①构造First集合 ②构造Follow集合 ③构造LL(1)分析表 ④识别句子

例题

前提:LL(1) 适用条件

image-20220616165155939

①求 First(X)

image-20220616165438384

A -> Bc

B -> ε

则 First(A) = { c };

若 A -> B

B -> ε

则 First(A) = { ε };

②求 Follow(X)

image-20220616171358707

③构造分析表

image-20220616173215360

image-20220616173408649

④分析过程

image-20220616175857393

image-20220616175908255

2.3 LR(1) 分析

给出文法:①构造拓广文法 ②求First集合 ③构造LR(1)项目集规范族 ④构造LR(1)分析表并消除二义性 ⑤识别句子

构造带向前搜索符的DFA,无归约-归约冲突则是LR(1)文法。

例题1

构造文法G[S]的LR(1)项目集规范族:
S -> aCaCb
S -> aDb
C -> a
D -> a
①构造拓广文法:
S' -> S
S -> aCaCb
S -> aDb
C -> a
D -> a
②求 First 集合:

image-20220617015244163

③构造 LR(1) 项目集规范族:

image-20220617015341765

④构造 LR(1) 分析表并消除二义性:

消除二义性需要认为定义运算优先级,发生移入-规约冲突时选择正确的项填入。

⑤识别句子 aaaabaab

image-20220617015635034

例题2

对下列文法做 LR(1) :

S -> AS | b
A -> SA | a

img

2.4 中间代码生成

给出翻译模式和高级语言程序,翻译句子,一般涉及多种类型句子的综合,也可能涉及声明语句填写符号表。

1. 过程中的说明语句

img img

image-20220617171901490

2.算术表达式的翻译

image-20220617171233909 image-20220617171306138 image-20220617172649169

3. 布尔表达式的翻译

重点:回填。

image-20220617204129295

image-20220617203954441

4. 控制流语句的翻译

重点:if 和 while 。

image-20220617210312858

image-20220617214337930

image-20220617214400974

2.5 目标代码生成

给出基本块代码:①构造DAG ②写出优化后的中间代码 ③写出DAG目标优化后的中间代码 ④根据变量活跃性和寄存器信息,写出目标代码。

例题1

给出基本块代码为:

image-20220618174837303

①构造DAG

image-20220618174926596

②写出优化后的中间代码

image-20220618174945854

③写出DAG目标优化后的中间代码
(1) T6 = R - r
(2) T2 = R * r
(3) T4 = T2
(4) T1 = 6.28
(5) T3 = 6.28
(6) A = 6.28 * T2
(7) T5 = A
(8) B = A * T6
(9) T0 = 3.14
④根据变量活跃性和寄存器信息,写出目标代码

假定 B 是基本块出口之后活跃的,有寄存器 R0R1 可用,目标代码为:

DAG 优化中,不活跃变量,目标代码依然要生成计算其值的代码,只是不生成存储到主存的代码。计算代码被优化是后续优化完成的,不是 DAG 完成的。

LD R0, R
SUB R0, r          R0: T6     R1: \
LD R1, R
MUL R1, r          R0: T6     R1: T2
ST R0, T6
LD R0, 6.28        R0: 6.28   R1: T2
MUL R0, R1         R0: A      R1: T2
MUL R0, T6         R0: B      R1: T2
ST R0, B

附:历年考试回忆

2004-2005

image-20220621071345662

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MmUNQHdO-1655768860486)(https://s2.loli.net/2022/06/21/kX5Cfm2ILzv4ibW.png)]

image-20220621071405483

2015-2016

一、简答(30 分)
1.给了一个文法(具体忘了),让证明二义性。
2.写出文法,表示{0、1}集上的所有正规式
3.解释 S-属性文法
4.说出传地址和传质这两种参数传递方式的异同
5.结合具体例子谈一谈回填思想

二、(babbb)* 画 NFA 转 DFA ,最小化(15 分)

三、(1)一个文法,判断是否为 LL(1)文法(10 分)

(2)上题中的文法是否为 SLR 文法,给出证明(15 分)
(与课本上例子不同之处在于有 B ——〉ε)

四、利用语法指导思想写四元式(15 分)
(比较简单,就是一个 while-do 语句)

五、给出一段中间代码,画 DAG 图;只有 R 在代码块外是活跃的,做优化;如
果有寄存器 R0 和 R1,写出目标代码。(15 分)

2016-2017

一、简答题
1.编译流程图及各部分的作用。⒉举例说明文法二义性。
3.什么是L属性文法?
4.写出允许过程嵌套的C活动记录结构。5.中间代码优化有哪几种?
6.符号表作用。

二、词法分析
RE NFA DFA最小化DFA整个流程走一遍

三、自上而下文法分析
给了一个文法,证明文法是LL1的,画表。

四、自下而上文法分析
给了一个文法,证明文法是LR1的,画表。

五、中间代码生成
—段代码,四元式翻译。涉及循环和判断的嵌套。

六、代码优化和目标代码生成。
给了一个代码段,
先画DAG图,然后优化,最后输出汇编代码。

2017-2018

image-20220621071437139

2019-2020

一、五个小题(25分)

1.判断一个文法是否二义

2.编译的前端,后端,什么是一遍扫描

3.什么是S属性

4.什么是语法制导翻译

5.在语法制导翻译中,空返产生式的作用(M->e)

二、自动机(15分)

一个单词表由a,b组成,请写出代表偶数个a的正规式,NFA,并确定化、最小化

三、判断一个文法是不是LL(1)的,如果是就写出预测分析表,不是就说明原因(15分)

四、判断一个文法是不是SLR(1)的,如果是就写出预测分析表,不是就说明原因(15分)

五、中间代码生成程序(15分)

while a<c and b<d do if c==1 then c:=c+1 else c:=c+2;

六、代码优化(15分)

DAG优化,最后写出四元式的形式(这个是一个坑,四元式是目标代码,也就是此时要做目标代码生成)

同时目标代码生成要列表(Rvalue 寄存器描述,Avalue地址描述)

2020-2021

image-20220621070322834

2021-2022第二学期

一、简答题

  1. 画编译流程图;
  2. 判断一个文法是不是二义文法;
  3. 给一个句型,找它的句柄;
  4. 中缀表达式转后缀,比较长,最好使用算法一步步推导;
  5. 消除循环左递归;
  6. 给一个 DFA,把它转换为正规式

二、计算题

  1. 词法分析:给定正规式 ①构造NFA ②确定化 ③最小化
  2. LL(1)分析,给出文法 ①构造First集合 ②构造Follow集合 ③构造LL(1)分析表(可能涉及消除二义文法冲突)④识别句子
  3. LR(1)分析,给出文法 ①构造拓广文法 ②构造拓广文法的LR(1)项目集规范族 ③构造LR(1)分析表(及消除二义文法冲突) ④识别句子
  4. 给出基本块代码 ①构造DAG ②写出优化后的中间代码 ③写出DAG目标优化后的中间代码 ④根据变量活跃性和寄存器信息,写出目标代码
  5. 给出翻译模式和高级语言程序,翻译句子 while a < b do if c > d then x = y * z ,会给出翻译规则。

2022-2023第一学期

一、概念题 5分×5

  1. 画编译流程图
  2. 给出有穷自动机的概念,说明 NFA DFA的区别
  3. 简述推导和归约的概念
  4. 说明法制导定义的概念。S-SDD、L-SDD的概念
  5. 基本块划分方法

二、a((b(a|b)*)|空)ab 画NFA转DFA并最小化

三、说明下列文法是LL(1)的,给出语法分析表,并分析ccccd
S -> CC
C -> cC
C -> d

四、说明下列文法是LR(0)的,给出分析表并分析accd
S -> aA|bB
A -> cA|d
B -> cB|d

五、说明语法制导翻译的思想(7分)

六、代码优化列举四种并说明(8分)

2022-2023第二学期

1、考试时间:2023/5/26 14:00-16:00
2、考试科目:编译原理(老师:LiuHong)
3、考后感悟:本次考试题目近80%都是2023年初开学考试的题目,真的一模一样,符号都不带变的。提醒一下最好带个尺子、铅笔和橡皮,这样更方便画图和表格。

一、简答题(5*5’=25’)

  1. 画出编译原理的程序框图。
  2. 什么是文法的二义性?为什么要消除二义性?如何消除二义性?
    1、二义性:给定文法,若存在某个句子,有多个最左/右推导,即可以生成多棵解析树,则这个文法就是二义的。 2、通常要求程序设计语言的文法的无二义性的,否则会导致一个程序有多个“正确”的解释。即使文法允许二义性,但仍需要在文法之外加以说明,来剔除不要的语法分析树。总之,必须保证文法消除了二义性使得最后的语法解析树只有一棵。 3、①改写原文法 ②引入消除二义性的规则。
  3. 简述推导和归约的概念。
    推导:将终结符替换为它的某个产生式的体。
    归约:将一个与某个产生式的体相匹配的特定子串替换为该产生式的头。
  4. 简述递归下降语法分析技术的基本思想。
    对于LL(1)文法,不必实际构建解析树,而且可以借助系统栈来实现预测分析,这就是递归下降算法。
  5. 简述划分基本块的算法。
    ①确定首指令:第一个三地址指令;任意一个转移指令的目标指令;转移指令后的一个指令。
    ②确定基本块:从一个首指令开始到下一个首指令之间的部分为一个基本块。

二、词法分析(20’)
根据正则式:a( (b(a|b)*) | ε \varepsilon ε)ba,写出NFA ,确定化,最小化。
①NFA:
在这里插入图片描述
②确定化:
在这里插入图片描述
DFA:
在这里插入图片描述
③最小化:
初步划分为{0,1,2,4,5}、{3};
根据a将{0,1,2,4,5}划分为{0,1,5}、{2,4};
根据a将{0,1,5}划分为{0},{1},{5};
{2,4}不用划分,归为一个节点;
最终节点划分为{0}、{1}、{2,4}、{3}、{5}。
在这里插入图片描述
三、语法分析(20’)
G(S)文法如下:

S → \rightarrow CC
C → \rightarrow cC
C → \rightarrow d

3.1 G(S)对应的First和Follow是什么?证明是G(S)是LL(1)。
First(S) = {c,d}
First(C) = {c,d}
Follow(S) = {$}
Follow(C) = {c,d,$}
证明:① G(S)不含左递归。
② 对于C——>cC,C——>d中,First(cC) ⋂ First(d) = ∅ \varnothing
③ 对于A,C,它们的首终结符都不含 ε \varepsilon ε
所以G(S)是LL(1)。

3.2写出G(S)的预测分析表。
Start(S——>CC) = {c,d}
Start(C——>cC) = {c}
Start(C——>d) = {d}

c d
S S——>CC S——>CC
C C——>cC C——>d

3.3根据预测分析表,写出cdccccd的TOP-DOWN的推导过程。

S$ cdccccd$
CC$ cdccccd$
cCC$ cdccccd$
CC$ dccccd$
dC$ dccccd$
C$ ccccd$
cC$ ccccd$
C$ cccd$
cC$ cccd$
C$ ccd$
cC$ ccd$
C$ cd$
cC$ cd$
C$ d$
d$ d$
$ $

四、语义分析(20’)
给出以下文法:

E → \rightarrow aA|bB
A → \rightarrow cA|d
B → \rightarrow cB|d

4.1证明是LR(0)。
证明:
对文法进行拓广:
(1)E'——>E
(2)E——>aB
(3)E——>bB
(4)A——>cA
(5)A——>d
(6)B——>cB
(7)B——>d
画图如下:
在这里插入图片描述
从上图可以看出该文法没有 移入-归约冲突,也没有 归约-归约冲突,所以是LR(0)文法。

4.2 写出预测分析表。

STATE ACTION GOTO
a b c d $ A B E
0 S2 S3 1
1 acc
2 S5 S6 4
3 S8 S9 7
4 r2 r2 r2 r2 r2
5 S5 S6 10
6 r5 r5 r5 r5 r5
7 r3 r3 r3 r3 r3
8 S8 S9 11
9 r7 r7 r7 r7 r7
10 r4 r4 r4 r4 r4
11 r6 r6 r6 r6 r6

注意归约E'——>E

五、语法制导翻译(7’)
写出语法制导翻译的基本思想。并说明抽象语法树在语法制导翻译中的角色。
1、基本思想:对字符串进行语法分析,构建语法分析树,然后根据需要遍历语法树并在语法书的各结点处按语义规则进行计算。这种有源程序的语法结构驱动的处理方法就是语法制导翻译。
2、抽象语法树中,每个结点代表一个语法结构,比如对应某个运算符;
结点的每个子结点代表其子结构,比如对应运算分量,表示这些子结构按照特定的方式组成了较大的结构,可以忽略掉一些标点符号等非本质的东西。
抽象语法树是将源代码转换为目标代码的中间表示形式,可以帮助我们更好地理解源代码的结构和语义。在语法制导翻译中,我们可以通过遍历抽象语法树来执行语义动作,生成目标代码。

六、代码优化(8’)
说明局部优化和全局优化的不同。写出至少四个优化方法,并简述其算法。
1、局部优化是指单个基本块范围内的优化;全局优化是指面向多个基本块的优化。
2、优化方法:
①删除公共子表达式:如果表达式 x op y 先前已被计算过,并且从先前的计算到现在,x op y 中变量的值没有改变。那么可以删除公共子表达式。
②删除无用代码:在复制语句x = y的后面尽可能地用y代替x
③常量合并:如果在编译时刻推导出一个表达式的值是常量,就可以 使用该常量来替代这个表达式
④代码移动:对于那些不管循环执行多少次都得到相同结果的表达式,在进入循环之前就对它们求值。
⑤强度削弱:用较快的操作代替较慢的操作。

猜你喜欢

转载自blog.csdn.net/Elford/article/details/125383881