编译自:http://www.aosabook.org/en/500L/a-python-interpreter-written-in-python.html 作者:Taavi Burns 翻译:鸿 如有翻译问题或建议,请公众号留言
变量 接下来,给解释器添加变量。 变量需要一条指令来存储变量的值,STORE_NAME; 一条检索它的指令,LOAD_NAME; 以及变量名称到值的映射。 目前,先忽略命名空间和范围,将变量映射存储在解释器对象本身上。 最后,除了常量列表之外,我们必须确保what_to_execute具有变量名称列表。
>>> def s():
... a = 1
... b = 2
... print(a + b)
# a friendly compiler transforms `s` into:
what_to_execute = {
"instructions": [("LOAD_VALUE", 0),
("STORE_NAME", 0),
("LOAD_VALUE", 1),
("STORE_NAME", 1),
("LOAD_NAME", 0),
("LOAD_NAME", 1),
("ADD_TWO_VALUES", None),
("PRINT_ANSWER", None)],
"numbers": [1, 2],
"names": ["a", "b"] }
现在新实现如下: 为了跟踪哪些名称绑定了什么值,我们将添加一个环境(environment)字典到init方法。 我们还会添加STORE_NAME和LOAD_NAME。 这些方法首先查找变量名称,然后使用字典来存储或检索其值。现在指令中的参数意味着两个不同的东西了:既是“数字”列表的索引,也可以是“名称”列表的索引。 解释器通过检查正在执行的指令知道它应该是哪个索引。 实现这种逻辑(指令和参数的映射关系)。
class Interpreter:
def __init__(self):
self.stack = []
self.environment = {}
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):
""" Understand what the argument to each instruction means."""
numbers = ["LOAD_VALUE"]
names = ["LOAD_NAME", "STORE_NAME"]
if instruction in numbers:
argument = what_to_execute["numbers"][argument]
elif instruction in names:
argument = what_to_execute["names"][argument]
return argument
def run_code(self, what_to_execute):
instructions = what_to_execute["instructions"]
for each_step in instructions:
instruction, argument = each_step
argument = self.parse_argument(instruction, argument, what_to_execute)
if instruction == "LOAD_VALUE":
self.LOAD_VALUE(argument)
elif instruction == "ADD_TWO_VALUES":
self.ADD_TWO_VALUES()
elif instruction == "PRINT_ANSWER":
self.PRINT_ANSWER()
elif instruction == "STORE_NAME":
self.STORE_NAME(argument)
elif instruction == "LOAD_NAME":
self.LOAD_NAME(argument)
虽然只有五条指令,但是run_code方法开始变得单调乏味了。 如果继续保持这种结构,就需要给每条指令一个if语句分支。这时可以利用Python的动态方法查找进行简化。 定义一个名为FOO的方法来执行名为FOO的指令,所以我们可以使用Python的getattr函数来实现方法查找,而不是使用大量的if语句。 run_code方法看起来像这样:
def execute(self, what_to_execute):
instructions = what_to_execute["instructions"]
for each_step in instructions:
instruction, argument = each_step
argument = self.parse_argument(instruction, argument, what_to_execute)
bytecode_method = getattr(self, instruction)
if argument is None:
bytecode_method()
else:
bytecode_method(argument)