跟着A Python Interpreter Written in Python写Python 二

我们来编写 10 + 13 + 20
根据之前的指令集我们依葫芦画瓢

what_to_execute = {  
	"instructions": [("LOAD_CONST", 0), # the first number  
					 ("LOAD_CONST", 1), # the second number  
					 ("BINARY_ADD", None),  
					 ("LOAD_CONST", 2),  
					 ("BINARY_ADD", None),  
					 ("RETURN_VALUE", None)],  
	"co_consts": (10, 13, 20)}

代码不变,执行即可

给解释器增加变量的支持

需要指令变量到值得映射关系
STORE_NAME:将栈顶元素弹出存储到变量中
LOAD_NAME:将变量压栈
目前,我们会忽略命名空间和作用域,所以我们可以把变量和值的映射直接存储在解释器对象中
def s():
… a = 1
… b = 2
… print(a + b)

what_to_execute = {
        "instructions": [("LOAD_CONST", 0),
                         ("STORE_NAME", 0),
                         ("LOAD_CONST", 1),
                         ("STORE_NAME", 1),
                         ("LOAD_NAME", 0),
                         ("LOAD_NAME", 1),
                         ("BINARY", None),
                         ("RETURN_VALUE", None)],
        "co_consts": [1, 2],
        "co_varnames":   ("a", "b")

LOAD_NAME将变量压栈,我们肯定不是将真正的变量进行压栈,而是建立变量和值的映射关系,将值进行压栈,为了跟踪哪个变量指向了哪个值,我们在__init__方法增加一个字典

目前我们有两个数据集,指令的参数就有两个不同含义,所以我们就需要判断该参数应该对应哪个数据集和传递给哪个函数。Python解释器通过字节码分为操作码和操作数来辨别。我们这里就把指令和对应参数的映射关系放在一个独立的方法中

class Interpreter:  
	def __init__(self):  
		self.stack = []  
		self.environment = {}  
	  
	def LOAD_CONST(self, number):  
		self.stack.append(number)  
	  
	def RETURN_VALUE(self):  
		answer = self.stack.pop()  
		print(answer)  
	  
	def BINARY_ADD(self):  
		first_number = self.stack.pop()  
		second_number = self.stack.pop()  
		result = first_number + second_number  
		self.stack.append(result)  
	  
	def STORE_NAME(self, name):  
		# 将栈顶元素弹出,存储到变量中
		val = self.stack.pop()  
		# 要建立映射,我们把映射放在一个字典里面
		self.environment[name] = val  
	  
	def LOAD_NAME(self, name):  
		# 将变量进行压栈,但我们其实是将值压栈,所以通过建立的映射拿到值
		val = self.environment[name]  
		# 将拿到的值进行压栈
		self.stack.append(val)  
	  
	def parse_argument(self, instruction, argument, what_to_execute): 
		# 该函数是为了判断指令后面的参数该在哪个数据集中取值  
		co_consts = ["LOAD_CONST"]  
		co_varnames = ["LOAD_NAME", "STORE_NAME"]  
		# 如果该指令在["LOAD_CONST"]中,将指令参数传递给其指令对应的数据集取到值
		if instruction in co_consts:  
		argument = what_to_execute["co_consts"][argument]  
		# 如果该指令在["LOAD_NAME", "STORE_NAME"]中,将指令参数传递给其指令对应数据集取到值
		elif instruction in co_varnames:  
		argument = what_to_execute["co_varnames"][argument]  
		# 将取到的值传递出去,如果遇到不需要参数的指令,指令参数为None,我们直接返回
		return argument

我们增加了STORE_NAME与LOAD_NAME方法,还增加了函数参数该如何选择的方法
现在,让我们来写把所有指令和数据结合在一起并执行的方法。

def run_code(self, par_what_to_execute):  
	instructions = par_what_to_execute["instructions"]  
		for each_step in instructions:  
			instruction, argument = each_step  
			# 调用我们写的给根据指令给出对应参数的函数,传入指令,指令参数,和指令集
			argument = self.parse_argument(instruction, argument, par_what_to_execute)  
			if instruction == "LOAD_CONST":  
				self.LOAD_CONST(argument)  
			elif instruction == "BINARY_ADD":  
				self.BINARY_ADD()  
			elif instruction == "RETURN_VALUE":  
				self.RETURN_VALUE()  
			elif instruction == "STORE_NAME":  
				self.STORE_NAME(argument)  
			elif instruction == "LOAD_NAME":  
				self.LOAD_NAME(argument)

写完发现,才五个指令,run_code方法就开始变得冗长了,后续继续增加指令的话,都需要一个if分支。所以就可以利用动态类型语言的关键——反射机制
将代码改写

def run_code(self, par_what_to_execute):  
instructions = par_what_to_execute["instructions"]  
	for each_step in instructions:  
	instruction, argument = each_step  
	argument = self.parse_argument(instruction, argument, par_what_to_execute)  
	# 动态的获取指令所对应的方法
	bytecode_method = getattr(self, instruction)
		# 根据函数是否有参数来进行参数的传递和调用  
		if argument is None:  
		bytecode_method()  
		else:  
		bytecode_method(argument)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值