第七章语义分析与中间代码生成
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
4
逆波兰表示法用不着使用括号, 逆波兰表示法用不着使用括号,根据运算量和算 符出现的先后位置, 符出现的先后位置,以及每个算符的目数就完全 决定一个表达式的分解。 决定一个表达式的分解。 例如: (a+b)*c将被表示成 例如: (a+b)*c将被表示成 ab+c* 所代表的表达式是a*(b+c) abc+* 所代表的表达式是a*(b+c) 所代表的表达式是(a+b)*(c+d) Ab+cd+* 所代表的表达式是(a+b)*(c+d) 把一般表达式翻译为后缀式是很容易的。 7.1给 把一般表达式翻译为后缀式是很容易的。表7.1给 出了把表达式翻译为后缀式的语义规则描述, 出了把表达式翻译为后缀式的语义规则描述,其 E.code表示 后缀形式,op表示任意二元操作符 表示E 中E.code表示E后缀形式,op表示任意二元操作符 ||”表示后缀形式的连接。 ,“||”表示后缀形式的连接。 产生式 E→E1 op E2 E →(E1) E → id 语义规则 E.code:=E1.code||E2.code||op E.code:=E1.code E.code:=id
语法分析器 静态检查器 中间代码产生器 代码 语法分析器
中间
图7.1静态检查和中间代码产生的地位
2
7.1中间语言 7.1中间语言
在6.2.4节中介绍了抽象语法树,它是源 6.2.4节中介绍了抽象语法树, 节中介绍了抽象语法树 程序的中间表示方法之一。 程序的中间表示方法之一。在此将介绍其 它几种常见的中间语言形式。 它几种常见的中间语言形式。 常见的中间语言有:后缀式( 常见的中间语言有:后缀式(逆波兰表示 ),树型表示 三元式和四元式等。 树型表示, 法),树型表示,三元式和四元式等。
E → E1 * E2
E.place := newtemp; E.code := E1.code || E2.code ||
gen(Байду номын сангаас.place':='E1.place'*'E2.plac
13
E → - E1
E.place := newtemp; E.code := E1.code || gen(E.place':=0-'E1
表中的@表示求负运算符。 表中的@表示求负运算符。 凡只需一个运算符的算符一律规定使用ARG1 凡只需一个运算符的算符一律规定使用ARG1
16
2.三元式
为了避免把临时变量填入到符号表, 为了避免把临时变量填入到符号表,三元 式是由算符OP,两个操作数域ARG1 ARG2组 OP,两个操作数域ARG1和 式是由算符OP,两个操作数域ARG1和ARG2组 成。两操作数域或者是指向符号表的指针 或者是指向三元式表的指针。 ,或者是指向三元式表的指针。
12
表7.3 对赋值语句产生三地址代码的属性文法 S → id := E E → E1 + E2
S.code := E.code || gen( id.place':='E.place E.place := newtemp; E.code := E1.code || E2.code || gen(E.place':='E1.place'+'E2
8
之所以称为三地址代码是因为每条语句通常包含 三个地址,两个用来表示操作数, 三个地址,两个用来表示操作数,一个用来存放 结果。对于后面给出的三地址代码中, 结果。对于后面给出的三地址代码中,用户定义 的名字在实际实现时将由指乡符号表中的相应名 字入口的指针所代替。 字入口的指针所代替。 常用的三地址语句种类 1)x := y op z 2)x := op y 3)x := y 4)if x relop y goto l 双目运算 单目运算 赋值 条件转移
第七章 语义分析和中间代码产生
紧接在词法分析和语法分析之后,编译程序 要做的工作就是进行静态语义检查和翻译。 静态语义检查通常包括: 1)类型检查 2)控制流检查 3)一致性检查 4)相关名字检查 其它如名字的作用域分析等也都是静态语义 分析的工作。
1
虽然源程序可以直接翻译为目标语言代码,但是许 多编译程序却采用了独立于机器的、复杂性介于源 语言和机器语言之间的中间语言。这样的好处是: 1)便于进行与机器无关的代码优化工作; 2)使编译程序改变目标机更容易; 3)使编译程序的结构在逻辑上更为简单明确。以 中间语言为界面,编译前端和后端的接口更清晰。 静态语义检查和中间代码产生在编译程序中的地位 如图7.1 如图7.1
5
逆波兰表示法中栈的使用
后缀式的计值用栈实现非常方便,一般 后缀式的计值用栈实现非常方便, 的计值过程是:自左至右扫描后缀式, 的计值过程是:自左至右扫描后缀式,每 碰到运算量就把它推进栈,每碰到K 碰到运算量就把它推进栈,每碰到K目算 符就把它作用于栈顶部的k个项, 符就把它作用于栈顶部的k个项,并用运 算的结果来代替这k个项( 算的结果来代替这k个项(运算的结果仅 有一项)。 有一项)。
11
表7.3是为赋值语句生成三地址代码的S-属性文法 7.3是为赋值语句生成三地址代码的 是为赋值语句生成三地址代码的S 定义。如给定输入a:=b*-c+b*- 非终结符号S 定义。如给定输入a:=b*-c+b*-c。非终结符号S 有综合属性S.code,它代表赋值语句S 有综合属性S.code,它代表赋值语句S的三地址 代码。非终结符号E有如下两个属性: 代码。非终结符号E有如下两个属性: 1)E.place表示存放E值的名字; E.place表示存放 值的名字; 表示存放E 2)E.code表示对E求值的三地址语句序列。 E.code表示对 求值的三地址语句序列。 表示对E 函数newtemp的功能是 每次调用它时, 的功能是, 函数newtemp的功能是,每次调用它时,将返回 一个不同临时变量名字, T1,T2, 一个不同临时变量名字,如T1,T2,…。 为方便,使用gen(x’:=‘y’+’z)表示生成三地址语 为方便,使用gen(x’:=‘y’+’z)表示生成三地址语 X:=y+z。代替x,y或 句X:=y+z。代替x,y或z出现的表达式在传递给 gen时求值,用单引号括起来的运算符或操作数 gen时求值 时求值, 将保留引号里字面的符号。在实际实现中, 将保留引号里字面的符号。在实际实现中,三地 址语句序列往往是被存放到一个输出文件中, 址语句序列往往是被存放到一个输出文件中,而 不是将三地址语句序列置入code属性之中 属性之中。 不是将三地址语句序列置入code属性之中。
表达式的三元式: 表达式的三元式:w*x+(y+z) (1) *, w, x (2) +, y, z (3) +, (1), (2) 第三个三元 式中的操作数(1) 式中的操作数 (2)表示第 和第 表示第(1)和第 表示第 (2)条三元式的计 条三元式的计 算结果。 算结果。
6
7.1.2图表示法 图表示法
图表示法包括DAG与抽象语法树 图表示法包括DAG与抽象语法树 无循环有向图DAG:与抽象语法树一样, 无循环有向图DAG:与抽象语法树一样, 对表达式中的每个子表达式,DAG中都有 对表达式中的每个子表达式,DAG中都有 一个结点。 一个结点。一个内部结点代表一个操作符 它的孩子代表操作数。 ,它的孩子代表操作数。 不同点: DAG中代表公共子表达式的结 不同点:在DAG中代表公共子表达式的结 点具有多个父结点, 点具有多个父结点,而在一棵抽象语法树 中公共子表达式被表示为重复的子树。 中公共子表达式被表示为重复的子树。 可见P167 可见P167
E → ( E1 ) E → id E → num
E.place:= E1.place; E.code:= E1.code E.place:= id.place; E.code:= ' ' E.place:= num.val;E.code:= ' ' ;
注释: || 表示代码序列的连接 注释:
14
三地址语句可看成中间代码的一种抽象形 通常有三种表示方法:四元式、 式。通常有三种表示方法:四元式、三元 间接三元式。 式、间接三元式。 1.四元式 1.四元式 四元式是一种比较普遍采用的中间代码形 四元式的四个组成部分是:算符OP, OP,第 式.四元式的四个组成部分是:算符OP,第 一运算量ARG1,第二运算量ARG2 ARG1,第二运算量ARG2以及运算结 一运算量ARG1,第二运算量ARG2以及运算结 RESULT.其中 其中, 果RESULT.其中,运算量和运算结果有时指 用户自定义的变量, 用户自定义的变量,有时指编译程序引进 的临时变量。 的临时变量。 如果OP是一个算术或逻辑算符, OP是一个算术或逻辑算符 如果OP是一个算术或逻辑算符,则RESULT 总是一个新引进的临时变量, 总是一个新引进的临时变量,它用来存放 运算结果。 运算结果。
15
例如:赋值语句A:=-B*(C+D)的四元式表示。 A:=例如:赋值语句A:= B*(C+D)的四元式表示。 的四元式表示
OP ARG1 ARG2 (1) @ (2) + (3) * (4) := B C T1 T3 — D T2 — RESULT T1 T2 T3 A 注解 T1为临时变量 T2为临时变量 T3为临时变量 赋值运算
7
7.1.3三地址代码 三地址代码
一般形式 x := y op z 其中 x, y, z 为变量名、常数或编译产生的 为变量名、 临时变量; 临时变量; Op代表运算符号如定点运算符、浮点运算符、 Op代表运算符号如定点运算符、浮点运算符、 代表运算符号如定点运算符 逻辑运算符等等。 逻辑运算符等等。 每个语句的右边只能有一个运算符。例如,源 每个语句的右边只能有一个运算符。例如, 语言表达式x+y*z可被翻译为如下语句序列: x+y*z可被翻译为如下语句序列 语言表达式x+y*z可被翻译为如下语句序列: T1:=y*z T2:=x+T1 其中T1 T2为编译时产生的临时变量。 其中T1,T2为编译时产生的临时变量。 T1, 为编译时产生的临时变量
9
其他三地址种类
goto l param x (n是参数个数 是参数个数) call p, n (n是参数个数) return x x := y[i] x[i] := y x := &y x := *y *x = y 无条件转移 实在参数 过程调用 过程返回 数组运算 指针运算
10
生成三地址代码时,临时变量的名字对应抽 生成三地址代码时, 象语法树的内部结点。对于产生式E 象语法树的内部结点。对于产生式E →E1+E2的左端的非终结符号 而言, →E1+E2的左端的非终结符号E而言,它的 的左端的非终结符号E 经过计算得出的值往往放到一个新的临时变 量T中。 赋值语句id:=E的三地址代码包括 的三地址代码包括: 赋值语句id:=E的三地址代码包括:对表达 求值并置于变量T 式E求值并置于变量T中,然后进行赋值 id.place:=T。 id.place:=T。如果一个表达式仅有一个单个 标示符,例如y,则由 自身保留表达式的值。 则由y 标示符,例如y,则由y自身保留表达式的值。
3
7.1.1逆波兰表示法 7.1.1逆波兰表示法
逆波兰表示法是把运算量(操作数)写在前面, 逆波兰表示法是把运算量(操作数)写在前面, 把算符写在后面(后缀),也称后缀表示法。 ),也称后缀表示法 把算符写在后面(后缀),也称后缀表示法。 例如:a+b写成 写成ab+, a*b写成 写成ab* .用这种办法表 例如:a+b写成ab+, a*b写成ab* .用这种办法表 示的表达式称为后缀式。 示的表达式称为后缀式。 一个表达式E的后缀形式定义: 一个表达式E的后缀形式定义: 1)如果E是一个变量或常量,则E的后缀式是E自 如果E是一个变量或常量, 的后缀式是E 身。 2)如果E是E1 op E2形式的表达式,这里op是任 如果E E2形式的表达式 这里op是任 形式的表达式, 何二元操作符, 的后缀式为E1’E2’op 何二元操作符,则E的后缀式为E1’E2’op ,这里 E’和E2’分别为E1和E2的后缀式。 E’和E2’分别为 和E2的后缀式 分别为E1 的后缀式。 3)如果E是(E1)形式的表达式,则E1的后缀式 如果E E1)形式的表达式, E1的后缀式 就是E的后缀式。 就是E的后缀式。
逆波兰表示法用不着使用括号, 逆波兰表示法用不着使用括号,根据运算量和算 符出现的先后位置, 符出现的先后位置,以及每个算符的目数就完全 决定一个表达式的分解。 决定一个表达式的分解。 例如: (a+b)*c将被表示成 例如: (a+b)*c将被表示成 ab+c* 所代表的表达式是a*(b+c) abc+* 所代表的表达式是a*(b+c) 所代表的表达式是(a+b)*(c+d) Ab+cd+* 所代表的表达式是(a+b)*(c+d) 把一般表达式翻译为后缀式是很容易的。 7.1给 把一般表达式翻译为后缀式是很容易的。表7.1给 出了把表达式翻译为后缀式的语义规则描述, 出了把表达式翻译为后缀式的语义规则描述,其 E.code表示 后缀形式,op表示任意二元操作符 表示E 中E.code表示E后缀形式,op表示任意二元操作符 ||”表示后缀形式的连接。 ,“||”表示后缀形式的连接。 产生式 E→E1 op E2 E →(E1) E → id 语义规则 E.code:=E1.code||E2.code||op E.code:=E1.code E.code:=id
语法分析器 静态检查器 中间代码产生器 代码 语法分析器
中间
图7.1静态检查和中间代码产生的地位
2
7.1中间语言 7.1中间语言
在6.2.4节中介绍了抽象语法树,它是源 6.2.4节中介绍了抽象语法树, 节中介绍了抽象语法树 程序的中间表示方法之一。 程序的中间表示方法之一。在此将介绍其 它几种常见的中间语言形式。 它几种常见的中间语言形式。 常见的中间语言有:后缀式( 常见的中间语言有:后缀式(逆波兰表示 ),树型表示 三元式和四元式等。 树型表示, 法),树型表示,三元式和四元式等。
E → E1 * E2
E.place := newtemp; E.code := E1.code || E2.code ||
gen(Байду номын сангаас.place':='E1.place'*'E2.plac
13
E → - E1
E.place := newtemp; E.code := E1.code || gen(E.place':=0-'E1
表中的@表示求负运算符。 表中的@表示求负运算符。 凡只需一个运算符的算符一律规定使用ARG1 凡只需一个运算符的算符一律规定使用ARG1
16
2.三元式
为了避免把临时变量填入到符号表, 为了避免把临时变量填入到符号表,三元 式是由算符OP,两个操作数域ARG1 ARG2组 OP,两个操作数域ARG1和 式是由算符OP,两个操作数域ARG1和ARG2组 成。两操作数域或者是指向符号表的指针 或者是指向三元式表的指针。 ,或者是指向三元式表的指针。
12
表7.3 对赋值语句产生三地址代码的属性文法 S → id := E E → E1 + E2
S.code := E.code || gen( id.place':='E.place E.place := newtemp; E.code := E1.code || E2.code || gen(E.place':='E1.place'+'E2
8
之所以称为三地址代码是因为每条语句通常包含 三个地址,两个用来表示操作数, 三个地址,两个用来表示操作数,一个用来存放 结果。对于后面给出的三地址代码中, 结果。对于后面给出的三地址代码中,用户定义 的名字在实际实现时将由指乡符号表中的相应名 字入口的指针所代替。 字入口的指针所代替。 常用的三地址语句种类 1)x := y op z 2)x := op y 3)x := y 4)if x relop y goto l 双目运算 单目运算 赋值 条件转移
第七章 语义分析和中间代码产生
紧接在词法分析和语法分析之后,编译程序 要做的工作就是进行静态语义检查和翻译。 静态语义检查通常包括: 1)类型检查 2)控制流检查 3)一致性检查 4)相关名字检查 其它如名字的作用域分析等也都是静态语义 分析的工作。
1
虽然源程序可以直接翻译为目标语言代码,但是许 多编译程序却采用了独立于机器的、复杂性介于源 语言和机器语言之间的中间语言。这样的好处是: 1)便于进行与机器无关的代码优化工作; 2)使编译程序改变目标机更容易; 3)使编译程序的结构在逻辑上更为简单明确。以 中间语言为界面,编译前端和后端的接口更清晰。 静态语义检查和中间代码产生在编译程序中的地位 如图7.1 如图7.1
5
逆波兰表示法中栈的使用
后缀式的计值用栈实现非常方便,一般 后缀式的计值用栈实现非常方便, 的计值过程是:自左至右扫描后缀式, 的计值过程是:自左至右扫描后缀式,每 碰到运算量就把它推进栈,每碰到K 碰到运算量就把它推进栈,每碰到K目算 符就把它作用于栈顶部的k个项, 符就把它作用于栈顶部的k个项,并用运 算的结果来代替这k个项( 算的结果来代替这k个项(运算的结果仅 有一项)。 有一项)。
11
表7.3是为赋值语句生成三地址代码的S-属性文法 7.3是为赋值语句生成三地址代码的 是为赋值语句生成三地址代码的S 定义。如给定输入a:=b*-c+b*- 非终结符号S 定义。如给定输入a:=b*-c+b*-c。非终结符号S 有综合属性S.code,它代表赋值语句S 有综合属性S.code,它代表赋值语句S的三地址 代码。非终结符号E有如下两个属性: 代码。非终结符号E有如下两个属性: 1)E.place表示存放E值的名字; E.place表示存放 值的名字; 表示存放E 2)E.code表示对E求值的三地址语句序列。 E.code表示对 求值的三地址语句序列。 表示对E 函数newtemp的功能是 每次调用它时, 的功能是, 函数newtemp的功能是,每次调用它时,将返回 一个不同临时变量名字, T1,T2, 一个不同临时变量名字,如T1,T2,…。 为方便,使用gen(x’:=‘y’+’z)表示生成三地址语 为方便,使用gen(x’:=‘y’+’z)表示生成三地址语 X:=y+z。代替x,y或 句X:=y+z。代替x,y或z出现的表达式在传递给 gen时求值,用单引号括起来的运算符或操作数 gen时求值 时求值, 将保留引号里字面的符号。在实际实现中, 将保留引号里字面的符号。在实际实现中,三地 址语句序列往往是被存放到一个输出文件中, 址语句序列往往是被存放到一个输出文件中,而 不是将三地址语句序列置入code属性之中 属性之中。 不是将三地址语句序列置入code属性之中。
表达式的三元式: 表达式的三元式:w*x+(y+z) (1) *, w, x (2) +, y, z (3) +, (1), (2) 第三个三元 式中的操作数(1) 式中的操作数 (2)表示第 和第 表示第(1)和第 表示第 (2)条三元式的计 条三元式的计 算结果。 算结果。
6
7.1.2图表示法 图表示法
图表示法包括DAG与抽象语法树 图表示法包括DAG与抽象语法树 无循环有向图DAG:与抽象语法树一样, 无循环有向图DAG:与抽象语法树一样, 对表达式中的每个子表达式,DAG中都有 对表达式中的每个子表达式,DAG中都有 一个结点。 一个结点。一个内部结点代表一个操作符 它的孩子代表操作数。 ,它的孩子代表操作数。 不同点: DAG中代表公共子表达式的结 不同点:在DAG中代表公共子表达式的结 点具有多个父结点, 点具有多个父结点,而在一棵抽象语法树 中公共子表达式被表示为重复的子树。 中公共子表达式被表示为重复的子树。 可见P167 可见P167
E → ( E1 ) E → id E → num
E.place:= E1.place; E.code:= E1.code E.place:= id.place; E.code:= ' ' E.place:= num.val;E.code:= ' ' ;
注释: || 表示代码序列的连接 注释:
14
三地址语句可看成中间代码的一种抽象形 通常有三种表示方法:四元式、 式。通常有三种表示方法:四元式、三元 间接三元式。 式、间接三元式。 1.四元式 1.四元式 四元式是一种比较普遍采用的中间代码形 四元式的四个组成部分是:算符OP, OP,第 式.四元式的四个组成部分是:算符OP,第 一运算量ARG1,第二运算量ARG2 ARG1,第二运算量ARG2以及运算结 一运算量ARG1,第二运算量ARG2以及运算结 RESULT.其中 其中, 果RESULT.其中,运算量和运算结果有时指 用户自定义的变量, 用户自定义的变量,有时指编译程序引进 的临时变量。 的临时变量。 如果OP是一个算术或逻辑算符, OP是一个算术或逻辑算符 如果OP是一个算术或逻辑算符,则RESULT 总是一个新引进的临时变量, 总是一个新引进的临时变量,它用来存放 运算结果。 运算结果。
15
例如:赋值语句A:=-B*(C+D)的四元式表示。 A:=例如:赋值语句A:= B*(C+D)的四元式表示。 的四元式表示
OP ARG1 ARG2 (1) @ (2) + (3) * (4) := B C T1 T3 — D T2 — RESULT T1 T2 T3 A 注解 T1为临时变量 T2为临时变量 T3为临时变量 赋值运算
7
7.1.3三地址代码 三地址代码
一般形式 x := y op z 其中 x, y, z 为变量名、常数或编译产生的 为变量名、 临时变量; 临时变量; Op代表运算符号如定点运算符、浮点运算符、 Op代表运算符号如定点运算符、浮点运算符、 代表运算符号如定点运算符 逻辑运算符等等。 逻辑运算符等等。 每个语句的右边只能有一个运算符。例如,源 每个语句的右边只能有一个运算符。例如, 语言表达式x+y*z可被翻译为如下语句序列: x+y*z可被翻译为如下语句序列 语言表达式x+y*z可被翻译为如下语句序列: T1:=y*z T2:=x+T1 其中T1 T2为编译时产生的临时变量。 其中T1,T2为编译时产生的临时变量。 T1, 为编译时产生的临时变量
9
其他三地址种类
goto l param x (n是参数个数 是参数个数) call p, n (n是参数个数) return x x := y[i] x[i] := y x := &y x := *y *x = y 无条件转移 实在参数 过程调用 过程返回 数组运算 指针运算
10
生成三地址代码时,临时变量的名字对应抽 生成三地址代码时, 象语法树的内部结点。对于产生式E 象语法树的内部结点。对于产生式E →E1+E2的左端的非终结符号 而言, →E1+E2的左端的非终结符号E而言,它的 的左端的非终结符号E 经过计算得出的值往往放到一个新的临时变 量T中。 赋值语句id:=E的三地址代码包括 的三地址代码包括: 赋值语句id:=E的三地址代码包括:对表达 求值并置于变量T 式E求值并置于变量T中,然后进行赋值 id.place:=T。 id.place:=T。如果一个表达式仅有一个单个 标示符,例如y,则由 自身保留表达式的值。 则由y 标示符,例如y,则由y自身保留表达式的值。
3
7.1.1逆波兰表示法 7.1.1逆波兰表示法
逆波兰表示法是把运算量(操作数)写在前面, 逆波兰表示法是把运算量(操作数)写在前面, 把算符写在后面(后缀),也称后缀表示法。 ),也称后缀表示法 把算符写在后面(后缀),也称后缀表示法。 例如:a+b写成 写成ab+, a*b写成 写成ab* .用这种办法表 例如:a+b写成ab+, a*b写成ab* .用这种办法表 示的表达式称为后缀式。 示的表达式称为后缀式。 一个表达式E的后缀形式定义: 一个表达式E的后缀形式定义: 1)如果E是一个变量或常量,则E的后缀式是E自 如果E是一个变量或常量, 的后缀式是E 身。 2)如果E是E1 op E2形式的表达式,这里op是任 如果E E2形式的表达式 这里op是任 形式的表达式, 何二元操作符, 的后缀式为E1’E2’op 何二元操作符,则E的后缀式为E1’E2’op ,这里 E’和E2’分别为E1和E2的后缀式。 E’和E2’分别为 和E2的后缀式 分别为E1 的后缀式。 3)如果E是(E1)形式的表达式,则E1的后缀式 如果E E1)形式的表达式, E1的后缀式 就是E的后缀式。 就是E的后缀式。