编译技术 2024 秋季学期的回忆版真题,来源于计算机速通之家 | QQ 群号:468081841。
🙇♂️🙇♂️🙇♂️时间仓促,有不足之处烦请及时告知。邮箱hez2z@foxmail.com 或者在 速通之家 群里
@9¾。
DFA/NFA/文法/语法树
最简 DFA
DFA 化为最简
步骤 1:化简正则式
原式:
先看里面的括号:
- 表示可以是空串 或者一个字母 。
- 所以 可以展开为两种情况:
因此:
于是整个正则式化为:
步骤 2:进一步化简
注意:
- 内部子式可以写成并集:。其中 匹配任意个 b(包含空串), 匹配以 a 开头并跟任意个 b 的串。
- 外层的 Kleene 星对这两类块任意重复,因此能够拼出任意由 a 和 b 组成的串(包括空串)。因此可以直接断言:
即原正则式能够生成所有由 a 和 b 组成的字符串。
步骤 3:构造最简 DFA
对于语言 ({a,b}^*)(所有由 a 和 b 组成的字符串),DFA 很简单:
- 只需要一个状态 ()。
- () 同时是初态和终态。
- 对 a 或 b 的输入仍然回到 ()。
形式化表示:
- 状态集:
- 字母表:
- 初态:
- 终态:
- 转移函数:
步骤 4:验证
- 空串 可接受,状态在 。
- 任意 b 序列,如 bbb,状态始终在 (q_0),接受。
- 任意 a 或 a 和 b 混合序列,如 “abba”,状态始终在 (q_0),接受。
- DFA 只有一个状态,无法再简化。
✅ DFA 最简。
结论:
原正则式
对应的最简 DFA 只有一个状态 ,初态也是终态,所有 a、b 的输入都回到 。
判断二义性
Q: 判断文法 是否有二义性
判断二义性举反例就行了,即一个句子有两棵不同的语法树。答案不唯一。
A: 句子 的两棵语法树如下:
graph TD
S1[S] --> S2[S]
S1 --> OP1[+]
S1 --> S3[S]
S2 --> A1[a]
S3 --> S4[S]
S3 --> OP2[+]
S3 --> S5[S]
S4 --> A2[a]
S5 --> A3[a]
graph TD
S1[S] --> S2[S]
S1 --> OP1[+]
S1 --> S3[S]
S2 --> S4[S]
S2 --> OP2[+]
S2 --> S5[S]
S3 --> A3[a]
S4 --> A1[a]
S5 --> A2[a]
语法分析树
画出语句的语法分析树,写出短语,直接短语,句柄
这里分析的短语,直接短语,句柄
短语:
直接短语:
句柄:
伪代码
写出文法对应的递归下降分析程序伪代码
A:
判断文法是否是?
左递归、左公因子
若不是,改造文法。构造相关的First集合与FOLLOW集合
构造 分析表
利用分析表给出句子的分析过程
写出文法的递归下降分析器
首先判断该文法是否是 文法:
-
消除左递归
存在左递归,改造为:
-
消除左公因子:文法中没有左公因子。
因此,该文法变成:
接下来,构造各非终结符的 和 集合:
| 非终结符 | FIRST 集合 | FOLLOW 集合 |
|---|---|---|
| S | { a, ( } | { $ } |
| A | { c } | { a, (, ) } |
| A’ | { b, ε } | { a, (, ) } |
注意:在产生式 中,符号 后面跟随的是非终结符 ,因此 包含 。另外在 中,右括号 也是 的一部分。
然后,构造 分析表(仅列出非空项):
| a | ( | c | b | ) | $ | |
|---|---|---|---|---|---|---|
| S | S → a A S | S → ( A ) | ||||
| A | A → c A’ | |||||
| A’ | A’ → ε | A’ → ε | A’ → b A’ | A’ → ε |
这个题没有给出具体输入串,所以跳过逐步分析过程。
下面是该文法的递归下降分析器伪代码:
{% tabs LL(1) %}
void S() {
if (lookahead == 'a') {
advance(); // match('a')
A();
S();
} else if (lookahead == '(') {
advance(); // match('(')
A();
advance(); // match(')')
} else {
error("Expect 'a' or '('");
}
}
void A() {
if (lookahead == 'c') {
advance(); // match('c')
A_prime(); // 调用 A'
} else {
error("Expect 'c'");
}
}
void A_prime() {
if (lookahead == 'b') {
advance(); // match('b')
A_prime(); // 递归处理后续的 b
} else if (lookahead == 'a' || lookahead == '(' || lookahead == ')') {
// 属于 A' 的 FOLLOW 集合,匹配 epsilon,直接返回
return;
} else {
error("Unexpected token in A'");
}
}
void S() {
if (lookahead == 'a') {
match('a');
A();
S();
} else if (lookahead == '(') {
match('(');
A();
match(')');
} else {
error("Expect 'a' or '('");
}
}
void A() {
if (lookahead == 'c') {
match('c');
A_prime(); // 调用 A'
} else {
error("Expect 'c'");
}
}
void A_prime() {
if (lookahead == 'b') {
match('b');
A_prime(); // 递归处理后续的 b
} else if (lookahead == 'a' || lookahead == '(' || lookahead == ')') {
// 属于 A' 的 FOLLOW 集合,匹配 epsilon,直接返回
return;
} else {
error("Unexpected token in A'");
}
}
{% endtabs %}
伪代码的形式好像有 advance() 和 match() 两种。都是对的应该。
分析
注: 为终结符
- 改造为 文法
- 写出各非终结符的 、 集
- 画出 分析表
- 写出语句 分析过程
A:
设 为 , 为 , 为
则文法为:
首先判断该文法是否是 文法:
存在左公因子,改造为:
因此,改造后的文法为:
接下来,构造各非终结符的 和 集合:
| 非终结符 | FIRST 集合 | FOLLOW 集合 |
|---|---|---|
| S | { int, float, char } | { $ } |
| T | { int, float, char } | { ID } |
| V | { ID } | { ; } |
| V’ | { ’,’, ε } | { ; } |
然后,构造 分析表:
| int | float | char | ID | , | ; | $ | |
|---|---|---|---|---|---|---|---|
| S | S → TV; | S → TV; | S → TV; | ||||
| T | T → int | T → float | T → char | ||||
| V | V → ID V’ | ||||||
| V’ | V’ → , V | V’ → ε |
最后分析一下语句 char x, y, z; 的分析过程:
| 步骤 | 栈 | 输入 | 动作 |
|---|---|---|---|
| 1 | # S | char ID , ID , ID ; # | S → T V ; |
| 2 | # ; V T | char ID , ID , ID ; # | T → char |
| 3 | # ; V char | char ID , ID , ID ; # | 匹配 char |
| 4 | # ; V | ID , ID , ID ; # | V → ID V’ |
| 5 | # V’ ID ; | ID , ID , ID ; # | 匹配 ID(x) |
| 6 | # V’ ; | , ID , ID ; # | V’ → , V |
| 7 | # V , ; | , ID , ID ; # | 匹配 , |
| 8 | # V ; | ID , ID ; # | V → ID V’ |
| 9 | # V’ ID ; | ID , ID ; # | 匹配 ID |
| 10 | # V’ ; | , ID ; # | V’ → , V |
| 11 | # V , ; | , ID ; # | 匹配 , |
| 12 | # V ; | ID ; # | V → ID V’ |
| 13 | # V’ ID ; | ID ; # | 匹配 ID |
| 14 | # V’ ; | ; # | V’ → ε |
| 15 | # ; | ; # | 匹配 ; |
| 16 | # | # | 接受 |
分析成功!
判断文法,构造分析表,分析输入串
已知文法为:
- 判断该文法是否是 文法,是否是 文法
- 若是 文法,构造相应分析表
- 对输入串 # 给出分析过程
A:
- 构造文法的 项目集规范族
- 构造识别活前缀的DFA
- 这个文法哪类 文法并说明理由
太多了,压榨 AI 也没法写了,直接看视频吧。
四元式
写出程序的四元式序列
while(a > 0 && b > 0) {
if(x > y) {
// 两个赋值语句
}
else
}
详细解析
由于题目中两个赋值语句的具体内容没有给出,我们假设为典型的赋值语句:a = b + c 和 x = y * z。实际题目中可能有具体赋值语句。
四元式生成过程
-
while循环的条件处理:
a > 0 && b > 0需要分解为两个条件- 先计算
a > 0,再计算b > 0,最后做逻辑与
-
控制流标签:
L1: while循环开始标签L2: 循环体开始标签L3: if条件为真时跳转标签L4: if语句结束标签L5: while循环结束标签
四元式序列
| 序号 | 四元式 | 注释 |
|---|---|---|
| 1 | (j, , , L1) | 跳转到循环开始 |
| 2 | (>, a, 0, t1) | t1 = a > 0 |
| 3 | (jfalse, t1, , L5) | 如果 t1 为假,跳出循环 |
| 4 | (>, b, 0, t2) | t2 = b > 0 |
| 5 | (jfalse, t2, , L5) | 如果 t2 为假,跳出循环 |
| 6 | (>, x, y, t3) | t3 = x > y |
| 7 | (jfalse, t3, , L4) | 如果 t3 为假,跳转到else |
| 8 | (+, b, c, t4) | t4 = b + c (第一个赋值) |
| 9 | (=, t4, , a) | a = t4 |
| 10 | (*, y, z, t5) | t5 = y * z (第二个赋值) |
| 11 | (=, t5, , x) | x = t5 |
| 12 | (j, , , L1) | 跳转回循环开始 |
| 13 | (label, , , L4) | else分支开始标签 |
| 14 | (j, , , L1) | else为空,直接跳回循环 |
| 15 | (label, , , L5) | 循环结束标签 |
说明
-
条件短路:
&&操作符具有短路特性,如果第一个条件a > 0为假,直接跳出循环 -
逻辑与:通过两个连续的条件判断实现
&& -
控制流:使用
jfalse和j四元式实现条件跳转和无条件跳转 -
临时变量:使用
t1, t2, t3, t4, t5存储中间结果 -
标签:使用标签实现循环和分支的跳转目标
设计文法
文法一
设有文法:
文法二
求语法指导翻译方案和语法定义
题目有给例子
-
输出每个 的位置 样例输出 (2 5 8)
-
输出 的数量 样例输出 (6)
救命,这是什么