PL\0编译原理实验(南航)四:中间代码的解释器

解释器的工作原理

数据结构

mid_code:这个是列表,存放的语法分析生成的中间代码

stack:数据栈,这里用列表开辟8K个空间,其实代码只用到了15个,这个可以大大缩减

B:基址寄存器,

T:栈顶寄存器,指向栈顶的位置

I:指令寄存器,存放的是当前要执行的中间代码

P:存放下一条指令的列表下标,通过mid_code[p]获取指令

解释器的运行

通过 I = mid_code[p] 获取指令,通过指令的操作码,也就是 I['F'] 来执行不同的指令

JMP和JPC指令是把P指向跳转的指令地址,也就是 I['A'] 的值

INT指令是给数据栈开辟空间,需要修改栈顶寄存器T的值

LIT指令是把常数,也就是 I['A'] 的值放到栈顶

OPR指令除了OPR 0,0 其余都是通过栈顶或者次栈顶和栈顶数据的运算,然后把结果放在栈顶

LOD、STO、CAL等指令牵涉到get_sl函数,需要详细讲解一下SL,DL,RA的作用

活动组织记录

每一次过程调用都将在运行栈增加一个过程活动记录,当前活动记录的起始单元由基址寄存器B指出

过程活动记录中的头3个单元是固定的联系信息:

静态链SL:存放的是定义该过程所对应的上一层过程,最近一次运行时的活动记录的起始单元。

动态链DL:存放的是调用该过程前正在运行过程的活动记录的起始单元。过程返回时当前活动记录要被撤销,此时需要动态链信息来修改基址寄存器b的内容。

返回地址RA:记录该过程返回后应该执行的下一条指令地址,即调用该过程的指令执行时指令地址寄存器p的内容加1

每当一个过程被调用,就需要在栈上先分配3个空间用来存储上述信息,然后才是分配空间存储过程的局部变量。对于主过程,SL=DL=RA=0

下面举个例子详细说明,PL\0代码如下

var m, n, g;
procedure gcd(m,n);
	begin
		if n = 0 then
			g := m;
		else
			g := gcd(n, m mod n)
	end
begin
	m := 24;
	n := 16;
	g := gcd(m, n)
end

运行时栈如下图所示

RA的作用

当主函数执行到 g := gcd(m,n) 语句时,中间代码生成的是CAL指令,这时会创建一个新的活动记录,新的活动记录会先开辟三个空间SL、DL、RA。RA记录的是CAL 指令的下一条指令,也就是过程调用返回的指令OPR 0,0在中间代码中的位置P,这样gcd过程调用结束后可以直接获取下一条需要执行的指令

DL的作用

DL记录的是当前过程返回后上一层活动记录的起始单元,当执行CAL指令创建新的活动记录的时候,基址寄存器B就会指向栈顶寄存器+1,也就是新的过程活动记录的SL。但是当前活动结束后需要执行OPR 0,0指令返回到调用该过程的那一层,所以需要恢复调用层的SL在数据栈的位置(也可以说是B寄存器的值,但是不能理解为SL的值,SL的值下面讲),这样返回到上一层的局部变量才可以正确根据基址寄存器+偏移量来访问

SL的作用

DL中描述了过程内部的变量访问是根据基址寄存器B+偏移量I['A']来找到在数据栈中的位置,但如果是主函数那一层定义的变量需要在子过程内部被访问呢?这个时候就是SL起作用

注:在C语言里面定义的全局变量是可以被定义的所有函数直接使用的,这里思想是一样的,外层定义的变量内部的子过程是可以使用的,但是如何在数据栈中找到外层定义的变量呢?

LOD、STO指令的第二个值说的是层差,这里的层差就是说当前过程所在层和定义层的差。举个例子,LOD, 1, 4指令是将某个变量的值放入栈顶,如何寻找这个变量呢,本层的活动记录SL的值记录的就是定义该变量在数据栈所在层的基址,基址+偏移量4就获取该变量在数据栈的位置了。那么SL的值如何求呢,指令中的1表示层差是1,也就是该变量是在本层的上一层定义的,gcd过程使用的m变量就是定义在外层的,所以SL的值就是上一层的活动记录的起始地址,也就是上一层SL在数据栈中的位置

总结

RA是记录当前过程结束后执行的下一条指令的位置

DL是当前过程结束后返回到上一层后恢复当前活动记录的基址寄存器B,因为本层会先占据寄存器B的值,等结束后再恢复回去

SL是为了本层使用了外层定义的变量,需要知道外层的基址,这样才能根据基址+偏移量获取到变量的值

代码实现

# 这里开始进行中间代码解释执行
stack = [0 for i in range(0, 8000)]  # 数据栈 前三个0是主函数的SL DL RA


#  根据当前B的值和level层差获取SL的值
def get_sl(B, level):
    global stack
    res_B = B
    while level > 0:
        res_B = stack[res_B]
        level -= 1
    return res_B


# 解释器
def interpreter():
    # 先定义好需要用到的数据
    global stack
    B = 0  # 基址寄存器
    T = 0  # 栈顶寄存器
    I = None  # 存放要执行的代码
    P = 0  # 存放下一条要执行的代码在mid_code数组的下标
    # 开始执行
    I = mid_code[P]
    P += 1
    while P != 0:  # P为0表示主函数结束 指令回到起点 那么就算执行结束
        if I['F'] == 'JMP':  # 直接跳转到对应指令
            P = I['A']
        elif I['F'] == 'JPC':
            if stack[T] == 0:  # 栈顶值为0才跳转
                P = I['A']
            T -= 1  # 无论是否跳转都要去除栈顶的值
        elif I['F'] == 'INT':
            T += I['A'] - 1  # 开辟空间
        elif I['F'] == 'LOD':
            T += 1
            stack[T] = stack[get_sl(B, I['L']) + I['A']]
        elif I['F'] == 'STO':
            stack[get_sl(B, I['L']) + I['A']] = stack[T]
            T -= 1
        elif I['F'] == 'LIT':
            T += 1
            stack[T] = I['A']
        elif I['F'] == 'CAL':  # 函数调用
            T += 1
            stack[T] = get_sl(B, I['L'])
            stack[T + 1] = B
            stack[T + 2] = P
            B = T
            P = I['A']
        elif I['F'] == 'OPR':
            if I['A'] == 0:  # 函数返回
                T = B - 1
                P = stack[T + 3]
                B = stack[T + 2]
            elif I['A'] == 1:  # 取反操作
                stack[T] = -stack[T]
            elif I['A'] == 2:  # 加法
                T -= 1
                stack[T] = stack[T] + stack[T + 1]
            elif I['A'] == 3:  # 减法
                T -= 1
                stack[T] = stack[T] - stack[T + 1]
            elif I['A'] == 4:  # 乘法
                T -= 1
                stack[T] = stack[T] * stack[T + 1]
            elif I['A'] == 5:  # 除法
                T -= 1
                stack[T] = int(stack[T] / stack[T + 1])
            elif I['A'] == 6:  # odd 奇偶
                stack[T] = stack[T] % 2
            elif I['A'] == 8:  # ==
                T -= 1
                stack[T] = stack[T] == stack[T + 1]
            elif I['A'] == 9:  # !=
                T -= 1
                stack[T] = stack[T] != stack[T + 1]
            elif I['A'] == 10:  # <
                T -= 1
                stack[T] = stack[T] < stack[T + 1]
            elif I['A'] == 11:  # >=
                T -= 1
                stack[T] = stack[T] >= stack[T + 1]
            elif I['A'] == 12:  # >
                T -= 1
                stack[T] = stack[T] > stack[T + 1]
            elif I['A'] == 13:  # <=
                T -= 1
                stack[T] = stack[T] <= stack[T + 1]
        I = mid_code[P]  # 获取下一条指令
        if P == 0:
            break
        P += 1  # 默认P+1获取下一条指令 除非跳转

 

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值