中间代码形式

后缀式、语法树、三地址码

常见语句翻译

说明语句翻译

一般不产生代码;仅将有关变量的属性填入符号表(如类型、存储偏移位置-offset)、

过程嵌套符号表管理

每个过程有自己单独的符号表,并且表头指向父过程符号表头,而父过程符号表某表项指向本过程符号表头。

赋值语句翻译

三地址码直接写一个赋值语句代替即可,其具体翻译方案包括分配新存储空间newtemp、emit产生代码等语义动作。

数组元素翻译

1.数组类型

  1. Pascal的数组声明:
 A : array[low1..high1,...,lown..highn] of integer;

数组元素表示:A[ i , j, k,…] (或表示为 A[i][j][k]…)

其中:(下界)$low_1 \leq i \leq high_1$(上界), j,k,…同理

  1. C语言的数组声明
int  A [100][100][100]; 

数组元素表示:A[ i ][30][40],$0 \leq i \leq (100-1)$。

2.翻译时下标计算

​ 对于Pascal数组声明,下标计算分为可变部分和常量部分;C语言中数组常量部分恒为数组首地址。

  1. 一维:A[ i ] 的地址addr为
\[addr &= A + ( i - low_1 ) * w \\ &= i w + ( A - low_1 * w )\]
  1. 二维:A[ i1 , i2 ]的地址addr为
\[addr &= A+((i_1 - low_1)*n_2+(i_2-low_2))*w \\ &= (i_1*n_2+i_2)*w + A-(low1*n_2+low_2)*w\]

其中n2表示第二维的大小,即$high_2-low_2+1$

  1. k维
\[addr = (...((i_1*n2+i_2)*n_3+i_3)...*n_k+i_k)*w+A-(...((low_1*n2+low_2)*n_3+low_3)...*n_k+low_k)*w\]

可以看到,常数部分和可变部分下标是对应的,因为实际上总是对$i_k-low_k$展开,$i_k$和$low_k$乘以的系数是相同的,都是$n_{k+1}$

3.翻译生成代码例子

数组A:A[ 1..10, 1..20] of integer; 数组B:B[ 1..10, 1..20] of integer; w : 4 (integer)

翻译语句:A[ i, j ] := B[ i, j ] * k

翻译时遵循的技巧:先计算可变部分,然后计算常数部分(即变换后首地址),最后用可变部分作为偏移,常数部分作为首地址来访问数组元素。

(1) t1 := i * 20
(2) t1 := t1 + j
(3) t2 := A - 84  // 84 == ( (1*20)+1 ) * 4
(4) t3 := t1 * 4  
// 以上A[ i, j ]的(左值)翻译
(5) t4 := i * 20
(6) t4 := t4 + j
(7) t5 := B - 84 
(8) t6 := t4 * 4
(9) t7 := t5[ t6 ]
//以上计算B[i,j]的右值
(10) t8 := t7 * k
//以上整个右值表达
//式计算完毕
(11)  t2[ t3 ] := t8
// 完成数组元素的赋值

条件语句

循环语句

SWITCH语句

其他控制流翻译

布尔表达式的翻译

布尔表达式的正确理解需要考虑逻辑运算符的优先级:

  1. 非高于与高于或。
  2. 括号优先。

1.按值翻译

​ 按值翻译的原理为:对于每一个布尔表达式,计算得到一个值,真为1、假为0(真则goto赋值为1的位置,假则顺序执行赋值为0,并跳转到下一个表达式),最后从后往前依次进行逻辑运算,最后得到的计算结果就是布尔表达式的值。对于其中任意一个布尔表达式,其翻译得到的中间代码如下:

if ... goto 4 
t1 = 0 //False
goto 5
t1 = 1 //True
... //next expression

​ 注意,对于包含not的表达式,先按上述方法不考虑not计算得到值,然后加一条对值取反即可。

​ 一个长的复杂布尔表达式,如果按值翻译,最后从后往前依次进行逻辑运算即可不用考虑优先级,因为最终结果相同。

2.短路翻译

​ 短路计算的核心思想为如果连续与运算,有一个假则整个式子为假,如果连续与运算,有一个真则全真,另外非运算被算在子表达式内。具体写中间代码时,连续与运算中某子表达式为假,出口为整个表达式的假出口L_false,否则为下一个表达式入口;连续或运算中某子表达式为真,出口为整个表达式的真出口L_true,否则为下一个表达式入口;如果有非运算作为某子表达式前缀,上述规则反转。

​ 例如i && j && i > j || !(i>10),首先正确区分优先级,先计算连续与子表达式,然后计算或。代码如下:

if i!=0 goto 3
goto 7 
if j!=0 goto 5
goto 7
if i>j goto L_true //式子必为真,因为最外层是连续或
goto 7 //接着判断
if i<=10 goto L_true
goto L_false

​ 短路翻译总是先判断真的情况,顺序执行的时候为假。更精简的做法不这样做。

​ 子表达式的真假出口在哪里要取决于外一层表达式是连续或还是连续与。

3.精简翻译

​ 核心思想为:

​ 每一个表达式仅使用一行代码,对于连续与的子表达式,除最后一个均用if判断假的情况(最后一个判断真的情况,因为顺序执行到最后一个,如果真那么可以直接跳到连续与真出口),这样可以直接跳到假出口,而真则一直自然顺序执行到最后一个子表达式,最后一个子表达式用if判断真的情况,这样可直接跳到真出口,假出口在自然顺序执行的下一条语句。

​ 对于连续或子表达式,除最后一个均用if判断真的情况,这样可以直接跳到真出口,而假则一直自然顺序执行到最后一个子表达式,最后一个子表达式用if判断假的情况,这样可直接跳到假出口,真出口在自然顺序执行的下一条语句。

​ 例如:i && j && i > j || !(i>10)

if i==0 goto L_false
if j==0 goto L_false
if i>j goto L_true
if i<=10 goto L_true
goto L_false