中间代码
(1)后缀式(逆波兰表示)
典型特征是操作数在前,操作符紧跟其后。例如:a+b*c -> abc*+
(2)三地址码
result:=arg1 op arg2 / result:=op arg1或op arg2。
形式上是最多仅由一个二元运算组成的赋值句。
例如x:=a+b*c可以是
T1:=b*c
T2:=a+T1
x:=T2
它的实现为三元式或四元式。
三元式:(op,arg1,arg2),表示:=arg1 op arg2
四元式:(op,arg1,arg2,result),表示result:=arg1 op arg2,这为优化代码提供了方便。
(3)树的优化表示:DAG
它可以识别表达式中的公共子表达式。构造时,查询要构造的节点是否已存在。若存在,则返回该节点指针即可,若不存在才构造。
以a+a*(b-c)+d*(b-c)为例,它的DAG是这样的
符号表
符号表是连接声明与引用的桥梁。在声明时,一个名字的相关信息被写入符号表。
1. 符号表的条目
每个声明的名字在符号表中占据一栏,之后是属性。
构成名字的字符串有两种储存方式:a.直接储存 b.间接储存。
间接储存的方式可以延伸。对于一个复杂的属性,可以另外开辟空间,然后把指向该空间的指针放在此属性在符号表中的对应位置即可。
2. 名字的作用域
a.静态作用域原则。即仅从静态读程序就可以确定作用域
b.最近嵌套原则。名字的声明在离其最近的内层起作用
实现符号表的数据结构
1. 线性表
为了正确反映名字的作用域,线性表应有栈的性质。当从某个作用域退出时,从栈顶把该作用域中的所有名字踢掉,放在临时表里。进入某作用域时,把需要的名字加入。只有确认某名字永远不会被使用时才会把这个名字真正删除。
2. 散列表
在正常散列表的结构上,增加了一个作用域链
hash link:连接所有具有相间hash 值的元素,表头在表头数组中。
scope link:链接所有在同一作用城中的元素,表头在作用域链中。
在散列表里可以进行如下操作:
(I)查找。首先计算做散列函数,然后从散列函数所指示的入口进入某个线性表,在线性表中沿hash link,像查找单链表中的名字一样进行查找。
(2)插入。首先查找,以确定要插入的名字是否已在表中,若不在,则要将其分别沿hashlink和scope link 插入到两个链中,方法都是插在表头,即两个表均可看做是栈。
(3)删除。把以作用域链接在一起的所有元素从当前符号表中删除,保留作用域链所链的子表,为后继工作使用(如果是临时删除,则下次使用时将该元素直接沿作用域链加入到.散列链中即可)。