一、变量的定义和声明
1.变量的类型定义与声明
类型定义为编译器提供储存空间的大小信息,而变量声明为变量分配储存空间
2.变量声明的语法制导翻译
二、数组变量的声明
数组在符号表中占领与简单变量同样多的域,对于数组的详细信息另外安排内情向量来记录,并且符号表中应有一个指针指向内情向量地址。
1.静态数组的内情向量
为了数组元素的引用,数组声明时需要保存的信息包括offset,数组的维数n,每维的成员个数di,type..等等。还需要约定行储存还是列储存。
2.动态数组的内情向量
无法在编译时确定动态数组的大小。此时希望程序运行时填写内情向量中的内容,使得需要它们的时候是确定的。按下述方式进行动态数组的储存分配和数组元素地址计算的翻译
(1)在编译时给数组分配一个内情向量表区,即讲原来放在编译时的符号表中的内情向量移到运行时的存储空间中
(2)编译时加入一段特殊的代码,该代码根据运行时确定的di值填写内情向量,并分配储存区,并填写向量表的各项内容。
三、过程的定义&声明
1.左值和右值
左值是容器,右值是内容。它们的实质是是否对应储存空间。
2.参数传递
(a) 值调用:调用时首先计算形参,并把它的右值传递给被调用过程。过程内部对参数的修改不影响实参变量原来的值
(b) 引用调用:调用时首先计算实参,并把它的右值传递给被调用过程。过程内部对参数的修改等价于对实参的修改
(c) 复写-恢复:在过程体中不对实参进行操作,此时与值调用完全相同,过程返回时把操作结果拷贝给实参
(d) 换名调用:每次对过程的调用,实则是用过程体替换过程调用。用实参中的文字替换体中的形参。(例如在C中是#define,预处理时进行宏替换。)
3.作用域信息的保存
(a)过程的作用域:设主程序的嵌套深度d=1,则
若过程A直接嵌套定义过程B,则dB=dA+1
变量声明时所在过程的嵌套深度被认为是该变量的嵌套深度
(b)符号表中的作用域信息
4.记录的域名
记录把若干个变量或者记录封装在一起形成新的数据类型。所以记录的域名也是嵌套的。
四、算术表达式&赋值句
1.简单变量的语法制导翻译
其中 emit(result':='arg1'op'arg2)产生result:=argl op arg2的三地址码指令。
2.变量的类型转换
T := itr E 整型转实型,并放在T里
T := rti E 实型转整型,并放在T里
五、数组元素引用的语法制导翻译
数组元素的地址由不变部分和可变部分共同确定,可以用变址的方式表示为
CONSPART[VARPART],或者T1[T]
将不变部分作为基址,而可变部分作为变址,于是取数组元素的值和向数组元素赋值的三地址码可以分别如下所示:
取值X:=T1[T]
赋值T1[T]:=X
六、布尔表达式
1.数值表示&直接计算的语法制导翻译
引入一个新的变量nextstat,它总是指向第-一个可用的三地址码序号,每调用一次emit操作nextstat 增值1
2.短路计算的语法制导翻译
一旦能确定表达式的真假,就转向对应的代码。需要为布尔表达式E引入两个新的属性和一个产生标号的函数
属性.true 表达式的真出口
属性.false 表达式的假出口
函数newlabel 产生一个标号
3.拉链&回填
为了在一次遍历中就确定三地址码的真出口和假出口,我们在三地址码中的转向不确定时,把所有转向同一地址的三地址码拉成一个链,一旦转向的地址确定,则沿着拉链回填地址。为此需要新增两个属性
属性.tc 真出口链
属性.fc 假出口链
引入以下函数实现拉链和回填
mkchain(i) 为序号为i的三地址码构造一个新链并返回指针
merge(p1,p2) 将链p1,p2合并,返回指针
backpatch(p,i) 给链p填i值
引入M->ε,为M引入新的属性.stat记录第一个可用的三地址码的序号
七、控制语句
1.标号&无条件转移
标号在一定的作用域仅可定义一次,但可以引用多次。标号定义出现时就把与其相关的内容填进符号表里,标号引用出现时,就根据符号表里的信息生成正确转移地址的三地址码。
在符号表里,为标号设置以下信息域
. type 记录标识符的类型,如‘标号’‘未知’
.def 若是标号,记录是否定义
.addr 标号定义后,作为此标号对应三地址码的序号
引入fill(entry(id.name),a,b,c)把a,b,c填写到符号表中id.name的type,def,addr中。
2.条件转移
条件转移具有if-then结构,if-then-else结构和while-do结构。
对于if-then结构
对于if-then-else结构
对于while-do结构