赞
踩
这是上学期编译原理的归纳总结,为非正式版,函待完善
编译器是将程序从一种语言翻译成另一种语言的计算机程序
将物理描述转换为tokens串(token为有意义的字符序列,Token 表示某种字符模式,例如标识符必须以字母开头,并且仅包含字母和数字)
扫描流程:
Each token is associated with a lexeme.
Each token may have optional attributes.
选择有用的tokens:
Regular expressions are a family of descriptions that can be used to capture certain languages (the regular languages). 正则表达式是可用于捕获某些语言(常规语言)的一系列描述。
Often provide a compact and human-readable description of the language. 通常提供紧凑且人类可读的语言描述。
符号ε是正则表达式,与空字符串匹配。
对于任何符号 a,符号 a 是仅匹配 a 的正则表达式。
如果 R1 和 R2 是正则表达式,则 R1R2 是表示 R1 和 R2 语言的串联concatenation的正则表达式。
如果 R1 和 R2 是正则表达式,则 R1 |R2 是表示 R1 和 R2 并集union的正则表达式。
如果 R 是正则表达式,则 R* 是 R 的 Kleene 闭包的正则表达式。
如果 R 是正则表达式,则 (R) 是与 R 具有相同含义的正则表达式。
优先级:(R) > R* > R1R2 > R1 |R2
So ab*c|d is parsed as ((a(b*))c)|d
其他标志:
大括号:(0|1){4} 代表有四个0或1
问号:1*0?1* = 1*(0 | ε)1* 表示0个或1个
中括号:选择其中的一个数字或字母[A-Za-z] 一个任意字母,[0-9]一个任意数字
加号:表示1个或多个
NFAs (nondeterministic finite automata),DFAs (deterministic finite automata),
只要至少到一个接受状态,自动机就被接受(结束)
ε-transitions自动转换,不消耗输入!
模拟NFA:
跟踪一组当前状态,最初是开始状态以及ε移动可到达的所有内容。
对于输入中的每个字符:维护一组下一个状态,最初为空。
对于每个当前状态:遵循用当前字母标记的所有过渡,将这些状态添加到下一个状态集。将可通过ε移动到达的每个状态添加到下一个状态集。
复杂度:O(mn2) 表示长度为 m 的字符串和具有 n 个状态的自动机。
DFA:每个状态必须为每个字母定义一个转换,不允许ε移动。
DFA的两种表示方法
在字符串上运行DFA的时间复杂度O(n),n代表字符串的长度
目的是分解到每个箭头上面只有一个字符标记,转换流程:
任何长度为 n 的正则表达式都可以转换为具有 O(n) 状态的 NFA。
可以确定长度为 m 的字符串是否与时间 O(mn2) 中长度为 n 的正则表达式匹配
我们如何确定哪些词素与每个标记token相关联?
当有多种方法可以扫描输入时,我们如何知道要选择哪一种?
解决方案是使用DFA scanner
回顾一下DFA的特性:
子集构造:
NFA 可以同时处于多个状态,而 DFA 一次只能处于一个状态。
关键思想:让DFA模拟NFA。
使 DFA 的状态对应于 NFA 的状态集。
DFA 状态之间的转换对应于 NFA 中状态集之间的转换。
转化步骤:
1.1. 消除ε-transition
1.2 消除同一个状态出发的同一字符的转化
2.子集构造
使用 DFA 的一种状态来替换通过从单个输入字符上的状态转换而达到的 NFA 状态集
3.1 建立ε-closure 闭包
3.2
I
a
I_a
Ia 子集
I 是一组状态,a 是字母表中的字符
难以理解?看看例子:
I
a
I_a
Ia 子集就是从a的下一状态开始找闭包,
I
b
I_b
Ib 子集就是从b的下一状态开始找闭包
4.从给定的 NFA M 构造 DFA M’的算法
图中T4包含10接受状态,所以后面状态T4是接受状态
要把状态的数量最小化
它们都是正则表达式 a* 的 DFA,但后者是最小的
理论:给定任何 DFA,都有一个等效的 DFA,其中包含最小数量的状态,并且此最小状态 DFA 是唯一的
等效状态概念:
如果 s 和 t 是两个状态,则它们等价当且仅当:
- s和t都是可接受或者不可接受状态
- 对于每个字符a∈Σ(Σ代表字符表集合),s 和 t 在a上具有到等效状态的转换
例子:
C 和 F 都是接受状态。它们在“a”到 C 上有过渡,在“b”到 E 上有过渡,所以它们是等效状态
S 是不接受状态,C 是接受状态。它们不是等效状态
状态最小化算法
将状态集拆分为一些不相交的集合,因此一个集合中的状态彼此等效,而不同集合的任意两个状态是可区分的。
最小化的步骤:
1.将状态集分成两组,一组由所有接受状态组成,另一组由所有不接受状态组成。
2.考虑每个子集的字母表的每个字符“a”上的转换,确定子集中的所有状态是等效的还是应该拆分子集。
2.1 如果一个子集中有两个状态 s 和 t,它们在不同的集合中具有转化的 ‘a’ 上,我们说 ‘a’ 区分状态 s 和 t
2.2 所考虑的状态集必须根据其 a 转换所在的位置进行拆分
3. 继续此过程,直到所有集合都只包含一个元素(原始 DFA 最小)或直到不再发生集合的进一步拆分。
例子1:
将下图中的NFA转化为DFA
1.分为接受状态集和不接受状态集
{S,A,B}{C,D,E,F}
2 继续拆分
通过a拆分:{S,A,B}=>{S}{A}{B}
{C,D,E,F}
3 设 D 代表 {C,D,E,F}
可化成
例子2:
1) 所有状态都是接受状态:{1,2,3}
2)没有一个状态用b来区分(3个状态经过b都到3),但a区分状态1与状态2和3:{1}{2,3}
3) {2,3} 不能用 a 或 b 来区分
如何确定词素和token的关联方式,当有多种方法可以扫描输入时,我们如何知道要选择哪一种?
假设所有标记都指定为正则表达式。
从左到右扫描,始终匹配其余文本的最长可能前缀(Maximal munch规则)
如何匹配Maximal munch最长前缀:
将表达式转换为 NFA。
并行运行所有 NFA,跟踪最后(最长)的匹配last match。
当所有自动机都卡住时,报告最后一个匹配并在该点重新开始搜索。
比如下列匹配,如double既满足do又满足double,取最长前缀
前面词法分析扫出token,而语法分析时可以分析恢复token结构
语法分析的输入输出:
输入:解析器调用扫描程序过程来获取下一个token
输出:需要构造显式或隐式语法树。语法树的每个节点都包含编译过程其余部分所需的属性
Scanner扫进token后给parser进行解析,如果解析正常就继续让scanner获取token
字母表是符号的集合∑,用作字母。
∑上的语言是一组由∑中的符号组成的字符串。
扫描时,我们的字母表是ASCII或Unicode字符。我们产生了token。
解析时,我们的字母表是scanner生成的一组tokens
正则表达式无法表达所有集合,如
这里没办法保证两边的a的数量相等
正则表达式表达能力不足:
无法定义与所有表达式匹配的正则表达式,并带有适当平衡的括号。
无法定义与具有正确嵌套块结构的所有函数匹配的正则表达式。
因此引入上下文无关文法,上下文无关语法(或CFG)是编程语言语法结构的规范。与正则表达式类似,不同之处在于上下文无关语法涉及递归规则(recursive rules),是正则语言的超集
例如算术表达式的上下文无关文法:
exp即expression的简写,op即operator的简写
上下文无关的语法 G= ( V T V_T VT, V N V_N VN, P, S):
解释:
例子:
Notation Conventions 表示法约定:
注意符号不要与正则表达式混淆,应该进行改写,不能使用 *、|或括号。
改写1:
改写2:
四种语法,不过只要掌握后面两个即可,越往下表示范围越小
推导的功能:
上下文无关语法规则确定一组语法合法的标记字符串
语法规则通过派生或归约来确定token的合法字符串
Derivation意味着用产生式右侧来替换非终结符
推导的例子:
当且仅当存在 0 个或多个推导步骤序列 (n>=0),
α= α1 且 β =αn. 如果 n=0, then α=β
设 G 是起始符号为 S 的上下文无关语法。那么G的语言是:
S 是 G 的起始符号,s 表示任意的标记符号字符串(有时称为句子)
S 是 G 的起始符号,如果S传递闭包为α,α仅包含终结符和非终结符,则α是G的sentential form
若w是G的sentential form,且w仅包含终结符,则w是G的Sentence
例子:给定 G,L(G) 可以通过推导获得
则S,0S1 ,00S11 ,000S111,00001111 都是 G 的sentential form
只有00001111是G的句子
例题:根据描述写出语法(产生式)
一种语言由 0 和 1 组成,该语言的每个字符串都有相同的 0 和 1 数
定义:
解析树是token字符串结构的有用表示形式
解析树直观地表示派生
语法分析树的组成部分:
派生可以表示为解析树
对于产生式 X→Y1 …Yn 添加子项 Y1, …, Yn 到节点 X
推导和语法分析树:
派生不唯一地表示它们构造的字符串的结构,而解析树可以
在推导的每个步骤中替换最左边的非终结符的推导,相当于前序遍历
示例:
在推导的每个步骤中替换最右边的非终结符的推导 它对应于解析树的后序遍历的反向
解析树包含的信息比编译器生成可执行代码绝对必要的信息多得多,抽象语法树仅包含真正需要的信息
区别:AST的运算符在中间节点而不是叶子结点
AST 是用于后续编译阶段的更好结构
抽象语法树表示实际源代码标记序列的抽象
parser将遍历解析树表示的所有步骤,但通常只会构造抽象语法树
字符串的语法分析树和抽象语法树:
可能一个表达式的最左最右推导的解析数是不同的,有二义性ambiguity
因此需要消除二义性规则(乘法优先,减法左结合)
或者重写语法,将运算符分组为优先级相等的组,对于每个优先级,我们必须编写不同的规则。
将语法更改为强制构造正确解析树的形式(考试考的少)
考试主要考判断是否具有二义性并给出理由(画语法树)
小结:
语法分析(解析)从扫描程序生成的token中提取结构。
语言通常由上下文无关语法 (CFG) 指定。
语法分析树显示如何从语法派生字符串。
如果字符串具有多个分析树,则语法不明确。
抽象语法树 (AST) 包含程序语法的抽象表示形式。
parsing语法分析的目标:恢复程序的语法结构
自顶向下就是从开始符号开始推导,过程大约如下:
最终要推导出的是终结符
但是有多个产生式多个推导时如何选择?因此要进行Predictive parsing预测解析
两种预测解析的方法:
Recursive-descent parsing 递归下降分析
LL(1) parsing
输入字符串的分析以语法的开始符号开头
如果它可以根据输入中的前瞻token唯一地确定下一个在推导中使用哪个产生式,则解析是预测性的
Predictive parsers接受 LL(k) 语法
定义
β可以推导出的所有token当中的首终结符集合,
如果β是ε的传递闭包,则ε也在β的First集当中
.
简单说就是β的产生式右边的第一个终结符就是First集属性
简单来说,非终结符A的Follow集就是产生式右边跟在A后面的终结符,如果有产生式后面什么都不跟那么$也在Follow集中
例子:
Let the current input symbol is “x” 不同情况选择不同的产生式
如果 x ∈FIRST(bAS)={b} ,则选择 A→bAS 进行派生
如果 x ∈FOLLOW(A)={$,a,d } ,则选择 A→ε 进行派生
因为 FIRST(bAS)∩FOLLOW(A)= Φ ,所以可以选择产生式,下面有个规则
判断是否为LL(1)文法的两个条件
例子:判断下列产生式是否符合LL(1)文法
因为First(A)∩Follow(A)={b}≠Φ,
故G[S] 不是 LL(1) 语法,当最左边要替换的非终端是 A 并且当前输入符号是 “b” 时,解析无法确定选择 A 的哪些产生式:A→bA 或 A→ε
a) 计算可空非终结符集合
b) 为每个产生式的右侧字符串α计算 FIRST(α)集
c) 计算每个非终端 A 的Follow(A)
d) 根据LL(1)的定义进行识别
存在一个推导使非终结符A的传递闭包为ε 就证明可空
若U 表示可空的非终结符集合
例子:
First集计算分成计算每个符号的First集和计算串的First集两种,这里先介绍计算每个符号的First集合
步骤:
例子:
例1:
这里关注S和C的First集,S推导出AB,而A,B均可空,因此A,B的First本身含的a,b然后再把ε都加进First(S),C推导出AD,A可空而D不可空,把AD含的a,b,c加进First( C )
计算产生式右侧的First集
假设α=X1X2…Xn,要计算First(α)
例2:这里计算的是产生式右侧的First集
例子:求每个非终结符的Follow集,首先要求出每个符号的First集
这里的G(S)证明S是起始符号,一定有$
另外产生式右侧非终结符后面没符号也加上$
注意事项:
例子:判断G[S]是否为LL(1)文法
消除left factor左公因子和左递归是LL(1)文法的必要不充分条件
同一个非终结符推出的,具有公共前缀,这个公共前缀就是左公因子,有这个左公因子就不是LL(1)
从A开始推导又推出A开头的符号串,则为左递归
这里a式子是immediate left recursion立即左递归,b式子是indirect left recursion间接左递归,推好几次才回到A开头
判断左递归的方法
对于下面的两个左边相同的产生式
一个产生式右边的First集包含另一个产生式右边的First集,就是左递归的,这里First(Aα)包含First(β),当然直接以A开头的立即左递归本身就很明显
不能保证消除左递归和消除左公因子这些技术的应用会将语法转换为LL(1)语法,转换之后要使用上节手段进行判断
消除的方法:
把
A→Aα1|Aα2|…|Aαm|β1|β2|…|βn
改成
A→β1A’|β2A’|…|βnA’
A’→α1 A’|α2 A’|…|αm A’|ε
即把左递归变成右递归,但要加上ε
简单例子:
A → Aα| β 改写成
A → βA’, A’ → αA’| ε
复杂例子:
这里E和T都有能看出来的直接左递归
这个比较简单,提公因子就是了
由一个主过程和一组递归过程组成,每个过程对应于语法的非终结符
使用的变量:
3.1 “或运算”|的递归下降代码
对于非终结符U,如果U → x1 | x2 |…|xn, and x1,…,xn≠ ε,则过程U的代码:
if TOKEN in First(x1) then p_x1
else if TOKEN in First(x2) then p_x2
else …
….
else if TOKEN in First(xn) then p_xn
else ERROR
如果产生式包含U → ε
则要把
if TOKEN in First(xn) then p_xn
else ERROR
改写为
if TOKEN in First(xn) then p_xn
else if TOKEN not in Follow(U) then ERROR
3.2 “与”运算的递归下降代码
x=y1y2…yn的代码
begin p_y1;p_y2;…;p_yn end
如果 yi∈VN (非终结符)则p_yi是过程 yi 的调用;否则,如果 yi∈VT(终结符) 则p_yi是match(yi)
例:
E→TE ’ E’→+TE’│ε
T→FT ’ T’→*FT’│ε
F→(E)│i
1)调用开始符号E的main函数
(1) program MAIN; /* main */
begin
GETNEXT (TOKEN);
E ; /* call E */
if TOKEN ≠‘$' then ERROR
end.
2)E→TE '的递归下降
(2) procedure E; /*E→TE'*/
begin
T; /*call T*/
E’ /*call E’*/
end
(3)E’→+TE’│ε
这里注意要算E’的Follow集
(3) procedure E’; /*E'→+TE'│ε*/
begin
if TOKEN=’+’ then /*E'→+TE'*/
begin
match(‘+’);(match表示当前必须匹配加号,否则不符合语法规则)
T; /*call T*/
E’ /*call E’*/
end
else /*E’ → ε*/
if TOKEN≠’)’ and TOKEN≠’$’ then ERROR
end;
4) F→(E)│i
(4) procedure F; /* F→(E)│i */
begin
if TOKEN = '(' then /*F→(E) */
begin
match(‘(’);
E; /* call E */
match(‘)’)
end
else /* F→i */
if TOKEN=‘i’ then match(‘i’)
else error;
end;
3.3 大括号(表示重复的,与while关联)
递归可以这样改写
E→ T {+ T}
(1) procedure E;
begin
T;
while token=‘+’ do
match(‘+’);
T;
end while;
end
3.4 [ ] 中括号表示可选的,出现0次或1次
Example:
if-stmt->if (exp) stmt
| if (exp) stmt else stmt
改写成
if-stmt->if (exp) stmt [else stmt]
例子:
if-stmt → if (Exp) Stmt [else Stmt]
翻译成递归下降代码
Procedure ifStmt;
begin
match(‘if’);
match(‘(‘);
Exp;
match(‘)’);
Stmt;
if token=‘else’ then //识别分支
match(‘else’);
Stmt;
end if;
end ifStmt;
Predictive Parsing的方法一种是递归下降分析,一种是LL(1)分析
例子:
对每个非终结符 A 和产生式 A → α 重复以下两个步骤:
对First(α)集里面每一个token,把产生式填进去(A直接推出)
若A的First集包含空串ε,进行第二步
如果ε在First(α)中 ,对Follow(A)的每个终结符’a’对应的产生式加进去(a可以出现在A后面,让A消失后面的a推出)
例子:要构造左边的语法的LL(1)分析表
(1)清除左递归,清除左公因子
(2) 计算出所有非终结符的First集合Follow集合
(3)根据前面所述构造分析表
如果关联的 LL(1) 分析表在每个表条目(每个单元格)中最多有一个产生式,则语法就是 LL(1) 语法。
若用该分析表对某个串进行分析,过程如下:
定义:从输入串开始,通过归约(反向利用产生式)最后把token串归约为开始符号
输入字符串是语法分析树的叶子,从这开始到开始符号(根节点)
但重点是确定要使用哪个字符串和产生式归约,比如
是两种不同的归约,一种可以到S一种不可以
采用Parsing Stack分析栈,自下而上的解析器使用显式堆栈来执行解析
自底向上的两种操作:
因此自底向上分析也叫shift-reduce parsing
例子:
Right Sentential Form 右句型
Viable Prefix 可行前缀
Handle 句柄
A shift-reduce parser traces out a rightmost derivationof the input string in reverse order 最右推导(从右向左扫,先替换最右边的匹配的非终结符)的逆过程就是归约(因此规约是从左向右换)
最右推导及过程中得到的句型都为右句型
例子:
重复从字符串输入移入(shift)到堆栈,直到可以执行归约(reduce)以获得下一个正确的句子形式
每个右句型是shift-reduce parsing的一个中间结果
解析堆栈上的符号序列(即Stack那一列出现过的)称为右句型的可行前缀
例如
对应的右句型为aAcde,则其可行前缀有aA, aAc, aAcd
只要解析堆栈的内容是右子句型的可行前缀,shift-reduce parsing就是正确的
右句型γ句柄是与产生式 A->β的右侧匹配的子字符串β,可以替换为 A 以在γ的最右推导中生成以前的右句型
例子:根据产生式和给出的串,写出字符串的句柄
首先写出开始符号的最右推导,最后一步替换出来的子串就是句柄
但是,在许多情况下,与某些生产 A->β 的右侧匹配的最左侧子字符串β可能不是句柄,因为产生式 A->β 的归约会产生不是右句型的字符串。 (即不是右句型,无法被归约为开始符号)
例如:
主要的三种:
每行代表一个状态,每列是一个语法符号
ACTION: take a state and a terminal symbol, determine the next action to take place 获取状态和终结符,确定要发生的下一个操作
GOTO: take a state and a grammar symbol, determine the next state 获取状态和语法符号(终结非终结都可),确定下一个状态
GOTO的Parsing Table仅保留非终结符就行
例子:
这是泛用的Parsing Table
这是利用Parsing table的parsing过程(LR Parser)
我们需要能通过上下文无关文法(产生式)写出其LR(0)项
语法 G 的 LR(0) 项是 G 的产生式,其右侧具有可区分的位置
例如,产生式 U→XYZ 有四个项
[0] U→ • XYZ [1]U→X • YZ
[2] U→XY • Z [3]U→XYZ •
产生式 A→ε只有一个项目 A→•
这些项称为 LR(0) 项,因为它们不包含对前瞻的显式引用
项是记录识别产生式右侧的中间步骤
LR(0) 项可以用作有限自动机的状态,该状态维护有关解析堆栈和移位-归约解析进度的信息
例子:
符号 X 从状态 i 到状态 j 的转换:
如果 I 是一组 LR(0) 项,则closure(I) 是由 I 构造的一组项,由以下规则构造:
如果**‘ • ’ 在最右端或者后面只有终结符**,闭包就是项本身
例子:
X∈VN∪VT
goto(I, X)= closure(J)
其中 J 是所有项目 [ A→αX•β] 的集合,使得 [A→α• Xβ] 在 I 中
例子:
项的冲突:
例子:判断是否为LR(0)文法
具体消除冲突操作规则:
I={X→α•bβ‚Α→r•‚Β→δ•}, where b∈VT,
if FOLLOW(A) ∩FOLLOW(B)=φ and not includes b, the action for I is based on the next input token ‘a’ 如果A和B的FOLLOW集无交集则 I 的操作基于下一个输入标记“a”
步骤如下:
例子:
语义:与正在翻译的程序的最终含义密切相关的信息
语义分析:分析编程语言语义规则所需的程序,以确定其正确性并保证正确执行
类型检查:
运算符的操作数类型是否相等?
赋值assignment的左侧和右侧的类型是否相等?
形参的类型是否与对应的实参相同?
数组的索引类型是否合适?
是否已声明使用的标识符?
V 是否已声明为“V[E]”的数组类型的变量?
构建符号表以跟踪声明中建立的名称的含义
对表达式和语句执行类型检查,以确定它们在语言类型规则中的正确性
属性的定义:属性是编程语言构造的任何属性
属性包括:
变量的数据类型 The data type of a variable
表达式的值 The value of an expression
过程的目标代码 The object code of a procedure
属性语法用来描述语义规则
属性直接与语法符号(终结符和非终结符)相关联
如果 X 是语法符号,a 是与 X 关联的属性,则与 X 关联的 a 的值写为 X.a
属性 a1,…,ak 的属性语法是语言的所有语法规则的所有属性方程的集合
通常,属性语法以表格形式编写
例1:无符号数对应的属性语法
无符号数的属性就是值
例2:变量声明对应的属性语法.
变量声明的属性就是数据类型
给定属性语法,每个语法规则都有一个关联的依赖关系图
每个符号的每个属性 Xi.aj 对应一个节点
对于每个属性方程 Xi.aj=fij(…,Xm.ak,…),从右侧的每个节点 Xm.ak 到节点 Xi.aj 都有一个边(表示 Xi.aj 对 Xm.ak 的依赖性)
三个属性语法的依赖关系图:
由上下文无关语法生成的合法字符串的依赖关系图是表示字符串解析树的每个节点的语法规则选择的依赖关系图的联合(很拗口,也就是说多个规则组成一个大规则)
如图,val属性是Synthesized Attribute
假设解析树或语法树已由解析器构造
合成的属性值可以通过树的单个自下而上或后序遍历来计算
先计算儿子节点的综合属性,再往上算
继承的属性具有从解析树中的父级流向子级或从同级流向同级的依赖项
在变量声明的属性文法中,dtype是继承属性
继承的属性可以通过解析树或语法树的先序遍历来计算,计算T的每个儿子的继承属性,从上往下递归算
与综合属性不同,由于继承的属性可能在儿子节点属性之间具有依赖关系,因此计算子项的继承属性的顺序非常重要
代码生成的任务是为目标计算机生成可执行代码,该代码表示源代码的语义
代码生成通常分为几个步骤
1.Intermediate code generation(与机器无关)
2.生成某种形式的汇编代码
3.优化:提高目标代码的速度和大小
中间代码有很多种,前面说到的抽象语法树也是一种,但是与实际代码相去甚远,所以这里采用三地址码
三地址码的一般形式,y和z进行op运行赋值给x即x=y op z
其中,x,y,z 是名称、常量或编译器生成的临时名称,x、y和z各代表内存中的一个地址;op代表任何算术或逻辑运算符,例如 + 、‘and’
例如2*a+(b-3)的三地址码:
三地址码只要不超过3个地址就行,下面是9种形式:
下面是将源代码改写为三地址码的例子:
生成的三地址代码被视为字符串属性
中间代码成为可以使用属性语法定义并由语法树的后序遍历生成的synthesized attribute合成属性
三地址码的属性语法:
字符串串联的符号:
函数:
newtemp( ) :返回一个新的临时名称
例子:
给定表达式的语法:
exp -> id=exp | aexp
aexp -> aexp+factor | factor
factor -> (exp) | num | id
假定令牌 id标识符 和 num整型常量具有预先计算的属性 strval,该属性是token的字符串值
对应的属性语法
再改写成三地址码,由于三地址码属性是综合属性,因此自底向上计算
前面主要是用于赋值和简单算术表达式的代码生成
现在说说控制语句和逻辑表达式的代码生成
如果布尔表达式用于计算逻辑值,则布尔表达式将以类似于算术表达式的方式进行转换
例子:
如果将它们用作控制语句上下文中的测试,例如 if-then 或 while-do,则布尔表达式的值不会临时保存,而是由程序中达到的位置表示
下面是控制语句的语法:
控制语句上下文中布尔表达式的翻译:
类型1:S->if E then S1
E.true 是附加到 S1 代码生成的第一条指令
E.false 是附加到 S 代码之后要执行的第一个指令
类型2:S->if E then S1 else S2
E.true 附加到 S1 代码的第一条指令
E.false 附加到 S2 代码的第一条指令
类型3:S->while E do S1
E.true 附加到 S1 代码的第一条指令
E.false 附加到 S 代码之后要执行的第一个指令
基本形式:
例1:E -> E1 or E2
先判断E1,E1.true则E.true
若E1.false,则创建新标签,判断E2,E2的真假出口和E相同
例2:E -> E1 and E2
例3:关系运算a<b or c<d and e<f
假设整个表达式的 true 和 false 出口已设置为 Ltrue 和 Lfalse
例4:
下述控制语句的代码生成
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。