一、表达式的中间代码生成
基于LL(1)方法
原始不带有语义动作的LL(1)文法:
简单表达式带有语义动作的LL(1)文法:
GenCode安排在一个运算符的右分量后面,因为这个时候涉及到这个运算符的所有操作数都已经进入语义栈Gem中,可以进行该运算符的计算,生成对应的四元式。
简单表达式的LL(1)分析表
表达式的中间代码生成过程分析举例
语义栈看上去压入的是x,但是实际上压入的是x的语义信息。
首先对应输入流的第一个字符x,文法从E开始推导,(这里的语义栈是从左往右写的),使用E->T Es
生成然后T继续使用T->P Ts
,继续向下生长子树,继续执行P->id #Push(id)#
,(注意写的顺序,id在分析栈的顶部),这个时候可以把id(也就是x)和输入流中的x进行匹配,将id匹配掉,执行push,剩下的不是句柄,继续进行。
执行加减乘除的操作的时候生成对应的四元式。最后的计算结果存储在语义栈Gem的栈顶。
⚠️符号表是在什么时候被创建的?实际上是在语义动作中实现符号表的创建、检查和删除。
⚠️如果是有回溯的自底向上语法如何插入语义动作?首先确定正确的可执行路经,然后在确定好的路径上添加想要执行的语义动作。
例题:
将下列表达式翻译成四元式序列:X*2+A*(i+1)/(j+1)
,其中i和j为整型变量,其它为实型变量。
(将给定的表达式翻译成四元式,根据计算过程,一般给的是程序)
(float,2,_,t1)//首先将常数赋值给中间变量t1,整型变量变成float
(multf,X,t1,t2)//执行+前面的部分
(addi,i,1,t3)//首先执行i+1的部分
(float,t3,_,t4)//因为i和j是整型变量,在这里进行实型变量运算,所以需要将其转换成float计算
(multf,A,t4,t5)//执行A*(i+1)
(addi,j,1,t6)
(float,t6,_,t7)
(divf,t5,t7,t8)
(addf,t2,t8,t9)
二、变量的中间代码生成
- 标识符变量——直接压入Gem
- 指针变量——将栈顶的元素取出,然后将其地址压入Gem
- 域选择变量——类似结构体structure中的某个分量,eg:
student.ID
- 下标变量(数组)——需要两步寻址
首先检查域名和标识符的正确性;给中间变量开辟一个空间;找到当前访问的是structure中的哪个部分,确定其具体的偏移;Gen([],sem(s-2),x,t)生成当前要访问的域选择变量的地址。[]表示地址生成运算,sem(s-2)代表当前structure的起始地址,x代表偏移,t存储计算地址得到的结果。将用来计算地址的两个变量弹出Gem,然后将得到的地址结果压入Gem。
typedef struct node{
int data1;//占一个空间
float data2;//占2个空间
}Node;
对应的地址:如果层数是l,偏移是Off1
data1——Off1
data2——Off1+1
在取出E的值之前首先要判断一下V是否是数组类型的变量
;计算V[E]的所在地址
[(用E减下界)*单位元数大小]+初始地址
#Addnext中要执行的动作:
1. (-,sem(s-1),sem(s-2).low,t1)
2. (*,t1,si,t2)
3. ([ ],sem(s-2),t2,t3)
4. pop(2); push(t3)
上面的过程详细分析:
看起来第一步好像没有什么用途,但是并不是所有语言的数组下界都是从0开始的,所以还是有意义的。
对应的一般情况
例题:
上面的答案没有涉及到数组下界的那个四元式
首先进行a[][]中下标的计算:
(+,5,i,t1)
(-,t1,0,t2)//和第一维下标相减,使用的是定义的A[i][j]中的i
(*,t2,30,t3)
//这里数组a的类型是rt,而rt中每个元素占3个单位
//第一维a[i]指向的是一个10个元素的序列,所以size/per = 30
([],a,t3,t4)
(-,6,0,t5)和第二维下标相减,使用的是定义的A[i][j]中的j
(*,t5,3,t6)
([],t4,t6,t7)
([],t7,1,t8)//访问域变量标识符,int占1个字节,所以这里的偏移量是1
(float,m,_,t9)//m是整型,与浮点型进行计算,向上转型成float,然后参与计算
(*,t9,z,t10)
(+,t8,t10,t11)
对应生成的语法树如下:
数组的下标的下界看定义的时候从哪里开始,这里定义的时候是从1开始的所以是(-,t1,1,t2)
三、赋值语句的中间代码
left中间代码的生成按照正常前面的标识符变量、指针变量、域选择变量、下标变量等进行计算;right中间代码的生成按照表达式中间代码的生成计算。
(赋值语句的left可能是前面的下标变量,left需要计算;right部分可能是一个表达式,right也需要计算。)
ASSIG代表赋值操作
Assign过程中的一些操作:涉及到栈的操作,类型的比较,最后四元式的生成
最后直接使用(=,t8,-,t9)
将两边得到的结果直接使用一句=进行操作即可。